Domanda

Si supponga di avere i seguenti documenti in mia collezione:

{  
   "_id":ObjectId("562e7c594c12942f08fe4192"),
   "shapes":[  
      {  
         "shape":"square",
         "color":"blue"
      },
      {  
         "shape":"circle",
         "color":"red"
      }
   ]
},
{  
   "_id":ObjectId("562e7c594c12942f08fe4193"),
   "shapes":[  
      {  
         "shape":"square",
         "color":"black"
      },
      {  
         "shape":"circle",
         "color":"green"
      }
   ]
}

interrogazione Do:

db.test.find({"shapes.color": "red"}, {"shapes.color": 1})

o

db.test.find({shapes: {"$elemMatch": {color: "red"}}}, {"shapes.color": 1})

I ritorni documento abbinato (documento 1) , ma sempre con tutti gli elementi dell'array nella shapes:

{ "shapes": 
  [
    {"shape": "square", "color": "blue"},
    {"shape": "circle", "color": "red"}
  ] 
}

Tuttavia, mi piacerebbe ottenere il documento (documento 1) solo con la matrice che contiene color=red:

{ "shapes": 
  [
    {"shape": "circle", "color": "red"}
  ] 
}

Come posso fare questo?

È stato utile?

Soluzione

MongoDB 2.2 c'è di nuovo $elemMatch operatore di proiezione fornisce un altro modo per alterare il documento restituito contenga solo il prima elemento shapes trovati:

db.test.find(
    {"shapes.color": "red"}, 
    {_id: 0, shapes: {$elemMatch: {color: "red"}}});

Returns:

{"shapes" : [{"shape": "circle", "color": "red"}]}

In 2.2 è anche possibile farlo usando il $ projection operator , dove il $ in un nome proiezione campo oggetto rappresenta l'indice del primo elemento dell'array corrispondente del campo dalla query. I seguenti restituisce gli stessi risultati come sopra:

db.test.find({"shapes.color": "red"}, {_id: 0, 'shapes.$': 1});

MongoDB 3.2 Aggiornamento

A partire dalla release 3.2, è possibile utilizzare la nuova $filter aggregazione operatore per filtrare una matrice durante la proiezione, che ha il vantaggio di includere tutte corrispondenze, anziché solo la prima.

db.test.aggregate([
    // Get just the docs that contain a shapes element where color is 'red'
    {$match: {'shapes.color': 'red'}},
    {$project: {
        shapes: {$filter: {
            input: '$shapes',
            as: 'shape',
            cond: {$eq: ['$$shape.color', 'red']}
        }},
        _id: 0
    }}
])

Risultati:

[ 
    {
        "shapes" : [ 
            {
                "shape" : "circle",
                "color" : "red"
            }
        ]
    }
]

Altri suggerimenti

Il nuovo href="http://docs.mongodb.org/manual/applications/aggregation/"> Aggregazione quadro in MongoDB 2.2+ fornisce un'alternativa alla Map / Reduce. L'operatore $unwind può essere utilizzata per separare l'array shapes in un flusso di documenti che possono essere abbinati:

db.test.aggregate(
  // Start with a $match pipeline which can take advantage of an index and limit documents processed
  { $match : {
     "shapes.color": "red"
  }},
  { $unwind : "$shapes" },
  { $match : {
     "shapes.color": "red"
  }}
)

Risultati in:

{
    "result" : [
        {
            "_id" : ObjectId("504425059b7c9fa7ec92beec"),
            "shapes" : {
                "shape" : "circle",
                "color" : "red"
            }
        }
    ],
    "ok" : 1
}
  

Attenzione: Questa risposta fornisce una soluzione che era rilevante in quel momento , prima che le nuove funzionalità di MongoDB 2.2 e fino sono state introdotte. Vedi le altre risposte se si utilizza una versione più recente di MongoDB.

Il parametro selettore campo è limitato alle proprietà complete. Non può essere utilizzato per selezionare parte di un array, solo l'intero array. Ho provato ad utilizzare il $ posizionale operatore , ma che non ha lavoro.

Il modo più semplice è quello di filtrare solo le forme nel client .

Se davvero necessità l'uscita corretta direttamente da MongoDB, è possibile utilizzare una mappa-ridurre per filtrare le forme.

function map() {
  filteredShapes = [];

  this.shapes.forEach(function (s) {
    if (s.color === "red") {
      filteredShapes.push(s);
    }
  });

  emit(this._id, { shapes: filteredShapes });
}

function reduce(key, values) {
  return values[0];
}

res = db.test.mapReduce(map, reduce, { query: { "shapes.color": "red" } })

db[res.result].find()

Un altro modo è quello di utilizzare interesing $ Oscura , che è uno dei nuovi aggregazione caratteristiche di MongoDB 2.6 . Se si utilizza 2.6, non è necessario un $ di svolgimento che potrebbe causare problemi di prestazioni se si dispone di grandi array.

db.test.aggregate([
    { $match: { 
         shapes: { $elemMatch: {color: "red"} } 
    }},
    { $redact : {
         $cond: {
             if: { $or : [{ $eq: ["$color","red"] }, { $not : "$color" }]},
             then: "$$DESCEND",
             else: "$$PRUNE"
         }
    }}]);

$redact "limita il contenuto dei documenti basati su informazioni memorizzate nei documenti stessi" . Così sarà eseguito solo all'interno del documento . Si esegue la scansione in fondo la vostra parte superiore del documento verso il basso, e controlla se corrisponde con la vostra condizione if che è in $cond, se non c'è partita sarà sia mantenere il contenuto ($$DESCEND) o rimuovere ($$PRUNE).

Nell'esempio precedente, primi ritorna $match l'intero array shapes, e $ Oscura strisce verso il basso per il risultato atteso.

Si noti che {$not:"$color"} è necessaria, perché sarà la scansione del documento superiore così, e se $redact non trova un campo color al livello superiore questo tornerà false che potrebbero togliere l'intero documento, che non vogliamo.

meglio si può interrogare in corrispondenza elemento dell'array utilizzando $slice è utile a restituire l'oggetto significativo in una matrice.

db.test.find({"shapes.color" : "blue"}, {"shapes.$" : 1})

$slice è utile quando si conosce l'indice dell'elemento, ma a volte si vuole a seconda di quale elemento dell'array corrisponde ai tuoi criteri. È possibile restituire l'elemento corrispondente con l'operatore $.

 db.getCollection('aj').find({"shapes.color":"red"},{"shapes.$":1})

USCITE

{

   "shapes" : [ 
       {
           "shape" : "circle",
           "color" : "red"
       }
   ]
}

La sintassi per trovare in MongoDB è

    db.<collection name>.find(query, projection);

e la seconda query che hai scritto, che è

    db.test.find(
    {shapes: {"$elemMatch": {color: "red"}}}, 
    {"shapes.color":1})

In questo si è utilizzato l'operatore $elemMatch nella query parte, mentre se si utilizza questo operatore nella parte di proiezione, allora si otterrà il risultato desiderato. È possibile scrivere la vostra domanda come

     db.users.find(
     {"shapes.color":"red"},
     {_id:0, shapes: {$elemMatch : {color: "red"}}})

Questo vi darà il risultato desiderato.

Grazie al JohnnyHK .

Qui voglio solo aggiungere un po 'l'uso più complesso.

// Document 
{ 
"_id" : 1
"shapes" : [
  {"shape" : "square",  "color" : "red"},
  {"shape" : "circle",  "color" : "green"}
  ] 
} 

{ 
"_id" : 2
"shapes" : [
  {"shape" : "square",  "color" : "red"},
  {"shape" : "circle",  "color" : "green"}
  ] 
} 


// The Query   
db.contents.find({
    "_id" : ObjectId(1),
    "shapes.color":"red"
},{
    "_id": 0,
    "shapes" :{
       "$elemMatch":{
           "color" : "red"
       } 
    }
}) 


//And the Result

{"shapes":[
    {
       "shape" : "square",
       "color" : "red"
    }
]}

Hai solo bisogno di eseguire query di

db.test.find(
{"shapes.color": "red"}, 
{shapes: {$elemMatch: {color: "red"}}});

uscita di questa query è

{
    "_id" : ObjectId("562e7c594c12942f08fe4192"),
    "shapes" : [ 
        {"shape" : "circle", "color" : "red"}
    ]
}

come vi aspettavate che sarà dà il campo esatto dalla matrice che accostamenti cromatici:. 'Red'

con $ progetto sarà più appropriati altri elementi saggio di corrispondenza verrà bastonato insieme ad altri elementi nel documento.

db.test.aggregate(
  { "$unwind" : "$shapes" },
  { "$match" : {
     "shapes.color": "red"
  }},
{"$project":{
"_id":1,
"item":1
}}
)
db.test.find( {"shapes.color": "red"}, {_id: 0})
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top