Remove property from mongoose document instance - node.js

I need to remove a property from a mongoose document instance. I've found a lot of questions that show how to remove it from the database, but that's not what I'm looking for.
I need to pull the document down including a field to check security access, I then want to strip that field so that it doesn't get disclosed if downstream code decides to call toObject() and send the object back to the client.
Any thoughts?

I needed to remove password property from the document instance but I didn't find anything in the API documentation. Here is what I did:
doc.set('password', null); // doc.password is null
Then I found you can also do this:
delete doc._doc.password // doc.password is undefined

Since version 2.4 you can do:
doc.field = undefined;
await doc.save();
This will essentially $unset the field

Using the set function with a value of null will simply assign the value, not remove it. Best to first convert the document using toObject() (so that it is becomes a plain Object), make the changes and revive it back to a model document:
let tempDoc = doc.toObject();
delete tempDoc.password;
doc = new this(tempDoc);

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

Srapi - retrieve 1-n property from the Lifecycle call back model parameter

i am using Strapi for a prototype and i am meeting the following issue. I have created a new content type "Checklist" and i added in it a relation property 1 to many with the User model provided by the users-permissions plugin.
Then i wanted to add some custom logic on the lifecycle call back, in beforeSave and in beforeUpdate from which i would like to access the user assigned to the Checklist.
The code looks like that:
{
var self = module.exports = {
// Before saving a value.
// Fired before an `insert` or `update` query.
generateLabel : (model) => {
var label = "";
var day = _moment(model.date,_moment.ISO_8601).year();
var month = _moment(model.date,_moment.ISO_8601).day();
var year = _moment(model.date,_moment.ISO_8601).month();
console.log(model);
if (model.user) {
label = `${model.user}-${year}-${month}-${day}`;
}else{
label = `unassigned-${year}-${month}-${day}`;
}
return label;
I call the method generateLabel from the callback. It works, but my model.user always returned undefined. It is a 1-n property. I can access model.date property (one of the field i have created) without any issue, so i guess the pbs is related to something i have to do to populate the user relation, but i am not sure on how to proceed.
When i log the model object, the console display what i guess is a complete mongoose object but i am not sure where to go from there as if i try to access the property that i see in the console, i will always reach an undefined.
Thanks in advance for your time, i use the following
strapi: 3.0.0-alpha.13.0.1
nodejs: v9.10.1
mongodb: 3.6.3
macos high sierra
Also running into the similar / same issue, I think this has to do with the user permissions plugin, and having to use that to access the User model. Or I thought about trying to find the User that’s associated with the id of the newly created record. I’m trying to use AfterCreate. Anyone that could shed some light on this would be great!
It's because relational attributes are not send in create fonction (See your checklist service add function).
Relations are handled in an other function updateRelations.
The thing you can do is to send values in Checklit.create()

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

Serialize then deserialize mongoose.Model instance

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.

How to make #DbLookup in XPages verify the existence of a value in another database?

I have an XPage that is doing an #DbLookup with a user's input and trying to find that value in a view in a different database yet on the same server.
I have already verified that the view is in fact sorted by the first column and therefore #DbLookup friendly. The following code below appears in the server-side Javascript OnClick event handler for a button on my XPage.
My problem is that the an error occurs when trying to assign the value of lRep to the 'firstNameLabel'. lRep is returning a null value from the dbLookup even though the a record under the 'FirstName' field exists with the key 'P301993'. This dbLookup should be finding a returning a single 'FirstName' result. However, it is not.
var resultLabel = getComponent("firstNameLabel");
var dbName = new Array(#DbName()[0],"UKCSandbox.nsf");
var lRep = #DbLookup(dbName,"customerLookup","P301993","FirstName");
resultLabel.setValue(lRep.toString());
Unless your formatting was lost in copy and paste, your code has flaws. This is not Java, this is JavaScript. Line endings matter and functions don't act on the object, but return a value. Also #DbLookup returns a string when you have exactly one match, so checking for string doesn't help you.
Your code should look like that:
var ukcNumber = Registration.getItemValueString('UKCNumber').toUpperCase();
var resultLabel = getComponent("ukcNumberLabel");
var dbName = #DbName();
dbName[1] = "UKC\\UKCSandbox.nsf";
var lRep = #DbLookup(dbName,"customerLookup",ukcNumber,1);
resultLabel.setValue((lRep) ? "Success" : "Failed");
Does that work for you?
Update: 2 things to check:
does the lookup work in the same database using #DbName?
XPages have the same security constraints as Java agents. Do you have enough rights in the server document to do a 'get value from other database'? Default is No!
Have you tried making the dblookup work outside of xpages, i.e. with ScanEZ or in the Notes client?
Check your ukcNumber variable so it contains a value.
Edit
Check so the user has rights to do the lookup in the other database.
Also try a similar code on an old Notes Form and see if you get the same result.
Why can't you use keyword '[FAILSILENT]' in your #DBLookup call. It'll return "", if no entry matches with your key.
If you still have issues, use SSJS/java code to see where it's breaking up.

Resources