Digamos que consultamos el servidor con esta solicitud, solo queremos obtener el siguiente correo electrónico del usuario. Mi implementación actual solicita todo el objeto Usuario de MongoDB, lo que me imagino es extremadamente ineficiente.
GQL { user(id:"34567345637456") { email } }¿Cómo haría para crear un filtro MongoDB que solo devuelva esos campos especificados? P.ej,
JS object { "email": 1 }Mi servidor actual ejecuta Node.js, Fastify y Mercurius
lo que puedo imaginar es extremadamente ineficiente.
Hacer esta tarea es una característica avanzada con muchas trampas. Sugeriría comenzar a construir una extracción simple que lea todos los campos. Esta solución funciona y no devuelve ningún campo adicional al cliente.
Las trampas son:
Aquí un ejemplo que hace lo que buscas. Gestiona aliasing y múltiples consultas.
const Fastify = require('fastify') const mercurius = require('mercurius') const app = Fastify({ logger: true }) const schema = ` type Query { select: Foo } type Foo { a: String b: String } ` const resolvers = { Query: { select: async (parent, args, context, info) => { const currentQueryName = info.path.key // search the input query AST node const selection = info.operation.selectionSet.selections.find( (selection) => { return ( selection.name.value === currentQueryName || selection.alias.value === currentQueryName ) } ) // grab the fields requested by the user const project = selection.selectionSet.selections.map((selection) => { return selection.name.value }) // do the query using the projection const result = {} project.forEach((fieldName) => { result[fieldName] = fieldName }) return result }, }, } app.register(mercurius, { schema, resolvers, graphiql: true, }) app.listen(3000)Llámalo usando:
query { one: select { a } two: select { a aliasMe:b } }Devoluciones
{ "data": { "one": { "a": "a" }, "two": { "a": "a", "aliasMe": "b" } } }Ampliando la respuesta original de @Manuel Spigolon , donde afirmó que una de las trampas de su implementación es que no funciona en consultas anidadas y 'consultas múltiples en una solicitud' que esta implementación busca solucionar.
function formFilter(context:any) { let filter:any = {}; let getValues = (selection:any, parentObj?:string[]) => { //selection = labelSelection(selection); selection.map((selection:any) => { // Check if the parentObj is defined if(parentObj) // Merge the two objects _.merge(filter, [...parentObj, null].reduceRight((obj, next) => { if(next === null) return ({[selection.name?.value]: 1}); return ({[next]: obj}); }, {})); // Check for a nested selection set if(selection.selectionSet?.selections !== undefined){ // If the selection has a selection set, then we need to recurse if(!parentObj) getValues(selection.selectionSet?.selections, [selection.name.value]); // If the selection is nested else getValues(selection.selectionSet?.selections, [...parentObj, selection.name.value]); } }); } // Start the recursive function getValues(context.operation.selectionSet.selections); return filter; }Aporte
{ role(id: "61f1ccc79623d445bd2f677f") { name users { user_name _id permissions { roles } } permissions } }Salida (JSON.stringify)
{ "role":{ "name":1, "users":{ "user_name":1, "_id":1, "permissions":{ "roles":1 } }, "permissions":1 } }