Retrieve only the queried element in an object array in MongoDB collection
up vote
306
down vote
favorite
Suppose you have the following documents in my collection:
{
"_id":ObjectId("562e7c594c12942f08fe4192"),
"shapes":[
{
"shape":"square",
"color":"blue"
},
{
"shape":"circle",
"color":"red"
}
]
},
{
"_id":ObjectId("562e7c594c12942f08fe4193"),
"shapes":[
{
"shape":"square",
"color":"black"
},
{
"shape":"circle",
"color":"green"
}
]
}
Do query:
db.test.find({"shapes.color": "red"}, {"shapes.color": 1})
Or
db.test.find({shapes: {"$elemMatch": {color: "red"}}}, {"shapes.color": 1})
Returns matched document (Document 1), but always with ALL array items in shapes
:
{ "shapes":
[
{"shape": "square", "color": "blue"},
{"shape": "circle", "color": "red"}
]
}
However, I'd like to get the document (Document 1) only with the array that contains color=red
:
{ "shapes":
[
{"shape": "circle", "color": "red"}
]
}
How can I do this?
mongodb mongodb-query aggregation-framework
add a comment |
up vote
306
down vote
favorite
Suppose you have the following documents in my collection:
{
"_id":ObjectId("562e7c594c12942f08fe4192"),
"shapes":[
{
"shape":"square",
"color":"blue"
},
{
"shape":"circle",
"color":"red"
}
]
},
{
"_id":ObjectId("562e7c594c12942f08fe4193"),
"shapes":[
{
"shape":"square",
"color":"black"
},
{
"shape":"circle",
"color":"green"
}
]
}
Do query:
db.test.find({"shapes.color": "red"}, {"shapes.color": 1})
Or
db.test.find({shapes: {"$elemMatch": {color: "red"}}}, {"shapes.color": 1})
Returns matched document (Document 1), but always with ALL array items in shapes
:
{ "shapes":
[
{"shape": "square", "color": "blue"},
{"shape": "circle", "color": "red"}
]
}
However, I'd like to get the document (Document 1) only with the array that contains color=red
:
{ "shapes":
[
{"shape": "circle", "color": "red"}
]
}
How can I do this?
mongodb mongodb-query aggregation-framework
add a comment |
up vote
306
down vote
favorite
up vote
306
down vote
favorite
Suppose you have the following documents in my collection:
{
"_id":ObjectId("562e7c594c12942f08fe4192"),
"shapes":[
{
"shape":"square",
"color":"blue"
},
{
"shape":"circle",
"color":"red"
}
]
},
{
"_id":ObjectId("562e7c594c12942f08fe4193"),
"shapes":[
{
"shape":"square",
"color":"black"
},
{
"shape":"circle",
"color":"green"
}
]
}
Do query:
db.test.find({"shapes.color": "red"}, {"shapes.color": 1})
Or
db.test.find({shapes: {"$elemMatch": {color: "red"}}}, {"shapes.color": 1})
Returns matched document (Document 1), but always with ALL array items in shapes
:
{ "shapes":
[
{"shape": "square", "color": "blue"},
{"shape": "circle", "color": "red"}
]
}
However, I'd like to get the document (Document 1) only with the array that contains color=red
:
{ "shapes":
[
{"shape": "circle", "color": "red"}
]
}
How can I do this?
mongodb mongodb-query aggregation-framework
Suppose you have the following documents in my collection:
{
"_id":ObjectId("562e7c594c12942f08fe4192"),
"shapes":[
{
"shape":"square",
"color":"blue"
},
{
"shape":"circle",
"color":"red"
}
]
},
{
"_id":ObjectId("562e7c594c12942f08fe4193"),
"shapes":[
{
"shape":"square",
"color":"black"
},
{
"shape":"circle",
"color":"green"
}
]
}
Do query:
db.test.find({"shapes.color": "red"}, {"shapes.color": 1})
Or
db.test.find({shapes: {"$elemMatch": {color: "red"}}}, {"shapes.color": 1})
Returns matched document (Document 1), but always with ALL array items in shapes
:
{ "shapes":
[
{"shape": "square", "color": "blue"},
{"shape": "circle", "color": "red"}
]
}
However, I'd like to get the document (Document 1) only with the array that contains color=red
:
{ "shapes":
[
{"shape": "circle", "color": "red"}
]
}
How can I do this?
mongodb mongodb-query aggregation-framework
mongodb mongodb-query aggregation-framework
edited Jul 2 '17 at 5:34
Neil Lunn
96.4k22169180
96.4k22169180
asked Oct 21 '10 at 7:31
Sebtm
2,71762430
2,71762430
add a comment |
add a comment |
11 Answers
11
active
oldest
votes
up vote
334
down vote
accepted
MongoDB 2.2's new $elemMatch
projection operator provides another way to alter the returned document to contain only the first matched shapes
element:
db.test.find(
{"shapes.color": "red"},
{_id: 0, shapes: {$elemMatch: {color: "red"}}});
Returns:
{"shapes" : [{"shape": "circle", "color": "red"}]}
In 2.2 you can also do this using the $ projection operator
, where the $
in a projection object field name represents the index of the field's first matching array element from the query. The following returns the same results as above:
db.test.find({"shapes.color": "red"}, {_id: 0, 'shapes.$': 1});
MongoDB 3.2 Update
Starting with the 3.2 release, you can use the new $filter
aggregation operator to filter an array during projection, which has the benefit of including all matches, instead of just the first one.
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
}}
])
Results:
[
{
"shapes" : [
{
"shape" : "circle",
"color" : "red"
}
]
}
]
57
Note that as the documentation tells you both$elemMatch
and$
only return the first match.
– kynan
Feb 3 '13 at 0:26
12
any solution if I want it to return every elements that matches it instead of just the first?
– Steve Ng
Dec 25 '13 at 8:12
@JohnnyHK, yup, i used it eventually, thanks!
– Steve Ng
Dec 26 '13 at 6:19
but this just returns the first matched element!
– charliebrownie
Jan 10 '16 at 20:18
1
@charliebrownie See the option using$filter
to get all matches.
– JohnnyHK
Jan 10 '16 at 20:29
|
show 6 more comments
up vote
93
down vote
The new Aggregation Framework in MongoDB 2.2+ provides an alternative to Map/Reduce. The $unwind
operator can be used to separate your shapes
array into a stream of documents that can be matched:
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"
}}
)
Results in:
{
"result" : [
{
"_id" : ObjectId("504425059b7c9fa7ec92beec"),
"shapes" : {
"shape" : "circle",
"color" : "red"
}
}
],
"ok" : 1
}
6
@JohnnyHK: In this case,$elemMatch
is another option. I actually got here by way of a Google Group question where $elemMatch wouldn't work because it only returns the first match per document.
– Stennie
Sep 3 '12 at 4:24
1
Thanks, I wasn't aware of that limitation so that's good to know. Sorry for deleting my comment you're responding to, I decided to post another answer instead and didn't want to confuse people.
– JohnnyHK
Sep 3 '12 at 4:35
3
@JohnnyHK: No worries, there are now three useful answers for the question ;-)
– Stennie
Sep 3 '12 at 4:41
For other searchers, in addition to this I also tried adding{ $project : { shapes : 1 } }
- which seemed to work and would be helpful if the enclosing documents were large and you just wanted to view theshapes
key values.
– user1063287
Dec 4 '14 at 8:23
2
@calmbird I updated the example to include an initial $match stage. If you're interested in a more efficient feature suggestion I would watch/upvote SERVER-6612: Support projecting multiple array values in a projection like the $elemMatch projection specifier in the MongoDB issue tracker.
– Stennie
Jan 28 '15 at 8:52
|
show 3 more comments
up vote
28
down vote
Caution: This answer provides a solution that was relevant at that time, before the new features of MongoDB 2.2 and up were introduced. See the other answers if you are using a more recent version of MongoDB.
The field selector parameter is limited to complete properties. It cannot be used to select part of an array, only the entire array. I tried using the $ positional operator, but that didn't work.
The easiest way is to just filter the shapes in the client.
If you really need the correct output directly from MongoDB, you can use a map-reduce to filter the shapes.
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()
add a comment |
up vote
27
down vote
Another interesing way is to use $redact, which is one of the new aggregation features of MongoDB 2.6. If you are using 2.6, you don't need an $unwind which might cause you performance problems if you have large arrays.
db.test.aggregate([
{ $match: {
shapes: { $elemMatch: {color: "red"} }
}},
{ $redact : {
$cond: {
if: { $or : [{ $eq: ["$color","red"] }, { $not : "$color" }]},
then: "$$DESCEND",
else: "$$PRUNE"
}
}}]);
$redact
"restricts the contents of the documents based on information stored in the documents themselves". So it will run only inside of the document. It basically scans your document top to the bottom, and checks if it matches with your if
condition which is in $cond
, if there is match it will either keep the content($$DESCEND
) or remove($$PRUNE
).
In the example above, first $match
returns the whole shapes
array, and $redact strips it down to the expected result.
Note that {$not:"$color"}
is necessary, because it will scan the top document as well, and if $redact
does not find a color
field on the top level this will return false
that might strip the whole document which we don't want.
1
perfect answer. As you mentioned $unwind will consume lot of RAM. So this will be better when compared.
– manojpt
Apr 21 '15 at 11:21
I have a doubt. In the example, "shapes" is an array. Will "$redact" scan all the objects in the "shapes" array ?? How this will be good with respect to performance??
– manojpt
Apr 23 '15 at 8:13
not all of it, but the result of your first match. That is the reason why you put$match
as your first aggregate stage
– anvarik
Apr 23 '15 at 16:36
okkk.. if an index created on "color" field, even then it will scan all the objects in the "shapes" array??? Which could be the efficient way of matching multiple objects in an array???
– manojpt
Apr 24 '15 at 4:47
2
Brilliant! I do not understand how $eq works here. I left it off originally and this didn't work for me. Somehow, it looks in the array of shapes to find the match, but the query never specifies which array to look in. Like, if the documents had shapes and, for example, sizes; would $eq look in both arrays for matches? Is $redact just looking for anything within the document that matches the 'if' condition?
– Onosa
Dec 30 '15 at 14:46
|
show 1 more comment
up vote
16
down vote
Better you can query in matching array element using $slice
is it helpful to returning the significant object in an array.
db.test.find({"shapes.color" : "blue"}, {"shapes.$" : 1})
$slice
is helpful when you know the index of the element, but sometimes you want
whichever array element matched your criteria. You can return the matching element
with the $
operator.
add a comment |
up vote
11
down vote
The syntax for find in mongodb is
db.<collection name>.find(query, projection);
and the second query that you have written, that is
db.test.find(
{shapes: {"$elemMatch": {color: "red"}}},
{"shapes.color":1})
in this you have used the $elemMatch
operator in query part, whereas if you use this operator in the projection part then you will get the desired result. You can write down your query as
db.users.find(
{"shapes.color":"red"},
{_id:0, shapes: {$elemMatch : {color: "red"}}})
This will give you the desired result.
1
This works for me. However, It appears that"shapes.color":"red"
in the query parameter (the first parameter of the find method) is not necessary. You can replace it with{}
and get the same results.
– Erik Olson
May 9 '14 at 20:35
2
@ErikOlson Your suggestion is right in the above case, where we need to find all the document that with red color and to apply the projection on them only. But let's say if somebody requires to find out all the document that have color blue but it should return only those element of that shapes array that have color red. In this case the above query can be referenced by somebody else also..
– Vicky
May 11 '14 at 9:22
This seems to be the easiest, but I can't make it work it. It only returns the first matching subdocument.
– newman
Aug 29 '15 at 23:05
add a comment |
up vote
11
down vote
db.getCollection('aj').find({"shapes.color":"red"},{"shapes.$":1})
OUTPUTS
{
"shapes" : [
{
"shape" : "circle",
"color" : "red"
}
]
}
add a comment |
up vote
7
down vote
Thanks to JohnnyHK.
Here I just want to add some more complex usage.
// 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"
}
]}
add a comment |
up vote
5
down vote
You just need to run query
db.test.find(
{"shapes.color": "red"},
{shapes: {$elemMatch: {color: "red"}}});
output of this query is
{
"_id" : ObjectId("562e7c594c12942f08fe4192"),
"shapes" : [
{"shape" : "circle", "color" : "red"}
]
}
as you expected it'll gives the exact field from array that matches color:'red'.
add a comment |
up vote
2
down vote
along with $project it will be more appropriate other wise matching elements will be clubbed together with other elements in document.
db.test.aggregate(
{ "$unwind" : "$shapes" },
{ "$match" : {
"shapes.color": "red"
}},
{"$project":{
"_id":1,
"item":1
}}
)
can you pls describe that this accomplishes with an input and output set?
– Alexander Mills
Nov 24 '15 at 17:13
add a comment |
up vote
0
down vote
db.test.find( {"shapes.color": "red"}, {_id: 0})
Welcome to Stack Overflow! Thank you for the code snippet, which might provide some limited, immediate help. A proper explanation would greatly improve its long-term value by describing why this is a good solution to the problem, and would make it more useful to future readers with other similar questions. Please edit your answer to add some explanation, including the assumptions you've made.
– sepehr
Oct 25 at 15:06
add a comment |
protected by Samuel Liew♦ Oct 5 '15 at 9:21
Thank you for your interest in this question.
Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).
Would you like to answer one of these unanswered questions instead?
11 Answers
11
active
oldest
votes
11 Answers
11
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
334
down vote
accepted
MongoDB 2.2's new $elemMatch
projection operator provides another way to alter the returned document to contain only the first matched shapes
element:
db.test.find(
{"shapes.color": "red"},
{_id: 0, shapes: {$elemMatch: {color: "red"}}});
Returns:
{"shapes" : [{"shape": "circle", "color": "red"}]}
In 2.2 you can also do this using the $ projection operator
, where the $
in a projection object field name represents the index of the field's first matching array element from the query. The following returns the same results as above:
db.test.find({"shapes.color": "red"}, {_id: 0, 'shapes.$': 1});
MongoDB 3.2 Update
Starting with the 3.2 release, you can use the new $filter
aggregation operator to filter an array during projection, which has the benefit of including all matches, instead of just the first one.
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
}}
])
Results:
[
{
"shapes" : [
{
"shape" : "circle",
"color" : "red"
}
]
}
]
57
Note that as the documentation tells you both$elemMatch
and$
only return the first match.
– kynan
Feb 3 '13 at 0:26
12
any solution if I want it to return every elements that matches it instead of just the first?
– Steve Ng
Dec 25 '13 at 8:12
@JohnnyHK, yup, i used it eventually, thanks!
– Steve Ng
Dec 26 '13 at 6:19
but this just returns the first matched element!
– charliebrownie
Jan 10 '16 at 20:18
1
@charliebrownie See the option using$filter
to get all matches.
– JohnnyHK
Jan 10 '16 at 20:29
|
show 6 more comments
up vote
334
down vote
accepted
MongoDB 2.2's new $elemMatch
projection operator provides another way to alter the returned document to contain only the first matched shapes
element:
db.test.find(
{"shapes.color": "red"},
{_id: 0, shapes: {$elemMatch: {color: "red"}}});
Returns:
{"shapes" : [{"shape": "circle", "color": "red"}]}
In 2.2 you can also do this using the $ projection operator
, where the $
in a projection object field name represents the index of the field's first matching array element from the query. The following returns the same results as above:
db.test.find({"shapes.color": "red"}, {_id: 0, 'shapes.$': 1});
MongoDB 3.2 Update
Starting with the 3.2 release, you can use the new $filter
aggregation operator to filter an array during projection, which has the benefit of including all matches, instead of just the first one.
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
}}
])
Results:
[
{
"shapes" : [
{
"shape" : "circle",
"color" : "red"
}
]
}
]
57
Note that as the documentation tells you both$elemMatch
and$
only return the first match.
– kynan
Feb 3 '13 at 0:26
12
any solution if I want it to return every elements that matches it instead of just the first?
– Steve Ng
Dec 25 '13 at 8:12
@JohnnyHK, yup, i used it eventually, thanks!
– Steve Ng
Dec 26 '13 at 6:19
but this just returns the first matched element!
– charliebrownie
Jan 10 '16 at 20:18
1
@charliebrownie See the option using$filter
to get all matches.
– JohnnyHK
Jan 10 '16 at 20:29
|
show 6 more comments
up vote
334
down vote
accepted
up vote
334
down vote
accepted
MongoDB 2.2's new $elemMatch
projection operator provides another way to alter the returned document to contain only the first matched shapes
element:
db.test.find(
{"shapes.color": "red"},
{_id: 0, shapes: {$elemMatch: {color: "red"}}});
Returns:
{"shapes" : [{"shape": "circle", "color": "red"}]}
In 2.2 you can also do this using the $ projection operator
, where the $
in a projection object field name represents the index of the field's first matching array element from the query. The following returns the same results as above:
db.test.find({"shapes.color": "red"}, {_id: 0, 'shapes.$': 1});
MongoDB 3.2 Update
Starting with the 3.2 release, you can use the new $filter
aggregation operator to filter an array during projection, which has the benefit of including all matches, instead of just the first one.
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
}}
])
Results:
[
{
"shapes" : [
{
"shape" : "circle",
"color" : "red"
}
]
}
]
MongoDB 2.2's new $elemMatch
projection operator provides another way to alter the returned document to contain only the first matched shapes
element:
db.test.find(
{"shapes.color": "red"},
{_id: 0, shapes: {$elemMatch: {color: "red"}}});
Returns:
{"shapes" : [{"shape": "circle", "color": "red"}]}
In 2.2 you can also do this using the $ projection operator
, where the $
in a projection object field name represents the index of the field's first matching array element from the query. The following returns the same results as above:
db.test.find({"shapes.color": "red"}, {_id: 0, 'shapes.$': 1});
MongoDB 3.2 Update
Starting with the 3.2 release, you can use the new $filter
aggregation operator to filter an array during projection, which has the benefit of including all matches, instead of just the first one.
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
}}
])
Results:
[
{
"shapes" : [
{
"shape" : "circle",
"color" : "red"
}
]
}
]
edited Sep 25 at 18:04
Faraz Durrani
2,6542934
2,6542934
answered Sep 3 '12 at 4:19
JohnnyHK
204k37431362
204k37431362
57
Note that as the documentation tells you both$elemMatch
and$
only return the first match.
– kynan
Feb 3 '13 at 0:26
12
any solution if I want it to return every elements that matches it instead of just the first?
– Steve Ng
Dec 25 '13 at 8:12
@JohnnyHK, yup, i used it eventually, thanks!
– Steve Ng
Dec 26 '13 at 6:19
but this just returns the first matched element!
– charliebrownie
Jan 10 '16 at 20:18
1
@charliebrownie See the option using$filter
to get all matches.
– JohnnyHK
Jan 10 '16 at 20:29
|
show 6 more comments
57
Note that as the documentation tells you both$elemMatch
and$
only return the first match.
– kynan
Feb 3 '13 at 0:26
12
any solution if I want it to return every elements that matches it instead of just the first?
– Steve Ng
Dec 25 '13 at 8:12
@JohnnyHK, yup, i used it eventually, thanks!
– Steve Ng
Dec 26 '13 at 6:19
but this just returns the first matched element!
– charliebrownie
Jan 10 '16 at 20:18
1
@charliebrownie See the option using$filter
to get all matches.
– JohnnyHK
Jan 10 '16 at 20:29
57
57
Note that as the documentation tells you both
$elemMatch
and $
only return the first match.– kynan
Feb 3 '13 at 0:26
Note that as the documentation tells you both
$elemMatch
and $
only return the first match.– kynan
Feb 3 '13 at 0:26
12
12
any solution if I want it to return every elements that matches it instead of just the first?
– Steve Ng
Dec 25 '13 at 8:12
any solution if I want it to return every elements that matches it instead of just the first?
– Steve Ng
Dec 25 '13 at 8:12
@JohnnyHK, yup, i used it eventually, thanks!
– Steve Ng
Dec 26 '13 at 6:19
@JohnnyHK, yup, i used it eventually, thanks!
– Steve Ng
Dec 26 '13 at 6:19
but this just returns the first matched element!
– charliebrownie
Jan 10 '16 at 20:18
but this just returns the first matched element!
– charliebrownie
Jan 10 '16 at 20:18
1
1
@charliebrownie See the option using
$filter
to get all matches.– JohnnyHK
Jan 10 '16 at 20:29
@charliebrownie See the option using
$filter
to get all matches.– JohnnyHK
Jan 10 '16 at 20:29
|
show 6 more comments
up vote
93
down vote
The new Aggregation Framework in MongoDB 2.2+ provides an alternative to Map/Reduce. The $unwind
operator can be used to separate your shapes
array into a stream of documents that can be matched:
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"
}}
)
Results in:
{
"result" : [
{
"_id" : ObjectId("504425059b7c9fa7ec92beec"),
"shapes" : {
"shape" : "circle",
"color" : "red"
}
}
],
"ok" : 1
}
6
@JohnnyHK: In this case,$elemMatch
is another option. I actually got here by way of a Google Group question where $elemMatch wouldn't work because it only returns the first match per document.
– Stennie
Sep 3 '12 at 4:24
1
Thanks, I wasn't aware of that limitation so that's good to know. Sorry for deleting my comment you're responding to, I decided to post another answer instead and didn't want to confuse people.
– JohnnyHK
Sep 3 '12 at 4:35
3
@JohnnyHK: No worries, there are now three useful answers for the question ;-)
– Stennie
Sep 3 '12 at 4:41
For other searchers, in addition to this I also tried adding{ $project : { shapes : 1 } }
- which seemed to work and would be helpful if the enclosing documents were large and you just wanted to view theshapes
key values.
– user1063287
Dec 4 '14 at 8:23
2
@calmbird I updated the example to include an initial $match stage. If you're interested in a more efficient feature suggestion I would watch/upvote SERVER-6612: Support projecting multiple array values in a projection like the $elemMatch projection specifier in the MongoDB issue tracker.
– Stennie
Jan 28 '15 at 8:52
|
show 3 more comments
up vote
93
down vote
The new Aggregation Framework in MongoDB 2.2+ provides an alternative to Map/Reduce. The $unwind
operator can be used to separate your shapes
array into a stream of documents that can be matched:
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"
}}
)
Results in:
{
"result" : [
{
"_id" : ObjectId("504425059b7c9fa7ec92beec"),
"shapes" : {
"shape" : "circle",
"color" : "red"
}
}
],
"ok" : 1
}
6
@JohnnyHK: In this case,$elemMatch
is another option. I actually got here by way of a Google Group question where $elemMatch wouldn't work because it only returns the first match per document.
– Stennie
Sep 3 '12 at 4:24
1
Thanks, I wasn't aware of that limitation so that's good to know. Sorry for deleting my comment you're responding to, I decided to post another answer instead and didn't want to confuse people.
– JohnnyHK
Sep 3 '12 at 4:35
3
@JohnnyHK: No worries, there are now three useful answers for the question ;-)
– Stennie
Sep 3 '12 at 4:41
For other searchers, in addition to this I also tried adding{ $project : { shapes : 1 } }
- which seemed to work and would be helpful if the enclosing documents were large and you just wanted to view theshapes
key values.
– user1063287
Dec 4 '14 at 8:23
2
@calmbird I updated the example to include an initial $match stage. If you're interested in a more efficient feature suggestion I would watch/upvote SERVER-6612: Support projecting multiple array values in a projection like the $elemMatch projection specifier in the MongoDB issue tracker.
– Stennie
Jan 28 '15 at 8:52
|
show 3 more comments
up vote
93
down vote
up vote
93
down vote
The new Aggregation Framework in MongoDB 2.2+ provides an alternative to Map/Reduce. The $unwind
operator can be used to separate your shapes
array into a stream of documents that can be matched:
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"
}}
)
Results in:
{
"result" : [
{
"_id" : ObjectId("504425059b7c9fa7ec92beec"),
"shapes" : {
"shape" : "circle",
"color" : "red"
}
}
],
"ok" : 1
}
The new Aggregation Framework in MongoDB 2.2+ provides an alternative to Map/Reduce. The $unwind
operator can be used to separate your shapes
array into a stream of documents that can be matched:
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"
}}
)
Results in:
{
"result" : [
{
"_id" : ObjectId("504425059b7c9fa7ec92beec"),
"shapes" : {
"shape" : "circle",
"color" : "red"
}
}
],
"ok" : 1
}
edited Jan 28 '15 at 8:41
answered Sep 3 '12 at 3:44
Stennie
45.8k8108138
45.8k8108138
6
@JohnnyHK: In this case,$elemMatch
is another option. I actually got here by way of a Google Group question where $elemMatch wouldn't work because it only returns the first match per document.
– Stennie
Sep 3 '12 at 4:24
1
Thanks, I wasn't aware of that limitation so that's good to know. Sorry for deleting my comment you're responding to, I decided to post another answer instead and didn't want to confuse people.
– JohnnyHK
Sep 3 '12 at 4:35
3
@JohnnyHK: No worries, there are now three useful answers for the question ;-)
– Stennie
Sep 3 '12 at 4:41
For other searchers, in addition to this I also tried adding{ $project : { shapes : 1 } }
- which seemed to work and would be helpful if the enclosing documents were large and you just wanted to view theshapes
key values.
– user1063287
Dec 4 '14 at 8:23
2
@calmbird I updated the example to include an initial $match stage. If you're interested in a more efficient feature suggestion I would watch/upvote SERVER-6612: Support projecting multiple array values in a projection like the $elemMatch projection specifier in the MongoDB issue tracker.
– Stennie
Jan 28 '15 at 8:52
|
show 3 more comments
6
@JohnnyHK: In this case,$elemMatch
is another option. I actually got here by way of a Google Group question where $elemMatch wouldn't work because it only returns the first match per document.
– Stennie
Sep 3 '12 at 4:24
1
Thanks, I wasn't aware of that limitation so that's good to know. Sorry for deleting my comment you're responding to, I decided to post another answer instead and didn't want to confuse people.
– JohnnyHK
Sep 3 '12 at 4:35
3
@JohnnyHK: No worries, there are now three useful answers for the question ;-)
– Stennie
Sep 3 '12 at 4:41
For other searchers, in addition to this I also tried adding{ $project : { shapes : 1 } }
- which seemed to work and would be helpful if the enclosing documents were large and you just wanted to view theshapes
key values.
– user1063287
Dec 4 '14 at 8:23
2
@calmbird I updated the example to include an initial $match stage. If you're interested in a more efficient feature suggestion I would watch/upvote SERVER-6612: Support projecting multiple array values in a projection like the $elemMatch projection specifier in the MongoDB issue tracker.
– Stennie
Jan 28 '15 at 8:52
6
6
@JohnnyHK: In this case,
$elemMatch
is another option. I actually got here by way of a Google Group question where $elemMatch wouldn't work because it only returns the first match per document.– Stennie
Sep 3 '12 at 4:24
@JohnnyHK: In this case,
$elemMatch
is another option. I actually got here by way of a Google Group question where $elemMatch wouldn't work because it only returns the first match per document.– Stennie
Sep 3 '12 at 4:24
1
1
Thanks, I wasn't aware of that limitation so that's good to know. Sorry for deleting my comment you're responding to, I decided to post another answer instead and didn't want to confuse people.
– JohnnyHK
Sep 3 '12 at 4:35
Thanks, I wasn't aware of that limitation so that's good to know. Sorry for deleting my comment you're responding to, I decided to post another answer instead and didn't want to confuse people.
– JohnnyHK
Sep 3 '12 at 4:35
3
3
@JohnnyHK: No worries, there are now three useful answers for the question ;-)
– Stennie
Sep 3 '12 at 4:41
@JohnnyHK: No worries, there are now three useful answers for the question ;-)
– Stennie
Sep 3 '12 at 4:41
For other searchers, in addition to this I also tried adding
{ $project : { shapes : 1 } }
- which seemed to work and would be helpful if the enclosing documents were large and you just wanted to view the shapes
key values.– user1063287
Dec 4 '14 at 8:23
For other searchers, in addition to this I also tried adding
{ $project : { shapes : 1 } }
- which seemed to work and would be helpful if the enclosing documents were large and you just wanted to view the shapes
key values.– user1063287
Dec 4 '14 at 8:23
2
2
@calmbird I updated the example to include an initial $match stage. If you're interested in a more efficient feature suggestion I would watch/upvote SERVER-6612: Support projecting multiple array values in a projection like the $elemMatch projection specifier in the MongoDB issue tracker.
– Stennie
Jan 28 '15 at 8:52
@calmbird I updated the example to include an initial $match stage. If you're interested in a more efficient feature suggestion I would watch/upvote SERVER-6612: Support projecting multiple array values in a projection like the $elemMatch projection specifier in the MongoDB issue tracker.
– Stennie
Jan 28 '15 at 8:52
|
show 3 more comments
up vote
28
down vote
Caution: This answer provides a solution that was relevant at that time, before the new features of MongoDB 2.2 and up were introduced. See the other answers if you are using a more recent version of MongoDB.
The field selector parameter is limited to complete properties. It cannot be used to select part of an array, only the entire array. I tried using the $ positional operator, but that didn't work.
The easiest way is to just filter the shapes in the client.
If you really need the correct output directly from MongoDB, you can use a map-reduce to filter the shapes.
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()
add a comment |
up vote
28
down vote
Caution: This answer provides a solution that was relevant at that time, before the new features of MongoDB 2.2 and up were introduced. See the other answers if you are using a more recent version of MongoDB.
The field selector parameter is limited to complete properties. It cannot be used to select part of an array, only the entire array. I tried using the $ positional operator, but that didn't work.
The easiest way is to just filter the shapes in the client.
If you really need the correct output directly from MongoDB, you can use a map-reduce to filter the shapes.
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()
add a comment |
up vote
28
down vote
up vote
28
down vote
Caution: This answer provides a solution that was relevant at that time, before the new features of MongoDB 2.2 and up were introduced. See the other answers if you are using a more recent version of MongoDB.
The field selector parameter is limited to complete properties. It cannot be used to select part of an array, only the entire array. I tried using the $ positional operator, but that didn't work.
The easiest way is to just filter the shapes in the client.
If you really need the correct output directly from MongoDB, you can use a map-reduce to filter the shapes.
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()
Caution: This answer provides a solution that was relevant at that time, before the new features of MongoDB 2.2 and up were introduced. See the other answers if you are using a more recent version of MongoDB.
The field selector parameter is limited to complete properties. It cannot be used to select part of an array, only the entire array. I tried using the $ positional operator, but that didn't work.
The easiest way is to just filter the shapes in the client.
If you really need the correct output directly from MongoDB, you can use a map-reduce to filter the shapes.
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()
edited Jul 19 '16 at 18:42
answered Oct 21 '10 at 9:25
Niels van der Rest
22.8k137182
22.8k137182
add a comment |
add a comment |
up vote
27
down vote
Another interesing way is to use $redact, which is one of the new aggregation features of MongoDB 2.6. If you are using 2.6, you don't need an $unwind which might cause you performance problems if you have large arrays.
db.test.aggregate([
{ $match: {
shapes: { $elemMatch: {color: "red"} }
}},
{ $redact : {
$cond: {
if: { $or : [{ $eq: ["$color","red"] }, { $not : "$color" }]},
then: "$$DESCEND",
else: "$$PRUNE"
}
}}]);
$redact
"restricts the contents of the documents based on information stored in the documents themselves". So it will run only inside of the document. It basically scans your document top to the bottom, and checks if it matches with your if
condition which is in $cond
, if there is match it will either keep the content($$DESCEND
) or remove($$PRUNE
).
In the example above, first $match
returns the whole shapes
array, and $redact strips it down to the expected result.
Note that {$not:"$color"}
is necessary, because it will scan the top document as well, and if $redact
does not find a color
field on the top level this will return false
that might strip the whole document which we don't want.
1
perfect answer. As you mentioned $unwind will consume lot of RAM. So this will be better when compared.
– manojpt
Apr 21 '15 at 11:21
I have a doubt. In the example, "shapes" is an array. Will "$redact" scan all the objects in the "shapes" array ?? How this will be good with respect to performance??
– manojpt
Apr 23 '15 at 8:13
not all of it, but the result of your first match. That is the reason why you put$match
as your first aggregate stage
– anvarik
Apr 23 '15 at 16:36
okkk.. if an index created on "color" field, even then it will scan all the objects in the "shapes" array??? Which could be the efficient way of matching multiple objects in an array???
– manojpt
Apr 24 '15 at 4:47
2
Brilliant! I do not understand how $eq works here. I left it off originally and this didn't work for me. Somehow, it looks in the array of shapes to find the match, but the query never specifies which array to look in. Like, if the documents had shapes and, for example, sizes; would $eq look in both arrays for matches? Is $redact just looking for anything within the document that matches the 'if' condition?
– Onosa
Dec 30 '15 at 14:46
|
show 1 more comment
up vote
27
down vote
Another interesing way is to use $redact, which is one of the new aggregation features of MongoDB 2.6. If you are using 2.6, you don't need an $unwind which might cause you performance problems if you have large arrays.
db.test.aggregate([
{ $match: {
shapes: { $elemMatch: {color: "red"} }
}},
{ $redact : {
$cond: {
if: { $or : [{ $eq: ["$color","red"] }, { $not : "$color" }]},
then: "$$DESCEND",
else: "$$PRUNE"
}
}}]);
$redact
"restricts the contents of the documents based on information stored in the documents themselves". So it will run only inside of the document. It basically scans your document top to the bottom, and checks if it matches with your if
condition which is in $cond
, if there is match it will either keep the content($$DESCEND
) or remove($$PRUNE
).
In the example above, first $match
returns the whole shapes
array, and $redact strips it down to the expected result.
Note that {$not:"$color"}
is necessary, because it will scan the top document as well, and if $redact
does not find a color
field on the top level this will return false
that might strip the whole document which we don't want.
1
perfect answer. As you mentioned $unwind will consume lot of RAM. So this will be better when compared.
– manojpt
Apr 21 '15 at 11:21
I have a doubt. In the example, "shapes" is an array. Will "$redact" scan all the objects in the "shapes" array ?? How this will be good with respect to performance??
– manojpt
Apr 23 '15 at 8:13
not all of it, but the result of your first match. That is the reason why you put$match
as your first aggregate stage
– anvarik
Apr 23 '15 at 16:36
okkk.. if an index created on "color" field, even then it will scan all the objects in the "shapes" array??? Which could be the efficient way of matching multiple objects in an array???
– manojpt
Apr 24 '15 at 4:47
2
Brilliant! I do not understand how $eq works here. I left it off originally and this didn't work for me. Somehow, it looks in the array of shapes to find the match, but the query never specifies which array to look in. Like, if the documents had shapes and, for example, sizes; would $eq look in both arrays for matches? Is $redact just looking for anything within the document that matches the 'if' condition?
– Onosa
Dec 30 '15 at 14:46
|
show 1 more comment
up vote
27
down vote
up vote
27
down vote
Another interesing way is to use $redact, which is one of the new aggregation features of MongoDB 2.6. If you are using 2.6, you don't need an $unwind which might cause you performance problems if you have large arrays.
db.test.aggregate([
{ $match: {
shapes: { $elemMatch: {color: "red"} }
}},
{ $redact : {
$cond: {
if: { $or : [{ $eq: ["$color","red"] }, { $not : "$color" }]},
then: "$$DESCEND",
else: "$$PRUNE"
}
}}]);
$redact
"restricts the contents of the documents based on information stored in the documents themselves". So it will run only inside of the document. It basically scans your document top to the bottom, and checks if it matches with your if
condition which is in $cond
, if there is match it will either keep the content($$DESCEND
) or remove($$PRUNE
).
In the example above, first $match
returns the whole shapes
array, and $redact strips it down to the expected result.
Note that {$not:"$color"}
is necessary, because it will scan the top document as well, and if $redact
does not find a color
field on the top level this will return false
that might strip the whole document which we don't want.
Another interesing way is to use $redact, which is one of the new aggregation features of MongoDB 2.6. If you are using 2.6, you don't need an $unwind which might cause you performance problems if you have large arrays.
db.test.aggregate([
{ $match: {
shapes: { $elemMatch: {color: "red"} }
}},
{ $redact : {
$cond: {
if: { $or : [{ $eq: ["$color","red"] }, { $not : "$color" }]},
then: "$$DESCEND",
else: "$$PRUNE"
}
}}]);
$redact
"restricts the contents of the documents based on information stored in the documents themselves". So it will run only inside of the document. It basically scans your document top to the bottom, and checks if it matches with your if
condition which is in $cond
, if there is match it will either keep the content($$DESCEND
) or remove($$PRUNE
).
In the example above, first $match
returns the whole shapes
array, and $redact strips it down to the expected result.
Note that {$not:"$color"}
is necessary, because it will scan the top document as well, and if $redact
does not find a color
field on the top level this will return false
that might strip the whole document which we don't want.
answered Jun 4 '14 at 8:31
anvarik
3,68532947
3,68532947
1
perfect answer. As you mentioned $unwind will consume lot of RAM. So this will be better when compared.
– manojpt
Apr 21 '15 at 11:21
I have a doubt. In the example, "shapes" is an array. Will "$redact" scan all the objects in the "shapes" array ?? How this will be good with respect to performance??
– manojpt
Apr 23 '15 at 8:13
not all of it, but the result of your first match. That is the reason why you put$match
as your first aggregate stage
– anvarik
Apr 23 '15 at 16:36
okkk.. if an index created on "color" field, even then it will scan all the objects in the "shapes" array??? Which could be the efficient way of matching multiple objects in an array???
– manojpt
Apr 24 '15 at 4:47
2
Brilliant! I do not understand how $eq works here. I left it off originally and this didn't work for me. Somehow, it looks in the array of shapes to find the match, but the query never specifies which array to look in. Like, if the documents had shapes and, for example, sizes; would $eq look in both arrays for matches? Is $redact just looking for anything within the document that matches the 'if' condition?
– Onosa
Dec 30 '15 at 14:46
|
show 1 more comment
1
perfect answer. As you mentioned $unwind will consume lot of RAM. So this will be better when compared.
– manojpt
Apr 21 '15 at 11:21
I have a doubt. In the example, "shapes" is an array. Will "$redact" scan all the objects in the "shapes" array ?? How this will be good with respect to performance??
– manojpt
Apr 23 '15 at 8:13
not all of it, but the result of your first match. That is the reason why you put$match
as your first aggregate stage
– anvarik
Apr 23 '15 at 16:36
okkk.. if an index created on "color" field, even then it will scan all the objects in the "shapes" array??? Which could be the efficient way of matching multiple objects in an array???
– manojpt
Apr 24 '15 at 4:47
2
Brilliant! I do not understand how $eq works here. I left it off originally and this didn't work for me. Somehow, it looks in the array of shapes to find the match, but the query never specifies which array to look in. Like, if the documents had shapes and, for example, sizes; would $eq look in both arrays for matches? Is $redact just looking for anything within the document that matches the 'if' condition?
– Onosa
Dec 30 '15 at 14:46
1
1
perfect answer. As you mentioned $unwind will consume lot of RAM. So this will be better when compared.
– manojpt
Apr 21 '15 at 11:21
perfect answer. As you mentioned $unwind will consume lot of RAM. So this will be better when compared.
– manojpt
Apr 21 '15 at 11:21
I have a doubt. In the example, "shapes" is an array. Will "$redact" scan all the objects in the "shapes" array ?? How this will be good with respect to performance??
– manojpt
Apr 23 '15 at 8:13
I have a doubt. In the example, "shapes" is an array. Will "$redact" scan all the objects in the "shapes" array ?? How this will be good with respect to performance??
– manojpt
Apr 23 '15 at 8:13
not all of it, but the result of your first match. That is the reason why you put
$match
as your first aggregate stage– anvarik
Apr 23 '15 at 16:36
not all of it, but the result of your first match. That is the reason why you put
$match
as your first aggregate stage– anvarik
Apr 23 '15 at 16:36
okkk.. if an index created on "color" field, even then it will scan all the objects in the "shapes" array??? Which could be the efficient way of matching multiple objects in an array???
– manojpt
Apr 24 '15 at 4:47
okkk.. if an index created on "color" field, even then it will scan all the objects in the "shapes" array??? Which could be the efficient way of matching multiple objects in an array???
– manojpt
Apr 24 '15 at 4:47
2
2
Brilliant! I do not understand how $eq works here. I left it off originally and this didn't work for me. Somehow, it looks in the array of shapes to find the match, but the query never specifies which array to look in. Like, if the documents had shapes and, for example, sizes; would $eq look in both arrays for matches? Is $redact just looking for anything within the document that matches the 'if' condition?
– Onosa
Dec 30 '15 at 14:46
Brilliant! I do not understand how $eq works here. I left it off originally and this didn't work for me. Somehow, it looks in the array of shapes to find the match, but the query never specifies which array to look in. Like, if the documents had shapes and, for example, sizes; would $eq look in both arrays for matches? Is $redact just looking for anything within the document that matches the 'if' condition?
– Onosa
Dec 30 '15 at 14:46
|
show 1 more comment
up vote
16
down vote
Better you can query in matching array element using $slice
is it helpful to returning the significant object in an array.
db.test.find({"shapes.color" : "blue"}, {"shapes.$" : 1})
$slice
is helpful when you know the index of the element, but sometimes you want
whichever array element matched your criteria. You can return the matching element
with the $
operator.
add a comment |
up vote
16
down vote
Better you can query in matching array element using $slice
is it helpful to returning the significant object in an array.
db.test.find({"shapes.color" : "blue"}, {"shapes.$" : 1})
$slice
is helpful when you know the index of the element, but sometimes you want
whichever array element matched your criteria. You can return the matching element
with the $
operator.
add a comment |
up vote
16
down vote
up vote
16
down vote
Better you can query in matching array element using $slice
is it helpful to returning the significant object in an array.
db.test.find({"shapes.color" : "blue"}, {"shapes.$" : 1})
$slice
is helpful when you know the index of the element, but sometimes you want
whichever array element matched your criteria. You can return the matching element
with the $
operator.
Better you can query in matching array element using $slice
is it helpful to returning the significant object in an array.
db.test.find({"shapes.color" : "blue"}, {"shapes.$" : 1})
$slice
is helpful when you know the index of the element, but sometimes you want
whichever array element matched your criteria. You can return the matching element
with the $
operator.
edited Sep 18 '14 at 9:14
Egor Neliuba
10.2k54163
10.2k54163
answered Sep 18 '14 at 8:35
Naren Dran
40246
40246
add a comment |
add a comment |
up vote
11
down vote
The syntax for find in mongodb is
db.<collection name>.find(query, projection);
and the second query that you have written, that is
db.test.find(
{shapes: {"$elemMatch": {color: "red"}}},
{"shapes.color":1})
in this you have used the $elemMatch
operator in query part, whereas if you use this operator in the projection part then you will get the desired result. You can write down your query as
db.users.find(
{"shapes.color":"red"},
{_id:0, shapes: {$elemMatch : {color: "red"}}})
This will give you the desired result.
1
This works for me. However, It appears that"shapes.color":"red"
in the query parameter (the first parameter of the find method) is not necessary. You can replace it with{}
and get the same results.
– Erik Olson
May 9 '14 at 20:35
2
@ErikOlson Your suggestion is right in the above case, where we need to find all the document that with red color and to apply the projection on them only. But let's say if somebody requires to find out all the document that have color blue but it should return only those element of that shapes array that have color red. In this case the above query can be referenced by somebody else also..
– Vicky
May 11 '14 at 9:22
This seems to be the easiest, but I can't make it work it. It only returns the first matching subdocument.
– newman
Aug 29 '15 at 23:05
add a comment |
up vote
11
down vote
The syntax for find in mongodb is
db.<collection name>.find(query, projection);
and the second query that you have written, that is
db.test.find(
{shapes: {"$elemMatch": {color: "red"}}},
{"shapes.color":1})
in this you have used the $elemMatch
operator in query part, whereas if you use this operator in the projection part then you will get the desired result. You can write down your query as
db.users.find(
{"shapes.color":"red"},
{_id:0, shapes: {$elemMatch : {color: "red"}}})
This will give you the desired result.
1
This works for me. However, It appears that"shapes.color":"red"
in the query parameter (the first parameter of the find method) is not necessary. You can replace it with{}
and get the same results.
– Erik Olson
May 9 '14 at 20:35
2
@ErikOlson Your suggestion is right in the above case, where we need to find all the document that with red color and to apply the projection on them only. But let's say if somebody requires to find out all the document that have color blue but it should return only those element of that shapes array that have color red. In this case the above query can be referenced by somebody else also..
– Vicky
May 11 '14 at 9:22
This seems to be the easiest, but I can't make it work it. It only returns the first matching subdocument.
– newman
Aug 29 '15 at 23:05
add a comment |
up vote
11
down vote
up vote
11
down vote
The syntax for find in mongodb is
db.<collection name>.find(query, projection);
and the second query that you have written, that is
db.test.find(
{shapes: {"$elemMatch": {color: "red"}}},
{"shapes.color":1})
in this you have used the $elemMatch
operator in query part, whereas if you use this operator in the projection part then you will get the desired result. You can write down your query as
db.users.find(
{"shapes.color":"red"},
{_id:0, shapes: {$elemMatch : {color: "red"}}})
This will give you the desired result.
The syntax for find in mongodb is
db.<collection name>.find(query, projection);
and the second query that you have written, that is
db.test.find(
{shapes: {"$elemMatch": {color: "red"}}},
{"shapes.color":1})
in this you have used the $elemMatch
operator in query part, whereas if you use this operator in the projection part then you will get the desired result. You can write down your query as
db.users.find(
{"shapes.color":"red"},
{_id:0, shapes: {$elemMatch : {color: "red"}}})
This will give you the desired result.
edited Jan 31 '14 at 8:19
Jinxcat
4191316
4191316
answered Dec 22 '13 at 8:14
Vicky
447314
447314
1
This works for me. However, It appears that"shapes.color":"red"
in the query parameter (the first parameter of the find method) is not necessary. You can replace it with{}
and get the same results.
– Erik Olson
May 9 '14 at 20:35
2
@ErikOlson Your suggestion is right in the above case, where we need to find all the document that with red color and to apply the projection on them only. But let's say if somebody requires to find out all the document that have color blue but it should return only those element of that shapes array that have color red. In this case the above query can be referenced by somebody else also..
– Vicky
May 11 '14 at 9:22
This seems to be the easiest, but I can't make it work it. It only returns the first matching subdocument.
– newman
Aug 29 '15 at 23:05
add a comment |
1
This works for me. However, It appears that"shapes.color":"red"
in the query parameter (the first parameter of the find method) is not necessary. You can replace it with{}
and get the same results.
– Erik Olson
May 9 '14 at 20:35
2
@ErikOlson Your suggestion is right in the above case, where we need to find all the document that with red color and to apply the projection on them only. But let's say if somebody requires to find out all the document that have color blue but it should return only those element of that shapes array that have color red. In this case the above query can be referenced by somebody else also..
– Vicky
May 11 '14 at 9:22
This seems to be the easiest, but I can't make it work it. It only returns the first matching subdocument.
– newman
Aug 29 '15 at 23:05
1
1
This works for me. However, It appears that
"shapes.color":"red"
in the query parameter (the first parameter of the find method) is not necessary. You can replace it with {}
and get the same results.– Erik Olson
May 9 '14 at 20:35
This works for me. However, It appears that
"shapes.color":"red"
in the query parameter (the first parameter of the find method) is not necessary. You can replace it with {}
and get the same results.– Erik Olson
May 9 '14 at 20:35
2
2
@ErikOlson Your suggestion is right in the above case, where we need to find all the document that with red color and to apply the projection on them only. But let's say if somebody requires to find out all the document that have color blue but it should return only those element of that shapes array that have color red. In this case the above query can be referenced by somebody else also..
– Vicky
May 11 '14 at 9:22
@ErikOlson Your suggestion is right in the above case, where we need to find all the document that with red color and to apply the projection on them only. But let's say if somebody requires to find out all the document that have color blue but it should return only those element of that shapes array that have color red. In this case the above query can be referenced by somebody else also..
– Vicky
May 11 '14 at 9:22
This seems to be the easiest, but I can't make it work it. It only returns the first matching subdocument.
– newman
Aug 29 '15 at 23:05
This seems to be the easiest, but I can't make it work it. It only returns the first matching subdocument.
– newman
Aug 29 '15 at 23:05
add a comment |
up vote
11
down vote
db.getCollection('aj').find({"shapes.color":"red"},{"shapes.$":1})
OUTPUTS
{
"shapes" : [
{
"shape" : "circle",
"color" : "red"
}
]
}
add a comment |
up vote
11
down vote
db.getCollection('aj').find({"shapes.color":"red"},{"shapes.$":1})
OUTPUTS
{
"shapes" : [
{
"shape" : "circle",
"color" : "red"
}
]
}
add a comment |
up vote
11
down vote
up vote
11
down vote
db.getCollection('aj').find({"shapes.color":"red"},{"shapes.$":1})
OUTPUTS
{
"shapes" : [
{
"shape" : "circle",
"color" : "red"
}
]
}
db.getCollection('aj').find({"shapes.color":"red"},{"shapes.$":1})
OUTPUTS
{
"shapes" : [
{
"shape" : "circle",
"color" : "red"
}
]
}
answered Dec 7 '16 at 6:25
Viral Patel
54369
54369
add a comment |
add a comment |
up vote
7
down vote
Thanks to JohnnyHK.
Here I just want to add some more complex usage.
// 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"
}
]}
add a comment |
up vote
7
down vote
Thanks to JohnnyHK.
Here I just want to add some more complex usage.
// 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"
}
]}
add a comment |
up vote
7
down vote
up vote
7
down vote
Thanks to JohnnyHK.
Here I just want to add some more complex usage.
// 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"
}
]}
Thanks to JohnnyHK.
Here I just want to add some more complex usage.
// 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"
}
]}
edited May 23 '17 at 11:54
Community♦
11
11
answered Mar 23 '14 at 7:05
Eddy
1,8272427
1,8272427
add a comment |
add a comment |
up vote
5
down vote
You just need to run query
db.test.find(
{"shapes.color": "red"},
{shapes: {$elemMatch: {color: "red"}}});
output of this query is
{
"_id" : ObjectId("562e7c594c12942f08fe4192"),
"shapes" : [
{"shape" : "circle", "color" : "red"}
]
}
as you expected it'll gives the exact field from array that matches color:'red'.
add a comment |
up vote
5
down vote
You just need to run query
db.test.find(
{"shapes.color": "red"},
{shapes: {$elemMatch: {color: "red"}}});
output of this query is
{
"_id" : ObjectId("562e7c594c12942f08fe4192"),
"shapes" : [
{"shape" : "circle", "color" : "red"}
]
}
as you expected it'll gives the exact field from array that matches color:'red'.
add a comment |
up vote
5
down vote
up vote
5
down vote
You just need to run query
db.test.find(
{"shapes.color": "red"},
{shapes: {$elemMatch: {color: "red"}}});
output of this query is
{
"_id" : ObjectId("562e7c594c12942f08fe4192"),
"shapes" : [
{"shape" : "circle", "color" : "red"}
]
}
as you expected it'll gives the exact field from array that matches color:'red'.
You just need to run query
db.test.find(
{"shapes.color": "red"},
{shapes: {$elemMatch: {color: "red"}}});
output of this query is
{
"_id" : ObjectId("562e7c594c12942f08fe4192"),
"shapes" : [
{"shape" : "circle", "color" : "red"}
]
}
as you expected it'll gives the exact field from array that matches color:'red'.
edited Nov 10 '16 at 7:50
azhar
8341932
8341932
answered Sep 17 '16 at 17:22
Vaibhav Patil
542514
542514
add a comment |
add a comment |
up vote
2
down vote
along with $project it will be more appropriate other wise matching elements will be clubbed together with other elements in document.
db.test.aggregate(
{ "$unwind" : "$shapes" },
{ "$match" : {
"shapes.color": "red"
}},
{"$project":{
"_id":1,
"item":1
}}
)
can you pls describe that this accomplishes with an input and output set?
– Alexander Mills
Nov 24 '15 at 17:13
add a comment |
up vote
2
down vote
along with $project it will be more appropriate other wise matching elements will be clubbed together with other elements in document.
db.test.aggregate(
{ "$unwind" : "$shapes" },
{ "$match" : {
"shapes.color": "red"
}},
{"$project":{
"_id":1,
"item":1
}}
)
can you pls describe that this accomplishes with an input and output set?
– Alexander Mills
Nov 24 '15 at 17:13
add a comment |
up vote
2
down vote
up vote
2
down vote
along with $project it will be more appropriate other wise matching elements will be clubbed together with other elements in document.
db.test.aggregate(
{ "$unwind" : "$shapes" },
{ "$match" : {
"shapes.color": "red"
}},
{"$project":{
"_id":1,
"item":1
}}
)
along with $project it will be more appropriate other wise matching elements will be clubbed together with other elements in document.
db.test.aggregate(
{ "$unwind" : "$shapes" },
{ "$match" : {
"shapes.color": "red"
}},
{"$project":{
"_id":1,
"item":1
}}
)
answered Feb 9 '13 at 15:45
shakthydoss
1,32521530
1,32521530
can you pls describe that this accomplishes with an input and output set?
– Alexander Mills
Nov 24 '15 at 17:13
add a comment |
can you pls describe that this accomplishes with an input and output set?
– Alexander Mills
Nov 24 '15 at 17:13
can you pls describe that this accomplishes with an input and output set?
– Alexander Mills
Nov 24 '15 at 17:13
can you pls describe that this accomplishes with an input and output set?
– Alexander Mills
Nov 24 '15 at 17:13
add a comment |
up vote
0
down vote
db.test.find( {"shapes.color": "red"}, {_id: 0})
Welcome to Stack Overflow! Thank you for the code snippet, which might provide some limited, immediate help. A proper explanation would greatly improve its long-term value by describing why this is a good solution to the problem, and would make it more useful to future readers with other similar questions. Please edit your answer to add some explanation, including the assumptions you've made.
– sepehr
Oct 25 at 15:06
add a comment |
up vote
0
down vote
db.test.find( {"shapes.color": "red"}, {_id: 0})
Welcome to Stack Overflow! Thank you for the code snippet, which might provide some limited, immediate help. A proper explanation would greatly improve its long-term value by describing why this is a good solution to the problem, and would make it more useful to future readers with other similar questions. Please edit your answer to add some explanation, including the assumptions you've made.
– sepehr
Oct 25 at 15:06
add a comment |
up vote
0
down vote
up vote
0
down vote
db.test.find( {"shapes.color": "red"}, {_id: 0})
db.test.find( {"shapes.color": "red"}, {_id: 0})
edited Oct 25 at 8:04
Suraj Rao
22.4k75469
22.4k75469
answered Oct 25 at 8:00
Poonam Agrawal
92
92
Welcome to Stack Overflow! Thank you for the code snippet, which might provide some limited, immediate help. A proper explanation would greatly improve its long-term value by describing why this is a good solution to the problem, and would make it more useful to future readers with other similar questions. Please edit your answer to add some explanation, including the assumptions you've made.
– sepehr
Oct 25 at 15:06
add a comment |
Welcome to Stack Overflow! Thank you for the code snippet, which might provide some limited, immediate help. A proper explanation would greatly improve its long-term value by describing why this is a good solution to the problem, and would make it more useful to future readers with other similar questions. Please edit your answer to add some explanation, including the assumptions you've made.
– sepehr
Oct 25 at 15:06
Welcome to Stack Overflow! Thank you for the code snippet, which might provide some limited, immediate help. A proper explanation would greatly improve its long-term value by describing why this is a good solution to the problem, and would make it more useful to future readers with other similar questions. Please edit your answer to add some explanation, including the assumptions you've made.
– sepehr
Oct 25 at 15:06
Welcome to Stack Overflow! Thank you for the code snippet, which might provide some limited, immediate help. A proper explanation would greatly improve its long-term value by describing why this is a good solution to the problem, and would make it more useful to future readers with other similar questions. Please edit your answer to add some explanation, including the assumptions you've made.
– sepehr
Oct 25 at 15:06
add a comment |
protected by Samuel Liew♦ Oct 5 '15 at 9:21
Thank you for your interest in this question.
Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).
Would you like to answer one of these unanswered questions instead?