I am trying to find (and update) a particular object from items array of the cart collection. A cart documnet looks something like this
{
_id: ObjectId("661fb9ddc2b236ae8703ba92"),
display_id: '38bd6126-02a1-47b9-b567-00aaf2eb076f',
user_id: ObjectId("661137f22a31832ceb92ddbc"),
items: [
{
product_id: ObjectId("6622376051d5886f39ac7365"),
size: ObjectId("660d444d0d7716ee24129675"),
qty: 3
},
{
product_id: ObjectId("662214f8cc5306f999369e99"),
size: ObjectId("660d444d0d7716ee24129675"),
qty: 2
},
{
product_id: ObjectId("662214f8cc5306f999369e99"),
size: ObjectId("660d444d0d7716ee24129674"),
qty: 2
}
],
__v: 4
}
The query I am trying to run is
db.carts.find({user_id: new ObjectId("661137f22a31832ceb92ddbc"), "items.product_id": new ObjectId("662214f8cc5306f999369e99"), "items.size":new ObjectId("660d444d0d7716ee24129675")}, {"items.$":1})
or in mongoose
const doc = await Cart.findOne({
user_id: user_id,
"items.product_id": product_id,
"items.size": oldSize
},{"items.$":1})
Ideally it should return this object :
{
product_id: ObjectId("662214f8cc5306f999369e99"),
size: ObjectId("660d444d0d7716ee24129675"),
qty: 2
}
but it returns this one :
{
product_id: ObjectId("6622376051d5886f39ac7365"),
size: ObjectId("660d444d0d7716ee24129675"),
qty: 3
},
Please tell what am I doing wrong.
It could be confusing about the query when working with sub-documents. For your current query, it is actually querying 3 things in an AND manner:
user_id: 661137f22a31832ceb92ddbc
items
that have product_id: 662214f8cc5306f999369e99
items
that have size: 660d444d0d7716ee24129675
Note that for #2 and #3, they don't need to be the same subdocument. In other words, a document with 2 separate subdocuments fulfilling only #2 or #3 can also be returned.
For your case, your document has items that fulfill both #2 and #3, but your projection clause (i.e. {"items.$":1}
) is just asking to return the first element in the array.
So, what can you do to make sure #2 and #3 is matching the same subdocument? You can use $elemMatch
. By this way, you can also use the positional operator $
in the projection clause.
db.collection.find({
"user_id": ObjectId("661137f22a31832ceb92ddbc"),
"items": {
"$elemMatch": {
"product_id": ObjectId("662214f8cc5306f999369e99"),
"size": ObjectId("660d444d0d7716ee24129675")
}
}
},
{
"items.$": 1
})
In fact, if you find yourself working at the items
level most often, you are suggested to "flatten" the items
entry into individual documents like below. You can potentially gain benefits like simpler queries and performance boosts from indexing.
{
display_id: "38bd6126-02a1-47b9-b567-00aaf2eb076f",
user_id: ObjectId("661137f22a31832ceb92ddbc"),
product_id: ObjectId("662214f8cc5306f999369e99"),
size: ObjectId("660d444d0d7716ee24129675"),
qty: 2
}