Home > Net >  Can we use Query inside mongoose virtuals?
Can we use Query inside mongoose virtuals?

Time:09-28

Recently I have been working with mongoose virtuals and I was wondering if we can use query inside the mongoose virtual get method like this,

bookSchema.virtual("review").get(async function () {
  const data = await Review.findOne({ book_id: this._id });
  if (data) {
    return data
  } else {
    return false
  }
});

So far I think we can't do this but if that's the case is there any workaround for it?

Any help will be appreciated

CodePudding user response:

The getters of virtual properties must return synchronously, so you can't execute a query in them. From your example, it seems you are looking for the query population feature, which will allow you to do things like:

// If you have an existing instance
await someBook.populate('review');
console.log(someBook.review);

// When querying
const someOtherBook = Book.findOne().populate('review');
console.log(someOtherBook.review);

CodePudding user response:

Credits to RickN's for pointing me in the right direction.

Here's the detailed answer to the problem.

The Problem:

Basically the developer prior to me did not use document references anywhere in the Schema on which I could've "populated" the record. The requirement was to get book reviews for a particular book but there was no review field to populate on. I could've just make a new field but that won't work on already existing records so I had to find another way.

The Idea:

One way I thought of resolving the issue was to virtual getters. I thought I could query on the Review collection by passing the book's id but turns out you can't do that inside virtuals. Neither a set nor a get method can help here.

The Solution:

So finally the solution as pointed out by RickN is to use virtual populate. You can find more about them here. I'll tell you what I did.

I added a "review" virtual to the book schema and used document's id as local field and book's id as foreign field similar to this

bookSchema.virtual("review", {
  ref: "Review",
  localField: "_id",
  foreignField: "book_id",
  justOne: false,
});

Here the ref is the reference to the Review Schema and book_id is the field that schema.

There is one things to keep in mind here.

Initially the book_id field in my Review Schema was stored as string value (I saved it as an ObjectId and it does work when I populate on it but for some reason it was stored as string), so when I tried to match the document's Id (Type ObjectId) with book_id (Type String) it did not work, only after I explicitly changed the book_id to ObjectId did it work. So you might want to explicitly convert the string to ObjectId when you try to create the record using mongoose similar to this.

const review = await new Review({ book_id: mongoose.Types.ObjectId(book_id) });

That's it. If anyone wants to add something or correct me if I am wrong anywhere feel free to do so.

Again, credits to RickN

  • Related