Tengo los siguientes documentos en una colección:
[{ id: 1, name: 'My document 1', allItems: [{ id: 'item1', name: 'My item 1' }, { id: 'item2', name: 'My item 2' }, { id: 'item3' name: 'My item 3' }], containers: [{ id: 'container1', selectedItems: [ 'item3', 'item1' ] }, { id: 'container2', selectedItems: [ 'item3' ] }] }, ...] Ahora quiero escribir una consulta para encontrar el documento con un ID de contenedor específico (es decir container1 ) y devolverlo, mientras transformo su matriz de elementos selectedItems para contener los valores reales del objeto de la matriz allItems del mismo documento.
Quiero obtener este documento:
{ id: 1, name: 'My document 1', container: { id: 'container1', selectedItems: [{ id: 'item3' name: 'My item 3' }, { id: 'item1', name: 'My item 1' } } }Actualmente tengo la siguiente agregación (Node.js):
db.collection.aggregate([{ $match: {'containers.id': 'container1'}, $addFields: { container: { $filter: { input: '$containers', as: 'container', cond: { $eq: ['$$container.id', 'container1'] } } } }, $project: {containers: 0}, $unwind: {path: '$container'} // I now have a new property "container" with the correct subdocument from the array // and removed the original array with all containers. // I now need a stage here to populate the container subdocuments in the `selectedItems` array $project: {allItems: 0} // remove the list of all items when not needed anymore in a following stage }]); Sé que hay $lookup , pero como quiero completar los elementos de matriz coincidentes del mismo documento (no una colección diferente), creo que no lo necesito. Incluso al especificar la misma colección en una $lookup , llenaría la matriz con el documento raíz de mi colección y se volvería muy complejo desenredar y hacer coincidir las propiedades necesarias del subdocumento.
Pensé en crear una colección de items propia, pero necesitaría búsquedas más lentas allí y mis datos no necesitan tener estas relaciones.
Espero que mi pregunta sea clara, ¡agradezco cualquier ayuda que pueda obtener!
Estoy haciendo algunas transformaciones adicionales en mis datos reales, como copiar algunas propiedades del documento raíz original y ocultar otras, pero eso no debería importar.
¡Gracias de nuevo!
$match tus condiciones$filter para iterar el bucle de matriz de containers y filtrar por id$arrayElemAt para seleccionar el primer elemento del resultado filtrado anterior$let declarar una variable y almacenar hacer el proceso anterior y almacenar el resultado$filter para iterar el bucle de la matriz allItems y filtrar los elementos por el resultado anterior db.collection.aggregate([ { $match: { "containers.id": "container1" } }, { $project: { name: 1, container: { $let: { vars: { container: { $arrayElemAt: [ { $filter: { input: "$containers", cond: { $eq: ["$$this.id", "container1"] } } }, 0 ] } }, in: { id: "$$container.id", selectedItems: { $filter: { input: "$allItems", cond: { $in: ["$$this.id", "$$container.selectedItems"] } } } } } } } } ])La segunda versión, puede hacer el proceso etapa por etapa,
$match tus condiciones$filter para iterar el bucle de matriz de containers y filtrar por id$arrayElemAt para seleccionar el primer elemento del resultado filtrado anterior$filter para iterar el bucle de la matriz allItems y filtrar elementos por elementos selectedItems$$REMOVE eliminará campos db.collection.aggregate([ { $match: { "containers.id": "container1" } }, { $addFields: { containers: "$$REMOVE", container: { $arrayElemAt: [ { $filter: { input: "$containers", cond: { $eq: ["$$this.id", "container1"] } } }, 0 ] } } }, { $addFields: { allItems: "$$REMOVE", "container.selectedItems": { $filter: { input: "$allItems", cond: { $in: ["$$this.id", "$container.selectedItems"] } } } } } ])Tienes razón al no necesitar usar $lookup , una serie de expresiones $map y $filter es todo lo que necesitas.
El enfoque es simple, comenzamos filtrando el contenedor requerido, luego iteramos sobre los elementos seleccionados del contenedor selectedItems usando $map y hacemos coincidir los campos relevantes para cada elemento del campo allItems .
{ $match: { id: 1, } }, { $project: { name: 1, container: { "$arrayElemAt": [ { $map: { input: { $filter: { input: "$containers", as: "container", cond: { $eq: [ "$$container.id", "container1" ] } }, }, as: "container", in: { "$mergeObjects": [ { id: "$$container.id" }, { selectedItems: { $map: { input: "$$container.selectedItems", as: "item", in: { "$arrayElemAt": [ { $filter: { input: "$allItems", as: "filter", cond: { $eq: [ "$$filter.id", "$$item" ] } } }, 0 ] } } } } ] } } }, 0 ] } } } ])