Filter a find by the last element on an embedded array - mongodb

In MongoDB how can I do a search that is filtered by a predicate applied on the last element of an embedded array?
I know if I wanted to do it on the first one I could do this:
db.inventory.find( { 'instock.0.qty': { $lte: 20 } } )
As specified on the documentation.
How do I write an analog query that looks at the last element, when I don't know the exact size of the embedded array?

we can use $arrayElemAt and pass -1 to it as a second argument to get the last element in the array
something like this
db.collection.find({
$expr: {
$gt: [
{
$arrayElemAt: ["$instock.qty", -1]
},
10
]
}
})
you can test it here Mongo Playground
hope it helps

Related

MongoDB $elemMatch comparison to field in same document

I'm wanting to create an aggregation step to match documents where the value of a field in a document exists within an array in the same document.
In a very worked example (note this is very simplified; this will be fitting into a larger existing pipeline), given documents:
{
"_id":{"$oid":"61a9085af9733d0274c41990"},
"myArray":[
{"$oid":"61a9085af9733d0274c41991"},
{"$oid":"61a9085af9733d0274c41992"},
{"$oid":"61a9085af9733d0274c41993"}
],
"myField":{"$oid":"61a9085af9733d0274c41991"} // < In 'myArray' collection
}
and
{
"_id":{"$oid":"61a9085af9733d0274c41990"},
"myArray":[
{"$oid":"61a9085af9733d0274c41991"},
{"$oid":"61a9085af9733d0274c41992"},
{"$oid":"61a9085af9733d0274c41993"}
],
"myField":{"$oid":"61a9085af9733d0274c41994"} // < Not in 'myArray' collection
}
I want to match the first one because the value of myField exists in the collection, but not the second document.
It feels like this should be a really simple $elemMatch operation with an $eq operator, but I can't make it work and every example I've found uses literals. What I've got currently is below, and I've tried with various combinations of quotes and dollar signs round myField.
[{
$match: {
myArray: {
$elemMatch: {
$eq: '$this.myField'
}
}
}
}]
Am I doing something very obviously wrong? Is it not possible to use the value of a field in the same document with an $eq?
Hoping that someone can come along and point out where I'm being stupid :)
Thanks
You can simply do a $in in an aggregation pipeline.
db.collection.aggregate([
{
"$match": {
$expr: {
"$in": [
"$myField",
"$myArray"
]
}
}
}
])
Here is the Mongo playground for your reference.

Find count of number of matched values in MongoDB document

In MongoDB, I want to find the document that have few or all values in them as specified in condition and find count of how many values matched.
My sudo target document:
{ "name":"room1",
"colors":["red","blue","green"],
"objects":["chair","bed"]
}
Now i want the documents that have any of the colors and any of the objects present in "room1". It should give result if even 1 or all of the color,objects are found.
{ "name":"room2",
"colors":["blue","green"],
"objects":["chair","bed","sofa","fridge"]
}
{ "name":"room3",
"colors":["yellow","pink"],
"objects":["chair"]
}
So the result should be as:
for room2: matchcount=4 as it shares 4 common values with room1
for room3: matchcount=1 as it shares 1 common value with room1
So far I have tried using $in and aggregate function to find count, it finds the documents with similar values in them but counting what is the match count is still a issue.
Lets suppose your collection name is "room", then query will be this :
db.room.aggregate([{
$match: { colors: { $in: ["red","blue","green"] } }
}, {$project:{matchedColorCount:{
$size: {
"$setIntersection": [["red","blue","green"], '$colors' ]
}
}}}])
Here, $match: { colors: { $in: ["red","blue","green"] } will tell which colors need to be find in the document.
and "$setIntersection": [["red","blue","green"], '$colors' ] will extract the matched colors from the particular document.
$size will provide the matched count of colors.

How to add order in $all operator in MongoDB

How can I get documents from mongo with an array containing some elements but IN THE SAME ORDER?
I know that $all do the job but ignoring the order of elements. The order in my case is important and I can't sort my arrays since it's describing a path that I want to keep the order.
111,222,333 is not the same as 222,111,333
Is there a way to do it using $all or maybe another operator in mongo aggregation framework?
You can avoid the first "intersect" field, is just to give you back as debug what MongoDB make with this command. You should create the $and operator dynamically.
db.Test6.aggregate([
{
$project: {
_id:1,
pages:1,
intersect: {$setIntersection: [[111,666], "$pages"]},
theCondition: {$let: {
vars: {
intersect: {$setIntersection: [[111,666], "$pages"]}
},
in: {
$cond:[ {$and:[
{$eq:[{$arrayElemAt:["$$intersect", 0]}, 111]},
{$eq:[{$arrayElemAt:["$$intersect", 1]}, 666]}
]} , true, false]
}
}
}
}
}
]);

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