Retrieve specific index in Mongo array - arrays

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

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

Referencing another field of subdocument in `$elemMatch`

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

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

In Mongo, how can I use $IN condition with values coming from another field?

Data :
{
_id :1111,
col1_array : ['a','b','c'],
col2_array : ['a','f','g']
}
I would like to find all documents where col2_array contains any value of col1_array. I tried using the $IN condition but failed to refer to other field content as an array. How can I do ?
db.collection.aggregate([
{ $unwind: "$col2_array" },
{
$project:
{
index: { $indexOfArray: ["$col1_array", "$col2_array"] },
col2_array: 1,
col1_array: 1,
_id: 1
}
},
{ $match: { index: { $gte: 0 } } },
{ $group: { _id: "$_id" } }
])
After that you have to find records of this particular _id
db.collection.find({_id:{$in:[result_of_above_query_ids]}})
I have finally resolved the problem using arrays intersections I have just discovered digging Mongo documentation :
db.getCollection('test').aggregate([
{$set : {
commonValues: { $setIntersection: [ "$col1_array", "$col2_array"]},
}},
{$match : {"commonValues" : {"$ne" : []}}}
])

Resources