MongoDB: find document whose subdocument contains a given value - mongodb

Given is a Document like the following
{
"type" : "sometype",
"title" : "sometitle",
"references":{
"1": "someref",
"2": "otherref",
"3": ""
}
}
How can I find all documents which have the reference someref set in the subdocument references?
A simple find({references: "someref"}) doesn't work because references is not a valid array.

Document structure is not ideal for this kind of queries but you can use aggregation with $objectToArray operator:
db.collection.aggregate([
{$addFields: {refs: {$objectToArray: "$references"}}},
{$match: {"refs.v": "someref"}},
{$project:{refs: 0}}
])

If the number of subdocument fields you are looking into is small, then you can simply filter your collection using $or
db.collection.find(
{ $or: [
{"references.1": "someref"},
{"references.2": "someref"},
{"references.3": "someref"}
]} )

Related

How to match two different Object ids using MongoDB find() query?

I have an entry like below,
[
{
"_id":ObjectId("59ce020caa87df4da0ee2c78"),
"name": "Tom",
"owner_id": ObjectId("59ce020caa87df4da0ee2c78")
},
{
"_id":ObjectId("59ce020caa87df4da0ee2c79"),
"name": "John",
"owner_id": ObjectId("59ce020caa87df4da0ee2c78")
}
]
now, I need to find the person whose _id is equal to owner_id using find() in MongoDB.
Note, we can't not use $match (aggregation) due to some reason.
I am using this query,
db.people.find({ $where: "this._id == this.owner_id" })
but, it's not returning the expected output. Can anyone help me with this.
Thanks.
Using $expr and $eq you can get desired values avoiding the use of $where into a find stage (not aggregation necessary).
db.collection.find({
"$expr": {
"$eq": [
"$_id",
"$owner_id"
]
}
})

Can I use the existing document field value in the $push of mongodb ?, I tried the below approach , its not working

db.getCollection('test').update({
"b_id": "3"
},
{
"$push": {"books": {
"status": "available",
"shelf_id": "$shelf_id",
"rack_no": "$rack_no",
"book_id": new ObjectId()
}
}
})
shelf_id and rack_no are already available in the document. Here, I need to create the array "books" which includes status, shelf_id, rack_no and book_id.
I am not able to get the existing value of shelf_id and rack_no in this case. It considers $rack_id as string instead the value from the document. Please suggest a way.
Yes, you can do that in MongoDB 4.2, using the pipeline form of update, but not with $push.
Passing an array of pipeline stages as the second argument to update will allow you to use aggregation operators.
The $push aggregation operator behaves very differently from the $push update operator, so in a pipeline use $set with $concatArrays, like:
db.getCollection('test').update({
"b_id": "3"
},
[{$set:
{
books: {$concatArrays:[
"$books",
[{
"status": "available",
"shelf_id": "$shelf_id",
"rack_no": "$rack_no",
"book_id": new ObjectId()
}]
]}
}
}])

How to check order of Array element in Mongodb?

In MongoDB, is there any easy way to check Order of element in Array? For example I have a document like this:
{
_id: 1,
tags: ["mongodb", "rethinkdb", "couchbase", "others"]
}
I would like to check in tags field if mongodb come before rethinkdb or not(lets see in array element, mongodb=0, rethinkdb=1 index, so mongodb come first and our case match.)?
but if there is another document (like below) where rethinkdb comes before mongodb,It case does not match.
{
_id: 2,
tags: ["rethinkdb", "mongodb", "couchbase"]
}
Here mongodb(1) comes after rethinkdb(0) so our case does not match.
Your question is not really as clear as you think it is, and thus why there are several ways to answer it:
If you are looking just to find out if a document has "mongodb" as the first element of the array then you just issue a query like this:
db.collection.find({ "tags.0": "mongodb" })
And that will return only the documents that match the given value at the specified index position using "dot notation".
If you actually expect to match if an array is in an "expected order" then you can get some help from the aggregation pipeline and set operators that are available and other features in MongoDB 2.6:
db.collection.aggregate([
{ "$project": {
"$_id": "$$ROOT",
"matched": { "$setEquals": [
"$tags",
["mongodb", "rethinkdb", "couchbase", "others"]
]}
}},
{ "$match": { "matched": true }}
])
Or if your want is to make sure that the "mongodb" value comes before the "rethinkdb" value, then you will need to evaluate in JavaScript with mapReduce, or something equally not nice like the $where operator:
db.collection.find({
"$where": function() {
return this.tags.indexOf("mongodb") < this.tags.indexOf("rethinkdb");
}
})

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.

Mongodb: find documents where at least one array element matches?

I have a document with the following structure:
[{
"items": [
{
"sent_to_lab": 123,
"received_from_lab": 456,
},
{
"sent_to_lab": 123,
},
]
}
... more orders ...
]
I want to fetch all orders where at least one item matches the following criteria:
'$and': [
{'items.sent_to_lab': {'$exists': True}},
{'items.received_from_lab': {'$exists': False}},
]
So in this case, I would like to return the aforementioned item, because at least one element of the items array matches my criteria.
How can I do this in mongo?
You need to use the $elemMatch operator:
db.collection.find({items:
{$elemMatch:{
sent_to_lab:{$exists:true},
received_from_lab:{$exists:false}}
})
This is query $elemMatch - if you only want to get back the item that matched the condition (and not the entire document with the whole array) then you can use the projection $elemMatch operator similarly.