Saltar al contenido

Consulta de Mongoose para filtrar un array y rellenar contenido relacionado

Sé libre de compartir nuestra página y códigos en tus redes sociales, danos de tu ayuda para ampliar esta comunidad.

Solución:

Debe “proyectar” la coincidencia aquí, ya que todo lo que hace la consulta de MongoDB es buscar un “documento” que tenga “al menos un elemento” es decir “mas grande que” la condición que solicitó.

Así que filtrando un “array”no es lo mismo que la condición de” consulta “que tiene.

Una simple “proyección” devolverá el “primer” elemento coincidente a esa condición. Así que probablemente no sea lo que quieres, pero como ejemplo:

Order.find( "articles.quantity":  "$gte": 5  )
    .select( "articles.$": 1 )
    .populate(
        "path": "articles.article",
        "match":  "price":  "$lte": 500  
    ).exec(function(err,orders) 
       // populated and filtered twice
    
)

Ese “tipo de” hace lo que quieres, pero el problema realmente va a ser que solo volverá como mucho. uno elemento dentro del "articles" array.

Para hacer esto correctamente necesitas .aggregate() para filtrar el array contenido. Idealmente, esto se hace con MongoDB 3.2 y $filter. Pero también hay una forma especial de .populate() aquí:

Order.aggregate(
    [
         "$match":  "artciles.quantity":  "$gte": 5   ,
         "$project": 
            "orderdate": 1,
            "articles": 
                "$filter": 
                    "input": "$articles",
                    "as": "article",
                    "cond": 
                       "$gte": [ "$$article.quantity", 5 ]
                    
                
            ,
            "__v": 1
        
    ],
    function(err,orders) 
        Order.populate(
            orders.map(function(order)  return new Order(order) ),
            
                "path": "articles.article",
                "match":  "price":  "$lte": 500  
            ,
            function(err,orders) 
                // now it's all populated and mongoose documents
            
        )
    
)

Entonces, lo que sucede aquí es el “filtrado” real de la array sucede dentro del .aggregate() declaración, pero por supuesto el resultado de esto ya no es un “documento mangosta” porque un aspecto de .aggregate() es que puede “alterar” la estructura del documento, y por esta razón mangosta “presume” que es el caso y simplemente devuelve un “objeto simple”.

Eso no es realmente un problema, ya que cuando ves el $project etapa, en realidad estamos solicitando todos los mismos campos presentes en el documento de acuerdo con el esquema definido. Entonces, aunque es solo un “objeto simple”, no hay problema para “convertirlo” en un documento de mangosta.

Aquí es donde el .map() entra, ya que devuelve un array de “documentos” convertidos, que luego es importante para la siguiente etapa.

Ahora tu llamas Model.populate() que luego puede ejecutar la “población” adicional en el “array de documentos de mangosta “.

Entonces, el resultado es finalmente lo que desea.


MongoDB versiones anteriores a 3.2.x

Las únicas cosas que realmente cambian aquí son la tubería de agregación, así que eso es todo lo que debe incluirse por brevedad.

MongoDB 2.6 – Puede filtrar matrices con una combinación de $map y $setDifference. El resultado es un “conjunto”, pero eso no es un problema cuando la mangosta crea un _id campo en todas las matrices de subdocumentos de forma predeterminada:

    [
         "$match":  "artciles.quantity":  "$gte": 5   ,
         "$project": 
            "orderdate": 1,
            "articles": 
                "$setDiffernce": [
                    "$map": 
                      "input": "$articles",
                      "as": "article",
                      "in": 
                         "$cond": [
                              "$gte": [ "$$article.price", 5 ] ,
                             "$$article",
                             false
                         ]
                      
                   ,
                   [false]
                ]
            ,
            "__v": 1
        
    ],

Las revisiones más antiguas de las que deben usarse $unwind:

    [
         "$match":  "artciles.quantity":  "$gte": 5  ,
         "$unwind": "$articles" ,
         "$match":  "artciles.quantity":  "$gte": 5  ,
         "$group": 
          "_id": "$_id",
          "orderdate":  "$first": "$orderdate" ,
          "articles":  "$push": "$articles" ,
          "__v":  "$first": "$__v" 
        
    ],

La alternativa de $ lookup

Otra alternativa es hacer todo en el “servidor”. Esta es una opción con $lookup de MongoDB 3.2 y superior:

Order.aggregate(
    [
         "$match":  "artciles.quantity":  "$gte": 5  ,
         "$project": 
            "orderdate": 1,
            "articles": 
                "$filter": 
                    "input": "$articles",
                    "as": "article",
                    "cond": 
                       "$gte": [ "$$article.quantity", 5 ]
                    
                
            ,
            "__v": 1
        ,
         "$unwind": "$articles" ,
         "$lookup": 
            "from": "articles",
            "localField": "articles.article",
            "foreignField": "_id",
            "as": "articles.article"
        ,
         "$unwind": "$articles.article" ,
         "$group": 
          "_id": "$_id",
          "orderdate":  "$first": "$orderdate" ,
          "articles":  "$push": "$articles" ,
          "__v":  "$first": "$__v" 
        ,
         "$project": 
            "orderdate": 1,
            "articles": 
                "$filter": 
                    "input": "$articles",
                    "as": "article",
                    "cond": 
                       "$lte": [ "$$article.article.price", 500 ]
                    
                
            ,
            "__v": 1
        
    ],
    function(err,orders) 

    
)

Y aunque esos son documentos sencillos, son los mismos resultados que los que obtendría del .populate() Acercarse. Y, por supuesto, siempre puede ir y “emitir” documentos de mangosta en todos los casos de nuevo si realmente debe hacerlo.

El camino “más corto”

Esto realmente se remonta a la declaración original en la que básicamente “acepta” que la “consulta” no está destinada a “filtrar” el array contenido. los .populate() felizmente puede hacerlo porque es sólo otra “consulta” y está rellenando “documentos” por conveniencia.

Por lo tanto, si realmente no está ahorrando “grandes cantidades” de ancho de banda mediante la eliminación de array miembros en el documento original array, entonces solo .filter() sacarlos en el código de posprocesamiento:

Order.find( "articles.quantity":  "$gte": 5  )
    .populate(
        "path": "articles.article",
        "match":  "price":  "$lte": 500  
    ).exec(function(err,orders) 
        orders = orders.filter(function(order) 
            order.articles = order.articles.filter(function(article) 
                return (
                    ( article.quantity >= 5 ) &&
                    ( article.article != null )
                )
            );
            return order.aricles.length > 0;
        )

        // orders has non matching entries removed            
    
)

Sección de Reseñas y Valoraciones

Al final de todo puedes encontrar las notas de otros creadores, tú además tienes la libertad de dejar el tuyo si te gusta.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *