Search on two number fields in Atlas Search - mongodb

I have mongo document like this:
{
"pw" : [
{
"id" : 123,
"qty" : 10
},
{
"id" : 456,
"qty" : 15
}
]
}
Id and qty is Number type in Atlas Search.
I want to search like "id = 123" and qty > 5.
I USED EQUALS and RANGE operator for this, but it is not working. How can I set criteria for specific document level (not in array level)?

Related

Update multiple array elements with $inc

I have the following document
{store : {
id : 'STORE1',
name : 'Electronics Store',
locations : [
{
id : 'LOC1',
quantity : 4
},
{
id : 'LOC2',
quantity : 10
},
{
id : 'LOC3',
quantity : 5
}
]
}
}
I want to update the quantity of multiple elements of the locations array based on their id field using $inc.
For example I want to update id : 'LOC1' with +5 to the quantity field and id : LOC3 with +2 to its quantity field.
Is it possible to do this with one query in mongodb instead of using multiple queries for each location.
You can make use of filtered positional operator $[<identifier>]. Below code will be helpful:
db.collection.update(
{},
{$inc: {'store.locations.$[elem1].quantity': 5, 'store.locations.$[elem2].quantity': 2}},
{arrayFilters: [{'elem1.id': 'LOC1'}, {'elem2.id': 'LOC3'}]}
)

Get one element from the array of results by index

So every user has 0-3 items in the database. I don't have their indexes, I sort them by creation date, from oldest to youngest. I was wondering if it is possible to get the element from result array by its index by native mongo/mongoose tools.
For example I have these 3 documents in DB for user theguy:
{ "_id" : 1, "name" : "theguy", otherdata: ["data 1", "data 2"] }
{ "_id" : 10, "name" : "theguy", otherdata: ["data 1", "data 2"] }
{ "_id" : 333, "name" : "theguy", otherdata: ["data 1", "data 2"] }
_id will be ObjectId
Then user will try to get some data. He inputs a number in the range from 1 to 3. There can be 0, 1, 2 or 3 entries in database under his name. The example above displays the situation when user has all 3 entries filled. But user doesn't care about all of them right now, he entered the index, he needs only second result out of these 3.
What works for me right now is this:
//user defined the index
index = 2;
//search all docs by this user, but get only ids
let usrlist = await User.find({"name": name}).distinct('_id');
//based on the ids array we can now request exact second document from the database:
//usrlist[index-1] or usrlist[1]
let exactusr = await User.findOne({"_id": usrlist[index-1]});
So the result for exactusr will be:
{ "_id" : 10, "name" : "theguy", otherdata: ["data 1", "data 2"] }
I try to minimize the load by getting only ids, instead of all 3 documents at once. Now the thing is, well, this doesn't look "nice". Getting an array of _id to create another query based on that doesn't seem optimal. And at the end, I don't even know what is better: to do 2 queries(as above) or do 1 query for all user documents and choose the one user needs by index. Documents may be kind of big, containing up to 12000 characters each.
So looking for native ways I found $slice, but I don't think it works with the result array, or I don't understand how.
My attempt of using $slice:
index = 2;
usr = await User.find({"name": name}, {$slice: [index-1, 1]});
Result:
[ {"_id" : 1}, {"_id" : 10}, {"_id" : 333} ]
Expected result:
{ "_id" : 10, "name" : "theguy", otherdata: ["data 1", "data 2"] }
Any ideas? Or other methods that I could make this work?
I created a collection called "sample" and inserted the sample data that you provided.
db.sample.aggregate([{$match : {"name": "theguy"}}]);
{ "_id" : 1, "name" : "theguy", "otherdata" : [ "data 1", "data 2" ] }
{ "_id" : 10, "name" : "theguy", "otherdata" : [ "data 1", "data 2" ] }
{ "_id" : 333, "name" : "theguy", "otherdata" : [ "data 1", "data 2" ] }
Initialize a variable index : var index=1;
Now if we consider the above data, the indexes of the three rows would be 0,1,2. If I want to retrieve the 2nd row with _id : 10, then the index is supposed to be 1. In that case, the aggregate query would look like :
db.sample.aggregate([
{ $match : {"name": "theguy"}},
{ $skip : index},
{ $limit : 1 }
]);
In case you want your index value to mean the position i.e. in this case the position is 2, then modify the query like :
var index=2;
db.sample.aggregate([
{ $match : {"name": "theguy"}},
{ $skip : index-1},
{ $limit : 1 }
]);
Try this solution & let us know, if it worked for you!

mongodb findOneAndUpdate only if a certain condition is met

Following is my mongo db entries.
my-mongo-set:PRIMARY> db.stat_collection.find({name : /s/})
{ "_id" : ObjectId("5aabf231a167b3808302b138"), "name" : "shankarmr", "email" : "abc#xyz", "rating" : 9901 }
{ "_id" : ObjectId("5aabf23da167b3808302b139"), "name" : "shankar", "email" : "abc1#xyz1", "rating" : 10011 }
{ "_id" : ObjectId("5aabf2b5a167b3808302b13a"), "name" : "shankar1", "email" : "abc2#xyz2", "rating" : 10 }
{ "_id" : ObjectId("5aabf2c2a167b3808302b13b"), "name" : "shankar2", "email" : "abc3#xyz3", "rating" : 100 }
Now i want to find an entry based on name but update a field only if a certain condition holds good.
I tried the following statement, but it gives me error at the second reference to $rating.
db.stat_collection.findOneAndUpdate({name: "shankar"}, {$set : {rating : {$cond : [ {$lt : [ "$rating", 100]}, 100, $rating]}}, $setOnInsert: fullObject}, {upsert : true} )
So in my case, it shouldnot update rating for the 2nd document as the rating is not less than 100. But for the third document, rating should be updated to 100.
How do i get it work?
$max is the operator you're looking for, try:
db.stat_collection.findOneAndUpdate( { name: "shankar1"}, { $max: { rating: 100 } }, { returnNewDocument: true } )
You'll either get old value (if is greater than 100) or modify a document and set 100
According to the documentation:
The $max operator updates the value of the field to a specified value if the specified value is greater than the current value of the field. The $max operator can compare values of different types, using the BSON comparison order.
You should put all conditions in the query part of the update:
db.stat_collections.findOneAndUpdate(
{ name: "Shankar", rating: { $lt: 100 } },
$set : { rating: 100 },
);
"If the name is Shankar and rating is less than 100, then set the rating to 100." is the above.

Update document if a field in last element in array does not equal value

I wonder how I should match a document by last element in an Array in mongodb document.
Say I want to update a specific document with new data if a field in last element of array is not equal some specific value.
I know that I can do this to check if a field in Array does not contain that value already:
myTable.update({ Thing: thisThing,
'myArray.Element': {$ne: parseInt(thisValue)} }, ...)
But how should one check that the last Element (myArray.Element) in myArray is not equal to thisValue?
Note that I want to do this with findand not aggregate.
Best Regards
Let's say we have collection names, looking like this:
/* 1 */
{
"_id" : ObjectId("58de74f8c1bb7f4256adf32c"),
"user" : "John",
"list_friends" : [
"Alice",
"Bob"
]
}
/* 2 */
{
"_id" : ObjectId("58de75d3c1bb7f4256adf32d"),
"user" : "Pop",
"list_friends" : [
"Eve",
"Oscar"
]
}
Now, let's say we want to change "user" field to "Updated" for all users whose last friend name is different than "Oscar" (in this case that is John). This query:
db.getCollection('names').update({$where: "this.list_friends[this.list_friends.length - 1] !== 'Oscar'"}, {"$set": {"user": "Updated"}})
modifies the collection and the final result is:
/* 1 */
{
"_id" : ObjectId("58de74f8c1bb7f4256adf32c"),
"user" : "Updated",
"list_friends" : [
"Alice",
"Bob"
]
}
/* 2 */
{
"_id" : ObjectId("58de75d3c1bb7f4256adf32d"),
"user" : "Pop",
"list_friends" : [
"Eve",
"Oscar"
]
}
I tested the solution using Mongo 3.2, I am not sure if it works for older versions.

MongoDB Query double nested documents in array

I was wondering how I can query from an embedded document inside an array. I have following structure:
{ "targetId" : 2, "metaData" : [ { "key" : "id", "value" : 1 }, { "key" : "name", "value" : "Parisa" }, { "key" : "img", "value" : { "imgid" : 1, "imgName" : "img1" } } ]
I could search simple key-values like key = id and value =1, but I could not search based on the values with embedded document e.g. key="img"
I tried following query but it does not work:
db.test.find({"metaData":{$elemMatch:{"key":"img", "value":{"imgid":1}}}})
Could you please help me!
I think the "value" part of your query is a little off. You need to put the document element in the criteria:
b.test.find({"metaData":{$elemMatch:{"key":"img", "value.imgid":1}}})