I have post
and vote
schemes. Vote contains kind
of content (can be Post
or Comment
, etc) and content's _id
in field obj
.
I created virtual population voted
. Everything works as expected.
const co = require('co');
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
mongoose.connect();
mongoose.Promise = global.Promise;
mongoose.set('debug', true);
const VoteSchema = new Schema({
value: Boolean,
obj: String,
kind: String,
user: String
});
const PostSchema = new Schema({
title: String
});
PostSchema.virtual('voted', {
ref: 'Vote',
localField: '_id',
foreignField: 'obj',
justOne: true,
});
const Vote = mongoose.model('Vote', VoteSchema);
const Post = mongoose.model('Post', PostSchema);
then I do some actions
co(function*() {
// clear collections
yield [
Post.remove(),
Vote.remove()
];
// create post
const post = new Post({
title: 'post-1'
});
// create vote
const vote = new Vote({
value: true,
obj: post.id,
kind: Post.modelName,
user: 'uid:1'
});
// store models to db
yield [
post.save(),
vote.save()
];
// populate related votes
yield post
.populate({path: 'voted', match: {user: 'uid:1', kind: Post.modelName}})
.execPopulate();
}).catch(e => console.log(e));
result:
{ voted:
{ _id: ObjectId("585bf226e481ec02c6f42143"),
value: true,
obj: '585bf226e481ec02c6f42142',
kind: 'Post',
user: 'uid:1',
__v: 0 },
__v: 0,
title: 'post-1',
_id: ObjectId("585bf226e481ec02c6f42142") }
The question is there any way to set getter over this virtual population, so I get true
or false
in field voted
but not the object as it now?
Now I'm doing it by overwriting toJSON
:
PostSchema.set('toJSON', {
transform: (doc, ret) => {
ret.voted = !!ret.voted;
return ret;
}
});
it's results as I need:
{ voted: true,
__v: 0,
title: 'post-1',
_id: ObjectId("585bf30e7af9a202d34debc3") }
User.virtual("myVoted")
.get(function getVoted() {
return this.voted.value;
});
Mongoose supports nesting getters, here's an example:
const VoteSchema = new Schema({
value: Boolean,
obj: String,
kind: String,
user: String,
voted: String
});
const PostSchema = new Schema({
title: String
});
var virtual = PostSchema.virtual('voted', {
ref: 'Vote',
localField: '_id',
foreignField: 'obj',
justOne: true,
});
virtual.getters.unshift(v => !!v);
const Vote = mongoose.model('Vote', VoteSchema);
const Post = mongoose.model('Post', PostSchema);
co(function * () {
// create post
const post = new Post({
title: 'post-1'
});
// create vote
const vote = new Vote({
value: true,
obj: post.id,
kind: Post.modelName,
user: 'uid:1'
});
// store models to db
yield [
post.save(),
vote.save()
];
// populate related votes
const r = yield Post.findById(post._id)
.populate({path: 'voted', match: {user: 'uid:1', kind: Post.modelName}});
console.log('r', r.toObject({ virtuals: true }));
console.log(r.voted);
});
Just make sure you unshift()
the getter rather than just chain .get()
because mongoose executes getters in _reverse_ order.
Oh!! This is exactly what I was looking for. I saw stacked getters but didn't know how to change the order ^_^. Thank you very much.
Yeah I wasn't quite aware of that myself. Not well documented at all but it works. Just be wary, it won't be reverse order anymore in 5.0, follow #4835 for updates
Most helpful comment
Mongoose supports nesting getters, here's an example:
Just make sure you
unshift()
the getter rather than just chain.get()
because mongoose executes getters in _reverse_ order.