Serialize then deserialize mongoose.Model instance - node.js

Here's my problem:
I have a system with two hosts: machine M1 and machine M2 each running a node.js process on the same codebase.
On M1 I have a mongoose.Model instance (user) which I need to pass (using a REST api call) to M2. I have to have the complete instance of user on M2, ie. all data, virtuals, plugins, save() should work as expected.
One solution is to only send user's ObjectId, then on M2 to perform a query to mongodb to fetch the full object. I don't want to do this!
Another solution would be to serialize it using user.toJSON() or user.toObject() then send it down the wire. On M2, all I do is new User(userObject).
The problem is that when calling .save() on this it will interpret it as a new object and attempt an insert() instead of an .update().
To fix this I can set .isNew = false on the object, however, when updating, the delta (difference between stored model and updated values) now contains all the data and mongo complains it will not update a document's _id
Is there an elegant way to solve this using a native method or plugin ?! Am I doing it wrong?!

It may be wrong, but you may try using upsert() on the created user object instead of saving it.
If you plan to use it usually, you may end up defining a new static method in your schema, like:
Schema.statics.createOrUpdate = function(object, callback) {
statics.upsert(this, object, callback);
};
This way you can pass your user data to M2, and on M2, you can update it easily.

This is a late call but I faced same problem before finding out Model.hydrate method.
http://mongoosejs.com/docs/api.html#model_Model.hydrate
Shortcut for creating a new Document from existing raw data, pre-saved
in the DB. The document returned has no paths marked as modified
initially.
So on M2 User.hydarate(userObject) can be used instead of new User(userObject) and no need to set isNew to false.

Related

Sequelize: Force update for a JSON array

Sequelize won't update a JSON field under some circumstances.
For example, I have:
[[1]] (an array inside array)
And I'm trying to push something:
instance.arr[0].push(1); // [[1,1]]
instance.save();
// or:
instance.update({arr: instance.arr});
Now inside the instance I have changed the array and nothing changed inside the database. Not even a query is sent. :(
From the Sequelize website:
https://sequelize.org/master/manual/model-instances.html
The save method is optimized internally to only update fields that really
changed. This means that if you don't change anything and call save,
Sequelize will know that the save is superfluous and do nothing, i.e.,
no query will be generated (it will still return a Promise, but it
will resolve immediately).
That's good, but it seems like it doesn't work for JSON. Can I do a force update?
As of today, I have to do a deep copy of the array to save it.
I'm using MariaDB. I don't know if that matters.
It seems you have to specify that the field has changed
instance.changed( 'arr', true);
instance.save

How to get an instance of the updated sequelize model from an afterUpsert hook?

Other sequelize hooks, like afterUpdate(instance, options) provide an instance of the modified model as a parameter. However, afterUpsert(created, options) provides a created boolean which indicates if the operation was a insert or update.
Is there a way to configure sequelize, or manipulate the afterUpsert parameters, to obtain an instance of the upserted model?
As far as I know you can not. As the result of upsert call to db via queryInterface is what is returned to the afterUpsert hook, which is boolean value whether there has been any newly created records or not in the upsert process.
While, you can use beforeUpsert with the values passed to upsert method. Not sure if that might be helpful ( may be to clear a cache )
I have faced the same issue recently, and I was able to manage it this way:
beforeUpsert: (values, options)=>{
/* afterUpsert will always get an array of type [Model, created] as first argument */
options.returning = true;
},
I decided to use the beforeUpsert hook to make sure that any call to Model.upsert is forced to return an instance and not only a created boolean. But, you can also call
upsert(values,{returning: true});
HTH

Express with pug, Postgres and proper MVC

I recently started using Node.js + Express.js (generated with pug) + pg-promise for handling db.
My first target is to obtain data from Postgres (already set up) and display it pretty using render and pug. Let's say it is user list from Users table.
On this restful tutorial I have learned how to get data and return it as JSON - it worked.
Based on Mozilla's tutorial I seperated my code:
routes/users.js: where for '/' I call user_controller.user_list method (using router.get)
controllers/userController.js I have exported user_list where I would like to ask model for data and call render if I have results
queries.js which is kinda my model? But I'm not sure. It has API: connection to db with promises and one function for every query I am going to use in Controllers. I believe I should have like one Model file per table (or any logical entity) but where to store pgp connections?
This file is based on first tutorial I mentioned
// queries.js (connectionString is set properly to my postgres)
var pgp = require('pg-promise')(options);
var db = pgp(connectionString);
function getUsers(req, res, next) {
db.any('SELECT (user_id, username) FROM public.users ORDER BY user_id ASC LIMIT 1000')
.then(function (data) {
res.json({ data: data });
})
.catch(function (err) {
return next(err);
});
}
module.exports = {
getUsers: getUsers
};
Here starts my problem as most tutorials uses mongoose which is very model-db-schema-friendly and what I have is simple 'SELECT ...' string I pass to pg-promise's any() function.
Therefore I have no model class like User.
In userControllers.js I don't know how to call getUsers() to handle its data. Returning JS object from getUsers() would be nice.
Also: where should I call render? In controller or only in
db.any(...).then(function (data) { <--here--> })
Before, I also tried to embed whole Postgres handling into Controller but from db.any() I got this array for handling:
[{ row: '(1,John)' },{ row: '(2,Amy)' },{ row: '(50,Peter)' } ]
Didn't know how go from there as I probably lost my API functionality as well ;-)
I am browsing through multiple tutorials how to handle MVC but usually they handle MongoDB and
satisfy readers with res.send() not render().
I am not sure that I understand what your question is exactly about, but since I do not have enough reputation to comment, I'll do my best to help you with your interrogations. :)
First, regarding the queries.js file, it is IMO not exactly a model, but rather a DAO (Data Access Object) file. DAO comes between you Model (which is actually you database) and your Controller layers. There usually is a DAO file per object (User, Pet, whatever you want) in your data model.
When the data model is rather complex, it can be useful to use an Object Relational Mapping (ORM) such as Mongoose to map your database and execute complexe processes on your objects. In such a case, you might need a specific file per object so as to describe your model and store your queries. But since you don't need an ORM, you DAO can directly interact with your database. That is why you do not have a User.js file.
Regarding the way the db object should be used, I think you should refer directly to pg-promise documentation on the matter.
IMPORTANT: For any given connection, you should only create a single
Database object in a separate module, to be shared in your application
(see the code example below). If instead you keep creating the
Database object dynamically, your application will suffer from loss in
performance, and will be getting a warning in a development
environment (when NODE_ENV = development)
As a matter of fact, a db object in pg-promise sort of represents the database itself and is actually designed for the simultaneous use of several databases, which does not seem to be your case for the moment.
Finally, when it comes to the render function, I believe it should be in the controller, as your DAO is not supposed to know how the data it has gathered is going to be used.
Modularity is always a time-saving choice on the long-term.
Furthermore, note that you might later need a Business Layer between your DAO and your controller, in order to preprocess and postprocess data you are going to persist or to display. In such a case, if you need for instance to ask for data from your database, you will need to render data after it is processed by the Business layer. If the render is made in the DAO layer, it will not be possible.
In the link I provided earlier to pg-promise's db object connection, you will also find documentation on the any() method. You might already have looked it up.
It specifically states that it returns
A promise object that represents the query result:
When no rows are returned, it resolves with an empty array.
When 1 or more rows are returned, it resolves with the array of rows.
so your returned data is a JS Array. If you want to make it a JS object, just use
JSON.stringify(yourArray) to process your data before rendering it in your controller.
But I wonder if Pug is not able to use your data directly.
Also, if you cannot get any data out of your DAO, maybe you should check that your data object is not empty, as such a case is tolerated by the any() method. If you expect your query to always return something, you might want to consider using the many() or the one() methods.
I hope this helps you.

How to load document out of database instead of memory

Using Raven client and server #30155. I'm basically doing the following in a controller:
public ActionResult Update(string id, EditModel model)
{
var store = provider.StartTransaction(false);
var document = store.Load<T>(id);
model.UpdateEntity(document) // overwrite document property values with those of edit model.
document.Update(store); // tell document to update itself if it passes some conflict checking
}
Then in document.Update, I try do this:
var old = store.Load<T>(this.Id);
if (old.Date != this.Date)
{
// Resolve conflicts that occur by moving document period
}
store.Update(this);
Now, I run into the problem that old gets loaded out of memory instead of the database and already contains the updated values. Thus, it never goes into the conflict check.
I tried working around the problem by changing the Controller.Update method into:
public ActionResult Update(string id, EditModel model)
{
var store = provider.StartTransaction(false);
var document = store.Load<T>(id);
store.Dispose();
model.UpdateEntity(document) // overwrite document property values with those of edit model.
store = provider.StartTransaction(false);
document.Update(store); // tell document to update itself if it passes some conflict checking
}
This results in me getting a Raven.Client.Exceptions.NonUniqueObjectException with the text: Attempted to associate a different object with id
Now, the questions:
Why would Raven care if I try and associate a new object with the id as long as the new object carries the proper e-tag and type?
Is it possible to load a document in its database state (overriding default behavior to fetch document from memory if it exists there)?
What is a good solution to getting the document.Update() to work (preferably without having to pass the old object along)?
Why would Raven care if I try and associate a new object with the id as long as the new object carries the proper e-tag and type?
RavenDB leans on being able to serve the documents from memory (which is faster). By checking for persisting objects for the same id, hard to debug errors are prevented.
EDIT: See comment of Rayen below. If you enable concurrency checking / provide etag in the Store, you can bypass the error.
Is it possible to load a document in its database state (overriding default behavior to fetch document from memory if it exists there)?
Apparantly not.
What is a good solution to getting the document.Update() to work (preferably without having to pass the old object along)?
I went with refactoring the document.Update method to also have an optional parameter to receive the old date period, since #1 and #2 don't seem possible.
RavenDB supports optimistic concurrency out of the box. The only thing you need to do is to call it.
session.Advanced.UseOptimisticConcurrency = true;
See:
http://ravendb.net/docs/article-page/3.5/Csharp/client-api/session/configuration/how-to-enable-optimistic-concurrency

mongoose .save() ignores ObjectId _id

I have a mongoose schema collection A. Then, I create a new object as follows
var myA = new A({
name: 'A simple name'
});
As long as I know, once we have done this, a new _id attribute is created for that object. This means that at the end of the day, myA should look (and it actually looks) like
{
name: 'A simple name',
_id: ObjectId
}
the problem is that when I hit
myA.save()
it is saved into mongodb with a different _id than the previously created.
An idea on why this could be happening? I'm using mongoose 4.4.8
I guess when you create a new document with new A({... it internally marks itself as new document which is evaluated later when calling save() to generate an appropriate MongoDB statement - and this 'flag' is probably not updated immediately when calling save(). This has the positive side effect of having the possibility to quickly create clones of documents when you want to generate test data :)
If you want to update your newly saved document right after creation then you should do that in the callback of the save method which gets the saved document in its 2nd parameter.

Resources