Angenommen, Sie haben folgende Dokumente in meiner Sammlung:
{
"_id":ObjectId("562e7c594c12942f08fe4192"),
"shapes":[
{
"shape":"square",
"color":"blue"
},
{
"shape":"circle",
"color":"red"
}
]
},
{
"_id":ObjectId("562e7c594c12942f08fe4193"),
"shapes":[
{
"shape":"square",
"color":"black"
},
{
"shape":"circle",
"color":"green"
}
]
}
Fragen Sie ab:
db.test.find({"shapes.color": "red"}, {"shapes.color": 1})
Oder
db.test.find({shapes: {"$elemMatch": {color: "red"}}}, {"shapes.color": 1})
Gibt das übereinstimmende Dokument (Dokument 1) zurück, aber immer mit ALL-Feldelementen in shapes
:
{ "shapes":
[
{"shape": "square", "color": "blue"},
{"shape": "circle", "color": "red"}
]
}
Ich möchte jedoch das Dokument (Document 1) nur mit dem Array erhalten, das color=red
enthält:
{ "shapes":
[
{"shape": "circle", "color": "red"}
]
}
Wie kann ich das machen?
Der neue $elemMatch
-Projektionsoperator von MongoDB 2.2 bietet eine weitere Möglichkeit, das zurückgegebene Dokument so zu ändern, dass es nur das first - übereinstimmende shapes
-Element enthält:
db.test.find(
{"shapes.color": "red"},
{_id: 0, shapes: {$elemMatch: {color: "red"}}});
Kehrt zurück:
{"shapes" : [{"shape": "circle", "color": "red"}]}
In 2.2 können Sie dies auch mithilfe von $ projection operator
durchführen, wobei der $
in einem Projektionsobjektfeldnamen den Index des ersten übereinstimmenden Feldelements des Felds aus der Abfrage darstellt. Das Folgende liefert die gleichen Ergebnisse wie oben:
db.test.find({"shapes.color": "red"}, {_id: 0, 'shapes.$': 1});
MongoDB 3.2 Update
Ab Version 3.2 können Sie den neuen Aggregatoperator $filter
verwenden, um ein Array während der Projektion zu filtern. Dies hat den Vorteil, dass all Matches anstelle des ersten Arrays verwendet werden.
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
}}
])
Ergebnisse:
[
{
"shapes" : [
{
"shape" : "circle",
"color" : "red"
}
]
}
]
Das neue Aggregation Framework in MongoDB 2.2+ bietet eine Alternative zu Map/Reduce. Mit dem Operator $unwind
können Sie Ihr shapes
-Array in einen Dokumentenstrom unterteilen, der abgeglichen werden kann:
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"
}}
)
Ergebnisse in:
{
"result" : [
{
"_id" : ObjectId("504425059b7c9fa7ec92beec"),
"shapes" : {
"shape" : "circle",
"color" : "red"
}
}
],
"ok" : 1
}
Achtung: Diese Antwort bietet eine Lösung, die zu diesem Zeitpunkt relevant war, bevor die neuen Funktionen von MongoDB 2.2 und höher eingeführt wurden . Sehen Sie sich die anderen Antworten an, wenn Sie eine neuere Version von MongoDB verwenden.
Der Feldauswahlparameter ist auf vollständige Eigenschaften beschränkt. Es kann nicht verwendet werden, um einen Teil eines Arrays auszuwählen, sondern nur das gesamte Array. Ich habe versucht, den $ positional operator zu verwenden, aber das hat nicht funktioniert.
Am einfachsten ist es, die Formen im Client zu filtern.
Wenn Sie wirklich brauchen die richtige Ausgabe direkt aus MongoDB, können Sie ein Map-Reduce verwenden, um die Formen zu filtern.
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()
Ein weiterer interessanter Weg ist die Verwendung von $ redact , einem der neuen Aggregationsfunktionen von MongoDB 2.6. Wenn Sie 2.6 verwenden, benötigen Sie keine $ Abwicklung, was bei großen Arrays zu Leistungsproblemen führen kann.
db.test.aggregate([
{ $match: {
shapes: { $elemMatch: {color: "red"} }
}},
{ $redact : {
$cond: {
if: { $or : [{ $eq: ["$color","red"] }, { $not : "$color" }]},
then: "$$DESCEND",
else: "$$Prune"
}
}}]);
$redact
"beschränkt den Inhalt der Dokumente auf der Grundlage von Informationen, die in den Dokumenten selbst gespeichert sind". Es wird also nur innerhalb des Dokuments ausgeführt. Es scannt Ihr Dokument im Wesentlichen von oben nach unten und prüft, ob es mit Ihrer if
-Bedingung in $cond
übereinstimmt. Wenn Übereinstimmung vorliegt, wird der Inhalt entweder beibehalten ($$DESCEND
) oder entfernt ($$Prune
).
Im obigen Beispiel gibt first $match
das gesamte shapes
-Array zurück, und $ redact entfernt es auf das erwartete Ergebnis.
Beachten Sie, dass {$not:"$color"}
erforderlich ist, da auch das oberste Dokument gescannt wird. Wenn $redact
auf der obersten Ebene kein color
-Feld findet, wird false
zurückgegeben, das möglicherweise das gesamte Dokument entfernt, das wir nicht möchten.
Besser Sie können mit $slice
in passenden Array-Elementen abfragen, ist es hilfreich, das signifikante Objekt in einem Array zurückzugeben.
db.test.find({"shapes.color" : "blue"}, {"shapes.$" : 1})
$slice
ist hilfreich, wenn Sie den Index des Elements kennen. Manchmal möchten Sie jedoch __. welches Arrayelement Ihren Kriterien entspricht. Sie können das entsprechende Element Mit dem Operator $
zurückgeben.
db.getCollection('aj').find({"shapes.color":"red"},{"shapes.$":1})
AUSGÄNGE
{
"shapes" : [
{
"shape" : "circle",
"color" : "red"
}
]
}
Die Syntax für find in mongodb lautet
db.<collection name>.find(query, projection);
und die zweite Abfrage, die Sie geschrieben haben
db.test.find(
{shapes: {"$elemMatch": {color: "red"}}},
{"shapes.color":1})
in diesem haben Sie den $elemMatch
-Operator im Abfrageteil verwendet. Wenn Sie diesen Operator im Projektionsteil verwenden, erhalten Sie das gewünschte Ergebnis. Sie können Ihre Anfrage als aufschreiben
db.users.find(
{"shapes.color":"red"},
{_id:0, shapes: {$elemMatch : {color: "red"}}})
Dadurch erhalten Sie das gewünschte Ergebnis.
Hier möchte ich nur etwas komplexere Verwendung hinzufügen.
// 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"
}
]}
Sie müssen nur die Abfrage ausführen
db.test.find(
{"shapes.color": "red"},
{shapes: {$elemMatch: {color: "red"}}});
ausgabe dieser Abfrage ist
{
"_id" : ObjectId("562e7c594c12942f08fe4192"),
"shapes" : [
{"shape" : "circle", "color" : "red"}
]
}
wie Sie es erwartet haben, wird das exakte Feld aus dem Feld, das mit der Farbe übereinstimmt, angegeben: "Rot".
zusammen mit $ project wird es sinnvoller sein, andere passende Elemente werden zusammen mit anderen Elementen im Dokument zusammengefügt.
db.test.aggregate(
{ "$unwind" : "$shapes" },
{ "$match" : {
"shapes.color": "red"
}},
{"$project":{
"_id":1,
"item":1
}}
)
db.test.find( {"shapes.color": "red"}, {_id: 0})