How do I create a query from an array with mongoose? - arrays

So I have this array with 3 IDs: ["56ba576f66b9add00e4d3082","56b9252266b9add00e4d3080","56b908f166b9add00e4d307f"]
I want to build a query that says something like that:
userModel.find({ $or:
[
{_id:"56ba576f66b9add00e4d3082"},
{_id:"56ba576f66b9add00e4d3082"},
{_id:"56ba576f66b9add00e4d3082"}
]},
{email:true});
Is there some sort of a method to use in order to produce such query with the array?
Should I be using mapReduce or the $where method or anything else? (I never used those before so I'm not sure if this is the case or not)
Perhaps I'm asking the wrong question? Any recommendations are very much welcome.

If you just have one element then just use $in:
var array = [
"56ba576f66b9add00e4d3082",
"56b9252266b9add00e4d3080",
"56b908f166b9add00e4d307f"
];
userModel.find({ "_id": { "$in": array } }, { "email": true });
An $in condition is basically an $or applied to the same field.

Try using the map function:
array = ["56ba576f66b9add00e4d3082","56b9252266b9add00e4d3080","56b908f166b9add00e4d307f"]
userModel.find({ $or: array.map(function(id) {
return {_id: id};
})}, {email:true});

Related

MongoDB aggregation for finding an index of array nested object with $gt condition

I am trying to get the positional index of an array element (to push more elements at $position). I'm specifically targeting a single document (say, id=x)
Simplified document:
{
id:"x",
samples:[{"timestamp":123},{"timestamp":234},{"timestamp":345}}
}
Aggregation pipeline:
collection.aggregate([{"$match": {"id": "x"}},
{"$project": {"matchedIndex": {"$indexOfArray": ["$samples.timestamp", 234]}}}])
This works to return matchedIndex=1 .
Now I would like to find the index of 'samples' where timestamp is greater than say 235, which should return '2'
I have tried combinations of:
collection.aggregate([{"$match": {"UUID": "5fd41e35-5e49-O977-t091-6f228bc65e37"}},
{"$project": {"matchedIndex":
{"$indexOfArray": ["$samples.timestamp",
{"$gt": ["$$timestamp", 235]}]}}}])
I understand this wont work, I'm just not sure how to go about it.
The purpose is to insert many elements between timestamps. Maybe there is a better way entirely to do this. Thanks
Instead of using "$samples.timestamp" as first argument you can use $map there to transform it into an array of true and false values depending on your condition, try:
{
"$project": {
"matchedIndex": {
"$indexOfArray": [
{ $map: { input: "$samples.timestamp", in: { $gte: [ "$$this", 235 ] } } },
true
]
}
}
}
Mongo Playground

Mongo find position by id in an Array of Objects

I have a range of documents
{
_id: ObjectId("5e388da4df54cb8efb47e61b"),
userId:'test_user'
productId:'product_6_id'
recommendations:{
_id:123
rankedList:[
0:{id:ObjectId('product_5_id'),Name:'Product_5'},
1:{id:ObjectId('product_6_id'),Name:'Product_6'},
2:{id:ObjectId('product_3_id'),Name:'Product_3'}],
Date:'2020-02-25T05:03:55.439+00:00'
}
},
{
_id: ObjectId("5e388da4df54cb8efb47e62b"),
userId:'test_user1'
productId:'product_3_id'
recommendations:{
_id:123
rankedList:[
0:{id:ObjectId('product_1_id'),Name:'Product_1'},
1:{id:ObjectId('product_5_id'),Name:'Product_5'},
2:{id:ObjectId('product_3_id'),Name:'Product_3'}],
Date:'2020-02-25T05:03:55.439+00:00'
}
}
and I need to find each time the position of productId within the Array of objects rankedList.
Thus here the answer would be positionIndex=1 for first doc and positionIndex=2 for second document.
I am quite confused with $indexOfArray and how I should use it here with aggregate.
Yes, you need $indexOfArray. The tricky part is that recommendations.rankedList is an array of objects however MongoDB allows you to use following expression:
$recommendations.rankedList.id
which evaluates to a list of strings, ['product_5_id', 'product_6_id', 'product_3_id'] in this case so your code can look like this:
db.collection.aggregate([
{
$project: {
index: {
$indexOfArray: [ "$recommendations.rankedList.id", "$productId" ]
}
}
}
])
Mongo Playground

MongoDb find opeartion on nested array having multiiple json object in it

I'm having dataset in which json consists of array having multiple json in it.
The objects in the array have 4 keys, out of which 1 is missing in some of them.
I want to get the documents where all the objects in an array are missing the key.
e.g, From these following documents:
{"test":1,desc:[{"price":1,"abc":"def"},{"price":2,"ac":"def"}]}
{"test":1,desc:[{"price":1,"abc":"def"},{"ac":"def"}]}
{"test":1,desc:[{"abc":"def"},{"ac":"def"}]}
I want to match only the last document.
Thanks in advance..
You can just use $exists:
db.collection.find(
{
"desc.price": {$exists: false}
}
)
And if you want for other fields as well:
db.collection.find(
{
$or: [
{
"desc.price": {$exists: false}
},
{
"desc.abc": {$exists: false}
},
{
"desc.ac": {$exists: false}
}
]
}
)
One thing to note is that an empty array, i.e desc = [] will always be matched by this query. if you want to ensure there's atleast one object use this query:
db.collection.find(
{
$and: [
{
"desc.price": {$exists: false}
},
{
"desc.0": {$exists: true}
}
]
}
)
Achieved that, just made a script where i used an array to store the price thing and then used .every method for price to be undefined , it returned me the desired result which i want.

How can I paginate a document array in mongoose using aggregate?

I want to paginate an array of documents called "Reviews" but I can't find a way to do it, since the code I execute paginate the documents for me and not the array of documents "Reviews".
Code you tried to run
return await this.animeModel
.aggregate()
.match({ _id: Types.ObjectId(reviewPaginateData.animeId) })
.unwind('$reviews')
.group({
_id: '$_id',
reviews: { $push: '$reviews' },
})
.sort(reviewPaginateData.sort)
.limit(reviewPaginateData.limit)
.skip(reviewPaginateData.limit * reviewPaginateData.skip)
.exec();
Document structure
Any way to solve this problem? Thank you very much in advance.
I don't have much experience with mongoose, but I can write a shell query to perform the pagination on an array. So, that you can integrate it in mongoose.
The reason you are not able to paginate is that you are applying the skip and limit after group which is giving you an array as output. So, to apply pagination on the array itself you need to use $slice for more read here
db.collection.aggregate([
{
$match:{
"_id":ObjectId("xyz")
}
},
{
$unwind:"$reviews"
},
{
$group:{
"_id":"$_id",
"reviews":{
$push:"$reviews"
}
}
},
{
$sort:{
sort_value:1
}
},
{
$project:{
"reviews":{
$slice:[
"$reviews",
skip_value,
limit_value
]
}
}
}
]).pretty()
Hope this will help :)

Updating nested arrays in mongodb

I have a document in mongodb with 2 level deep nested array of objects that I need to update, something like this:
{
id: 1,
items: [
{
id: 2,
blocks: [
{
id: 3
txt: 'hello'
}
]
}
]
}
If there was only one level deep array I could use positional operator to update objects in it but for second level the only option I've came up is to use positional operator with nested object's index, like this:
db.objects.update({'items.id': 2}, {'$set': {'items.$.blocks.0.txt': 'hi'}})
This approach works but it seems dangerous to me since I'm building a web service and index number should come from client which can send say 100000 as index and this will force mongodb to create an array with 100000 indexes with null value.
Are there any other ways to update such nested objects where I can refer to object's ID instead of its position or maybe ways to check if supplied index is out of bounds before using it in query?
Here's the big question, do you need to leverage Mongo's "addToSet" and "push" operations? If you really plan to modify just individual items in the array, then you should probably build these arrays as objects.
Here's how I would structure this:
{
id: 1,
items:
{
"2" : { "blocks" : { "3" : { txt : 'hello' } } },
"5" : { "blocks" : { "1" : { txt : 'foo'}, "2" : { txt : 'bar'} } }
}
}
This basically transforms everything in to JSON objects instead of arrays. You lose the ability to use $push and $addToSet but I think this makes everything easier. For example, your query would look like this:
db.objects.update({'items.2': {$exists:true} }, {'$set': {'items.2.blocks.0.txt': 'hi'}})
You'll also notice that I've dumped the "IDs". When you're nesting things like this you can generally replace "ID" with simply using that number as an index. The "ID" concept is now implied.
This feature has been added in 3.6 with expressive updates.
db.objects.update( {id: 1 }, { $set: { 'items.$[itm].blocks.$[blk].txt': "hi", } }, { multi: false, arrayFilters: [ { 'itm.id': 2 }, { 'blk.id': 3} ] } )
The ids which you are using are linear number and it has to come from somewhere like an additional field such 'max_idx' or something similar.
This means one lookup for the id and then update. UUID/ObjectId can be used for ids which will ensure that you can use Distributed CRUD as well.
Building on Gates' answer, I came up with this solution which works with nested object arrays:
db.objects.updateOne({
["items.id"]: 2
}, {
$set: {
"items.$.blocks.$[block].txt": "hi",
},
}, {
arrayFilters: [{
"block.id": 3,
}],
});
MongoDB 3.6 added all positional operator $[] so if you know the id of block that need update, you can do something like:
db.objects.update({'items.blocks.id': id_here}, {'$set': {'items.$[].blocks.$.txt': 'hi'}})
db.col.update({"items.blocks.id": 3},
{ $set: {"items.$[].blocks.$[b].txt": "bonjour"}},
{ arrayFilters: [{"b.id": 3}] }
)
https://docs.mongodb.com/manual/reference/operator/update/positional-filtered/#update-nested-arrays-in-conjunction-with
This is pymongo function for find_one_and_update. I searched a lot to find the pymongo function. Hope this will be useful
find_one_and_update(filter, update, projection=None, sort=None, return_document=ReturnDocument.BEFORE, array_filters=None, hint=None, session=None, **kwargs)
Example
db.pymongo_object.find_one_and_update( filter = {'id' : 1}, update= {$set: {"items.$[array1].blocks.$[array2].txt": "hi"}}, array_filters =[{"array1.id" :2}, {"array2.id": 3}])
Also see pymongo documentation.

Resources