I have a cart collection, which can have the same product with different variants. The problem is when I fetch the user cart and query for all those ids, when theres a duplicated one MongoDB is not including it in the list of the find. What can I do to prevent MongoDB from deleting the data?
my code
const infos = await Product.find({
'_id': cart.products.map(p => p.productId)
})
let result = []
cart.products.map((p, i)=>{
if(p.variation){result.push({ quantity: p.quantity, details: infos[i], variation: p.variation })}
else{result.push({ quantity: p.quantity, details: infos[i] })}
})
Whats the cart looks like:
{
_id: new ObjectId("633245552807412392be980a"),
userId: '631d58eebd28cc597596ecad',
products: [
{
productId: '631d5947bd28cc597596ecb9',
quantity: 2,
_id: new ObjectId("63335249d7cf172c8ffb9332")
},
{
productId: '632a27d17ffc962ac9a9f539',
quantity: 1,
variation: [Object],
_id: new ObjectId("633352a7d7cf172c8ffb9347")
},
{
productId: '632a27d17ffc962ac9a9f539',
quantity: 1,
variation: [Object],
_id: new ObjectId("63335892d7cf172c8ffb93b8")
}
],
}
What MongoDB returns to me:
{
_id: new ObjectId("631d5947bd28cc597596ecb9"),
name: 'Perfume 0',
images: [
'placeholder'
],
shortDescription: 'lançame3nto',
description: 'placeholder',
stock: 20,
price: 85,
inStock: true,
variations: [],
},
{
_id: new ObjectId("632a27d17ffc962ac9a9f539"),
name: 'Perfume com variações',
images: [
'placeholder'
],
shortDescription: 'placeholder',
description: 'placeholder',
stock: 20,
price: 110,
inStock: true,
variations: [ [Object], [Object], [Object], [Object] ],
}
]
As we can see theres only 2 products info, there should be three. Is there anyway I can do to prevent this data being deleted or any another way to fetch the user's product?
Observation: I ran collection.Find(), in the parameters as an example im inserting 3 ids, one of them is repeated. MongoDB is returning to me an array with only two ids fetch not 3.
CodePudding user response:
If all you want is to have result array filled with all products even duplicates then you can get the index of of the products in infos
instead of using the index of the product given in the map function since infos.length
array will always be <= cart.products.length
cart.products.map((p) => {
const i = infos.findIndex((v) => v._id == p.productId);
if (p.variation) {
result.push({
quantity: p.quantity,
details: infos[i],
variation: p.variation,
});
} else {
result.push({ quantity: p.quantity, details: infos[i] });
}
});
this is regardless that you are using map
as forEach
function so I think this will be much cleaner
const result = cart.products.map((p) => {
const i = infos.findIndex((v) => v._id == p.productId);
if (p.variation) {
return {
quantity: p.quantity,
details: infos[i],
variation: p.variation,
};
} else {
return { quantity: p.quantity, details: infos[i] };
}
});
And here is an even shorter way
const result = cart.products.map((p) => {
const i = infos.findIndex((v) => v._id == p.productId);
return {
quantity: p.quantity,
details: infos[i],
...(p.variation && { variation: p.variation }),
};
});
Hope this answers your question
CodePudding user response:
Based on the discussion in the comments, the concern is around how databases return result sets and how to adjust when the application uses one of the results multiple times.
In terms of behaviors, a simple demonstration would be given the following documents in a collection:
{ _id: "Product A" }
{ _id: "Product B" }
{ _id: "Product C" }
Then this query:
db.foo.find({ _id: { $in: [ "Product A", "Product B", "Product B" ] } })
The database will respond with:
{ _id: "Product A" }
{ _id: "Product B" }
Playground demonstration is here. This is effectively how all databases work since we are retrieving results that match the query from the collection and there are only two entries that match. (This behavior also allows the drivers or database to simplify the query itself, but that's beside the point here.)
How you handle this depends entirely on what your goals are and how your schema/application is currently configured. The answer from @Ahmed Magdi is very helpful in describing how to adapt the current code to adapt to this behavior. I also think it may fix a different bug with the current code in that the order of the infos
results is not guaranteed to be the same as is stored in the cart.products
object.
But I would definitely suggest that you revisit the problem overall to make sure that you are taking an appropriate approach. There are, for example, more complicated queries you could construct to return a result set that includes the 'duplicates', but just because you can do that doesn't mean that you should.
If this were my application, I would be asking why this subsequent process to 'inflate' the cart with additional details is required at all. Could the application instead just capture these details when the item is added to the cart in the first place? That would probably eliminate this additional step altogether.
Other approaches could include inflating the cart solely on the database side by, for example, using $lookup
to pull the additional details. This would similarly remove the code required by the application to manually join this information together.