Para los que están usando Mongo Compass y no pueden crear los índices:
En las últimas versiones de Compass, hasta abajo en una franja aparece una sección ">_MongoSH". incluso a mí al día de hoy me aparece en beta. (Si no les aparece actualicen a la última versión de Compass. Yo tengo la versión 1.22.1)
.
Den clic en ella y accederán al Shell de Mongo. Ahí pueden correr los siguientes comandos.
.
show dbs // Lista todas sus bases de datos.use <nombre de su base de datos>// use graphql-api_db por ejemplo.show collections // Lista las colecciones en esa base de datos.db.courses.createIndex({"$**":"text"})// Pueden cambiar el "courses" en caso de que sus colecciones tengan diferente nombre.db.students.createIndex({"$**":"text"})// Lo mismo para "students" en caso de nombre distinto.
Eso debería dejar listos los índices para poder trabajar con ellos y correr el query.
Unions permite hacer lo mismo que interfaces pero de una manera más extrema.
Agregamos una union a nuestro schema:
unionGlobalSearch=Course|Student|MonitortypeQuery{"Devuelve todos los cursos"getCourses:[Course]"Devuelve un curso"getCourse(id:ID!):Course"Devuelve todos los estudiantes"getPeople:[Person]"Devuelve un estudiante"getPerson(id:ID!):Person"Ejecuta una búsqueda global"searchItems(keyword:String!):[GlobalSearch]}
searchItems:async(root,{ keyword })=>{let db
let items
let courses
let people
try{ db =awaitconnectDb() courses =await db.collection('courses').find({$text:{$search: keyword }}).toArray() people =await db.collection('students').find({$text:{$search: keyword }}).toArray() items =[...courses,...people]}catch(error){errorHandler(error);}return items
}
Antes de continuar, debemos crear índices en MongoDB:
Y como el keyword que (al menos yo escogí) es 'programming', regresa un resultado:
{"data":{"searchItems":[{"__typename":"Course","title":"New cool title","description":"My description","topic":"Programming"},{"__typename":"Course","title":"My title 3","description":"My description 3","topic":"Programming"}]}}
Al hacer el query si busco "Titulo" obtengo resultados, pero no si busco "T", al parecer el indice "text" solo busca palabras iguales, pero ¿Cómo se pueden buscar que contenga la cualquier cadena?
Hola, pudiste solucionar esa duda, yo tambien estoy batallando con eso, al parecer mongo no permite hacer consultas de ese tipo con indices, creo que toca haria hacer otro tipo de consulta, alguna sugerencia?
Qué tan óptimo es realizar índices de este tipo en mongo?
lo pregunto porque cuando se hace la búsqueda, esta revisa en cada uno de los campos
Entrar a la colleccion en la que se va a crear el index
Ir al tab de search indexes
Click en CREATE INDEX el default es el wildcard field search pero hay muchas opciones en los DOCS
Default:
{"mappings":{"dynamic":true}}
Click en Create Index dentro de este menu.
Utilizar el query para el text search usando aggergate con el nombre del index en el field index.
const courses =await db
.collection('courses').aggregate([{$search:{index:'default',text:{query: keyword,path:{wildcard:'*'},},},},]).toArray();
Asi logramos crear el query de esta lección usando MongoDB Atlas sin conectarnos desde el shell y creando el index del curso.
Excelente.
{searchItems(keyword:"ejemplo"){ __typename
... on Course{ title
description
}... on Monitor{ name
phone
}... on Student{ name
email
}}}
{"data":{"searchItems":[{"__typename":"Course","title":"Curso de ejemplo numero 1","description":"una descripcion 1"}]}}
¿Se puede hacer un schema por cada entidad? es que tener todas las entidades en un solo esquema se me hace que es un poco desordenado
Sí, puedes crear schemas para cada entidad. Personalmente yo no lo escribo en archivo .js mis archivos de GraphQL, yo lo ahgo con la extensión de .graphql y luego hago merge de los schemas usando graphql-tools :D y lo mismo para los resolvers
Así el código queda modularizado y más escalable a mi parecer
Hola, alguien sabe por qué me regresa este error?
{MongoError: text index required for $text query
at MessageStream.messageHandler(/Users/andresnava/Documents/Programacion/graphQL/platzi-dir/node_modules/mongodb/lib/cmap/connection.js:261:20) at emitOne(events.js:116:13) at MessageStream.emit(events.js:211:7) at processIncomingData(/Users/andresnava/Documents/Programacion/graphQL/platzi-dir/node_modules/mongodb/lib/cmap/message_stream.js:144:12) at MessageStream._write(/Users/andresnava/Documents/Programacion/graphQL/platzi-dir/node_modules/mongodb/lib/cmap/message_stream.js:42:5) at doWrite(_stream_writable.js:396:12) at writeOrBuffer(_stream_writable.js:382:5) at MessageStream.Writable.write(_stream_writable.js:290:11) at TLSSocket.ondata(_stream_readable.js:639:20) at emitOne(events.js:116:13)operationTime:Timestamp{_bsontype:'Timestamp',low_:9,high_:1582842781},ok:0,errmsg:'text index required for $text query',code:27,codeName:'IndexNotFound','$clusterTime':{clusterTime:Timestamp{_bsontype:'Timestamp',low_:9,high_:1582842781},signature:{hash:[Object],keyId:[Object]}},name:'MongoError',[Symbol(mongoErrorContextSymbol)]:{}}{MongoError: text index required for $text query
at MessageStream.messageHandler(/Users/andresnava/Documents/Programacion/graphQL/platzi-dir/node_modules/mongodb/lib/cmap/connection.js:261:20) at emitOne(events.js:116:13) at MessageStream.emit(events.js:211:7) at processIncomingData(/Users/andresnava/Documents/Programacion/graphQL/platzi-dir/node_modules/mongodb/lib/cmap/message_stream.js:144:12) at MessageStream._write(/Users/andresnava/Documents/Programacion/graphQL/platzi-dir/node_modules/mongodb/lib/cmap/message_stream.js:42:Programacion/graphQL/platzi-dir
▶ node index.jsServer is listening at http://localhost:3000/api
{MongoError: text index required for $text query
at MessageStream.messageHandler(/Users/andresnava/Documents/Programacion/graphQL/platzi-dir/node_modules/mongodb/lib/cmap/connection.js:261:20) at emitOne(events.js:116:13) at MessageStream.emit(events.js:211:7) at processIncomingData(/Users/andresnava/Documents/Programacion/g
raphQL/platzi-dir/node_modules/mongodb/lib/cmap/message_stream.js:144:12) at MessageStream._write(/Users/andresnava/Documents/Programacion/graphQL/platzi-dir/node_modules/mongodb/lib/cmap/message_stream.js:42:5) at doWrite(_stream_writable.js:396:12) at writeOrBuffer(_stream_writable.js:382:5) at MessageStream.Writable.write(_stream_writable.js:290:11) at TLSSocket.ondata(_stream_readable.js:639:20) at emitOne(events.js:116:13)operationTime:Timestamp{_bsontype:'Timestamp',low_:1,high_:1582842800},ok:0,errmsg:'text index required for $text query',code:27,codeName:'IndexNotFound','$clusterTime':{clusterTime:Timestamp{_bsontype:'Timestamp',low_:2,high_:1582842800},signature:{hash:[Object],keyId:[Object]}},name:'MongoError',[Symbol(mongoErrorContextSymbol)]:{}}{MongoError: text index required for $text query
at MessageStream.messageHandler(/Users/andresnava/Documents/Programacion/graphQL/platzi-dir/node_modules/mongodb/lib/cmap/connection.js:261:20) at emitOne(events.js:116:13) at MessageStream.emit(events.js:211:7) at processIncomingData(/Users/andresnava/Documents/Programacion/graphQL/platzi-dir/node_modules/mongodb/lib/cmap/message_stream.js:144:12) at MessageStream._write(/Users/andresnava/Documents/Programacion/graphQL/platzi-dir/node_modules/mongodb/lib/cmap/message_stream.js:42:5) at doWrite(_stream_writable.js:396:12) at writeOrBuffer(_stream_writable.js:382:5) at MessageStream.Writable.write(_stream_writable.js:290:11) at TLSSocket.ondata(_stream_readable.js:639:20) at emitOne(events.js:116:13)operationTime:Timestamp{_bsontype:'Timestamp',low_:1,high_:1582842800},ok:0,errmsg:'text index required for $text query',code:27,codeName:'IndexNotFound','$clusterTime':{clusterTime:Timestamp{_bsontype:'Timestamp',low_:2,high_:1582842800},signature:{hash:[Object],keyId:[Object]}},name:'MongoError',[Symbol(mongoErrorContextSymbol)]:{}}
searchItems:async(root,{ keyword })=>{let db;let items;let courses;let people;try{ db =awaitconnectDb(); courses =await db.collection('courses').find({$text:{$search: keyword}}).toArray(); people =await db.collection('students').find({$text:{$search: keyword}}).toArray(); items =[...courses,...people];}catch(error){errorHandler(error);}return items;}
yo tengo este mismo error lo lograste resolver?
Ya lo logre resolver estaba usando , solo mongo Atlas pero no encontre como crear un indice de type text , tuve que instalar Robo 3 T y crearlo mediante el comando que usa el profesor
Alguno sabe cómo solucionar el tema de consultas que no sea obligatio el match de la palabra completa, me explico, si quiero buscar "carro". a medida que escriba "ca" pueda ir consultando y me traiga esa info.
Gracias.
Acabo de hacer la tarea. Realmente el artículo de referencia es este (ya que es un tema propio de mongodb, no de graphql), donde:
db.<nombre_coleccion>.find({"<nombre_campo>":/<valor_campo>/})// LIKE '%<valor_campo>%'.db.<nombre_coleccion>.find({"<nombre_campo>":/^<valor_campo>/})// LIKE '<valor_campo>%'.db.<nombre_coleccion>.find({"<nombre_campo>":/<valor_campo>$/})// LIKE '%<valor_campo>'.
Recuerda que todo lo que desees hacer debes construirlo en el resolver. Pero qué pasa con la búsqueda por índices, se debe escribir la palabra completa para que funcione, no una parte. Si escribes "ejemplo" te trae resultados, pero si trae "ejem" no traerá nada. Esto se debe a que las expresiones regulares (++$regex++) sólo se pueden hacer por medio de campos y no de índices.
Para que funcione con una parte de la palabra en la búsqueda, tendrías que en el resolver mapear cada campo, por ejemplo:
Alguien me puede ayudar o indicar como hacer la indexacion con mongodb compass ?
En la parte inferior de Compass tienes una pestaña con la Shell de compass, la abres y puedes ejecutar el mismo comando que hizo el profesor en la clase.
De esta manera podriamos buscar de manera mas dinamica, mediante busqueda regex, sin embargo hasta donde he encontrado si quiero que busque en todos los campos con una sola query find, no lo permite, debemos ingresar cada field
const courses =await db
.collection("courses").find({title:{$regex:`${keyword}.*`,},})
En este caso solo me gustaria que buscara basado en description o title, tambien vendria bien crear otro field, que contenga toda la info base de busqueda y aplicamos solo un regex sobre este field
const courses =await db
.collection("courses").find({$or:[{title:{$regex:`${keyword}.*`,},},{description:{$regex:`${keyword}.*`,},},],}).toArray();
Estaba revisando lo que mencionas y sólo escribiendo así funciona, sin necesidad de poner keyword entre corchetes...
No tiene sentido crear un field que contenga toda la info de la base de búsqueda (por eso se crearon los índices). Pero, si se puede construir otro query en GraphQL para que lo haga sólo para campos específicos, ya el resto es cómo construyas la consulta de MongoDB en el código.
// Busca en todos los campos.courses =await db.collection('courses').find({$text:{$search: keyword }}).toArray()// Buscar sólo en el campo 'title'.courses =await db.collection('courses').find({title:{$regex: keyword }}).toArray()// Busca sólo en el campo 'title' o 'description'.courses =await db.collection('courses').find({$or:[{title:{$regex: keyword }},{description:{$regex: keyword }}]}).toArray()
No me quedo claro una cosa en la creación de indices. El indice se crea para todos los campos tipo String de la colección o para un solo campo ?
Para todos los campos. Si buscas un texto en el campo de title, topic, description, name, etc traerá el resultado si coincide en la búsqueda (por eso añadió números al name de Student y Monitor, para verificar esto).
:)
Para los que usamos Ubuntu, los index los trate de crear por Atlas, pero no funcionaron en un principio. Los podemos crear por la linea de comando de mongo shell, con la misma instrucción tal como los explica el profe.