Mongoose Error: Arguments must be aggregate pipeline operators - node.js

I cannot get a result with aggregation option parameter. Here is my aggregation:-
var emails = getAllEmails();
var listMatchColl = 'list_matches_' + insertedId;
SurveyDL.aggregate([
{ $match: { email: { $in: emails } } },
{ $out: listMatchColl }
], {
allowDiskUse: true
}).exec(function(err, data) {
if (err) return console.log('err', err);
console.log('data',data);
});
}
When i execute above code it thrown and error i.e,
Error: Arguments must be aggregate pipeline operators
at Aggregate.append (/home/vivek/nrich/node_modules/mongoose/lib/aggregate.js:89:11)
at new Aggregate (/home/vivek/nrich/node_modules/mongoose/lib/aggregate.js:48:17)
I have also use the alternative way but it still throw same error. Alternative way :-
var emails = getAllEmails();
var listMatchColl = 'list_matches_' + insertedId;
SurveyDL.aggregate([
{ $match: { email: { $in: emails } } },
{ $out: listMatchColl }
], {
allowDiskUse: true
},function(err, data) {
if (err) return console.log('err', err);
console.log('data',data);
});

Try setting the allowDiskUse() option for the aggregation query:
var emails = getAllEmails();
var listMatchColl = 'list_matches_' + insertedId;
SurveyDL.aggregate([
{ '$match': { 'email': { '$in': emails } } },
{ '$out': listMatchColl }
]).allowDiskUse(true)
.exec(function(err, data) {
if (err) return console.log('err', err);
console.log('data',data);
});
or using the fluent API:
SurveyDL.aggregate()
.match({ 'email': { '$in': emails } })
.append({ '$out': listMatchColl })
.allowDiskUse(true)
.exec(function(err, data) {
if (err) return console.log('err', err);
console.log('data',data);
});
You might be running over the 16MB aggregation size limit and the allowDiskUse() option is not sufficient if your data will be > 16MB since it just lets you use sort when your data is large. Consider using the aggregation cursor form to access your aggregated documents:
var cursor = SurveyDL.aggregate([
{ $match: { email: { $in: emails } } }
]).cursor({ batchSize: 1000 }).exec();
cursor.each(function(error, doc) {
// use doc
});

var emails must be Array.
listMatchColl must be String.
{ $out: 'listMatchColl' }
and if you use $out, you can`t see result your aggregation.
Your result in new collection 'listMatchColl'.

Related

How to autoIncrement id's and post to mongodb using node js

I want to increment id's automatically in the mongoDB while posting the data. I am able to attach date for the req.body. How to attach ids with auto incrementation?
This is my post call:
router.post('/addVisualization', function (req, res, next) {
MongoClient.connect(url, function (err, db) {
if (err) throw err;
var dbo = db.db(dbName);
req.body.dateOfEntry = new Date();
function getNextSequence(id) {
var ret = db.counters.findAndModify(
{
query: { _id: id },
update: { $inc: { seq: 1 } },
new: true
}
);
return ret.seq;
}
dbo.collection("visualization").insertOne(req.body, function (err, resDB) {
if (err) {
throw err;
res.status(401);
res.send({
"status": 401,
"message": "Some error happened"
});
}
else {
console.log("1 document inserted");
res.status(201)
res.send({
"body": req.body,
"status": 201,
"message": "visualization has been added"
});
}
});
db.close();
});
});
Try out the below code to auto increment id's in mongoDB.
router.post('/addVisualization', function (req, res, next) {
MongoClient.connect(url, {
useNewUrlParser: true
}, function (err, db) {
if (err) throw err;
var dbo = db.db(dbName);
req.body.dateOfEntry = new Date();
req.body.isDeleted = "false";
var countRow;
var sequenceDocument = dbo.collection("counterVisualization").findOneAndUpdate({
_id: "tid"
}, {
$inc: {
sequence_value: 1
}
}, {
new: true
});
dbo.collection("counterVisualization").find({
_id: "tid"
}).toArray(function (err, result1) {
if (err) {
throw err;
} else {
countRow = result1[0].sequence_value;
req.body["_id"] = countRow;
dbo.collection("visualization").insertOne(req.body, function (err, resDB) {
if (err) {
throw err;
res.status(401);
res.send({
"status": 401,
"message": "Some error happened"
});
} else {
console.log("1 document inserted");
res.status(201)
res.send({
"body": req.body,
"status": 201,
"message": "visualization has been added"
});
}
});
}
});
});
});
In mongo db you don't have a auto increment ids as mysql or oracle, Please take a look at this tutorial for how to do it out of the box.
Use a separate counters collection to track the last id of the sequence.
db.counters.insert(
{
_id: "userid",
seq: 0
}
)
db.counters.insert(
{
_id: "productid",
seq: 0
}
)
Create a getNextSequence function that accepts a name of the sequence.
function getNextSequence(name) {
var ret = db.counters.findAndModify(
{
query: { _id: name },
update: { $inc: { seq: 1 } },
new: true,
upsert : true // Creates a new document if no documents match the query
}
);
return ret.seq;
}
Use this getNextSequence() function during insert.
db.users.insert(
{
_id: getNextSequence("userid"),
name: "Mr. X",
// ... more fields
}
)
db.products.insert(
{
_id: getNextSequence("productid"),
name: "Mr. Y",
// ... more fields
}
)

mongoose FindbyId and where array match to a string

I want to query a user by id, and then bring only the the foods that match to a type, how can i do this?
var type = 'fish';
var id = '597a348286ffe50d882a3033';
User.findById(id, { 'food.type': type }, 'food', function(err, model) {
if (err) {
console.log(err);
}
if (!model) {
console.log('no model');
}
if (model) {
console.log(model);
}
});
I would use aggregate combined with $filter:
User.aggregate([{
$match: { '_id': ObjectId('597a348286ffe50d882a3033') }
}, {
$project:
{
food: {
$filter: {
input: '$food',
as: 'food',
cond: {
$eq: ['$$food.type', type]
}
}
}
}
}])
This will first select the document matching the given Id then will return the list of foods of the given type.
Just filter the food after you get the user.
User.findById(id, function (err, user) {
if (err) {
console.log(err);
}
if (!user) {
console.log('no user');
}
if (user) {
var food = user.food.filter(function (food) { return food.type === type; });
}
});
You can create an obj something like this
var obj =
{
_id:'597a348286ffe50d882a3033',
'food.type' : 'fish'
}
and then pass this function as filter
User.find(obj, function(err, model) {
if (err) {
console.log(err);
}
// find will return empty array and not null
if (!model.length) {
console.log('no model');
}
if (model) {
console.log(model);
}
});
If you are finding the object using mongo _id field you don't have to use additional filter function after query execution you directly specify in the search query
Model.findOne({_id: '123jasdoisd123', 'food.type': type },
function(err, food){
if(err) return err;
if(!food){
// send the empty data
return []
}
return food;
}
)

TypeError: db.collection.createIndex is not a function in Search functionality for express project

Using the code below I get the error TypeError: db.collection.createIndex is not a function, which I want to use to implement the search functionality from this site: https://docs.mongodb.com/manual/text-search/
Perhaps there is an alternative way to do search for MongoDB?
app.post('/search', function(req, res) {
var results = [];
mongodb.MongoClient.connect(url, function(err, db) {
db.collection.createIndex( { Name: "text", Code: "text" } );
var searcher = db.collection('Modules').find({
$test: {
$search: "Bob",
$caseSensitive: false
}
});
searcher.forEach(function(doc, err) {
//assert.equal(null, err);
results.push(doc);
}, function() {
db.close();
res.json(results);
console.log("SEARCH IS WORKING")
});
});
});
You may want to check if the index exists first before creating it. Use the indexExists function to check this:
mongodb.MongoClient.connect(url, function(err, db) {
var collection = db.collection('Modules');
collection.indexExists('Name_text_Code_text').then(function(indexExists){
if(!indexExists)
collection.createIndex( { Name: 'text', Code: 'text' } );
}).then(function(result){
collection.find({
$test: {
$search: "Bob",
$caseSensitive: false
}
}).toArray();
}).then(function(results) {
db.close();
res.json(results);
console.log("SEARCH IS WORKING");
});
});

How to query mongoose such that the search have to match two ids

I have this code which searches for documents based on two ids.
User.find({
$or: [
{ id: req.body.myId },
{ id: req.body.id }
]
}, function(err, users) {
console.log(users)
});
This works fine, however, let's assume req.body.myId is undefined, then it would grab all documents that match req.body.id only. I need it to be such that if it can't be find any documents relating to one id, then it shouldn't grab anything for the other Id, giving back an empty array of users.
Make a check before executing the query and don't execute it if one of the parameters are undefined, so you don't make unnecessary calls to MongoDB. Something like:
if (!req.body.myId || !req.body.id) {
// return here empty array or whatever
}
By the way in your case usually the $in operator is used:
User.find({
id: {
$in: [req.body.myId, req.body.id]
}
}, function(err, users) {
console.log(users)
});
This is a strange requirement but ok:
function findByTwoIds(id1, id2, callback) {
if (!id1 || !id2) {
callback(null, []);
} else {
User.find({
$or: [
{ id: id1 },
{ id: id2 }
]
}, callback);
}
}
and use it with:
findByTwoIds(req.body.myId, req.body.id, function (err, users) {
if (err) {
console.log('Error:', err);
} else {
console.log(users);
}
});

Mongoose delete array element in document and save

I have an array in my model document. I would like to delete elements in that array based on a key I provide and then update MongoDB. Is this possible?
Here's my attempt:
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var favorite = new Schema({
cn: String,
favorites: Array
});
module.exports = mongoose.model('Favorite', favorite, 'favorite');
exports.deleteFavorite = function (req, res, next) {
if (req.params.callback !== null) {
res.contentType = 'application/javascript';
}
Favorite.find({cn: req.params.name}, function (error, docs) {
var records = {'records': docs};
if (error) {
process.stderr.write(error);
}
docs[0]._doc.favorites.remove({uid: req.params.deleteUid});
Favorite.save(function (error, docs) {
var records = {'records': docs};
if (error) {
process.stderr.write(error);
}
res.send(records);
return next();
});
});
};
So far it finds the document but the remove nor save works.
You can also do the update directly in MongoDB without having to load the document and modify it using code. Use the $pull or $pullAll operators to remove the item from the array :
Favorite.updateOne({ cn: req.params.name }, {
$pullAll: {
favorites: req.params.deleteUid,
},
});
To remove objects from array then
Favorite.updateOne({ cn: req.params.name }, {
$pullAll: {
favorites: [{_id: req.params.deleteUid}],
},
});
(you can also use updateMany for multiple documents)
http://docs.mongodb.org/manual/reference/operator/update/pullAll/
The checked answer does work but officially in MongooseJS latest, you should use pull.
doc.subdocs.push({ _id: 4815162342 }) // added
doc.subdocs.pull({ _id: 4815162342 }) // removed
https://mongoosejs.com/docs/api.html#mongoosearray_MongooseArray-pull
I was just looking that up too.
See Daniel's answer for the correct answer. Much better.
Answers above are shown how to remove an array and here is how to pull an object from an array.
Reference: https://docs.mongodb.com/manual/reference/operator/update/pull/
db.survey.update( // select your doc in moongo
{ }, // your query, usually match by _id
{ $pull: { results: { $elemMatch: { score: 8 , item: "B" } } } }, // item(s) to match from array you want to pull/remove
{ multi: true } // set this to true if you want to remove multiple elements.
)
Since favorites is an array, you just need to splice it off and save the document.
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var favorite = new Schema({
cn: String,
favorites: Array
});
module.exports = mongoose.model('Favorite', favorite);
exports.deleteFavorite = function (req, res, next) {
if (req.params.callback !== null) {
res.contentType = 'application/javascript';
}
// Changed to findOne instead of find to get a single document with the favorites.
Favorite.findOne({cn: req.params.name}, function (error, doc) {
if (error) {
res.send(null, 500);
} else if (doc) {
var records = {'records': doc};
// find the delete uid in the favorites array
var idx = doc.favorites ? doc.favorites.indexOf(req.params.deleteUid) : -1;
// is it valid?
if (idx !== -1) {
// remove it from the array.
doc.favorites.splice(idx, 1);
// save the doc
doc.save(function(error) {
if (error) {
console.log(error);
res.send(null, 500);
} else {
// send the records
res.send(records);
}
});
// stop here, otherwise 404
return;
}
}
// send 404 not found
res.send(null, 404);
});
};
This is working for me and really very helpful.
SubCategory.update({ _id: { $in:
arrOfSubCategory.map(function (obj) {
return mongoose.Types.ObjectId(obj);
})
} },
{
$pull: {
coupon: couponId,
}
}, { multi: true }, function (err, numberAffected) {
if(err) {
return callback({
error:err
})
}
})
});
I have a model which name is SubCategory and I want to remove Coupon from this category Array. I have an array of categories so I have used arrOfSubCategory. So I fetch each array of object from this array with map function with the help of $in operator.
keywords = [1,2,3,4];
doc.array.pull(1) //this remove one item from a array
doc.array.pull(...keywords) // this remove multiple items in a array
if you want to use ... you should call 'use strict'; at the top of your js file; :)
I used this format for my project and it's worked
router.delete('/dashboard/participant/:id', async (req, res, next) => {
try {
const participant = await Participant.findByIdAndDelete({ _id: req.params.id });
// { $pull: { templates: { _id: templateid } } },
const event = await Event.findOneAndUpdate({ participants: participant._id }, { $pull: { participants: participant._id } }, { new: true });
res.status(200).json({ request: 'Deleted', participant, event });
} catch (error) {
res.json(error)
}
});
Favorite.update({ cn: req.params.name }, { "$pull": { "favorites": { "_id": favoriteId } }}, { safe: true, multi:true }, function(err, obj) {
//do something smart
});

Resources