MongoDB: Using lean on aggregate function - mongodb

I'm trying to use the lean() option in order to speed up my queries. But when adding it to a query like this:
Pets.aggregate({ $match: { 'Name': 'Floofkins', 'lastMealDate': {$gt: today}}}, function (err, pets){
if (err) {
res.status(500).json({'error': err});
console.log('Could not fetch pets: ' + err);
return;
}
petsHadMealsToday = pets.length;
}).lean();
All I get is TypeError: Cannot read property 'lean' of undefined although pets.length returns the number of pets that matched the query.
If I'd remove the match option however and run something like below, it works like a charm.
Pets.aggregate({ 'Name': 'Floofkins', 'lastMealDate': {$gt: today}}, function (err, pets){
if (err) {
res.status(500).json({'error': err});
console.log('Could not fetch pets: ' + err);
return;
}
petsHadMealsToday = pets.length;
}).lean();
I guess I'm missing some fundamental point about how to use match and such, so feel free to educate me!

After reading a bit, this is my take on the answer to my question:
lean() is not needed on an aggregate function as the documents returned are plain JavaScript objects, and not Mongoose objects. This is because any shape of document can be returned. Source.
Therefore adding lean() to an aggregate function will generate an error since there is nothing to perform the lean() function upon.

No need to lean pipeline output . Aggregate output is already lean , using mongoose too
http://mongoosejs.com/docs/api.html#model_Model.aggregate
because output of aggregate pipeline is not a mongoose document

Related

Sails.js Waterline Update Method

I'm using Waterline in my Sails.js App to query my mongo database. I'm able to get a record out based on multiple query paramaters, such as this:
Model.findOne({param1: params[0], param2: params[1]})...
That works great. I looked at the docs for .update() and copied that but it isn't working. I still need to update a record based on two parameters, so this is my update function:
Model.update(
[{param1: params[0]}, {param2: params[1]}],
[{field1: update[0]}, {field2: update[1]}]
).exec(function(err, updatedModel) {
console.log(err);
console.log(updatedModel);
});
From what I read, it looks like the first argument to the update method is an object or array of objects with the values you're querying on to update a record; then the second parameter is the updated fields.
I'm not getting an error or an updatedModel, however, which probably means that I'm not finding an object from the Model table in mongo that fits the requirements. But I know that the object is there and the parameters are correct. Any suggestions?
Also, here's a gist with the update function as well.
Params in update function are objects not arrays.
Model.update(
{param1: params[0], param2: params[1]}, //this is find
{field1: update[0], field2: update[1]} // this is update
).exec(function(err, updatedModel) {
if(err)
console.error(err);
console.log(updatedModel);
});

Mongoose/MongoDB: Can I update a document if I have a reference to it already without a query?

I perform db.collection.findOne( {query}... In the callback, if I successfully find an object, I want to modify one of it's properties...
,function(err, obj){
//is there a better way to do this since I already have a reference to the object? Searching the docs always gives me db.collection APIs
db.collection.update( {_id: obj._id }, { modifications}, function(err, obj){ ... }
}
Your subject includes mongoose so I guess you are using mongoose.
So there are two good ways you can do that (of course there are much more ways to do that):
Use findOneAndUpdate
model.findOneAndUpdate(query, {modifications}, function(err, obj) { //callback } )
read more about it here
your returned obj in the callback is a mongoose model, so you can modify it and save it:
function(err, obj){
obj.some_field = 'blabla';
obj.save();
}
Sure you can.
You can also use the findOneAndUpdate to use a single query. If nothing is found, nothing should be modified.

Getting Mongoose Schema's and Documents without knowing Schema / Collection names?

Short Version: I basically want to do what show collections does in the mongo shell, in mongoose.
Long Version:
I want to create an application that allows users to basically create their own Schema's > Collections > Documents using mongo + mongoose + node.js.
The first step of this application would be to show previously created Collections and their Documents. But since these would be created by a user their names are not known.
The code I've found that came closest was:
function find (collec, query, callback) {
mongoose.connection.db.collection(collec, function (err, collection) {
collection.find(query).toArray(callback);
});
}
But I don't know what the name of the collection is so I can't pass the 'collec' parameter.
So, anyone know how to get the list of collections without knowing their names?
I ended up using node-mongodb-native (the mongo driver that mongoose works on top of) to get the collection names:
var nmn_connect = require('mongoose/node_modules/mongodb').connect;
nmn_connect('mongo://localhost:27017/DATABASE', function(err, db) {
if(!err){
db.collectionNames(function(err, names){ // what I was looking for
if(!err){
console.log(names);
db.close();
}else{
console.log(err);
db.close();
}
});
}else{
console.log(err)
}
});
The Object returned by mongoose.connect() has no method similar to collectionNames() that I can find.
I know nothing of mongoose, but this page suggests that you could use mongoose.connection.collections to access all collections: http://mongoosejs.com/docs/api.html
each collection again should have a name attribute.
try this:
mongoose.connection.db.collectionNames(function (err, names) {
});

Identify last document from MongoDB find() result set

I'm trying to 'stream' data from a node.js/MongoDB instance to the client using websockets. It is all working well.
But how to I identify the last document in the result? I'm using node-mongodb-native to connect to MongoDB from node.js.
A simplified example:
collection.find({}, {}, function(err, cursor) {
if (err) sys.puts(err.message);
cursor.each(function(err, doc) {
client.send(doc);
});
});
Since mongodb objectId contatins creation date you can sort by id, descending and then use limit(1):
db.collection.find().sort( { _id : -1 } ).limit(1);
Note: i am not familiar with node.js at all, above command is mongo shell command and i suppose you can easy rewrite it to node.js.
Say I have companies collection. Below snippet gives me last document in the collection.
db.companies.find({},{"_id":1}).skip(db.companies.find().count()-1);
Code cannot rely on _id as it may not be on a specific pattern always if it's a user defined value.
Use sort and limit, if you want to use cursor :
var last = null;
var findCursor = collection.find({}).cursor();
findCursor.on("data", function(data) {
last = data;
...
});
findCursor.on("end", function(data) {
// last result in last
....
});

Finding an Embedded Document by a specific property in Mongoose, Node.js, MongodDB

For this app, I'm using Node.js, MongoDB, Mongoose & Express
So I have a Param Object that contains an array of Pivots, and I want to read certain data from the pivots as outlined below
---in models.js-------------------------
var Pivot = new Schema({
value : String
, destination : String
, counter : Number
});
var Param = new Schema({
title : String
, desc : String
, pivots : [Pivot]
});
------------- in main.js --------------
var Param = db.model('Param');
app.get('/:title/:value', function(req, res){
Param.findOne({"title":req.param('title')}, function(err, record){
console.log(record.pivots);
record.pivots.find({"value":req.param('value')}, function(err, m_pivot){
pivot.counter++;
res.redirect(m_pivot.destination);
});
record.save();
});
});
I know that the code works until console.log(record.pivots), since i got a doc collection with the right pivot documents inside.
However, there does not seem to be a find method to let me match an embedded document by the 'value' property defined in the schema. Is it possible to search through this array of embedded documents using .find() or .findOne() , and if not, is there some easy way to access it through mongoose?
varunsrin,
This should do it
app.get('/:title/:value', function(req, res) {
Param.findOne({'pivots.value': req.param('value'), "title":req.param('title')}},
function(err, record) {
record.pivot.counter++;
res.redirect(m_pivot.destination);
record.save();
});
});
Note the pluralization of the query to match the field name in your schema
You can querying using embedded document properties like this:
{'pivot.value': req.param('value')}}
Update in response to comment:
app.get('/:title/:value', function(req, res) {
Param.findOne({'pivot.value': req.param('value'), "title":req.param('title')}},
function(err, record) {
record.pivot.counter++;
res.redirect(m_pivot.destination);
record.save();
});
});
I solved it temporarily using a simple for loop to parse the object array as follows:
for (var i=0; i <record.pivots.length; i++){
if (record.pivots[i].value == req.param('value')){
res.redirect(record.pivots.destination);
}
}
However, I still think that Mongoose must have a simpler way of interacting with embedded documents - and this loop is somewhat slow, especially when the number of embedded documents grows large.
If anyone has any suggestions for a faster way to search this object array either in js or with a mongoose function, please post below.
the biggest problem with this is that if your req has some fields empty (that should act as wildcard), you will not find anything since mongo tries to match empty params as well, so searching for {"user":"bob", "color":""} is not the same as {"user":"bob", "color":"red"} or {"user":"bob"}. this means that you have to first create a query object and filter out any unused parameters before you pass it in, and if you create a query object, you can no longer do something like "user.name=..." because mongo interperets this as an error since it does not first resolve the object literal into a string.
Any ideas on this problem?
ps. You'd think it would be easy enough to make an object like:
user.name="bob"; user.color:"green"; user.signup.time="12342561"
and then just use user as a query object :/
I think you are looking for the "$in" keyword?
As in:
{a: {$in: [10, "hello"]}}
source: MongoDB Queries CheatSheet