Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add transform option for populate() #3775

Closed
ghost opened this issue Jan 20, 2016 · 17 comments
Closed

Add transform option for populate() #3775

ghost opened this issue Jan 20, 2016 · 17 comments
Milestone

Comments

@ghost
Copy link

ghost commented Jan 20, 2016

My code:

var blogSchema = new Schema({
    name: String,
    blog_slug: String,
    category: { type: Schema.Types.ObjectId, ref: 'category' },
    content: String
});

var categorySchema = new Schema({
    name: String,
    category_slug: String
});

var blog = mongoose.model('blog', blogSchema);
var category = mongoose.model('category', categorySchema);

blog.find().populate({
        path: 'category', 
        select: 'name category_slug', 
        match: { category_slug: req.params.category_slug },
    }).exec(function(error, data){
        console.log(data);
    });
[{
    name: "some thing",
    blog_slug: "some-thing",
    category: {
        name: "Funny",
        category_slug: "funny"
    },
    content: "Blog content"
},
{
    name: "some thing 2",
    blog_slug: "some-thing-2",
    category: null, <-- **Here**
    content: "Blog content 2"
}
]

result[1].category : null . Why?
And how to find blog by category_slug?
I'm a beginner

@vkarpov15
Copy link
Collaborator

    blog.find().populate({
        path: 'category', 
        select: 'name category_slug', 
        match: { category_slug: req.params.category_slug },
    }).exec(function(error, data){
        console.log(data);
    });

The above code translates to:

  1. Find all blogs
  2. For each blog, find the category which has _id equal to the blog's category field and also has category_slug equal to req.params.category_slug.

Most likely, result.1.category is null because its category has a different category_slug than req.params.category_slug. Does that make sense?

@isayme
Copy link
Contributor

isayme commented May 12, 2016

@vkarpov15 when populate a path, if the result not match with match condition, a null will returned instead of real one ?

@vkarpov15
Copy link
Collaborator

@isayme yep that sounds right

@abhishekjain2604
Copy link

How to remove that implicit checking of _id matching with category? I am having similar issues. Why is not not taking only the criteria specified in the 'match'. Why taking an inplicit match on _id? Is there a way to solve this?

@vkarpov15
Copy link
Collaborator

@abhishekjain2604 I don't understand, can you please elaborate with code samples?

@abhishekjain2604
Copy link

abhishekjain2604 commented Sep 8, 2018

@vkarpov15

let productSchema = new SCHEMA({
    price: Number
});

let pricesSchema = new SCHEMA({
    user: SCHEMA.Types.ObjectId,
    productId: SCHEMA.Types.ObjectId,
    specificPrice: Number
});

productModel = MONGOOSE.model('product', productSchema);
pricesModel = MONGOOSE.model('prices', pricesSchema);

These are my 2 models. The flow is that the productModel has a deafult price. But I can also set user specific prices, as specified in the pricesModel. What I want to be able to do it to populate 'price' field in productModel from pricesModel based on which user is making the request to get it's detail.

So something like this:

productModel.populate(productObj, {
  path: 'price',
  match: {
    user: request.user.id,
    productId: productObj.id
  },
  select: 'specificPrice',
  model: 'pricesModel'
})

@vkarpov15
Copy link
Collaborator

@abhishekjain2604
Copy link

That's the problem. In this example, we are populating the path 'fans' which is of the type ObjectId. In my use case, I want to populate the field 'price' which is just a Number.

@vkarpov15
Copy link
Collaborator

In that case, you should consider making price a virtual like this:

let productSchema = new SCHEMA({
    defaultPrice: Number
});
productSchema.virtual('price').get(function() {
  if (this.userPrice != null) {
    return this.userPrice.specificPrice;
  }
  return this.defaultPrice;
});
productSchema.virtual('userPrice', {
  ref: 'Price',
  localField: '_id',
  foreignField: 'productId',
  justOne: true
});

@abhishekjain2604
Copy link

That solution would have worked but the 'specific prices' are user specific. In this 'userPrice' virtual example, localField of productSchema matches against the foreign field of priceSchema. But there's another field in priceSchema(user) that needs to be matched against a field that is not present in the productSchema. It will come from the 'request' object inside the controller. request.user.id to be specific.

I understand that my use case might be beyond the realms of what a framework needs to be able to do. But does this use case sounds like something that might be helpful enough to be introduced as a native feature? Something that might prove useful to many others as well?

@vkarpov15
Copy link
Collaborator

Perhaps the below will work?

productSchema.virtual('userPrice', {
  ref: 'Price',
  localField: '_id',
  foreignField: 'productId',
  justOne: true
});

// In route handler
Product.findOne({ _id: request.productId }).populate({ path: 'userPrice', match: { userId: request.user.id } });

See this example. The above code will find the userPrice doc whose productId matches the product's _id and whose userId matches the request-specific user id. Does that help?

@abhishekjain2604
Copy link

Sorry for replying so late that too on my own query. But yes, that did work and greatly reduced the overhead of computing everything manually by querying database especially when retrieving 100s of products at the same time.

Thank you so much for your help.

@Scientistt
Copy link

Is there a way to keep the _id instead of null when the match fails?

For example:

If I have the field PersonId (which is an Object Id from the model Person) in another model.

I do want to populate if this _id is not equal the current logged User, lets call id UserId.

so, my query is something like:

.populate({ path: "personId", select: "_id name avatar", match: {_id: {$ne: UserId}}, })

when the PersonId is different from UserId it returns the person data, but when it is equal it returns null. I see why is it and it makes sense.

But is there a way of avoiding the null result and keep the Object Id when the match fails?

@vkarpov15
Copy link
Collaborator

@Scientistt no but that is a good idea. Will keep this issue open for a future release.

@vkarpov15 vkarpov15 reopened this Mar 27, 2020
@vkarpov15 vkarpov15 added this to the 5.x Unprioritized milestone Mar 27, 2020
@vkarpov15 vkarpov15 changed the title .populate() match, results null Option to avoid setting property to null if populate() finds no results, leaving the original id. Mar 27, 2020
@giovanni-orciuolo
Copy link

Can we prioritize this? It's a very common use case when implementing populate with virtuals and I wonder why it's not the default behaviour.

@vkarpov15 vkarpov15 modified the milestones: 5.x Unprioritized, 5.12 Oct 24, 2020
@vkarpov15
Copy link
Collaborator

@Doublehub we'll see what we can do for 5.12. The 5.11 milestone is a little crowded right now.

@vkarpov15 vkarpov15 changed the title Option to avoid setting property to null if populate() finds no results, leaving the original id. Add transform option for populate() Jan 23, 2021
vkarpov15 added a commit that referenced this issue Feb 21, 2021
@vkarpov15
Copy link
Collaborator

vkarpov15 commented Feb 21, 2021

The following will work in Mongoose 5.12.0:

    const parent = yield Parent.findById(p).populate({
        path: 'children',
        transform: function(doc, id) {
          return doc == null ? id : doc;
        }
      });

Still need to double check that this works with populate virtuals.

@vkarpov15 vkarpov15 reopened this Feb 21, 2021
vkarpov15 added a commit that referenced this issue Mar 4, 2021
This was referenced Mar 13, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants
@vkarpov15 @isayme @abhishekjain2604 @Scientistt @giovanni-orciuolo and others