How to make object for mongoose query? - node.js

Is there way to build complete object for mongoose to use as query? When making route for search I need to pass many query parameters and sanitize them in express middleware. From those I would like to build query object.
I ended up with something like this:
Inside midleware:
res.locals.filter = {
query: ...,
projection: ...,
sort: ...,
limit: ....,
}
inside router:
User.find(res.locals.filter.query)
.sort(res.locals.filter.sort)
.limit(res.locals.filter.limit)
.exec()
Is there any way to format my filter so I can pass it all at once? Found some examples but nothing seem to work for me...
In other word do something like:
User.query(filter)

You can do this by adding query as a static method on your schema:
userSchema.statics.query = function(filter) {
return this.find(filter.query).sort(filter.sort).limit(filter.limit);
};
Which you could then call as:
User.query(res.locals.filter).exec(callback);

As it was pointed out by JohnnyHK, the style of find call with object fields like $query and $orderBy was only supported in the shell (and is now deprecated) so there isn't any built-in support.

Related

Laravel - how to use paginate with parameters

How can I use pagination with vuejs and laravel
I first used this in order to have my posts with users,categories and photo.
public function index()
{
//
$posts = Post::with('user', 'category', 'photo')->orderBy('id', 'desc')->get();
return response()->json([
'posts' => $posts,
], 200);
}
All works, but now I want to add pagination and for some reason it does not work. I did like this:
$posts = Post::with('user', 'category', 'photo')->orderBy('id', 'desc')->paginate(5)->get();
How can I make my pagination work?
I believe you just need to take out the get() from the end:
$posts = Post::with('user', 'category', 'photo')->orderBy('id', 'desc')->paginate(5);
When I use paginate I skip the get().
The paginate method looks for a page query string argument on the HTTP Request. It probably doesn't work if it's not present. You seem to be creating a route meant for an API. If that's the case, it's quite likely you don't have the page variable set in your HTTP request. You can check the presence of this variable or create your own paginator: https://laravel.com/docs/5.6/pagination#manually-creating-a-paginator

node.js mongodb projection ignored when there is a criterion

I am writing a node.js application using express, mongodb, and monk.
When I do a find with criteria only or with projections only, I get the expected result, but when I do a find with both, the full documents are returned, i.e., the projection is not performed. My code looks like this:
var collection = db.get('myDB');
collection.find({field1: "value"},{field2: 1, _id: 0},function(e,docs) {
...do stuff with docs...
});
It returns not just field2 but all fields of all the docs matching the criterion on field1. I can get field2 from this, but I don't like the inefficiency of it.
Is there a way to use both criteria and projections?
Monk uses a space-delimited string for field projection where you prefix a field name with - to exclude it.
So it should be:
var collection = db.get('myDB');
collection.find({field1: "value"}, 'field2 -_id', function(e,docs) {
...do stuff with docs...
});

Set field to empty for mongo object using mongoose

I have a document object that has an embedded sub-document.
To "clear" the sub-document, I try this:
obj.mysub = {};
obj.save();
This doesn't work, my object still has the contents of the mysub sub-document.
But this:
obj.mysub = undefined;
obj.save();
This does work, it removes my sub-document from the object.
My question is why doesn't the first version work? What is going on in Mongodb / Mongoose in the first example?
[edit] Why doesn't the empty object get saved in the first example above.
Mongoose sort of "protects" you from a lot of logic like you have presented in it's own internal resolution. So if you actually need to do this then do it at a lower level to the driver as in:
YourModel.update(
{ /*statement matching your document as a query */ },
{ "$unset": { "mysub": 1 } }
)
And per the normal MongoDB logic then this will work and remove that level in the document that was selected. See the $unset operator for more.

mongoose: how to get string representation of query

I implementing module who automatically generate mongoose query by requested params, so for simplified test process I need to be able to get text representation of final query. How could I do that?
Like we have something like this:
var q = AppModel.find({id:777}).sort({date:-1})
I need to get something like this
"db.appmodels.where({id:777}).sort({date: -1})"
You can set debug for mongoose, which would by default send the queries to console, to use the following:
mongoose.set('debug', function (collectionName, method, query, doc) {
// Here query is what you are looking for.
// so whatever you want to do with the query
// would be done in here
})
Given a query object q you can rebuild the query using its fields, namely q._conditions and q._update. This is undocumented though and could easily break between versions of Mongoose (tested on Mongoose 4.0.4).

How do you turn a Mongoose document into a plain object?

I have a document from a mongoose find that I want to extend before JSON encoding and sending out as a response. If I try adding properties to the doc it is ignored. The properties don't appear in Object.getOwnPropertyNames(doc) making a normal extend not possible. The strange thing is that JSON.parse(JSON.encode(doc)) works and returns an object with all of the correct properties. Is there a better way to do this?
Mongoose Models inherit from Documents, which have a toObject() method. I believe what you're looking for should be the result of doc.toObject().
http://mongoosejs.com/docs/api.html#document_Document-toObject
Another way to do this is to tell Mongoose that all you need is a plain JavaScript version of the returned doc by using lean() in the query chain. That way Mongoose skips the step of creating the full model instance and you directly get a doc you can modify:
MyModel.findOne().lean().exec(function(err, doc) {
doc.addedProperty = 'foobar';
res.json(doc);
});
JohnnyHK suggestion:
In some cases as #JohnnyHK suggested, you would want to get the Object as a Plain Javascript.
as described in this Mongoose Documentation there is another alternative to query the data directly as object:
const docs = await Model.find().lean();
Conditionally return Plain Object:
In addition if someone might want to conditionally turn to an object,it is also possible as an option argument, see find() docs at the third parameter:
const toObject = true;
const docs = await Model.find({},null,{lean:toObject});
its available on the functions: find(), findOne(), findById(), findOneAndUpdate(), and findByIdAndUpdate().
NOTE:
it is also worth mentioning that the _id attribute isn't a string object as if you would do JSON.parse(JSON.stringify(object)) but a ObjectId from mongoose types, so when comparing it to strings cast it to string before: String(object._id) === otherStringId
the fast way if the property is not in the model :
document.set( key,value, { strict: false });
A better way of tackling an issue like this is using doc.toObject() like this
doc.toObject({ getters: true })
other options include:
getters: apply all getters (path and virtual getters)
virtuals: apply virtual getters (can override getters option)
minimize: remove empty objects (defaults to true)
transform: a transform function to apply to the resulting document before returning
depopulate: depopulate any populated paths, replacing them with their original refs (defaults to false)
versionKey: whether to include the version key (defaults to true)
so for example you can say
Model.findOne().exec((err, doc) => {
if (!err) {
doc.toObject({ getters: true })
console.log('doc _id:', doc._id)
}
})
and now it will work.
For reference, see: http://mongoosejs.com/docs/api.html#document_Document-toObject
To get plain object from Mongoose document, I used _doc property as follows
mongooseDoc._doc //returns plain json object
I tried with toObject but it gave me functions,arguments and all other things which i don't need.
The lean option tells Mongoose to skip hydrating the result documents. This makes queries faster and less memory intensive, but the result documents are plain old JavaScript objects (POJOs), not Mongoose documents.
const leanDoc = await MyModel.findOne().lean();
not necessary to use JSON.parse() method
You can also stringify the object and then again parse to make the normal object.
For example like:-
const obj = JSON.parse(JSON.stringify(mongoObj))
I have been using the toObject method on my document without success.
I needed to add the flattenMap property to true to finally have a POJO.
const data = document.data.toObject({ flattenMaps: true });

Resources