My Dataset is like this
{
"_id" : ObjectId("6a1464430b4215046c768y66"),
"a" : 5,
"b" : 4,
"c" : 2,
"d" : 14,
"e" : 7,
"f" : 25,
"g" : 85,
}
Now I want to do sum of all fields on this id only.
My query is
db.collection.aggregate([
{$match:{_id: req.body.id}},
{$project: {total: { $sum: [ "$a", "$b", "$c", "$d","$e","$f","$g" ]}}}]);
Using $match, it returns Blank Array[]. When Not use $match then it will return the sum, but for all not for this id.
You can use $add as well.
db.collection.aggregate([
{$match:{_id: ObjectId("6a1464430b4215046c768y66")}},
{$project: { total: {$add: ["$a", "$b", "$c"]}}} // etc.
])
But $sum should work too. Could it be that the $match pipeline doesn't return any results?
I don't which driver you use, but doing it in your code it could interpret your id. But if you search directly on your database you should go with a specific _id
Try to execute only the match and find out:
db.collection.aggregate([
{$match:{_id: ObjectId("6a1464430b4215046c768y66")}}
])
If the $match executed in your database returns a result but not through your node.js application then try this:
ObjectId = require('mongodb').ObjectID;
db.collection.aggregate([
{$match:{_id: new ObjectId(req.body.id})}},
{$project: { total: {$add: ["$a", "$b", "$c"]}}} // etc. you could also use $sum
])
Its Done. Problem is not in Query. Actually Object Id problem. So convert id to object id with the help of some mongoose logic.
First call Mongoose i.e
var mongoose = require('mongoose');
After That Under Function Just add
var id = mongoose.Types.ObjectId(req.body.id);
Now Problem is solved. It convert String to Object.
Your query is absolutely fine , the problem is that what you are passing the "_id" field in the query is a string but type of your "_id" if the data record is ObjectId. So you will have to first convert the string to ObjectId and then pass into "_id" field in the $match field in the query.
var docId = mongoose.Types.ObjectId(req.body.id);
db.collection.aggregate([
{$match:{_id: docIid}},
{$project: {total: { $sum: [ "$a", "$b", "$c", "$d","$e","$f","$g" ]}}}]);
Use {$match:{_id:ObjectId("SomeId")}}
db.collection_name.aggregate([
{$match:{_id:ObjectId("5a153c6d4828c9f51d3adb53")}},
{$project: {total: { $sum: [ "$a", "$b", "$c", "$d","$e","$f","$g" ]}}}
])
Use mongoose.Types.ObjectId(id) to convert an id to ObjectId
output :
{
"_id" : ObjectId("5a153c6d4828c9f51d3adb53"),
"total" : 142 }
Related
my mongodb has a collection with data like this
{
"_id" : ObjectId("62ead2a8dd6922cfd6f466e4"),
"t" : "d",
"u" : {
"_id" : ObjectId("621d3469dd01e282b9a62321"),
"username" : "helxsz"
},
"users" : [
ObjectId("621d3469dd01e282b9a62321"),
ObjectId("628ee99ed0a58e00496a0730")
],
"createdAt" : ISODate("2022-08-03T19:55:20.965Z"),
"updatedAt" : ISODate("2022-08-03T19:55:20.965Z")
}
I am using node.js and mongoose to query the document.
let query = {
u:{
_id: "621d3469dd01e282b9a62321",
username: "helxsz"
},
t:'d',
};
collection
.findOne(query, 'u t ')
.exec(getResult);
why the executed query returns null to me
Maybe is because in your DB the u._id is ObjectId and in your query is a string. Mongoose should (?) parse it but I've faced not-parsed error like this many times.
So try parsing to ObjectId, in this example works.
Also an other problem is trying to search an object like this:
{
u:{
_id: "621d3469dd01e282b9a62321",
username: "helxsz"
}
}
Because in this way mongo looks for by the objects with the same order. You have to use dot notation
As an example, check how this query not work all times. To ensure the result you have to use dot notation:
let query = {
"u._id": "621d3469dd01e282b9a62321", // maybe casting to ObjectId is necessary
"u.username": "helxsz",
t: "d"
}
Example here
More info about Match an Embedded/Nested Document
i am working on node js project, i have a mongodb collection of orders in which i want to add a field to every document , the problem is that i want to pass a function as the value of this new field and the arguments of the function are two other fields of the document , this is an example that will make you understand .
my collection :
[
{
_id:"eyxwapfhiezfe664ec",
orderPrice : 20,
createdAt : 2021-01-15T17:16:25.844Z
endedAt : 2021-01-15T17:20:25.844Z
}
{
_id:"eyxwlcfeojrfeoc",
orderPrice : 50,
createdAt : 2021-01-15T17:16:25.844Z
endedAt : 2021-01-15T17:20:25.844Z
}
{
_id:"eyxwapfhiseflflpsssc",
orderPrice : 20,
createdAt : 2021-01-15T17:16:25.844Z
endedAt : 2021-01-15T17:20:25.844Z
}
the field i want to add is completedTime which is the difference between createdAt and endedAt i have a function differenceBetweenDates which takes two dates as argument createdAt and ended at but i don't know how to pass this function to $set in update .
So to be more clear i want to add this field to every document :
CompletedTime: differenceBetweenDates(createdAt, endedAt)
thank you .
If you are using MongoDB 4.2, you can pass aggregation expressions as part of the update.
db.collection.updateMany({},[{ $set:{ CompletedTime: {$subtract:["$endedAt", "$createdAt" ]}}}])
In MongoDB 4.4 you might also use an aggregation pipeline with a $merge stage to merge the result set back into the original collection:
db.collection.aggregate([
{ $set: { CompletedTime: {$subtract:["$endedAt", "$createdAt" ]}}},
{ $merge: "collection" }
])
I use two ways to retrieve documents from my collection, the first one:
db.comments.find({"nid" : "req.body.data"});
returns many doc like:
{
"nid" : 20404,
"_id" : ObjectId("5638ba331294943d3d0a092b"),
"uid" : 1937,
"posted" : ISODate("2015-11-03T13:44:19.811Z"),
"text" : "txt",
"title" : "Test nid 2",
"stars" : 3,
"__v" : 0
}
,
And for another query I need to use aggregate and the query:
var pipleline = [
{$match: {nid:req.body.data}}
];
Comments.aggregate(pipleline, function(err, rank){
if(err) {
res.send("Error", String(err));
}
res.send(rank);
});
Returns [] - empty array.
Any ideas?
You can use the built in function chaining mongoose provides. Aside from match, it also has sort, project, group, and few others I don't know off the top of my head. More info here
Comments.aggregate().match({nid:req.body.data})
.exec(function(err,rank){
if(err) {
res.send("Error", String(err));
}
res.send(rank);
});
This is my code:
this.aggregate(
{
$match: {
measurer_code : parseInt(_measurer_code),
user_code :parseInt(_user_code),
time : {$gte : iDate, $lte : eDate}
}
},
{
$group:{
sum: {$sum: "$value"},
cant: {$sum: 1}
}
},cb);
The collection has the following structure:
user_code : Number,
measurer_code : Number,
value : Number,
time : Date
Returns undefined. But if i run only $match or only $group returns documents.
Someone can help me?
Thanks!
When you group you have to specify how you want to group the data.
If you want count and sum across all the documents, you have to add "_id":null or "_id":1 (any constant) in the $group document. If you want to group by field "foo" you have to add "_id":"$foo" to the document.
In a new version on MongoDB we can use an $elemMatch projection operator to limit the response of a query to a single matching element of an array. http://docs.mongodb.org/manual/reference/projection/elemMatch/
But it seems doesn't work yet in mongoose 3 here is the example:
{
_id: ObjectId(5),
items: [1,2,3,45,4,67,9,4]
}
Folder.findOne({_id: Object(5)}, {$elemMatch: {$in: [1,67,9]}})
.exec(function (err, doc) {
});
I'm expected to get the follows doc:
{
_id: ObjectId(5),
items: [1,67,9]
}
But unfortunately what I'm getting is document with all items:
{
_id: ObjectId(5),
items: [1,2,3,45,4,67,9,4]
}
The mongodb docs here are misleading, we'll get them updated.
What its saying is that you can now use $elemMatch in your projection, that is, your field selection:
https://gist.github.com/3640687
See also: https://github.com/learnboost/mongoose/issues/1085
[Edit] pull request for docs sent: https://github.com/mongodb/docs/pull/185
Firstly, you are missing the items field name in front of the $elemMatch operator. Your query should read
Folder.findOne({_id: Object(5)}, {items: {$elemMatch: {$in: [1,67,9]}}})
.exec(function (err, doc) { });
But this would still not return the desired result, because as stated in the documentation:
The $elemMatch projection will only match one array element per source
document.
So you would only get back something like:
{
_id: ObjectId(5),
items: [1]
}
I haven't got mongoose set up to do this with node, but you can also get the result you want using the new aggregation framework in 2.2 - here's an example that gets you the result you wanted. First, my sample doc looks like this:
> db.foo.findOne()
{
"_id" : ObjectId("50472eb566caf6af6108de02"),
"items" : [
1,
2,
3,
45,
4,
67,
9,
4
]
}
To get to what you want I did this:
> db.foo.aggregate(
{$match : {"_id": ObjectId("50472eb566caf6af6108de02")}},
{$unwind : "$items"},
{$match : {"items": {$in : [1, 67, 9]}}},
{$group : {_id : "$_id", items : { $push : "$items"}}},
{$project : {_id : 0, items : 1}}
)
{
"result" : [
{
"_id" : ObjectId("50472eb566caf6af6108de02"),
"items" : [
1,
67,
9
]
}
],
"ok" : 1
}
To explain, in detail I will take it line by line:
{$match : {"_id": ObjectId("50472eb566caf6af6108de02")}}
This is fairly obvious - it is basically the equivalent to the find criteria on a regular query, the results are passed to the next step in the pipeline to be processed. This is the piece that can use indexes etc.
{$unwind : "$items"}
This will explode the array, creating a stream of documents, one for each element of the array.
{$match : {"items": {$in : [1, 67, 9]}}}
This second match will return only the documents in the list, basically reducing the stream of docs to a result set of three.
{$group : {_id : "$_id", items : { $push : "$items"}}}
We want our output to be an array, so we have to undo the unwind above now that we have selected the items we want, using the _id as the key to group. Note: this will have repeating values if there is more than one match, if you wanted a unique list you would use $addToSet instead of $push
{$project : {_id : 1, items : 1}}
Then finally, this projection is not really needed, but I included it to illustrate the functionality - you could choose to not return the _id if you wished etc.
Neither $elemMatch nor MongoDB in general will filter data from an array. $elemMatch can be used to match a document but it won't affect the data to be returned. You can only include/exclude fields from a documented by using the filter parameter (second parameter of a find() findOne() call) but you can not filter the result based on some query input.