Mongoose avg function by request field - node.js

I want to return the average of a Number field by another field(the document ID field):
Comments.aggregate([
{$group:
{
_id: ($nid: req.adID), // here is the field I want to set to req.adID
adAvg:{$avg:"$stars"}
}
}
], function(err, resulat){
if(err) {
res.send(String(err));
}
res.send(resulat);
}
)
The ID field is in the request object, req.adID, I didnt find an example for grouping by a query (_id : 'req.adID').
My schema looks like:
var CommentSchema = new Schema(
{
_id: Schema.Types.ObjectId, //scheme
nid:Number, // ad ID
posted: Date, // Date
uid: Number, // user ID
title: String, //String
text: String, // String
stars: Number //String
}
);
Also if someone can write the return data for this query it will be great!

From your follow-up comments on the question, looks like your aggregation needs the $match pipeline to query the documents that match the req.adID on the nid field, your $group pipeline's _id field should have the $nid field expression so that it becomes your distinct group by key. The following pipeline should yield the needed result:
var pipeline = [
{ "$match": { "nid": req.adID } },
{
"$group": {
"_id": "$nid",
"adAvg": { "$avg": "$stars" }
}
}
]
Comments.aggregate(pipeline, function(err, result){
if(err) {
res.send(String(err));
}
res.send(result);
})

Related

For each MongoDB document, add a new variable that increments

I have a collection with the following documents:
[{_id: abc, name: "foo"}, {_id: def, name: "bar"}, {_id: ghi, name: "baz"}]
I want to change every document in that collection so it has a new field, which is unique, and that has a letter and a number, the number increases with each document. So I want to have this:
[{_id: abc, name: "foo", customId: "m1"}, {_id: def, name: "bar", customId: "m2"}, {_id: ghi, name: "baz", customId: "m3"}]
I tried using the most voted answer in this question, but it only has a number which is kind of the index in the array, but I want a letter and the number next to it.
I am using NodeJS and Express with the mongoose package. I don't mind if the answer is either using javascript code or a mongo cli command.
Any help is very appreciated, thanks in advance.
I'm assuming you need to update the existing table and also need to create the counter field for the upcoming data's,
function update() { //updating existing table
user.aggregate(
[{
$match: {
"counter": { $exists: false }
}
}],
function (err, res) {
if (err) {
console.log(err)
}
var i = 0;
var newId;
res.forEach((element, index) => {
i = i + 1;
newId = "count" + i
user.update(
{ id: element.id },
{ $set: { "Counter": newId } }
);
});
})
}
function create(userparam) {//while creating new table
autonumber.find({}, function (err, res) {
let counter_value = "Count" + res[0].incrementer
//assuming incrementer to be feild in autonumber table
const user = new User(userparam);
user.Counter = counter_value;
return await user.save()
})
}
I'm beginner,so if this code is inefficient or wrong .... sorry in advance.

Express Route with /:id input returns empty array

I am trying to have my API take an id as input and return results from mongoDB according to the id given.
My example collection looks like this:
id: 1 {
count: 5
}
id: 2 {
count: 10
}
My mongoose Schemas looks like this:
var tripSchema = new Schema({
_id: Number,
count: Number
},
{collection: 'test'}
);
And I created another file for this route, where I think the error lies in:
module.exports = function(app) {
app.get('/trips/:id', function(req,res) {
console.log(req.params.id); // Does print the ID correctly
var aggr = Trip.aggregate([
{ "$match": {
"_id": {
"$eq": req.params.id
}
}
},
{
"$project": {
"_id" : 1,
"count": "$count"
}
}
])
aggr.options = { allowDiskUse: true };
aggr.exec(function(err, stations){
if(err)
res.send(err);
res.json(stations);
});
});
}
Now using postman I try to GET /trips/72, but this results in an empty array [], there is an entry in the DB for _id 72 with a corresponding count just like above. My question is if this is the correct approach and what I am doing wrong here.
--Update:
There seems to be something wrong with either the match stage or the whole aggregation. I opted for mongoose's findById, and with this it works now:
Trip.findById(req.params.id, function (err, doc){
res.json(doc);
});
req.params.id returns your id in String form, while I think in aggregate match section you need to pass it as ObjectId. So, you should convert it to ObjectId:
$match: { _id: ObjectId(req.params.id) }

Mongoose updating sub document array's individual element(document)

Schema of group and member are as below:
var group=new Schema({
group_id:Number,
group_name:String,
members:[member]
});
var member=new Schema({
member_id:number,
name:String,
});
Sample document after inserting some record in group collection
[{
_id:55ff7fca8d3f6607114dc57d
group_id:1001,
group_name:"tango mike",
members:[
{
_id:44ff7fca8d3f6607114dc21c
member_id:2001,
member_name:"Bob martin" ,
address:String,
sex:String
},
{
_id:22ff7fca8d3f6607114dc22d
member_id:2002,
member_name:"Marry",
address:String,
sex:String
},
{
_id:44ff7fca8d3f6607114dc23e
member_id:2003,
member_name:"Alice" ,
address:String,
sex:String
}
]
}]
My problem:
I am trying to update record of individual group member(element of subdocument members). While updating I have follwing data group: _id, group_id, members:_id and newdata. I am trying like this; but it is not working
var newData={
member_name:"Alice goda" ,
address:"xyz",
sex:"F"
}
groupModel.findOne({"_id":"55fdbaa7457aa1b9bd7f7cf7","group_id":1001},'members -_id',function(err,groupMembers){
if(err)
{
res.json({
"isError":true,
"error":{
"status":1042,
"message":err
}
});
}
else
{
var mem=groupMembers.id("44ff7fca8d3f6607114dc23e");
mem.member_name=newData.member_name;
mem.address=newData.address;
mem.sex=newData.sex;
mem.save(function(err,data){
if(!err)
//sucessfull updated
});
res.json(groupDetails);
}
});
As I understand from your question details, you would like to update one object from the members array, in accordance with the criteria that you specify.
Thus, in order to accurately run the update query for your use case, you could run the following update operation against your collection:
db.collection.update({ _id: "55ff7fca8d3f6607114dc57d",
group_id:1001,
members: {
$elemMatch: { _id: "44ff7fca8d3f6607114dc23e" }
}
},
{ $set: {
"members.$.member_name": "Alice goda",
"members.$.address": "xyz",
"members.$.sex": "F"
}});
Still, be aware that the $ positional operator only updates the first array item that matches your query.
Unfortunately, there is no possibility of updating all the array elements that match your criteria in a single operation. As you can see on MongoDB Jira, the aforementioned feature is one of the most requested functionality, but it has not yet been directly implemented in MongoDB.

Mongoose sort the aggregated result

I'm having a lot of difficulty in solving this mongodb (mongoose) problem.
There is this schema 'Recommend' (username, roomId, ll and date) and its collection contains recommendation of user.
I need to get a list of most recommended rooms (by roomId). Below is the schema and my tried solution with mongoose query.
var recommendSchema = mongoose.Schema({
username: String,
roomId: String,
ll: { type: { type: String }, coordinates: [ ] },
date: Date
})
recommendSchema.index({ ll: '2dsphere' });
var Recommend = mongoose.model('Recommend', recommendSchema);
Recommend.aggregate(
{
$group:
{
_id: '$roomId',
recommendCount: { $sum: 1 }
}
},
function (err, res) {
if (err) return handleError(err);
var resultSet = res.sort({'recommendCount': 'desc'});
}
);
The results returned from the aggregation pipeline are just plain objects. So you do the sorting as a pipeline stage, not as a separate operation:
Recommend.aggregate(
[
// Grouping pipeline
{ "$group": {
"_id": '$roomId',
"recommendCount": { "$sum": 1 }
}},
// Sorting pipeline
{ "$sort": { "recommendCount": -1 } },
// Optionally limit results
{ "$limit": 5 }
],
function(err,result) {
// Result is an array of documents
}
);
So there are various pipeline operators that can be used to $group or $sort or $limit and other things as well. These can be presented in any order, and as many times as required. Just understanding that one "pipeline" stage flows results into the next to act on.

Get result as an array instead of documents in mongodb for an attribute

I have a User collection with schema
{
name: String,
books: [
id: { type: Schema.Types.ObjectId, ref: 'Book' } ,
name: String
]
}
Is it possible to get an array of book ids instead of object?
something like:
["53eb797a63ff0e8229b4aca1", "53eb797a63ff0e8229b4aca2", "53eb797a63ff0e8229b4aca3"]
Or
{ids: ["53eb797a63ff0e8229b4aca1", "53eb797a63ff0e8229b4aca2", "53eb797a63ff0e8229b4aca3"]}
and not
{
_id: ObjectId("53eb79d863ff0e8229b97448"),
books:[
{"id" : ObjectId("53eb797a63ff0e8229b4aca1") },
{ "id" : ObjectId("53eb797a63ff0e8229b4acac") },
{ "id" : ObjectId("53eb797a63ff0e8229b4acad") }
]
}
Currently I am doing
User.findOne({}, {"books.id":1} ,function(err, result){
var bookIds = [];
result.books.forEach(function(book){
bookIds.push(book.id);
});
});
Is there any better way?
It could be easily done with Aggregation Pipeline, using $unwind and $group.
db.users.aggregate({
$unwind: '$books'
}, {
$group: {
_id: 'books',
ids: { $addToSet: '$books.id' }
}
})
the same operation using mongoose Model.aggregate() method:
User.aggregate().unwind('$books').group(
_id: 'books',
ids: { $addToSet: '$books.id' }
}).exec(function(err, res) {
// use res[0].ids
})
Note that books here is not a mongoose document, but a plain js object.
You can also add $match to select some part of users collection to run this aggregation query on.
For example, you may select only one particular user:
User.aggregate().match({
_id: uid
}).unwind('$books').group(
_id: 'books',
ids: { $addToSet: '$books.id' }
}).exec(function(err, res) {
// use res[0].ids
})
But if you're not interested in aggregating books from different users into single array, it's best to do it without using $group and $unwind:
User.aggregate().match({
_id: uid
}).project({
_id: 0,
ids: '$books.id'
}).exec(function(err, users) {
// use users[0].ids
})

Resources