Recuperare solo l'elemento interrogato in un array oggetto della collezione MongoDB
-
10-10-2019 - |
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?
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})