sql >> Base de Datos >  >> NoSQL >> MongoDB

Cómo encontrar documentos y subdocumentos únicos que coincidan con criterios dados en la colección MongoDB

Lo que está buscando es el $ posicional operador y "proyección". Para un solo campo, debe hacer coincidir el elemento de matriz requerido usando "notación de puntos", para más de un campo use $elemMatch :

db.products.find(
    { "items.date": "31.08.2014" },
    { "shop": 1, "name":1, "items.$": 1 }
)

O el $elemMatch para más de un campo coincidente:

db.products.find(
    { "items":  { 
        "$elemMatch": { "date": "31.08.2014",  "purchasePrice": 1 }
    }},
    { "shop": 1, "name":1, "items.$": 1 }
)

Sin embargo, estos funcionan solo para un elemento de matriz único y solo se devolverá uno. Si desea que se devuelva más de un elemento de matriz de sus condiciones, entonces necesita un manejo más avanzado con el marco de agregación.

db.products.aggregate([
    { "$match": { "items.date": "31.08.2014" } },
    { "$unwind": "$items" },
    { "$match": { "items.date": "31.08.2014" } },
    { "$group": {
        "_id": "$_id",
        "shop": { "$first": "$shop" },
        "name": { "$first": "$name" },
        "items": { "$push": "$items" }
    }}
])

O posiblemente en una forma más corta/rápida desde MongoDB 2.6 donde su conjunto de elementos contiene entradas únicas:

db.products.aggregate([
    { "$match": { "items.date": "31.08.2014" } },
    { "$project": {
        "shop": 1,
        "name": 1,
        "items": {
            "$setDifference": [
                { "$map": {
                    "input": "$items",
                    "as": "el",
                    "in": {
                        "$cond": [
                            { "$eq": [ "$$el.date", "31.08.2014" ] },
                            "$$el",
                            false 
                        ]
                    }
                }},
                [false]
            ]
        }
    }}
])

O posiblemente con $redact , pero un poco artificial:

db.products.aggregate([
    { "$match": { "items.date": "31.08.2014" } },
    { "$redact": {
        "$cond": [
             { "$eq": [ { "$ifNull": [ "$date", "31.08.2014" ] }, "31.08.2014" ] },
             "$$DESCEND",
             "$$PRUNE"
         ]
    }}
])

Más moderno, usaría $filter :

db.products.aggregate([
  { "$match": { "items.date": "31.08.2014" } },
  { "$addFields": {
    "items": {
      "input": "$items",
      "cond": { "$eq": [ "$$this.date", "31.08.2014" ] }
    }
  }}
])

Y con múltiples condiciones, el $elemMatch y $and dentro del $filter :

db.products.aggregate([
  { "$match": { 
    "$elemMatch": { "date": "31.08.2014",  "purchasePrice": 1 }
  }},
  { "$addFields": {
    "items": {
      "input": "$items",
      "cond": { 
        "$and": [
          { "$eq": [ "$$this.date", "31.08.2014" ] },
          { "$eq": [ "$$this.purchasePrice", 1 ] }
        ]
      }
    }
  }}
])

Por lo tanto, solo depende de si siempre espera que coincida un solo elemento o varios elementos, y luego qué enfoque es mejor. Pero donde sea posible el .find() El método generalmente será más rápido ya que carece de la sobrecarga de las otras operaciones, que en los últimos formularios no se retrasan tanto en absoluto.

Como nota al margen, sus "fechas" se representan como cadenas, lo que no es una muy buena idea en el futuro. Considere cambiarlos a Date adecuados. tipos de objetos, que te serán de gran ayuda en el futuro.