I have the following type of document where I have to find instock elements based on a given value. i.e Return all instocks where ele = 5.
{"item":"journal",
"instock":[
{ "warehouse":"A", "ele":[2,4,5] },
{ "warehouse":"C", "ele":[8,5,2] },
{ "warehouse":"F", "ele":[3] },
{ "warehouse":"K", "ele":[2,8,4] }
]
}
I tried to use $elemMatch but it just produces the first element.
db.inventory.find({"item": "journal"}, {"_id": 0, "item": 0, "instock":{$elemMatch: {"ele": {$in: [5]}}} })
But it just gives:
{ "instock" : [
{ "warehouse" : "A", "ele" : [ 2, 4, 5 ] }
]}
And the expectation is
{ "instock" : [
{ "warehouse" : "A", "ele" : [ 2, 4, 5 ] },
{ "warehouse" : "C", "ele" : [ 8, 5, 2 ] }
]}
How should I get the expected result?
The $elemMatch or instock.$ will return first match document in find()'s projection,
You can use aggregation expression in projection from MongoDB 4.4, for your example use array $filter operator in projection,
db.collection.find({
"item": "journal"
},
{
instock: {
$filter: {
input: "$instock",
cond: { $in: [5, "$$this.ele"] }
}
}
})
Playground
For the old version you can use aggregate() using above same operator.
I suggest you to unwind the instock array, then filter the data, and finally if you want you can group the result by item to obtain the desired result.
Here is an exemple: solution
Related
I want to be able to specify in my MongoDB query to, for each document, return the number of elements in a specific sub field (var_2 in this case), instead of the whole array. Example document:
{
"_id": "abc123",
"var_1": "A",
"var_2": [
"A",
"B",
"C"
]
}
I have tried this but it returns the whole array:
db.collection.find({var_1: "A"}, {var_1: 1, var_2: 1})
Desired output:
{
"_id": "abc123",
"var_1": "A",
"var_2": 3
}
Thanks in advance!
You can retrieve the size of your array by using $size but with an aggregation since it's not supported in find' projection:
db.your_collection.aggregate([
{
"$match": {
var_1: "A"
}
},
{
"$project": {
"var_1": 1,
"var_2": {
"$size": "$var_2"
}
}
}
])
Result
[
{
"_id": "abc123",
"var_1": "A",
"var_2": 3
}
]
This question already has answers here:
MongoDb query condition on comparing 2 fields
(4 answers)
Closed 4 years ago.
Given the collection "example":
{_id: 1, prediction: "H", result: "A"}
{_id: 2, prediction: "H", result: "H"}
{_id: 3, prediction: "A", result: "A"}
What do I need to do to find records where the prediction and result values match? ie. documents 2 and 3?
Finding all the "H" predictions is:
db.example.find( { prediction: "H" } )
But I need to replace the literal "H" with the value in the result field of the same document.
Edit: Sorry, I should have said that I was using Mongo 3.6.
You should be able to do this with an aggregation query, try out the $redact stage:
db.test.aggregate(
[
{ $redact: {
$cond: {
if: { $eq: [ "$prediction", "$result" ] },
then: "$$DESCEND",
else: "$$PRUNE"
}
}
}
]
);
This will yield the result:
{ "_id" : 2, "prediction" : "H", "result" : "H" }
{ "_id" : 3, "prediction" : "A", "result" : "A" }
More info on redact can be found here - https://docs.mongodb.com/manual/reference/operator/aggregation/redact/#redact-aggregation
You can use agregation
db.example.aggregate(
[
{
$project:
{
_id: 1,
prediction: 1,
result: 1,
compare: { $eq: [ "$prediction", "$result" ] }
}
},
{
$match:
{
compare: true
}
}
]
)
If you are using 3.6, this will work. refer this
db.getCollection('TEST').find( { $where: function() {
return this.prediction == this.result
} })
Result :
{
"_id" : 2,
"prediction" : "H",
"result" : "H"
}
{
"_id" : 3,
"prediction" : "A",
"result" : "A"
}
How can I execute a query in MongoDB that returns _id if FirstArray and SecondArray has elements in common in "Name" field?
This is the collection structure:
{
"_id" : ObjectId("58b8d9e3b2b4e07bff8feed5"),
"FirstArray" : [
{
"Name" : "A",
"Something" : "200 ",
},
{
"Name" : "GF",
"Something" : "100 ",
}
],
"SecondArray" : [
{
"Name" : "BC",
"Something" : "200 ",
},
{
"Name" : "A",
"Something" : "100 ",
}
]
}
3.6 Update:
Use $match with $expr. $expr allows use of aggregation expressions inside $match stage.
db.collection.aggregate([
{"$match":{
"$expr":{
"$eq":[
{"$size":{"$setIntersection":["$FirstArray.Name","$SecondArray.Name"]}},
0
]
}
}},
{"$project":{"_id":1}}
])
Old version:
You can try $redact with $setIntersection for your query.
$setIntersection to compare the FirstArrays Names with SecondArrays Names and return array of common names documents followed by $size and $redact and compare result with 0 to keep and else remove the document.
db.collection.aggregate(
[{
$redact: {
$cond: {
if: {
$eq: [{
$size: {
$setIntersection: ["$FirstArray.Name", "$SecondArray.Name"]
}
}, 0]
},
then: "$$KEEP",
else: "$$PRUNE"
}
}
}, {
$project: {
_id: 1
}
}]
)
This question already has answers here:
Retrieve only the queried element in an object array in MongoDB collection
(18 answers)
Closed 5 years ago.
The community reviewed whether to reopen this question 5 months ago and left it closed:
Original close reason(s) were not resolved
I have array in subdocument like this
{
"_id" : ObjectId("512e28984815cbfcb21646a7"),
"list" : [
{
"a" : 1
},
{
"a" : 2
},
{
"a" : 3
},
{
"a" : 4
},
{
"a" : 5
}
]
}
Can I filter subdocument for a > 3
My expect result below
{
"_id" : ObjectId("512e28984815cbfcb21646a7"),
"list" : [
{
"a" : 4
},
{
"a" : 5
}
]
}
I try to use $elemMatch but returns the first matching element in the array
My query:
db.test.find( { _id" : ObjectId("512e28984815cbfcb21646a7") }, {
list: {
$elemMatch:
{ a: { $gt:3 }
}
}
} )
The result return one element in array
{ "_id" : ObjectId("512e28984815cbfcb21646a7"), "list" : [ { "a" : 4 } ] }
and I try to use aggregate with $match but not work
db.test.aggregate({$match:{_id:ObjectId("512e28984815cbfcb21646a7"), 'list.a':{$gte:5} }})
It's return all element in array
{
"_id" : ObjectId("512e28984815cbfcb21646a7"),
"list" : [
{
"a" : 1
},
{
"a" : 2
},
{
"a" : 3
},
{
"a" : 4
},
{
"a" : 5
}
]
}
Can I filter element in array to get result as expect result?
Using aggregate is the right approach, but you need to $unwind the list array before applying the $match so that you can filter individual elements and then use $group to put it back together:
db.test.aggregate([
{ $match: {_id: ObjectId("512e28984815cbfcb21646a7")}},
{ $unwind: '$list'},
{ $match: {'list.a': {$gt: 3}}},
{ $group: {_id: '$_id', list: {$push: '$list.a'}}}
])
outputs:
{
"result": [
{
"_id": ObjectId("512e28984815cbfcb21646a7"),
"list": [
4,
5
]
}
],
"ok": 1
}
MongoDB 3.2 Update
Starting with the 3.2 release, you can use the new $filter aggregation operator to do this more efficiently by only including the list elements you want during a $project:
db.test.aggregate([
{ $match: {_id: ObjectId("512e28984815cbfcb21646a7")}},
{ $project: {
list: {$filter: {
input: '$list',
as: 'item',
cond: {$gt: ['$$item.a', 3]}
}}
}}
])
$and:
get data between 0-5:
cond: {
$and: [
{ $gt: [ "$$item.a", 0 ] },
{ $lt: [ "$$item.a", 5) ] }
]}
Above solution works best if multiple matching sub documents are required.
$elemMatch also comes in very use if single matching sub document is required as output
db.test.find({list: {$elemMatch: {a: 1}}}, {'list.$': 1})
Result:
{
"_id": ObjectId("..."),
"list": [{a: 1}]
}
Use $filter aggregation
Selects a subset of the array to return based on the specified
condition. Returns an array with only those elements that match the
condition. The returned elements are in the original order.
db.test.aggregate([
{$match: {"list.a": {$gt:3}}}, // <-- match only the document which have a matching element
{$project: {
list: {$filter: {
input: "$list",
as: "list",
cond: {$gt: ["$$list.a", 3]} //<-- filter sub-array based on condition
}}
}}
]);
Ho can I select specific array element by index defined in document?
For example I have following document:
{ "_id" : 1, "idx" : 1, "vals" : [ 1, 2 ] }
And I want to select vals element defined by idx index.
I have managed to select specific array element defined by literal:
> db.test.find({_id:1}, {vals:{$slice:[1, 1]}})
{ "_id" : 1, "idx" : 1, "vals" : [ 2 ] }
But how can I use idx field in $slice operator?
The optimal way to do this is in MongoDB 3.2 using the $arrayElemAt operator:
db.test.aggregate([
{ "$project": { "vals": { "$arrayElemAt": [ "$vals", "$idx" ] } } }
])
You can also use findOne if you use _id in your query criteria and to get idx value.
var idx = db.test.findOne({ "_id": 1 }).idx
db.test.find({ "_id": 1 }, { "vals": { "$slice": [ idx, 1 ]}})
With find you need to use cursor.map
db.test.find().map(function(doc) {
doc.vals = doc.vals.slice(doc.idx, 2);
return doc;
})
Result:
[ { "_id" : 1, "asd" : 1, "vals" : [ 2 ] } ]