I'm trying to override the _id field of a mongo doc to an integer with an auto inc.
I've tried some modules and none of them worked for me. I thought I'd write something of my own, like this :
productSchema.pre('save', function (next) {
this._id = 5; //or what ever
next();
});
but it didn't work and the _id field was null. Even tried this._doc['id'] = 5. I thought in merging the _id to the doc before it was saved by a redis but that's ugly since the redis.get is async so I'd have to pass the save action inside the redis callback.
Any solutions?
Related
I am using graphql to get some data from mongodb database. So, I was making an api which on running saves data in main collection but also saves data in some other collection with a couple of more data. I was trying to delete _id from the data that I get on saving on main collection but it's not working and I can't figure out why.
Here's what's happening
const data = await User.getResolver('updateById').resolve({ //default resolver from graphql-compose library to update a record. Returns the whole document in record //
args: {...rp.args}
})
const version = '4'
const NewObject = _.cloneDeep(data.record);
NewObject.version = version;
NewObject.oldId = data._id;
_.unset(NewObject, '_id'); //this doesn't work neither does delete NewObject._id//
console.log('new obj is', NewObject, version, NewObject._id, _.unset(NewObject, '_id')); //prints _id and surprisingly the unset in console.log is returning true that means it is successfully deleting the property//
I am very confused to what I am doing wrong.
Edit: Sorry should have mentioned, but according to lodash docs _.unset returns true if it successfully deletes a property.
Turn's out mongoDb makes _id as a Non configurable property. So, to do this I had to make use of toObject() method to make an editable copy of my object.
const NewObject = _.cloneDeep(data.record).toObject();
With this I was able to delete _id.
Alternatively _.omit of lodash also works.
I am attempting a CRUD app with MEAN stack. I am using mongoose in Express to call to the MongoDB. I am using the FindOne function with a specified parameter, and it's always returning the same (incorrect) document.
I know I am connected to the correct database, since I get a document back from that collection, but it's always the same document, no matter what I pass as the parameter.
module.exports = mongoose.model('Player', playersSchema, 'players'); //in player.js
const Player = require('./model/players');
app.get('/api/player/:id', (req, res) =>{
Player.findOne({id: req.params.playerId},
function(err, player) {
if(err) {
res.json(err);
}
else {
res.json(player);
}
});
});
I have 3 separate "players", with three distinct "playerID" fields (38187361, 35167321, 95821442). I can use Postman to GET the following URL, for example:
http://localhost:3000/api/player/anythingyouWantInHere
and it will return 38187361, the first document. I've been over this website, many tutorials, and the Mongoose documentation and I can't see what I'm doing wrong..
I'd like to eventually find by playerId OR username OR email, but one hurdle at a time...
From the mongoose documentation of findOne, if you pass Id a null or an empty value, it will query db.players.findOne({}) internally which will return first document of the collection everytime you fetch. So make sure you are passing non-empty id here.
Note: conditions is optional, and if conditions is null or undefined,
mongoose will send an empty findOne command to MongoDB, which will
return an arbitrary document. If you're querying by _id, use
findById() instead.
Your route is '/api/player/:id', so the key on the req.params object will be 'id' not 'playerId'.
I don't know what/where/if you're populating the playerId param, but if you update your query to call req.params.id it should actually change the document based on the path as you seem to be wanting to do.
I had the same problem, and it was that the name of column's table was different from the model I had created.
In my model the name of the wrong column was "role" and in my table it was "rol".
I have a mongoose schema with static methods, but and when I run:
model.statics.specialMethod = function(id, callback) {
return this.find({to_lock:id},callback);
}
It returns only property _id and to_lock. Why? Default setting is to include the whole document. Is it some bug? When I use other tools than NodeJS, it returns the correct values.
Thank you
I want to get the value of an object before the pre-save hook and compare that to the new value. As suggested in mongoose get db value in pre-save hook and https://github.com/Automattic/mongoose/issues/2952, I did a post-init hook that copied it to a doc._original. The issue is that I'm not sure how to access this ._original in a different hook.
FieldSchema
.post('save', function (doc) {
console.log(doc._original);
});
FieldSchema
.post('init', function (doc) {
doc._original = doc.toObject();
});
I know that the doc in the post save hook is different from the doc in the post init hook, but how would I access the original?
You can only access properties on a database which you have defined in its schema. So since you probably haven't defined _original as a property in your schema, you can't access, or even set it.
One way would be to obviously define _original in your schema.
But to get and set properties not defined in your schema: use .get, and .set with {strict:false}
FieldSchema
.post('save', function (doc) {
console.log(doc.get('_original'));
});
FieldSchema
.post('init', function (doc) {
doc.set('_original', doc.toObject(), {strict: false});
});
Note the option {strict: false} passed to .set which is required because you're setting a property not defined in the schema.
update:
First thing I didn't notice before is that in your question title you want pre-save hook but in your code you actually have a post-save hook (which is what I based my answer on). I hope you know what you're doing, in that post save hook is invoked after saving the document.
But in my opinion, and from what I can understand what your original intent was from the question, I think you should actually use a pre-save hook and an async version of it (by passing next callback), so that you can .find (retrieve) the document from the database, which would be the original version since the new version hasn't yet been saved (pre-save) thus enabling you to compare without actually saving any new fields, which seems an anti-pattern to begin with.
So something like:
FieldSchema
.pre('save', function (next) {
var new_doc = this;
this.constructor // ≈ mongoose.model('…', FieldSchema).findById
.findById(this.id, function(err, original){
console.log(original);
console.log(new_doc);
next();
});
});
http://mongoosejs.com/docs/middleware.html
I want to know what the 'clean' value of a dirty prop is in a pre-save mongoose hook like this:
UserSchema.pre('save', function(next) {
var user = this;
if (user.isModified('password')){
//i want to know what the value of user.password was before it was changed
}
next()
}
Is it possible to look up the old value without looking it up in the db?
By default, the old values are not stored. You would have to do is track the old data with a post init hook (a mongoose feature).
What we do is attach copy of the original document to all items pulled from MongoDB. We have this code for each schema we need to get pre-dirty data for comparison:
schema.post( 'init', function() {
this._original = this.toObject();
} );
NodeJS is pretty efficient, and does copy on write when possible, so you don't see double the memory consumption unless you modify the entire document. Only then does _original actually consume double the memory.
So in a pre-save hook, from what I can tell by reading this section of the source code, I don't think the previous value is stored anywhere. So you'll have to load the document from mongodb to get it.
However, you might want to use the virtuals mechanism instead of a pre-save hook to store the old value before changing it to the new value.
var virtual = schema.virtual('password');
virtual.set(function (v) {
var this._oldPassword = this.password;
return v;
});
Experiment with something along those lines and see if you can make something work suitably.