how to group records based on dates in mongodb - node.js

How to group data by dates? I have some docs in mongo that looks something like this: yes i have added my actuall mongo doc and my code too
{
"_id" : ObjectId("58c0e32161ccc654160b776a"),
"consumer_id" : ObjectId("579f03069b49a0840409df83"),
"user_id" : "579f034c9b49a0840409df85",
"values" : [
{
"date" : "2017/2/9",
"point" : 1
},
{
"date" : "2017/2/10",
"point" : -1
},
{
"date" : "2017/2/11",
"point" : -1
}
]
}
{
"_id" : ObjectId("58c0e3db61ccc654160b776b"),
"consumer_id" : ObjectId("579f03069b49a0840409df83"),
"user_id" : "579ef6f5a15b0eac1332034e",
"values" : [
{
"date" : "2017/2/9",
"point" : 1
},
{
"date" : "2017/2/10",
"point" : 1
},
{
"date" : "2017/2/11",
"point" : -1
}
]
}
I'd like to be able to count the no of points by date
my code is like this
var array = [];
var array2 = [];
db.coll.find({}).toArray(function(err, result) {
result.map(function(data) {
array.push(data.values)
})

You would use following :
db.your_collection.aggregate([
{ $unwind : '$values'},
{ $group : {
_id : '$values.date',
point : { $sum : '$values.point' }
}
}
]);
Which will give you below result :
{ "_id" : "2017/2/11", "point" : -2 }
{ "_id" : "2017/2/10", "point" : 0 }
{ "_id" : "2017/2/9", "point" : 2 }
However, its always good to have date stored in proper date format and not as a string.

You need to use the sort function from mongodb:
sort({datefield: -1}}
in Mongodb for example
db.products.find().sort({"created_at": 1}) --- 1 for asc and -1 for desc
in nodejs for example:
collection.find().sort({datefield: -1}, function(err, cursor){...});

You need to perform a map/reduce
I assume your array of objects are stored in the data field of your documents, themselves in an items collection.
// map function which pushes the points in an array associated to the given date date
var mapDataPoints = function() {
for (var i=0; i<this.data.length; i++) emit(this.data[i].date, this.data[i].points);
};
// reduce function, which sums the array of points for a given date
var reduceDataPoints = function(dateId, points) {
return Array.sum(points);
};
// map-reduce operation over the collection, which takes each document and apply the map function, then run the reduce function on the result of the map operation
// result is stored in a new collection, called pointsByDate
db.items.mapReduce(
mapDataPoints,
reduceDataPoints,
{ out: "pointsByDate" }
)
// pointsByDate can be queried just like any collection
db.pointsByDate.find({ "_id": "2017/2/10" });

The following code works for your problem.
db.Stack.aggregate([
{$unwind: "$values"},
{$group: {
_id: "$values.date",
sum: {$sum: "$values.point"}
}
}
])
This is the output for your code
/* 1 */
{
"_id" : "2017/2/11",
"sum" : -2.0
}
/* 2 */
{
"_id" : "2017/2/10",
"sum" : 0.0
}
/* 3 */
{
"_id" : "2017/2/9",
"sum" : 2.0
}

Related

How to implement Cursor based pagination for mongodb in node.js using official mongodb client when _id is overrided?

I want to implement pagination for mongodb in node.js enviroment using offical mongodb package.
I have mongodb collection whose rows are like
db.registrations.findOne()
{ "_id" : {"mtid" : 156568417304061, "uid" : 156568728002501}, "tid" : 2 }
Here _id is not instance of ObjectId() nor numeric. How can I implement Cursor based pagination on this collection? I will avoid offset-based and time-based pagination.
I treid to use idea given in https://stackoverflow.com/a/58411020/1762051 but I guess this will not fit in my case as _id is not numeric.
I don't think you have much to do.
You have by default an index on _id and according to mongodoc, the field you index can be a whole embedded document whose (sub) fields order matters.
That is {mtid, uid} is your embedded document and as long as you query in the same (sub)field order you should be fine.
You would then just need to sort by _id.
generate some data:
arr = Array(25).fill(0).map((x,i)=>i).sort(x=>Math.random()-0.5).map(x=>[(x-x%5)/5,x%5])
arr.forEach(([mtid, uid],i)=>db.dums.insert({_id:{mtid, uid},n:i}))
in my case:
{ "_id" : { "mtid" : 2, "uid" : 4 }, "n" : 0 }
{ "_id" : { "mtid" : 2, "uid" : 2 }, "n" : 1 }
{ "_id" : { "mtid" : 0, "uid" : 1 }, "n" : 2 }
{ "_id" : { "mtid" : 0, "uid" : 0 }, "n" : 3 }
{ "_id" : { "mtid" : 0, "uid" : 2 }, "n" : 4 }
{ "_id" : { "mtid" : 2, "uid" : 3 }, "n" : 5 }
{ "_id" : { "mtid" : 3, "uid" : 1 }, "n" : 6 }
...
consider below query
function query(str){
let [mtid, uid] = str.split('_')
mtid = parseInt(mtid)
uid = parseInt(uid)
const res = db.dums.find({
_id: {
$gt: { mtid, uid }
}
}).limit(2).sort({ _id: 1 }).toArray()
if(!res.length) return {items:[], next:null}
const last = res[res.length-1]._id
const next = last.mtid + '_' + last.uid
print(res.map(x=>x._id.mtid+'_'+x._id.uid).join(';'))
return {
items: res,
next,
goNext(){return query(next)}
}
}
results:
> var a=query('0_0');
0_1;0_2
> var a=a.goNext();
0_3;0_4
> var a=a.goNext();
1_0;1_1
> var a=a.goNext();
1_2;1_3
> var a=a.goNext();
1_4;2_0
> var a=a.goNext();
2_1;2_2
we can observe that the items are indeed ordered by mtid, uid

want to update only value element of specific key on mongodb

I have a dataset like this:
{
"_id" : ObjectId("5a7bee68996b551034015a15"),
"sequenceid" : 1,
"fruit" : [
{
"name" : "#APPLE",
"value" : 2
},
{
"name" : "#BANANA",
"value" : 1
},
{
"name" : "#ORANGE",
"value" : 5
}
}
want to update only Apple value i.e from 2 to 25. Expected result will be:
{
"_id" : ObjectId("5a7bee68996b551034015a15"),
"sequenceid" : 1,
"fruit" : [
{
"name" : "#APPLE",
"value" : 25
},
{
"name" : "#BANANA",
"value" : 1
},
{
"name" : "#ORANGE",
"value" : 5
}
}
I tried the code but this will replace all entry and do only one entry. My code is
db.Collection.update({'sequenceid': 1}, {$set: {'fruit' : {'name': '#APPLE', 'value': parseFloat(25)}}}, function(error, result){
if(error){
console.log('error');
} else {
console.log('success');
}
});
It can produce the result:
{
"_id" : ObjectId("5a7bee68996b551034015a15"),
"sequenceid" : 1,
"fruit" : [
{
"name" : "#APPLE",
"value" : 25
}
}//Delete all my rest entry
How I can Do this. I am a newbie on MongoDB
This will update only the first occurrence of record.For reference MongoDB - Update objects in a document's array (nested updating)
db.collection.update({ _id: ObjectId("5a7bf5586262dc7b9f3a8422") ,"fruit.name" : "#APPLE"},
{ $set:
{
"fruit.$.value" : 25
}
})
If you are writing JavaScript query then you can update like this
db.collection.find({'sequenceid': 1}).forEach(function(x){
x.fruit.forEach(function(y){
if(y.name=="#APPLE")
{
y.value = 25
}
})
db.collection.update({_id:x._id},x)
})
db.Collection.update({
_id: ObjectId("5a7bee68996b551034015a15"),
"fruit": {
$elemMatch: {
"name": "#APPLE"
}
}
}, {
$set: {
"fruit.$.value": 25
}
})
In above update operation $elemMatch operator is used to search a value in an array and in $set stage positional operator $ is used to update value of specific key belonging to an array element

push data to multidimensional mongodb document

Hi I have multi dimensional mongoDB document now I want to push new records to the locations filed with same objects time , lat , lng
here is my database record
{
"_id" : ObjectId("5979b1a81c4547652583397e"),
"shenase" : "002",
"name" : "??? ?????",
"vehicle" : "?????",
"locations" : {
"time" : "2017-07-27 13:55:30",
"latiude" : 35.7429305,
"langtiude" : 51.5001943
}
}
I want to know how can I do that with node js
Firstly your database schema is not correct. Locations must be an array and not an object. So the record must be
{
"_id" : ObjectId("5979b1a81c4547652583397e"),
"shenase" : "002",
"name" : "??? ?????",
"vehicle" : "?????",
"locations" : [
"time" : "2017-07-27 13:55:30",
"latiude" : 35.7429305,
"langtiude" : 51.5001943
]
}
Now to push the new record onto array you can simply write
var data = {
"time":"2017-07-29 13:55:30",
"latitude": 18.0667,
"longitude": 45.99890
};
YourModel.update(
{ _id: toBeUpdated._id },
{ $push: { location: data } },
function(err,data){
console.log(err,data);
}
);

How to update a nested array in Node.js + Mongo DB where index value is known

I want to update nested array in Mongo DB (using Node.js). I am able to get the array index location.
How can I update same ? I am facing some problem while using escape character in $set
Here is what I am doing :
testCollection.update({
"uniqueID": someID,
"topLevelObject.innerObjectValue": innerObjectVal
}, {
$set: {
'array.' + outerArrayIndex + '.value': updatedValue,
}
}, function(err, result) {
if (err) {
console.log("Error occurred while updating db info");
}
}
);
It's hard to tell what the problem is because you have not included an example document or shown an error message or what goes wrong. Assuming your document looks like
{
"_id" : 0,
"array" : [{ "value" : 2 }, { "value" : 6 }]
}
then your above query should work, e.g.
db.test.update({ "_id" : 0 }, { "$set" : { "array.1.value" : 906 } })
will modify the document to
{
"_id" : 0,
"array" : [{ "value" : 2 }, { "value" : 906 }]
}
I omitted the other query condition because it isn't necessary with a unique id specified - perhaps the condition isn't matching any documents?
If you document looks like
{
"_id" : 0,
"array" : [2, 6]
}
then you don't need the .value in the update query:
db.test.update({ "_id" : 0 }, { "$set" : { "array.1" : 906 } })
I'd also check that the string concatenation with the variable outerArrayIndex is producing the correct field path.

Mongoose nodejs - Grouping with arrays

I have got a collection with the following schema :
{OrderId : id, CustomerId : id, amount: Number, otherPayers : [{name : String, amount : Number}]}
I would like to make an average of the "amount" of all orders for a customerId, and I can do that with aggregate by grouping on customerId with avg on amount.
Now, I'd like the output "amount" field to be not only the avg of "amount", but also of the "amount" field of the "otherPayers" field.
I have had a look into mapreduce but I can't get it to work.
Any idea? In SQL it would have been quite simple with a subquery.
I'm guessing you want to average together all the amounts inside the otherPayers array plus the amount field on the top level of the document. Here is how you would do it with aggregation framework:
unwind = { $unwind : "$otherPayers" };
group = { $ group : { _id : "$CustomerId",
amt : { $first : "$amount" },
count : { $sum : 1 },
total : { $sum : "$otherPayers.amount" }
} };
project = { $project : { _id : 0,
CustomerId : "$_id",
avgPaid : { $divide : [
{ $add : [ "$total", "$amt" ] },
{ $add : [ "$count", 1 ] }
] }
} };
db.collection.aggregate(unwind, group, project);

Resources