Búsqueda de texto completo en MongoDB

MongoDB, una de las principales bases de datos NoSQL, es bien conocido por su rápido rendimiento, esquema flexible, escalabilidad y excelentes capacidades de indexación. En el núcleo de este rápido rendimiento se encuentran los índices de MongoDB, que admiten la ejecución eficiente de consultas al evitar los análisis de colección completa y, por lo tanto, limitar el número de documentos que las búsquedas de MongoDB. 

A partir de la versión 2.4, MongoDB comenzó con una función experimental que admite Búsqueda de texto completo utilizando Índices de texto. Esta característica ahora se ha convertido en una parte integral del producto (y ya no es una característica experimental). En este artículo vamos a explorar las funcionalidades de búsqueda de texto completo de MongoDB desde los fundamentos..

Si eres nuevo en MongoDB, te recomiendo que leas los siguientes artículos sobre Envato Tuts + que te ayudarán a entender los conceptos básicos de MongoDB:

  • Comenzando con MongoDB - Parte 1
  • Mapeo de bases de datos relacionales y de SQL a MongoDB 

Los basicos

Antes de entrar en detalles, veamos algunos antecedentes. La búsqueda de texto completo se refiere a la técnica de búsqueda de un base de datos de texto completo contra los criterios de búsqueda especificados por el usuario. Es algo similar a cómo buscamos cualquier contenido en Google (o, de hecho, cualquier otra aplicación de búsqueda) al ingresar ciertas palabras clave / frases de cadena y recuperar los resultados relevantes ordenados por su clasificación.

Aquí hay algunos escenarios más en los que veríamos una búsqueda de texto completo:

  • Considera buscar tu tema favorito en Wiki. Cuando ingresa un texto de búsqueda en Wiki, el motor de búsqueda muestra los resultados de todos los artículos relacionados con las palabras clave / frase que buscó (incluso si esas palabras clave se usaron en el artículo). Estos resultados de búsqueda están ordenados por relevancia en función de su puntuación coincidente.  
  • Como otro ejemplo, considere un sitio de redes sociales donde el usuario puede realizar una búsqueda para encontrar todas las publicaciones que contienen la palabra clave los gatosen ellos; o para ser más complejos, todas las publicaciones que tengan comentarios que contengan la palabra los gatos.  

Antes de continuar, existen ciertos términos generales relacionados con la búsqueda de texto completo que debe conocer. Estos términos son aplicables a cualquier implementación de búsqueda de texto completo (y no específica de MongoDB).

Para las palabras

Las palabras de parada son las palabras irrelevantes que se deben filtrar de un texto. Por ejemplo: a, an, the, is, at, which, etc.

Tallo

Detener es el proceso de reducir las palabras a su raíz. Por ejemplo: palabras como estar de pie, estar de pie, estar de pie, etc. tienen una base común..

Tanteo

Una clasificación relativa para medir cuál de los resultados de búsqueda es más relevante..  

Alternativas a la búsqueda de texto completo en MongoDB

Antes de que MongoDB propusiera el concepto de índices de texto, modelaríamos nuestros datos para respaldar las búsquedas de palabras clave o usaríamos expresiones regulares para implementar dichas funcionalidades de búsqueda. Sin embargo, el uso de cualquiera de estos enfoques tenía sus propias limitaciones:

  • En primer lugar, ninguno de estos enfoques admite funcionalidades como derivación, palabras de parada, clasificación, etc..  
  • El uso de búsquedas de palabras clave requeriría la creación de índices de múltiples claves, que no son suficientes en comparación con el texto completo.
  • El uso de expresiones regulares no es eficiente desde el punto de vista del rendimiento, ya que estas expresiones no utilizan efectivamente los índices.
  • Además de eso, ninguna de estas técnicas se puede usar para realizar búsquedas de frases (como buscar 'películas lanzadas en 2015') o búsquedas ponderadas.  

Aparte de estos enfoques, para aplicaciones más avanzadas y complejas centradas en la búsqueda, existen soluciones alternativas como Elastic Search o SOLR. Pero el uso de cualquiera de estas soluciones aumenta la complejidad arquitectónica de la aplicación, ya que MongoDB ahora tiene que hablar con una base de datos externa adicional.. 

Tenga en cuenta que la búsqueda de texto completo de MongoDB no se propone como un reemplazo completo de las bases de datos de motores de búsqueda como Elastic, SOLR, etc. Sin embargo, se puede usar efectivamente para la mayoría de las aplicaciones que se construyen con MongoDB hoy..

Introducción a la búsqueda de texto de MongoDB

Con la búsqueda de texto completo de MongoDB, puede definir un índice de texto en cualquier campo del documento cuyo valor sea una cadena o una matriz de cadenas. Cuando creamos un índice de texto en un campo, MongoDB tokeniza y deriva el contenido de texto del campo indexado, y configura los índices en consecuencia.  

Para entender mejor las cosas, ahora vamos a sumergirnos en algunas cosas prácticas. Quiero que sigas el tutorial conmigo probando los ejemplos en mongo shell. Primero crearemos algunos datos de muestra que usaremos a lo largo del artículo, y luego continuaremos para discutir conceptos clave..

A los efectos de este artículo, considere una colección mensajes que almacena documentos de la siguiente estructura: 

"subject": "Joe tiene un perro", "content": "Los perros son el mejor amigo del hombre", "likes": 60, "year": 2015, "language": "english"

Insertemos algunos documentos de muestra usando el insertar comando para crear nuestros datos de prueba:

db.messages.insert ("subject": "Joe es dueño de un perro", "content": "Los perros son el mejor amigo del hombre", "likes": 60, "year": 2015, "language": "english" ) db.messages.insert ("subject": "Los perros comen gatos y los perros comen también palomas", "content": "Los gatos no son malvados", "likes": 30, "year": 2015, "language": "english") db.messages.insert ("subject": "Los gatos comen ratas", "content": "Las ratas no cocinan comida", "likes": 55, "year": 2014, "language": "english") db.messages.insert ("subject": "Rats eat Joe", "content": "Joe se comió una rata", "likes": 75, "year": 2014, "language": " Inglés")

Creación de un índice de texto

Se crea un índice de texto muy similar a la forma en que creamos un índice regular, excepto que especifica el texto palabra clave en lugar de especificar un orden ascendente / descendente.

Indexando un solo campo

Crear un índice de texto en el tema campo de nuestro documento utilizando la siguiente consulta:

db.messages.createIndex ("subject": "text")

Para probar este índice de texto recién creado en el tema campo, buscaremos documentos utilizando el $ texto operador. Estaremos buscando todos los documentos que tengan la palabra clave. perros en su tema campo. 

Dado que estamos realizando una búsqueda de texto, también estamos interesados ​​en obtener algunas estadísticas sobre la relevancia de los documentos resultantes. Para ello utilizaremos el $ Meta: "textScore" expresión, que proporciona información sobre el procesamiento de la $ texto operador. También ordenaremos los documentos por su textScore utilizando la ordenar mando. Una mayor textScore Indica una coincidencia más relevante.. 

db.messages.find ($ text: $ search: "dogs", score: $ meta: "toextScore"). sort (score: $ meta: "textScore")

La consulta anterior devuelve los siguientes documentos que contienen la palabra clave perros en su tema campo. 

"_id": ObjectId ("55f4a5d9b592880356441e94"), "subject": "Los perros comen gatos y los perros también comen palomas", "content": "Los gatos no son malvados", "likes": 30, "year": 2015, "language": "english", "score": 1 "_id": ObjectId ("55f4a5d9b592880356441e93"), "subject": "Joe posee un perro", "content": "Los perros son el mejor amigo del hombre", " Me gusta ": 60," año ": 2015," idioma ":" inglés "," puntaje ": 0.6666666666666666

Como puede ver, el primer documento tiene una puntuación de 1 (ya que la palabra clave perro aparece dos veces en su tema) en comparación con el segundo documento con una puntuación de 0,66. La consulta también ha ordenado los documentos devueltos en orden descendente de su puntuación.

Una pregunta que puede surgir en su mente es que si estamos buscando la palabra clave perros, ¿Por qué el motor de búsqueda está tomando la palabra clave perro (sin 's') en consideración? ¿Recuerda nuestra discusión sobre la derivación, donde las palabras clave de búsqueda se reducen a su base? Esta es la razón por la cual la palabra clave perros se reduce a perro.

Indización de campos múltiples (Indización compuesta)

La mayoría de las veces, utilizará la búsqueda de texto en varios campos de un documento. En nuestro ejemplo, habilitaremos la indexación de texto compuesto en la tema y contenido campos. Continúa y ejecuta el siguiente comando en mongo shell:  

db.messages.createIndex ("subject": "text", "content": "text")

¿Funcionó esto? ¡¡No!! La creación de un segundo índice de texto le dará un mensaje de error que indica que ya existe un índice de búsqueda de texto completo. ¿Por que es esto entonces? La respuesta es que los índices de texto vienen con una limitación de solo un índice de texto por colección. Por lo tanto, si desea crear otro índice de texto, deberá eliminar el existente y volver a crear el nuevo.. 

db.messages.dropIndex ("subject_text") db.messages.createIndex ("subject": "text", "content": "text") 

Después de ejecutar las consultas de creación de índice anteriores, intente buscar todos los documentos con palabras clave gato.

db.messages.find ($ text: $ search: "cat", score: $ meta: "textScore"). sort (score: $ meta: "textScore")

La consulta anterior generaría los siguientes documentos:

"_id": ObjectId ("55f4af22b592880356441ea4"), "subject": "Los perros comen gatos y los perros también comen palomas", "content": "Los gatos no son malvados", "likes": 30, "year": 2015, "language": "english", "score": 1.333333333333333335 "_id": ObjectId ("55f4af22b592880356441ea5"), "subject": "Cats eat rats", "content": "Ratas no cocinan comida", "me gusta ": 55," año ": 2014," idioma ":" inglés "," puntaje ": 0.666666666666666666 

Puede ver que la puntuación del primer documento, que contiene la palabra clave gato en ambos tema y contenido campos, es mas alto. 

Indexación de todo el documento (Indexación comodín)

En el último ejemplo, ponemos un índice combinado en el tema y contenido campos. Pero puede haber escenarios en los que desee que cualquier contenido de texto en sus documentos pueda buscarse. 

Por ejemplo, considere almacenar correos electrónicos en documentos de MongoDB. En el caso de los correos electrónicos, todos los campos, incluidos Remitente, Destinatario, Asunto y Cuerpo, deben poder buscarse. En tales situaciones, puede indexar todos los campos de cadena de su documento utilizando el PS especificador de comodín.

La consulta sería algo como esto (asegúrese de que está eliminando el índice existente antes de crear uno nuevo):

db.messages.createIndex ("$ **": "texto")

Esta consulta configuraría automáticamente los índices de texto en cualquier campo de cadena en nuestros documentos. Para probar esto, inserte un nuevo documento con un nuevo campo ubicación en eso:

db.messages.insert ("subject": "Birds can cook", "content": "Las aves no comen ratas", "likes": 12, "year": 2013, ubicación: "Chicago", "language" :"Inglés")

Ahora si intentas buscar texto con palabra clave chicago (consulta a continuación), devolverá el documento que acabamos de insertar.

db.messages.find ($ text: $ search: "chicago", score: $ meta: "textScore"). sort (score: $ meta: "textScore")

Algunas cosas en las que me gustaría centrarme aquí:

  • Observe que no definimos explícitamente un índice en el ubicación campo después de insertar un nuevo documento. Esto se debe a que ya hemos definido un índice de texto en todo el documento utilizando el PS operador.
  • Los índices de comodines pueden ser lentos a veces, especialmente en escenarios donde sus datos son muy grandes. Por este motivo, planifique sus índices de documentos (también conocidos como índices de comodines), ya que puede causar un impacto en el rendimiento.

Búsqueda avanzada

Búsqueda de frases

Puedes buscar frases como “pájaros inteligentes que aman cocinar”utilizando índices de texto. Por defecto, la búsqueda de frase hace un O busque todas las palabras clave especificadas, es decir, buscará documentos que contengan las palabras clave inteligente, pájaro, amor o cocinar.

db.messages.find ($ text: $ search: "pájaros inteligentes que cocinan", score: $ meta: "text Score"). sort (score: $ meta: "text Score ")

Esta consulta generaría los siguientes documentos:

"_id": ObjectId ("55f5289cb592880356441ead"), "subject": "Los pájaros pueden cocinar", "el contenido": "Los pájaros no comen ratas", "me gusta": 12, "año": 2013, "ubicación": "Chicago", "idioma": "inglés", "puntaje": 2 "_id": ObjectId ("55f5289bb592880356441eab"), "sujeto": "Los gatos comen ratas", "contenido": "Las ratas no cocinan "," me gusta ": 55," año ": 2014," idioma ":" inglés "," puntuación ": 0.6666666666666666 

En caso de que desee realizar una búsqueda de frase exacta (lógica Y), puede hacerlo especificando comillas dobles en el texto de búsqueda. 

db.messages.find ($ text: $ search: "\" cook food \ "", score: $ meta: "textScore"). sort (score: $ meta: "textScore ")

Esta consulta resultaría en el siguiente documento, que contiene la frase "cocinar alimentos" juntos:

"_id": ObjectId ("55f5289bb592880356441eab"), "subject": "Los gatos comen ratas", "content": "Las ratas no cocinan comida", "likes": 55, "year": 2014, "language": "Inglés", "puntuación": 0.6666666666666666

Búsqueda de negación

Prefijando una palabra clave de búsqueda con - (signo menos) excluye todos los documentos que contienen el término negado. Por ejemplo, intente buscar cualquier documento que contenga la palabra clave rata pero no contiene aves utilizando la siguiente consulta:

db.messages.find ($ text: $ search: "rat -birds", score: $ meta: "textScore"). sort (score: $ meta: "textScore" )

Mirando detrás de las escenas

Una funcionalidad importante que no revelé hasta ahora es cómo se ve detrás de la escena y cómo se eliminan las palabras clave de búsqueda, cómo se aplica, se niega la palabra, se niegan, etc.. $ explicar al rescate. Puede ejecutar la consulta de explicación pasando cierto como su parámetro, que le dará estadísticas detalladas sobre la ejecución de la consulta.  

db.messages.find ($ text: $ search: "los perros que los gatos no comen comieron ratas \" los perros comen \ "-friends", puntuación: $ meta: "textScore"). sort ( puntuación: $ meta: "textScore"). explicar (verdadero) 

Si nos fijamos en el planificador de consultas objeto devuelto por el comando de explicación, podrá ver cómo MongoDB analizó la cadena de búsqueda dada. Observe que descuidó las palabras de alto como quien, y provino perros a perro

También puede ver los términos que descuidamos de nuestra búsqueda y las frases que usamos en el parsedTextQuery sección.  

"parsedTextQuery": "Terms": ["dog", "cat", "dont", "eat", "eat", "rat", "dog", "eat"], "negatedTerms": ["friend "]," frases ": [" los perros comen "]," frases negadas ": [] 

La consulta de explicación será muy útil ya que realizamos consultas de búsqueda más complejas y queremos analizarlas.

Búsqueda de texto ponderado

Cuando tenemos índices en más de un campo en nuestro documento, la mayoría de las veces un campo será más importante (es decir, más peso) que el otro. Por ejemplo, cuando busca en un blog, el título del blog debe ser de mayor peso, seguido del contenido del blog..

El peso predeterminado para cada campo indexado es 1. Para asignar pesos relativos para los campos indexados, puede incluir el pesos opción mientras usa el createIndex mando.

Entendamos esto con un ejemplo. Si intentas buscar el cocinar palabra clave con nuestros índices actuales, dará lugar a dos documentos, los cuales tienen la misma puntuación.   

db.messages.find ($ text: $ search: "cook", score: $ meta: "textScore"). sort (score: $ meta: "textScore")
"_id": ObjectId ("55f5289cb592880356441ead"), "subject": "Los pájaros pueden cocinar", "el contenido": "Los pájaros no comen ratas", "me gusta": 12, "año": 2013, "ubicación": "Chicago", "idioma": "inglés", "puntuación": 0.666666666666666666 "_id": ObjectId ("55f5289bb592880356441eab"), "subject": "Los gatos comen ratas", "contenido": "Las ratas no cocinan "," me gusta ": 55," año ": 2014," idioma ":" inglés "," puntuación ": 0.6666666666666666 

Ahora vamos a modificar nuestros índices para incluir pesos; con el tema campo que tiene un peso de 3 contra el contenido campo que tiene un peso de 1.

db.messages.createIndex ("$ **": "texto", "pesos": asunto: 3, contenido: 1)

Intenta buscar por palabra clave cocinar ahora, y verá que el documento que contiene esta palabra clave en el tema el campo tiene una puntuación más alta (de 2) que la otra (que tiene 0.66).

"_id": ObjectId ("55f5289cb592880356441ead"), "subject": "Los pájaros pueden cocinar", "el contenido": "Los pájaros no comen ratas", "me gusta": 12, "año": 2013, "ubicación": "Chicago", "idioma": "inglés", "puntaje": 2 "_id": ObjectId ("55f5289bb592880356441eab"), "sujeto": "Los gatos comen ratas", "contenido": "Las ratas no cocinan "," me gusta ": 55," año ": 2014," idioma ":" inglés "," puntuación ": 0.6666666666666666 

Particionando Índices de Texto

A medida que los datos almacenados en su aplicación aumentan, el tamaño de sus índices de texto también sigue creciendo. Con este aumento en el tamaño de los índices de texto, MongoDB tiene que buscar en todas las entradas indexadas cada vez que se realiza una búsqueda de texto.. 

Como técnica para mantener su búsqueda de texto eficiente con índices crecientes, puede limitar el número de entradas de índice escaneadas usando condiciones de igualdad con un $ texto buscar. Un ejemplo muy común de esto sería buscar todas las publicaciones realizadas durante un determinado año / mes, o buscar todas las publicaciones con una determinada categoría / etiqueta.

Si observa los documentos en los que estamos trabajando, tenemos un año Campo en el que aún no hemos utilizado. Un escenario común sería buscar mensajes por año, junto con la búsqueda de texto completo que hemos estado aprendiendo.. 

Para esto, podemos crear un índice compuesto que especifique una clave de índice ascendente / descendente en año seguido de un índice de texto en el tema campo. Al hacer esto, estamos haciendo dos cosas importantes:

  • Estamos particionando lógicamente todos los datos de la colección en conjuntos separados por año.
  • Esto limitaría la búsqueda de texto para escanear solo aquellos documentos que caen dentro de un año específico (o llamarlo set).

Suelte los índices que ya tiene y cree un nuevo índice compuesto en (año, tema):

db.messages.createIndex ("año": 1, "asunto": "texto")

Ahora ejecute la siguiente consulta para buscar todos los mensajes que se crearon en 2015 y contienen el los gatos palabra clave:

db.messages.find (year: 2015, $ text: $ search: "cats", score: $ meta: "textScore"). sort (score: $ meta: "textScore" )

La consulta devolvería solo un documento coincidente como se esperaba. Si tu explique esta consulta y mira el ejecuciónStats, encontraras eso totalDocsExamined para esta consulta fue 1, lo que confirma que nuestro nuevo índice se utilizó correctamente y que MongoDB solo tuvo que escanear un solo documento mientras ignoraba con seguridad todos los demás documentos que no estaban incluidos en 2015.

Índices de texto: Beneficios

¿Qué más pueden hacer los índices de texto??

Hemos recorrido un largo camino en este artículo aprendiendo sobre índices de texto. Hay muchos otros conceptos que puedes experimentar con índices de texto. Pero debido al alcance de este artículo, no podremos discutirlos en detalle hoy. Sin embargo, veamos brevemente cuáles son estas funcionalidades:

  • Los índices de texto brindan soporte en múltiples idiomas, lo que le permite buscar en diferentes idiomas usando el $ idioma operador. MongoDB actualmente soporta alrededor de 15 idiomas, incluyendo francés, alemán, ruso, etc..
  • Los índices de texto se pueden utilizar en las consultas de canalización de agregación. La etapa de coincidencia en una búsqueda agregada puede especificar el uso de una consulta de búsqueda de texto completo.
  • Puede utilizar sus operadores regulares para proyecciones, filtros, límites, ordenaciones, etc., mientras trabaja con índices de texto..

Indización de texto de MongoDB frente a bases de datos de búsqueda externas

Teniendo en cuenta el hecho de que la búsqueda de texto completo de MongoDB no es un reemplazo completo de las bases de datos de motores de búsqueda tradicionales que se utilizan con MongoDB, se recomienda el uso de la funcionalidad nativa de MongoDB por los siguientes motivos:

  • Según una reciente charla en MongoDB, el alcance actual de la búsqueda de texto funciona perfectamente bien para la mayoría de las aplicaciones (alrededor del 80%) que se construyen con MongoDB hoy.
  • La construcción de las capacidades de búsqueda de su aplicación dentro de la misma base de datos de la aplicación reduce la complejidad arquitectónica de la aplicación.
  • La búsqueda de texto de MongoDB funciona en tiempo real, sin retrasos ni actualizaciones por lotes. En el momento en que inserta o actualiza un documento, las entradas del índice de texto se actualizan.
  • La búsqueda de texto se integra en las funcionalidades del kernel db de MongoDB, es totalmente coherente y funciona bien incluso con fragmentación y replicación.
  • Se integra perfectamente con las funciones existentes de Mongo, como filtros, agregación, actualizaciones, etc..    

Índices de texto: inconvenientes

Como la búsqueda de texto completo es una característica relativamente nueva en MongoDB, existen ciertas funcionalidades de las que carece actualmente. Los dividiría en tres categorías. Echemos un vistazo.

Funcionalidades que faltan en la búsqueda de texto

  • Los Índices de texto actualmente no tienen la capacidad de admitir interfaces conectables como detractores conectables, palabras de parada, etc..
  • Actualmente no admiten funciones como la búsqueda basada en sinónimos, palabras similares, etc..
  • No almacenan posiciones de término, es decir, el número de palabras por las que se separan las dos palabras clave.
  • No puede especificar el orden de clasificación de una expresión de clasificación a partir de un índice de texto.

Restricciones en las funcionalidades existentes

  • Un índice de texto compuesto no puede incluir ningún otro tipo de índice, como índices de múltiples claves o índices geoespaciales. Además, si su índice de texto compuesto incluye claves de índice antes de la clave de índice de texto, todas las consultas deben especificar los operadores de igualdad para las claves anteriores.
  • Hay algunas limitaciones específicas de la consulta. Por ejemplo, una consulta puede especificar solo una $ texto expresión, no puedes usar $ texto con $ ni, no puedes usar el insinuación() comando con $ texto, utilizando $ texto con $ o necesita todas las cláusulas en tu $ o Expresión a indexar, etc..

Desventajas de rendimiento

  • Los índices de texto crean una sobrecarga al insertar nuevos documentos. Esto a su vez golpea el rendimiento de inserción.
  • Algunas consultas como búsquedas de frases pueden ser relativamente lentas.

Terminando 

La búsqueda de texto completo siempre ha sido una de las características más demandadas de MongoDB. En este artículo, comenzamos con una introducción de qué es la búsqueda de texto completo, antes de pasar a los conceptos básicos de la creación de índices de texto.. 

Luego exploramos la indexación compuesta, la indexación de comodines, las búsquedas de frases y las búsquedas de negación. Además, exploramos algunos conceptos importantes como el análisis de índices de texto, la búsqueda ponderada y la partición lógica de sus índices. Podemos esperar algunas actualizaciones importantes de esta funcionalidad en los próximos lanzamientos de MongoDB.. 

Te recomiendo que pruebes la búsqueda de texto y compartas tus pensamientos. Si ya lo has implementado en tu aplicación, comparte tu experiencia aquí. Finalmente, siéntase libre de publicar sus preguntas, pensamientos y sugerencias sobre este artículo en la sección de comentarios..