I am trying to run afterFind hook on a model, but the result doesn't seem to change in console.log with JSON.stringify after I run the query. However, the data is changed if I specifically log the attribute.
// on User model
User.hook('afterFind', function(result) {
result.teamCount = 1
return result.save()
}
})
Then when I try to log it:
const result = await User.find()
console.log(result.teamCount) // -> 1
console.log(JSON.stringify(result)) // no "teamCount" attribute present
Not sure what the hook is doing or if save() is even required but something seems off, how can I make the change visible in console.log?
You should declare the teamCount field as virtual:
sequelize.define('user', {
...
teamCount: {
type: DataTypes.VIRTUAL
}
...
});
Related
I'm trying to update counts on a pre hook. The issue is that for some unknown reason the findOneAndUpdate hook doesn't have access to the document, as far as I can tell.
I would like to do this:
source.pre('findOneAndUpdate', function (next) {
console.log('------------->>>>>> findOneAndUpdate: ');
this.objects = this.objects || [];
this.people = this.people || [];
this.events = this.events || [];
this.objectCount = this.objects.length;
this.peopleCount = this.people.length;
this.eventCount = this.events.length;
next();
});
But for some reason the this in the hook isn't the document, its a Query object which seems about useless.
What am I missing? How do I use a pre hook to update counts on a findOneAndUpdate?
You can do smthng like that ->
source.pre('findOneAndUpdate', function (next) {
console.log('------------->>>>>> findOneAndUpdate: ');
this._update.$set.objects = [];
this._update.$set.people = [];
this._update.$set.events = [];
next();
});
pay attention to _update.$set because in the context "this" will be a query. So you can easily add anything you want!
The documentation states:
Query middleware differs from document middleware in a subtle but important way: in document middleware, this refers to the document being updated. In query middleware, mongoose doesn't necessarily have a reference to the document being updated, so this refers to the query object rather than the document being updated.
An update action generally updates a document that only exists in the database (it tells the MongoDB server: "find document X and set property X to value Z"), so the full document isn't available to Mongoose and, hence, you can't update the counts (which requires access to at least the arrays whose length you want to determine).
As an aside: why do you need separate *Count properties in your schema anyway? If you want to query for arrays matching a certain size, you can use the $size operator on the arrays directly.
If you really do need the count properties, then for each update, you need to track the number of changes you made to each of the arrays (in terms of the number of items added/deleted) and use the $inc operator to adjust the counts.
I had a similar issue when I used the updateOne method and was also going to use the updateOne pre hook to make intermittent update before saving to the database. couldn't find a way for it to work. I ended up using the findOneAndUpdate pre hook and doing the updateOne in it.
schema.pre('findOneAndUpdate', async function(next){
const schema = this;
const { newUpdate } = schema.getUpdate();
const queryConditions = schema._condition
if(newUpdate){
//some mutation magic
await schema.updateOne(queryConditions, {newUpdate:"modified data"});
next()
}
next()
})
Another solution is to use the official MongoDB documentation on middleware. They explain why "this" does not refer to the document itself. You may try something in that sense:
source.pre('findOneAndUpdate', async function(next) {
const docToUpdate = await this.model.findOne(this.getFilter());
//modify the appropriate objects
docToUpdate.save(function(err) {
if(!err) {
console.log("Document Updated");
}
});
console.log(docToUpdate);
// The document that `findOneAndUpdate()` will modify
next();
});
This worked for me
SCHEMA.pre('findOneAndUpdate', function(next){
this._update.yourNestedElement
next();
});
schema.pre(['findOneAndUpdate'], async function(next) {
try {
const type = this.get('type')
const query = this.getQuery()
const doc = await this.findOne(query)
if (type) {
this.set('type', doc.type)
}
next()
} catch (e) {
next(new BaseError(e))
}
})
mongoose Documentation:
You cannot access the document being updated in pre('updateOne') or pre('findOneAndUpdate') query middleware. If you need to access the
document that will be updated, you need to execute an explicit query
for the document.
schema.pre('findOneAndUpdate', async function() {
const docToUpdate = await this.model.findOne(this.getQuery());
console.log(docToUpdate); // The document that `findOneAndUpdate()` will modify
});
I am trying to pass search result of a sequelize query to pug, but its not working
res.render('search',{
stack_holder:find_stack_holder(),
});
function find_stack_holder(){
return db.poregister.findAll({
attributes: [[Sequelize.fn('DISTINCT', Sequelize.col('stack_holder')), 'stack_holder']],
})
}
However pug gets promise instead of actual data. Aren't promises supposed to resolve themselves when the actual data are needed?
findAll returns a Promise, so, you need to do something like this:
const findAllPromise = find_stack_holder();
findAllPromise.then(function(result) {
res.render('search',{
stack_holder: result,
});
});
function find_stack_holder() {
return db.poregister.findAll({
attributes: [[Sequelize.fn('DISTINCT', Sequelize.col('stack_holder')), 'stack_holder']],
});
}
I want inject into constructor database client, but when I run tests, mocha throw exception, that method whitch is called is not a function.
export class CustomService {
constructor(database: any) {
database.init().then((res)=>{}));
}
}
describe('CRUD service', ()=>{
it('when i decide save item', ()=>{
let db = sinon.mock(new DatabaseService);
let instance = new CustomService(db);
db.expects('init').once();
db.verify();
});
});
In console:
TypeError: database.init is not a function
What is wrong?
Don't pass the return value of sinon.mock to the code you are testing but instead pass the original object you passed to sinon.mock. The return value of sinon.mock is only for setting assertions and checking them. You also need to set the order of the statements in your tests so that the expectations are set before the code that must satisfy them is run. Something like this:
describe('CRUD service', ()=>{
it('when i decide save item', ()=>{
const db = new DatabaseService();
let mock = sinon.mock(db);
mock.expects('init').once();
let instance = new CustomService(db);
mock.verify();
});
});
I'm having a strange behaviour with mongoose. When I console.log the result object, I see that the property is here, but when I try to get just the desired value like console.log(obj.propt)it return undefined.
ServerModel.findOne(function (err, server) {
if (err) {
return console.error(err);
}
console.log(server);
// output:
// {_id: 55ead0eb4105b7df958256af,
// name: 'st1',
// ip: '57.29.42.241',
// capacity: 0,
// totalUsed: 0,
// state: true }
console.log(server.ip);
// output: undefined
console.log(server.name);
// output: st1
// but that works if I use the toObject method
var srvr = server.toObject();
var serverAddress = srvr.ip;
// serverAddress is 57.29.42.241
});
Strangely, it works if I use the .toObject method. I must have missed something. Does anyone has an explanation for that?
This will happen when a field is present in the MongoDB document, but not defined in your Mongoose schema.
So be sure to define it in your ServerModel schema as
ip: String
Or, to access it even though it's not defined in your schema, use the get method:
console.log(server.get('ip'));
I am trying to find if the object is changed in pre-save and do some actions accordingly. Followinfg is my code
var eql = require("deep-eql");
OrderSchema.post( 'init', function() {
this._original = this.toObject();
});
OrderSchema.pre('save', function(next) {
var original = this._original;
delete this._original;
if(eql(this, original)){
//do some actions
}
next();
});
It returns false even when I don't change anything!
First of all, you don't need the original object at all. You can access it in the pre hook via this. Secondly post hook executes only after all pre hooks are executed, so your code doesn't make any sense at all (check mongoose docs).
You can do the check by checking isModified in your pre hook and remove the post hook at all.
OrderSchema.pre('save', function(next) {
if(!this.isModified()){
//not modified
}
next();
});
Update
In order to check if some property was modified, pass property name as a parameter to isModified function:
if (this.isModified("some-property")) {
// do something
}