mongo find function mismatch - mongodb

Mongo version : 3.2.8
My sample json is as below
My query to fetch name equal to apple doesn't work.
db.collection.find( { "products.foods.name": "apple" } )
Instead it fetches all the records, strange?
Neither does the $eq, $lt or $gt work. They result with the entire data.
db.aggregation.find( { "products.foods.min_price": {$eq:10} } )
Thanks in advance.

If your entire document is in an _id, then if the query matches db.collection.find( { "products.foods.name": "apple" } ) even though it is a document in foods array the entire document will be displayed, so that you are getting other fruits as well.
To Solve this first use $unwind the aggregation pipeline to break the foods array into individual documents and then use $match.
Please refer this post, It is a similar question and I have answered the steps in detail in that post.

Try with this:
db.test.aggregate([
{$match: {'products.foods.name': 'apple'}}])
Taken from Retrieve only the queried element in an object array in MongoDB collection
You can try other examples from that post.

Try with this:db.Exercise.aggregate([
{$match: {'products.foods.min_price': 10}}])

The solution is to n $unwind both the arrays.
db.foods.aggregate([
{ $unwind : "$products" },
{ $unwind : "$products.foods" },
{ $match : { "products.foods.min_price": 10 }}
])

Related

MongoDB: Count documents in array of nested document for a specific field in the main document

i have this collection on MongoDB
Collection structure
The main document has like main Field wineName. Now in this image the array of nested document (reviews) is only one but could be several reviews for each wines.
My question is how can I count the number of reviews that I have for each Wine?
Thank you in advance for the answer
You can use the $size aggregation operator for this:
db.collection.aggregate([
{
"$addFields": {
"reviewCount": {
$size: "$reviews"
}
}
}
])
Mongo Playground

how to find data in array of object in mongodb

I find in array of object and match _id and is_active both key
ex
{
_id:'12333333333333'
name:'test',
array:[{
id:'1233449',
is_active:true
},{
id:'7987979',
is_active:false
},{
id:'9558555',
is_active:true
},{
id:'2564654',
is_active:false
}]
}
find data using mongo query
db.getCollection('demo').find({'array.id':'7987979','array.is_active':false});
not working
The reason it isnt working for you is because you are running a find operation on the array directly.
When querying for things within an array you can use $elemMatch to obtain an entire document that contains the matching array element.
If you need a custom output you can use Aggregation, with the $unwind operation in the pipeline on the array field.
Try this out on your collection and understand what it is doing
db.collectionName.aggregate([{
$unwind:"$array"
},{
$match:{
$and:[
{"array.id":'your_id'},
{"array.is_active":boolean}
]
}
}
])

Need MongoDB Query

I'm sorry but I'm little confuse with a query , Kindly help. suppose we've one document that contains
{
"_id":100,
"name":"Demarcus Audette",
"scores":[
{
"score":47.42608580155614,
"type":"exam"
},
{
"score":44.83416623719906,
"type":"quiz"
},
{
"score":39.01726616178844,
"type":"homework"
},
"score":89.01726616178844,
"type":"homework"
}
]
}
And I want to write a query that should return only rows which contains homework in that , that means the out put should be like below
{
"_id":100,
"name":"Demarcus Audette",
"scores":[
{
"score":39.01726616178844,
"type":"homework"
},
"score":89.01726616178844,
"type":"homework"
}
]
}
Kindly suggest. Thanks in Advance
Use the $elemMatch operator.
db.collection.find({ "scores": { $elemMatch: { "type": "homework" } } } );
EDIT
What you are asking is not possible. You will need the above query and filter out the rest in whatever language you are programming. You can also use an aggregate function using $unwind and $match.
db.collection.aggregate(
{$unwind: "$messages"},
{$match: {"scores.type": "homework"}}
);
$unwind flattens your array and $match is your actually query which will return matching documents. Please note that $unwind will create a different document for each element in your array. This means you will get two results when you filter on 'homework' according to your example.

How to use $elemMatch on aggregate's projection?

This is my object:
{ "_id" : ObjectId("53fdcb6796cb9b9aa86f05b9"), "list" : [ "a", "b" ], "complist" : [ { "a" : "a", "b" : "b" }, { "a" : "c", "b" : "d" } ] }
And this is what I want to accomplish: check if "list" contains a certain element and get only the field "a" from the objects on "complist" while reading the document regardless of any of these values. I'm building a forum system, this is the query that will return the details of a forum. I need to read the forum information while knowing if the user is in the forum's white list.
With a find I can use the query
db.itens.find({},{list:{$elemMatch:{$in:["a"]}}})
to get only the first element that matches a certain value. This way I can just check if the returned array is not empty and I know if "list" contains the value I'm looking for. I can't do it on the query because I want the document regardless of it containing the value I'm looking for in the "list" value. I need the document AND know if "list" has a certain value.
With an aggregate I can use the query
db.itens.aggregate({$project:{"complist.a":1}})
to read only the field "a" of the objects contained in complist. This is going to get the forum's threads basic information, I don't want all the information of the threads, just a couple of things.
But when I try to use the query
db.itens.aggregate({$project:{"complist.b":1,list:{$elemMatch:{$in:["a"]}}}})
to try and do both, it throws me an error saying the operator $elemMatch is not valid.
Am I doing something wrong here with the $elemMatch in aggregate? Is there a better way to accomplish this?
Quite on old question but literally none of the proposed answers are good.
TLDR:
You can't use $elemMatch in a $project stage. but you can achieve the same result using other aggregation operators like $filter.
db.itens.aggregate([
{
$project: {
compList: {
$filter: {
input: "$complist",
as: "item",
cond: {$eq: ["$$item.a", 1]}
}
}
}
}
])
And if you want just the first item from the array that matches the condition similarly to what $elemMatch does you can incorporate $arrayElemAt
In Depth Explanation:
First let's understand $elemMatch:
$elemMatch is a query expressions while also this projection version of it exists this refers to a query projection and not $project aggregation stage.
So what? what does this have to do with anything? well a $project stage has certain input structure it can have while the one we want to use is:
<field>: <expression>
What is a valid expression?
Expressions can include field paths, literals, system variables, expression objects, and expression operators. Expressions can be nested.
So we want to use an expression operator, but as you can see from the doc's $elemMatch is not part of it. hence it's not a valid expression to be used in an aggregation $project stage.
For some reason $elemMatch doesn't work in aggregations. You need to use the new $filter operator in Mongo 3.2. See https://docs.mongodb.org/manual/reference/operator/aggregation/filter/
The answer to this question maybe help.
db.collection_name.aggregate({
"$match": {
"complist": {
"$elemMatch": {
"a": "a"
}
}
}
});
Actually, the simplest solution is to just $unwind your array, then $match the appropriate documents. You can wind-up the appropriate documents again using $group and $push.
Although the question is old, here is my contribution for November 2017.
I had similar problem and doing two consecutive match operations worked for me. The code below is a subset of my whole code and I changed elements names, so it's not tested. Anyway this should point you in the right direction.
db.collection.aggregate([
{
"$match": {
"_id": "ID1"
}
},
{
"$unwind": "$sub_collection"
},
{
"$match": {
"sub_collection.field_I_want_to_match": "value"
}
}
])
For aggregations simply use $expr:
db.items.aggregate([
{
"$match": {
"$expr": {"$in": ["a", "$list"]}
}
},
])
Well, it happens you can use "array.field" on a find's projection block.
db.itens.find({},{"complist.b":1,list:{$elemMatch:{$in:["a"]}}})
did what I needed.

Retrieve Array Of Documents in MongoDB

I have a MongoDB Document like as follows
{
"_id":1,
"name":"XYZ"
ExamScores:[
{ExamName:"Maths", UnitTest:1, Score:100},
{ExamName:"Maths", UnitTest:2, Score:80},
{ExamName:"Science", UnitTest:1, Score:90}
]
}
I Need to retrieve this document so that it has to show only Maths Array. Like as follows
{
"_id":1,
"name":"XYZ"
ExamScores:[
{ExamName:"Maths", UnitTest:1, Score:100},
{ExamName:"Maths", UnitTest:2, Score:80},
]
}
How Can I Do That ?
As #karin states there is no, normal, in query method of doing this.
In version 2.2 you can use $elemMatch to project the first matching result from ExamScores but you cannot get multiple.
That being said, the aggregation framework can do this:
db.col.aggregate([
{$unwind: '$ExamScores'},
{$match: {'ExamScores.ExamName':"Maths"}},
{$group: {_id: '$_id', name: '$name', ExamScores: {$push: '$ExamScores'}}}
])
Something like that anyway.
This has been asked before MongoDB query to limit values based on condition, the only answer there says it is not possible, but that there is a request to implement that.