Mongoose .find() = Query, but when does the Query finalize? - node.js

This is just a general question but I can provide an example if necessary. I've been working on a MongoDB/Mongoose, Node.JS, Express, and Handlebars stack app recently and I've ran into some issues with what is being passed from my route to the client side when dealing with Mongoose .find() queries.
I prefer to async await my queries. I have a deeply nested populate query that brings in all the data I need into one object. This is a very simplified breakdown of what I do
async function() { const finalObject = await Model.find({}).populate([*alot more populates*]) }
I pass this into my res.render() route, as so:
res.render('index', { finalObject });
The majority of my experience has been pretty straight forward. I have one issue in a separate stack overflow question about populating the same model twice in one chain (at different levels). I won't address that here.
I decided to do some calculations before passing the object to the route and attach some new values to the properties of the documents. This is where things get weird. I do some calculations and add new properties to documents (these properties are not part of the Schema). When the object is passed to the client-side, those properties I added are available to use (no problem here).
I needed to decode the finalObject into JSON and pass it as JSON as well for an easy use of the data in my client side JavaScript encodedFinalObject = encodeURIComponent(JSON.stringify(finalObject));
I decode it on the client side: clientSideFinalObject = JSON.parse(decodeURIComponent(encodedFinalObject)); and the properties I added are not there!
I'm wondering, how could the new properties I added pass through when I send the object but not pass through when I send the JSON? The decoding is the last thing I do before rendering the page, so it's not a matter of not having my code in the wrong order. I add the properties to the object then decode it.
Some research led me to find out that Model.find({}) does not return a native JavaScript object, but instead a Query. I'm assuming this is a user-defined class data-type that has special behaviors. My assumption is that between the passing from the back-end to the front-end, there is some middle step where the Query does its final processing.
The opposite situation has also occurred. As a solution to this, I decoded the Query finalObject immediately after the Model.find({}) and re-encoded it into a native JavaScript object. After that, I did all my calculations, attached the results as properties to the documents, and sent that new version over as the main finalObject (I still also sent over a separate decoded object for client-side JavaScript use).
This solved the issue of the new properties not being there. They appeared, but this time the decoded then re-encoded finalObject did not have the virtual properties of my Models. The main model has a virtual property that calculates some of it's other properties. It's almost as if that part of the query didn't execute within the .find({}) process. So, when I decoded the finalObject, the virtual properties were never there and were never decoded.
Can anyone explain to me what is going on? Thank you!

Related

dataObject creation in MEANJS 4.2

I recently load several versions of MEANJS (meanjs.org) to understand the file structure better and view the changes.
In 4.0, articles.client.controller.js to be specific they have:
and I'm able to make changes to new Articles there as I appended new fields to the mongoose Schema.
$scope.create = function () {
// Create new Article object
var article = new Articles({
title: this.title,
content: this.content
});
In 4.1, it comes like this.
// Create new Article object
var article = new Articles({
title: this.title,
content: this.content
});
Now with 4.2, I don't see that in articles.client.controller.js,
vm.article = article;
I have my modified Schema version. How to make changes to the creation of a new Articles object? This is a good question for upgrading app from 4.0, 4.1 to 4.2.
It’s changed slightly.
Trying to use the resources directly as was done in version 4 may cause problems where the page is ready but not the resource (Article).
To get around this problem angular uses resolves which which use promises to handle the timing issues.
The important thing to know is that the promise will give you some answer at some point in the future - Just, it may not be the answer you'd like!
Either way, it always tells you once it has the answer - or more correctly once it has resolved.
Angular uses the promises to help out with the timing issues mentioned above. The resolves are keyed to a promise and will only load the controller once the resolve(s)... erm... resolve!
This means we will always have articles when we expect.
Promises, resolves - Let me at'em - Let me at'em!!
The resolves option is used in the updated articles.client.routes. Here we see that articleResolve is keyed to getArticle which isn't a promise itself but is instead a function which returns one (which is just a good!)
If we look at the lines below we can see how we create this promise returning function. It's a function which uses Angular's $stateParams (to inspect the state) and fill in the articleId for the requested article. We get the articles using the injected and familiar Articles service.
In your case you want to know how new articles are created so we must travel a little further into the articles service which has recently been updated.
This is almost the same as the Articles service which you are used to using, however the additional lines add an extra method to this service which allow it to create, or if existing to save the article details.
These lines are how we extend a service in angular and the below implementation basically checks the article to see wether it has the ._id property. This is the string representation of the .id property that all saved mongo db documents get.
It uses this information call the appropriate method.
Finally, back where we began
In the controller we see the earlier created promise key articleResolve used as the second injected argument; As if to say "when you have this articles service resolved use it as this second parameter when I'm injecting the arguments".
When we look at the controller definition, we notice that the corresponding second parameter is named article.
Background: Within any controller this actually points to the scope (or $scope). As convention†, and to make things in angular look like standard JavaScript where we often say var that = this, we create a variable to reference our scope.
Within the controller we attach this article to the scope so that it is accessible in the views via vm.article.
Fin!
† Graze at Papa John's style guide when you get chances and slowly evolve your code style to match it. It will help you avoid traps and as a side effect makes lot of the angular code examples/tutorial more understandable, especially where the authors also follow it.

Postman converts Mongo arrays to Objects

I am working on an application that uses MongoDB for data persistence, alongside the mongoose library to allow angularjs to communicate with it. I'm using Postman to test my routes, and that's where the problem comes in.
In MongoDB, I inserted an entry thusly:
db.posts.insert{{id:6, author:"Blah", area:"Sport", body:"bleh", user_id:6, comments:[{author:"foo", body:"bar"}]})
I then queried it through Postman, and it successfully returned the Post, but there was a problem. The comments array had been returned as a strange data type, object Object, and I could not access it from my HTML files.
This is what Postman retrieved:
{"_id":"5536112cffc7bf00b2585d24","id":6,"author":"Blah","area":"Sport","body":"bleh","user_id":6,"comments":["[object Object]"]}]
I want to be able to access the author and body elements of comments independently, or else to be able to get at them from my http files. Does anyone know how I might stop this Object-ification from happening, or failing that, how I might query the data from this new Object?
Thanks.
Instead of passing complete db object we can pass required parameters. below is the example to form the required parameters.
res.json({"author":item.author,"body":item.body});
Here You can find call back function and array conversion from the cursor

The "right way" to load, crunch and pass variables from Express to Jade

I'm building an app in Express. In one of the views a logged in Superadmin is able to view all available clients/user. In this view I am loading a bunch of client data from my MongoDB/Mongoose with a simple:
app.get('/superadmin', function(req, res) {
Client.find({}, '_id company monthly_cost sms_cost', function (err, docs) ...
As you can see above i have choosen only the values that I need from the query. These four are: "_id", "company", "monthly_cost" and "sms_cost"
From the "_id" i can get a "creation date" by using .getTimestamp(), but the Dateobject this function return is bit to complex formated. I need a simpler date, something like: (YYYY-MM-DD). Im thinking of using a small node plugin like dateformat or possibly writing a very simple function that extract the YYYY, MM and DD from the IsoDate object and saving this in a new variable/array
Now to my questions:
Q1) WHERE is actually the right place for this code? I'm currently putting it inside the route handler above... consequently it will follow right below the code above. I'm thinking this is principally the right way according to a MVC pattern. I'm thinking I dont want to put this code in the Jade view template?
Q2) IN WHAT FORM should i save this data and HOW should i pass it along to Jade. Should I somehow add it to the "docs"-data... that is, the data I extract from my DB. Or should I rather put this creationDate in a separate array which i pass to jade side by side with the original DB-data.
I hope my questions are clear enough!
Q1:
If your Mongoose-query is solely dependent on your route /superadmin, this is exactly the right place to put your code. If you are using the same snippet in different routes or functions you might as well wrap it in a function that is accessible to every route in question. But don´t forget to also wrap req, res and other required variables. Have your calculations within your callback and use Jade only for representation of data.
Q2:
What do you mean by "save"? When you are already iterating over every document to do your calculations and transformations, create an extra field creationDate for every document and pass docs as a single parameter to the Jade file afterwards.

Jade renders unexpected values in templates for "populated" mongoose properties

I have a jade template where I'm printing out properties from a mongoose object. The object has properties that were populated via mongoose's dbref-like populate feature. When The template renders I get nonsense.
simplified example:
in the route
User.findById(req.params.user, function(err, user){
res.render('training', {training:user.training});
}).populate('training.details');
in the template:
a.training-link(href='/training/#{training.details.id}') ...
in the browser:
...
The first time the page is loaded, where I would expect a regular mongo hexadecimal string (4fac4e5f379cb0a68100015d) I get something like "O >°Cm5�". This only happens on the first page load after a server restart. All subsequent page loads render as expected.
What's happening here and how can I fix it?
Thanks.
This is a bit embarrassing but I figured it out.
There is a logical branch in our app that, for various reasons, causes the code in my question above to run on the second and subsequent loads but not on the first. The other branch, which only runs on the first load, makes a similar query but I hadn't added populate('training.details') to it. I believe the nonsense string O¬N_7°¦� is the result of calling .id on an ObjectId object rather than the populated training.details.
Looks like we need to refactor some code.

Storing and retrieving JavaScript objects in/from MongoDB

I am currently playing around with node.js and MongoDB using the node-mongo-native driver.
I tested a bit around using the Mongo console storing and retrieving JS objects. I figured out, that if I store an object that contains functions/methods the methods and functions will also be stored in the collection. This is interesting since I thought that functions could not be stored in MongoDB (with the exception of the system.js collection, as suggested by the Mongo docs).
Also it will not only store the methods but actually each method and member of the object's entire prototype chain. Besides that I dont like this behaviour and think it's unintuitive I mustn't have it.
I was going to manage users in a Mongo collection. To do this I have a User object containing all of the users methods functioning as a prototype for each instance of an user. The user object itself would only contain the users attributes.
If I store a user in the Mongo collection I only want to store the own properties of the user object. No prototype members and especially no prototype methods. Currently I do not see how to cleanly do this. The options that I figured might work are:
creating a shallow copy using foreach and hasOwnProperty and storing this copy in the collection.
Add a data attribute to each user that contains all the object's attributes and can be stored in the collection.
This just came to my mind writing this: I could also set all the prototypes properties to not enumerable which should prevent them from being stored in the collection.
However, I do have the same issues the other way around: when loading a user from a collection. AFAIK there is no way to change an objects prototype in JavaScript after it was created. And there's also no way to specify a prototype to use when Mongo instantiates objects it retrieved from a collection. So basically I always get objects that inherit from Object using Mongo. As far as I can tell I have 2 options to restore a usable user object from this point on:
Create a fresh object inheriting from User and copying each attribute on the result object to the newly created object. (Compatible to storing mechanisms 1 & 3)
Create a fresh object inheriting from User and storing the result object as a data attribute on the newly created object. (Compatible to storing mechanism 2)
Are my assumptions, especially about the possibility to specify a prototype for query results, correct? What's the right way to do it, and why? I'm surely not the first person struggling to store and resurrect objects in/from MongoDB using node.js.
Currently I would go with the approach 2/2. I don't really like it, but it is the most efficient and the only one that works cleanly with the API. However, I'd much rather hear that actually the API does nothing wrong, but I do for not knowing how to use it correctly. So please, enlighten me :)
I just recently realized, that it actually is possible to change an objects prototype in V8/node. While this is not in the standard it is possible in various browsers and especially in V8/node!
function User(username, email) {
this.username = username;
this.email = email;
}
User.prototype.sendMail = function (subject, text) {
mailer.send(this.email, subject, text);
};
var o = {username: 'LoadeFromMongoDB', email: 'nomail#nomail.no'};
o.__proto__ = User.prototype;
o.sendMail('Hello, MongoDB User!', 'You where loaded from MongoDB, but inherit from User nevertheless! Congratulations!');
This is used all over various modules and plugins - even core modules make use of this technique, allthough it is not ECMAScript standard. So I guess it is safe to use within node.js.
I'm not sure I'm following you question exactly... but fwiw one thing came to mind: Have you checked out the Mongoose ORM? (http://mongoosejs.com/)
It gives you a lot of options when it comes to defining models and methods. In particular "Virtuals" might be of interest (http://mongoosejs.com/docs/virtuals.html).
Anyway, hope it helps some!

Resources