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

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.

Related

How to query certain elements of an array of objects? (mongodb)

say I have a mongo DB collection with records as follows:
{
email: "person1#gmail.com",
plans: [
{planName: "plan1", dataValue = 100},
{planName: "plan2", dataValue = 50}
]
},
{
email: "person2#gmail.com",
plans: [
{planName: "plan3", dataValue = 25},
{planName: "plan4", dataValue = 12.5}
]
}
and I want to query such that only the dataValue returns where the email is "person1#gmail.com" and the planName is "plan1". How would I approach this?
You can accomplish this using the Aggregation Pipeline.
The pipeline may look like this:
db.collection.aggregate([
{ $match: { "email" :"person1#gmail.com", "plans.planName": "plan1" }},
{ $unwind: "$plans" },
{ $match: { "plans.planName": "plan1" }},
{ $project: { "_id": 0, "dataValue": "$plans.dataValue" }}
])
The first $match stage will retrieve documents where the email field is equal to person1#gmail.com and any of the elements in the plans array has a planName equal to plan1.
The second $unwind stage will output one document per element in the plans array. The plans field will now be an object containing a single plan object.
In the third $match stage, the unwound documents are further matched against to only include documents with a plans.planName of plan1. Finally, the $project stage excludes the _id field and projects a single dataValue field with a value of plans.dataValue.
Note that with this approach, if the email field is not unique you may have multiple documents consist with just a dataValue field.

MongoDB: find document whose subdocument contains a given value

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"}
]} )

removing element of an array that with a match of certain element

I have a mongodb collection.
{ user_id: 1,
items : [ { _id: 1 }, { _id: 2}, {_id:3} ] }
I want to remove the items of the array having specific id. Can anybody explain what is wrong with the above query.
db.col.findOneAndUpdate({user_id:1},{$pull:{items:{$elemMatch:{_id:2}}}})
$pull takes an expression as a parameter so you don't have to use $elemMatch (doesn't work in this case). Try:
db.col.update({user_id:1},{$pull:{items:{_id:2}}})
So expression in this case means that MongoDB will remove the document having _id set to 2 but that document can have other properties as well.

How to filter on more than one record mongodb embedded documents

This is my model:
order:{
_id: 88565,
activity:
[
{_id: 57235, content: "foo"},
{_id: 57236, content: "bar"}
]
}
This is my query:
db.order.find({
"$and": [
{
"activity.content "bar"
},
{
"activity._id": 57235
}
]
});
This query will select the order with id 88565 even if the conditions are satisfied together by 2 different embedded activities.
I would expect that this query returned nothing.
I know that I can use elemMatch to filter embedded documents with more precision but this behaviour seems very confusing.
Is there a way to obtain a proper filtering where an AND clause has a single embedded document scope?

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");
}
})