diff --git a/.obsidian/workspace.json b/.obsidian/workspace.json index 0b18e8c..2d42481 100644 --- a/.obsidian/workspace.json +++ b/.obsidian/workspace.json @@ -44,7 +44,7 @@ "autoReveal": false }, "icon": "lucide-folder-closed", - "title": "Files" + "title": "Explorador de archivos" } }, { @@ -78,8 +78,7 @@ } ], "direction": "horizontal", - "width": 300, - "collapsed": true + "width": 300 }, "right": { "id": "3ad564d399e497ab", @@ -172,32 +171,31 @@ }, "left-ribbon": { "hiddenItems": { - "bases:Create new base": false, - "switcher:Open quick switcher": false, - "graph:Open graph view": false, - "canvas:Create new canvas": false, - "daily-notes:Open today's daily note": false, - "templates:Insert template": false, - "command-palette:Open command palette": false, + "switcher:Abrir selector rápido": false, + "graph:Abrir vista gráfica": false, + "canvas:Crear nuevo lienzo": false, + "daily-notes:Abrir la nota de hoy": false, + "templates:Insertar plantilla": false, + "command-palette:Abrir paleta de comandos": false, + "bases:Crear nueva base": false, "table-editor-obsidian:Advanced Tables Toolbar": false, "obsidian-git:Open Git source control": false } }, "active": "f76c0dfd568461ec", "lastOpenFiles": [ + "CUARTO/PHAE/Teoria_2526.md", + "CUARTO/AII/Teoria_2526.md", "Pasted image 20260224113246.png", "Pasted image 20260224112101.png", "Pasted image 20260224110141.png", "Pasted image 20260217115003.png", - "CUARTO/PHAE/Teoria_2526.md", "Pasted image 20260210134109.png", "Pasted image 20260210131013.png", "Pasted image 20260210130956.png", "Pasted image 20260210125017.png", - "CUARTO/AII/Teoria_2526.md", "Pasted image 20260205135941.png", "Pasted image 20260205135914.png", - "Pasted image 20260129132034.png", "CUARTO/AII/Presentacion.md", "CUARTO/PHAE", "CUARTO/SSII/Teoria_2526.md", diff --git a/CUARTO/AII/Teoria_2526.md b/CUARTO/AII/Teoria_2526.md index 52045f0..76ba44d 100644 --- a/CUARTO/AII/Teoria_2526.md +++ b/CUARTO/AII/Teoria_2526.md @@ -169,4 +169,163 @@ Hacemos lo mismo con las queries, representarlas como vectores en el espacio. #### Distancia Euclídea No es buena idea puesto que si dos vectores se superponen (porque son el mismo documento) pero tienen distinto módulo va a salir que hay distancia de un documento consigo mismo. #### Mejor: usar el ángulo -Para usar el ángulo tenemos que normalizar los vectores volviéndolos unitarios y entonces podemos comparar la proximidad mediante el ángulo entre ellos. \ No newline at end of file +Para usar el ángulo tenemos que normalizar los vectores volviéndolos unitarios y entonces podemos comparar la proximidad mediante el ángulo entre ellos. +# Whoosh +## 1. Introducción +Whoosh sólo funciona con caracteres unicode. El workflow es el mismo siempre: +1. Crear un schema con los tipos de los datos que tendrá el índice + - Con `stored=True` en los tipos, se indica los campos que se almacenarán. Solamente los campos que estén almacenados se mostrarán en `print` o similares. +1. Crear el indexador +2. Crear el writer +3. Añadir documentos al writer (y commit) +### Ejemplo de búsqueda +```python +with ix.searcher() as searcher: + query = QueryParser("content", ix.schema).parse("hola") # contenido a buscar + results = searcher.search(query) + results[0] +``` +## 2. Diseño del schema +### Tipos +- **`TEXT`**: por defecto almacena la posición de cada término indexado por si hay que buscar por frases. Si no hace falta buscar por frases, mejor pasarle `phrase=False`. +- **`KEYWORD`**: para keywords separadas por `,` se usa `KEYWORD`. Se puede especificar si está separado por comas con `commas=True` (por defecto está separado por `' '`). También se puede usar `lowercase=True` para convertirlo a minúsculas automáticamente. Por último, `scorable=True` es por si el campo se usará para buscar. +- **`ID`**: se indexan sin dividirse en términos. No vale para el ranking. Se usa para la url, el path, la fecha, etc. Por defecto tienen `stored=False`. +- **`STORED`**: se almacena pero ni se indexa ni se puede buscar en él. Se suele usar para información que se mostrará solamente. +- **`NUMERIC`**: para números +- **`DATETIME`**: para fechas +- **`BOOLEAN`**: para booleanos. Permite buscar por `'yes'`, `'no'`, `'true'`, `'false'`, `1`, `0`, `'t'` o `'f'`. +### Creando un schema +```python +schema = Schema(from_addr=ID(stored=True), + to_addr=ID(stored=True), + subject=TEXT(stored=True), + body=TEXT(analyzer=StemmingAnalyzer()), + tags=KEYWORD) +``` +### Modificando el schema después de indexar +```python +writer = ix.writer() +writer.add_field("fieldname", fields.TEXT(stored=True)) +writer.remove_field("content") +writer.commit() +``` +Con `optimize=True` en el commit, se hace el borrado físico de los datos eliminados con `.remove_field()`. +### Campos dinámicos +Son campos cuyos valores se capturarán mediante regex. Se crean así: +```python +schema.add("*_d", fields.DATETIME(stored=True), glob=True) +``` +### Dando pesos a los campos para el ranking +Con el atributo `field_boost` podemos darle más valor a un campo que a otro para la búsqueda rankeada. +## 3. Indexando documentos +Para crear un índice usamos: +```python +ix = index.create_in("indexdir", schema) +``` +Y para buscar en uno ya creado: +```python +ix = index.open_dir("indexdir") +``` +Para añadir documentos al índice ya creado, se crea un writer y se añaden documentos. +```python +writer.add_document(title=u"Title to be indexed", _stored_title=u"Stored title") +``` +En realidad, un índice creado por `filedb` es un contenedor de varios subíndices llamados segmentos. Para borrar documentos se puede borrar de varias formas: + ```python +delete_by_term(fieldname, termtext) +delete_by_query(query) + ``` + Para actualizar documentos es obligatorio que haya un campo `unique=True`, **aunque esto no hace que no se pueda repetir (no tiene que ver con las KEY de BBDD relacionales)**. La función para actualizar: + ```python + writer.update_document(path=u"/a", content="Replacement for the first document") + ``` +## 4. Buscando en el índice +Se usa un objeto Searcher para buscar: +```python +qp = QueryParser("content", schema=myindex.schema) +q = qp.parse(u"hello world") + +with myindex.searcher() as s: + results = s.search(q) +``` +Se le puede poner `limit` a la query para que de mas de los 10 resultados rankeados por defecto que devuelve. Con `limit=None` devuelve todos. +### Objeto Results +Es una lista de dicts. Tiene varias funciones para ver el número de resultados sin rankear o rankeados. +```python +found = results.scored_length() +if results.has_exact_length(): + print("Scored", found, "of exactly", len(results), "documents") +else: + low = results.estimated_min_length() + high = results.estimated_length() + + print("Scored", found, "of between", low, "and", high, "documents") +``` +### Algoritmos de ranking +```python +with myindex.searcher(weighting=scoring.TF_IDF()) as s: + ... +``` +### Filtrando +```python +with myindex.searcher() as s: + qp = qparser.QueryParser("content", myindex.schema) + user_q = qp.parse(query_string) + + # Only show documents in the "rendering" chapter + allow_q = query.Term("chapter", "rendering") + # Don't show any documents where the "tag" field contains "todo" + restrict_q = query.Term("tag", "todo") + + results = s.search(user_q, filter=allow_q, mask=restrict_q) +``` +## 5. Parseando queries +- En las queries se pueden usar operadores como `AND` o `OR`. Por defecto es `AND`, pero si queremos que sean `OR` sin ponerlos, se puede hacer así: +```python +parser = qparser.QueryParser(fieldname, schema=myindex.schema, + group=qparser.OrGroup) +``` +- Para buscar en varios campos: +```python +mparser = MultifieldParser(["title", "content"], schema=myschema) +``` +- Para buscar frases se debe poner la query entre `""`. +- También se puede usar `~X` para ver si una palabra está `X` palabras después (`whoosh library~5`). +- Si no se pone `campo:valor` buscará siempre en el campo por defecto. +- Para términos inexactos se usan patrones que usan `?` para un sólo caracter y `*` para varios (**se aplican solo a palabras no a frases**). +- Para rangos, se usa `TO`. Se pueden usar también para fechas (formato por defecto: YYYYMMDD). + ``` + date:[20050101 TO 20090715] + [0000 TO 0025} + {prefix TO suffix} + [0025 TO] + {TO suffix} + ``` + - Se puede especificar el peso de la palabra independientemente del campo en el que aparezca con `^X` donde `X` es el peso. +## 6. Otros formatos de fecha +Con el `DateParserPlugin` (que se añade al QueryParser con `.add_plugin(DateParserPlugin())`) se puede buscar por varias cosas: +``` +20050912 +2005 sept 12th +june 23 1978 +23 mar 2005 +july 1985 +sep 12 +today +yesterday +tomorrow +now +next friday +last tuesday +5am +10:25:54 +23:12 +8 PM +4:46 am oct 31 2010 +last tuesday to today +today to next friday +jan 2005 to feb 2008 +-1 week to now +now to +2h +-1y6mo to +2 yrs 23d +```