Customize query according to the condition mongodb + node.js - node.js

I don't know whether I have too much expectation from mongodb, but I have a question here.
Is it possible to customize the query according to a boolean parameter? Firstly, I have looked https://docs.mongodb.com/manual/reference/operator/aggregation/cond/.
But didn't find an example where the condition is up to a another parameter.
Here let me clarify by an example;
Let assume I have a flag parameter. I want to execute two similar queries based on whether its value is true or false.
document is like;
{
"name" : "alex",
"age" : NumberInt(21)
}
{
"name" : "felix",
"age" : NumberInt(14)
}
To be able to look the condition I can break them into two different query as;
if(flag){
db.collection.find({age: "18"})
}else{
db.collection.find({age: "21"})
}
I am looking for something like this;
db.collection.aggregate([
{$cond: [flag,{$match: {age: "18"}} ,{$match: {age: "21"}}]}
])
But this throws an error which is not a surprise.
Is it possible to use $cond and execute two different query? Can a parameter pass in the $cond?

You can generate query first based on your condition then execute that query one time.
var query = {};
if(flag){
query.age = 18;
}else{
query.age = 21;
}
db.collection.find(query, function(err, result) {
if(err) {
// return error
}
console.log(result);
// return result
})

Related

How to filter results by multiple query parameters if I don't know beforehand how many query strings I may receive from client side?

I want to send in response some data according to searching by query parameters (using .find function of mongoose) from the client side. What do I need to do is a search according to the parameters received?
What I mean is :
I may receive
localhost:5000/admin/customers?customer_id=1&customer_email=abc#gmail.com
I could have used this code to send results according to this query :
Customer.find({
customer_id = req.query.customer_id,
customer_email = req.query.customer_email,
}, (err,docs)=> {
res.json(docs);
})
or
just
localhost:5000/admin/customers?customer_id=1
I could have used this code to send results according to this query :
Customer.find({
customer_id = req.query.customer_id
}, (err,docs)=> {
res.json(docs);
})
or
may be
localhost:5000/admin/customers?no_of_items_purchased=15
I could have used this code to send results according to this query :
Customer.find({
no_of_items_purchased = req.query.no_of_items_purchased
}, (err,docs)=> {
res.json(docs);
})
But what I want is to use .find function on anything received from query params. Like a general code to achieve this.
PS: Also please help with : "How to filter req.query so it only contains fields that are defined in your schema ?"
You can create a query variable to keep the field that you want to filter.
Suppose that your Customer model structure is:
{
customer_id: ...,
customer_name: ...,
customer_email: ...,
no_of_items_purchased: ...
}
Then your code will be:
let {customer_id, customer_name, customer_email, no_of_items_purchased} = req.query;
let query = {};
if (customer_id != null) query.customer_id = customer_id;
if (customer_name != null) query.customer_name = customer_name;
if (customer_email != null) query.customer_email = customer_email;
if (no_of_items_purchased != null) query.no_of_items_purchased = no_of_items_purchased;
let result = await Customer.find(query);
Just pass request.query as a parameter directly on find method:
Customer.find(request.query)

Getting error "pipeline element 3 is not an object error", while trying to find & update using aggregate

I am using node js mongodb driver & trying to update an object array inside an object array in a document.
The schema of the document collection is this :
What I Want :
For collection with order no = 1 & items.qty=2 & tax rate = 25, update the tax to "cst" & taxratetype to "flat".
What I Tried :
db.OrderInfo.aggregate(
{$match:{"orderno":"1"}},
{$unwind:'$items'},
{ $match: { 'items.qty' : 2}
},function(err,result1){
if(err){
throw(err);
}else{
indexes = result1[0].items.taxes.map(function(obj, index) {
if(obj.taxrate == 25) {
return index;
}
}).filter(isFinite);
var updateData = {};
updateData["items.$.taxes."+indexes[0]+".tax"]="cst";
updateData["items.$.taxes."+indexes[0]+".taxratetype"]="flat";
db.OrderInfo.update({ "orderno":"1",'items.qty': 2,'items.taxes.taxrate': 25 },{$set: updateData },function(err,result2){
console.log(result2);
});
}
});
Currently I am using db.eval to run this script from node but later will change it once I accomplish the same.
Getting this Error :
{"name":"MongoError","message":"Error: command failed: {\n\t\"ok\" :
0,\n\t\"errmsg\" : \"pipeline element 3 is not an
object\",\n\t\"code\" : 15942\n} : aggregate failed
:\n_getErrorWithCode#src/mongo/shell/utils.js:25:13\ndoassert#src/mongo/shell/assert.js:13:14\nassert.commandWorked#src/mongo/shell/assert.js:267:5\nDBCollection.prototype.aggregate#src/mongo/shell/collection.js:1312:5\n_funcs1#:1:31\n","ok":0,"errmsg":"Error:
command failed: {\n\t\"ok\" : 0,\n\t\"errmsg\" : \"pipeline element 3
is not an object\",\n\t\"code\" : 15942\n} : aggregate failed
:\n_getErrorWithCode#src/mongo/shell/utils.js:25:13\ndoassert#src/mongo/shell/assert.js:13:14\nassert.commandWorked#src/mongo/shell/assert.js:267:5\nDBCollection.prototype.aggregate#src/mongo/shell/collection.js:1312:5\n_funcs1#:1:31\n","code":139}
I know from this issue https://jira.mongodb.org/browse/SERVER-831
that I cannot use a direct update command & hence trying this workaround.
Any other approach for such updates is also fine with me.
EDIT :
As per answer given by #titi23, I had tried using [] also inside function.
It did not gave me any error but, my values also did not get updated.
Two problems in the query :
1) You are missing [] in the aggregate query.
2) The update method does not need the tax rate clause. It will find the nested document & the index from aggregate would serve the purpose in update.
Refer aggregate-definition for more info on how to use it.
Syntax - db.collection.aggregate(pipeline, options)
pipeline - array - A sequence of data aggregation operations or stages.
Try the following:-
db.OrderInfo.aggregate([
{$match:{"orderno":"1"}},
{$unwind:'$items'},
{ $match: { 'items.qty' : 2} }]).toArray(
function(err,result1){
if(err){
throw(err);
}
else{
console.log(result[0]); //See is there any record here
indexes = result1[0].items.taxes.map(function(obj, index) {
if(obj.taxrate == 25) {
return index;
}
}).filter(isFinite);
var updateData = {};
updateData["items.$.taxes."+indexes[0]+".tax"]="cst";
updateData["items.$.taxes."+indexes[0]+".taxratetype"]="flat";
db.OrderInfo.update({ "orderno":"1",'items.qty': 2}, /*Remove the tax rate clause from here..*/
{$set: updateData },function(err,result2){
console.log(result2);
});
}
});
It should not throw the error.
EDIT:- Do toArray() with the aggregate, see if it helps. Updated the query already.

Passing variables into a query in mongoose in the first argument

I am using MEAN stack, i have an entry like this in my mongodb
{ "_id" : ObjectId("5577467683f4716018db19ed"),
"requestMatrix" : { "1698005072" : { "rideId" : "641719948", "status" :"accepted" },"1698005073" : { "rideId" : "641719545", "status" :"rejected" } },
"partners":[ { "customerNumber" : 1698005072 }, { "customerNumber" : 1698072688 } ]}
I want to query the db to return me this entire document based on whether the status is accepted or rejected.
When I run the below query in a command prompt, i get the expected answer
db.joinedrides.find({'requestMatrix.1698005072.status':"accepted"})
But when i want to do the same from nodeJs, I am stuck as the number 1698005072 in the above query is a variable, i am not able to write a query for that.
tried something like this
var criteria = "'requestMatrix.'"+customerNumber+"'.status'";
JoinedRide.find({criteria:"accepted"},function(err,joinedRides){
})
where customerNumber will vary for different requests, in the above mentioned case its value is 1698005072
Any help is appreciated.
You need to do something like this:
var query = {};
var criteria = "requestMatrix." + customerNumber + ".status";
query[criteria] = "accepted"
JoinedRide.find(query,function(err,joinedRides){
})

Mongoose update changes only one value

I want to update multiple values in mongo using mongoose, more accurate I want to update all the values that have a certain field smaller than a value and I am trying something like this:
var conditions = {Number: {$lt : 6000}};
var update = {$set: {closed_flag : true}};
var options = {};
UPUSTP.update(conditions, update, options, function(err, data) {
if (err) {
console.log(err.message);
return;
}
console.log(data);
});
If I try the same thing with an update I get 1000 values that were found but if I run update with the same conditions the last console.log outputs 1, so just one value was updated... I can figure out what the problem is...
To update multiples items with mongodb, yout options object should like this :
{ multi : true }
This option will tell MongoDB to update ALL elements that satisfy the query

Filter subdocument and trigger

i have collection of objects inside an invitation, having hard time to filter particular object and trigger it's boolean field.
Document:
"Invitation" : [
{
"__v" : 0,
"userID" : ObjectId("54afaabd88694dc019d3b628"),//ObjectId of personA
"__t" : "USER",
"_id" : ObjectId("54b5022b583973580c706784"),
"Accepted" : false
},
{
"__v" : 0,
"userID" : ObjectId("54af6ce091324fd00f97a15f"),//ObjectId of personB
"__t" : "USER",
"_id" : ObjectId("54bde39cdd55dd9016271f14"),
"Accepted" : false
}
]
here i have only two objects inside Invitation array,it can be more than two.
Let's say personA and personB send me Invitation, so two different invitation objects are inserted into database having different fields, with objectId of both persons(userID in above document), now if i accept only invitation of personA, it should trigger accepted field of personA object only, here is what i tried so far, but not working as per expectation.
Controller:
User.find({_id: req.user._id},'Invitation',function(err,docs) {
if (err) {
console.log(err);
}
var results = [];
async.each(docs,function(doc,callback) {
async.each(doc.Invitation,function(invite,callback) {
User.findOneAndUpdate(
{'_id': doc._id, 'Invitation._id': invite._id},
{'$set': {'Invitation.$.Accepted': !invite.Accepted}},
function(err,doc) {
results.push(doc);
callback(err);
}
);
},callback);
},function(err) {
if (err)
console.log(err);
console.log('end'+results);
});
});
finally i am looking for a query which can be used to filter a single element or object, like if i accept invitation of personA then Accepted field of personA object should be set to true.
i would be really helpful if some logic is provided.
Thank you
Not a very clear question. But it seems all you really need to do here is just match the only sub-document you want to update in the first place:
User.find(
{
"_id": "req.user._id",
"Invitation._id": personA.id
},
{ "Invitation.$": 1 },
function(err,docs) {
// and continue
}
);
This is the form of the positional $ operator in a "projection" context. Where only the "singular" matched element is returned.
Once you have a "singular" result, then all the other code works as designed.
I should know after all because I wrote it for you. Not that you are paying any decent respect to that.
Update on Aggregate in Mongodb
Toggle boolean value of subdocuments
Or personA.userID or whatever makes it work.
Just use the unique identifier for the "user" where you expect that to match the query conditions.
You can do this:
db.user.update({"invitation.userID": 1}, {"$set" : {"invitation.$.Accepted" : true}});
Replacing the value 1 with the user ID you want to update.
The code is in the syntax of MongoShell, simply convert to driver syntax you are using
The operator used was the $. According to the documentation: The positional $ operator identifies an element in an array to update without explicitly specifying the position of the element in the array. To project, or return, an array element from a read operation, see the $ projection operator.
For more details see: http://docs.mongodb.org/manual/reference/operator/update/positional/

Resources