Referencing another field of subdocument in `$elemMatch` - database

I'm trying to perform an $elemMatch in a $match aggregation stage where I want to find if there is a document in an array (commitments) of subdocuments whose property tracksThisWeek is smaller than its frequency property, but I'm not sure how can I reference another field of the subdocument in question, I came up with:
{
$match: {
commitments: {
$elemMatch: {
tracksThisWeek: {
$lt: '$frequency',
},
},
},
},
},
I have a document in the collection that should be returned from this aggregation but isn't, any help is appreciated :)

This can't be done, you can't reference any fields in the query language, What you can do is use $expr with aggregation operators, like this:
db.collection.aggregate([
{
$match: {
$expr: {
$gt: [
{
$size: {
$filter: {
input: "$commitments",
cond: {
$lt: [
"$$this.tracksThisWeek",
"$$this.frequency"
]
}
}
}
},
0
]
}
}
}
])
Mongo Playground

Related

Finding documents in mongodb collection by order of elements index of array field

Array field in collection:
"fruits": [ "fruits": [ "fruits": [
{"fruit1": "banana"}, {"fruit2": "apple"}, {"fruit3": "pear"},
{"fruit2": "apple"}, {"fruit4": "orange"}, {"fruit2": "apple"},
{"fruit3": "pear"}, {"fruit1": "banana"}, {"fruit4": "orange"},
{"fruit4": "orange"} {"fruit3": "pear"} {"fruit1": "banana"}
]
I need to find those documents in collections, where "banana" signed before "apple". Does mongodb allows to compare elements in array just like :
if (fruits.indexOf('banana') < fruits.indexOf('apple')) return true;
Or maybe there is any other method to get result i need?
MongoDB's array query operations do not support any positional search as you want.
You can, however, write a $where query to do what you want:
db.yourCollection.find({
$where: function() {
return (this.fruits.indexOf('banana') < this.fruits.indexOf('apple'))
}
})
Be advised though, you won't be able to use indexes here and the performance will be a problem.
Another approach you can take is to rethink the database design, if you can specify what it is you're trying to build, someone can give you specific advise.
One more approach: pre-calculate the boolean value before persisting to DB as a field and query on true / false.
Consider refactoring your schema if possible. The dynamic field names(i.e. fruit1, fruit2...) make it unnecessarily complicated to construct a query. Also, if you require frequent queries by array index, you should probably store your array entries in individual documents with some sort keys to facilitate sorting with index.
Nevertheless, it is achievable through $unwind and $group the documents again. With includeArrayIndex clause, you can get the index inside array.
db.collection.aggregate([
{
"$unwind": {
path: "$fruits",
includeArrayIndex: "idx"
}
},
{
"$addFields": {
fruits: {
"$objectToArray": "$fruits"
}
}
},
{
"$addFields": {
"bananaIdx": {
"$cond": {
"if": {
$eq: [
"banana",
{
$first: "$fruits.v"
}
]
},
"then": "$idx",
"else": "$$REMOVE"
}
},
"appleIdx": {
"$cond": {
"if": {
$eq: [
"apple",
{
$first: "$fruits.v"
}
]
},
"then": "$idx",
"else": "$$REMOVE"
}
}
}
},
{
$group: {
_id: "$_id",
fruits: {
$push: {
"$arrayToObject": "$fruits"
}
},
bananaIdx: {
$max: "$bananaIdx"
},
appleIdx: {
$max: "$appleIdx"
}
}
},
{
$match: {
$expr: {
$lt: [
"$bananaIdx",
"$appleIdx"
]
}
}
},
{
$unset: [
"bananaIdx",
"appleIdx"
]
}
])
Mongo Playground

return complete document with array containing satisfying condition in mongodb

I'm really stuck in this problem tried everything using aggregation and projection but no luck yet, please help me figure out some optimal way to do it so,
this is how my document look like
{
engagement: {
loves: [ 5e87a316457e874ce886356c,5e87a316457e874ce886356d, 5e87a316457e874ce886356e, 5e87a316457e874ce886356f],
loveCount: 4,
},
_id: 5e87c097c9374e6628a499b
}
Now i want to provide an element of loves array and based on that it will produce following result
element: 5e87a316457e874ce886356d
result
{
engagement: {
loves: [ 5e87a316457e874ce886356d],
loveCount: 4,
},
_id: 5e87c097c9374e6628a499b
}
element: XYZABC
result
{
engagement: {
loves: [],
loveCount: 4,
},
_id: 5e87c097c9374e6628a499b
}
You need $filter:
db.collection.aggregate([
{
$addFields: {
"engagement.loves": {
$filter: {
input: "$engagement.loves",
cond: {
$eq: [ "$$this", "5e87a316457e874ce886356d" ]
}
}
}
}
}
])
Mongo Playground

How to $match an item from the list

I am trying to display the items, that have specific name brand.
This is how that field looks like:
"brand" : [
[
"Samsung",
"Iphone",
"Huawei"
]
]
Tried that query, but I get 0 results:
db.collection.aggregate([{$match: { brand: "Samsung" }}])
Any ideas, what's wrong?
Your data model is an array of arrays so you have to deal with two dimensions. You can use $map along with $in first and then use $anyElementTrue to see if there's any sub-array matching your condition:
db.collection.aggregate([
{
$match: {
$expr: {
$anyElementTrue: {
$map: {
input: "$brand",
in: { $in: [ "Samsung", "$$this" ] }
}
}
}
}
},
{
$project: {
_id: 1,
color: 1
}
}
]);
Mongo Playground
EDIT: use $project to display only certain fields

Retrieve specific index in Mongo array

I have many entities with a big array for each on these entities in DB. I want to retrieve the first and the last element of the array (there is too much data if I get all the arrays). How can I do it ?
I tried this:
db.my_collection.findOne({my_query:'is_awesome'}, {'big_array.0':1})
but it doesn't work...
Thank's !
You can use an aggregation instead of findOne, performing a $match.
In Mongodb 3.2, there is $slice in aggregation, so you can $slice the item at position 1 and -1 :
db.my_collection.aggregate([{
$match: { my_query: 'is_awesome' }
}, {
$project: {
my_query: 1,
firstElement: { $slice: ["$big_array", 1] },
lastElement: { $slice: ["$big_array", -1] }
}
}])
In Mongodb < 3.2, $slice cant be used in aggregation, so you can use an $unwind and $group, the following is working in Mongodb 3.0 :
db.my_collection.aggregate([{
$match: { my_query: 'is_awesome' }
}, {
$unwind: "$big_array"
}, {
$group: {
_id: "$_id",
my_query: { $first: "$my_query" },
firstElement: { $first: "$big_array" },
lastElement: { $last: "$big_array" }
}
}])

mongodb: query in deep arrays

I am using MongoDb to store following JSON. How do I search for all the articles with "2d641b7c-3d74-4cfa-8267-d5a01ed2614b" in pageLayouts array.
{
"magazine": {
"articles": [
{
"articleLayouts": [
{
"pageLayouts": [
"2d641b7c-3d74-4cfa-8267-d5a01ed2614b"
]
}
]
}
]
}
}
In MongoDb documentation, they only specify searching for elements in array that only 1 level deep. For example: Searching in "http://docs.mongodb.org/manual/reference/bios-example-collection/"
db.bios.find(
{
awards: {
$elemMatch: {
award: "Turing Award",
year: { $gt: 1980 }
}
}
}
)
How do I search deeper arrays? as in the articles array in first JSON?
The $elemMatch operator matches documents that contain an array field with at least one element that matches all the specified query criteria.
and query as :
db.collectionName.find({"magazine.articles":{"$elemMatch":{"articleLayouts":{"$elemMatch":{"pageLayouts":{"$in":["2d641b7c-3d74-4cfa-8267-d5a01ed2614b"]}}}}}}).pretty()
You could use aggregation framework for this, in particular the $unwind operator as it deconstructs an array field from the input documents to output a document for each element. Each output document replaces the array with an element value. Thus you can do a $match query further down the pipeline to filter the documents and return the ones that match your criteria.
The following aggregation pipeline will return documents with "2d641b7c-3d74-4cfa-8267-d5a01ed2614b" in pageLayouts array:
var pipeline = [
{
"$match": {
"magazine.articles.articleLayouts.pageLayouts": "2d641b7c-3d74-4cfa-8267-d5a01ed2614b"
}
},
{
"$unwind": "$articles"
},
{
"$unwind": "$articles.articleLayouts"
},
{
"$unwind": "$articles.articleLayouts.pageLayouts"
},
{
"$match": {
"magazine.articles.articleLayouts.pageLayouts": "2d641b7c-3d74-4cfa-8267-d5a01ed2614b"
}
}
];
db.collection.aggregate(pipeline);

Resources