sql >> Databasteknik >  >> NoSQL >> MongoDB

Hur man hittar dokument och enstaka underdokument som matchar givna kriterier i MongoDB-samlingen

Det du letar efter är den positionella $ operatör och "projektion". För ett enstaka fält måste du matcha det obligatoriska arrayelementet med "dot notation", för mer än ett fält använd $elemMatch :

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

Eller $elemMatch för mer än ett matchande fält:

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

Dessa fungerar dock bara för ett enstaka arrayelement och endast ett kommer att returneras. Om du vill att mer än ett arrayelement ska returneras från dina villkor behöver du mer avancerad hantering med aggregeringsramverket.

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" }
    }}
])

Eller möjligen i kortare/snabbare form sedan MongoDB 2.6 där ditt utbud av föremål innehåller unika poster:

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]
            ]
        }
    }}
])

Eller möjligen med $redact , men lite konstruerat:

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

Modernare skulle du använda $filter :

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

Och med flera villkor, $elemMatch och $and inom $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 ] }
        ]
      }
    }
  }}
])

Så det beror bara på om du alltid förväntar dig att ett enskilt element ska matcha eller flera element, och sedan vilket tillvägagångssätt som är bättre. Men där det är möjligt .find() Metoden kommer i allmänhet att vara snabbare eftersom den saknar overhead för de andra operationerna, som i de sista till formerna inte släpar så långt efter alls.

Som en sidoanteckning representeras dina "datum" som strängar, vilket inte är en särskilt bra idé framöver. Överväg att ändra dessa till korrekt Datum objekttyper, vilket kommer att hjälpa dig mycket i framtiden.



  1. Varför kräver en mongodb-replikuppsättning ett udda antal röstningsnoder?

  2. Java:Hur infogar man en hashmap i MongoDB?

  3. MongoDB kapslade objektfälts djupgräns

  4. Motsvarar mongo-skalet db.collection.runCommand() i Node.js