In my data, one of the fields is an array and I want to return only a section of that array, and only that, no more fields. But when I’m using the $slice
in projection, it will return all the fields.
My data:
{
"_id": {
"$oid": "641eca0f5687d0937c1041e2"
},
"email": "[email protected]",
"password": "$2b$10$MBuDAi3k7jTvckTsqQliQeWeg18JebLaDdltzMJwJ92bwP7i84Ee2",
"emailVerified": true,
"transactions": [
{
"category": "t2",
"subCategory": "bread",
"type": "expense",
"date": "2023-03-25T16:53:59.779Z",
"amount": "1000",
"id": {
"$oid": "641f2727dc59db57eac9fa29"
}
},
{
"category": "t3",
"subCategory": "bread",
"type": "expense",
"date": "2023-03-25T16:54:04.243Z",
"amount": "1000",
"id": {
"$oid": "641f272cdc59db57eac9fa2a"
}
},
{
"category": "t4",
"subCategory": "bread",
"type": "expense",
"date": "2023-03-25T16:54:08.780Z",
"amount": "1000",
"id": {
"$oid": "641f2730dc59db57eac9fa2b"
}
}
]
}
And my query in node.js is like this:
const result = await users.findOne(
{ _id: new ObjectId(id) },
{ projection: { transactions: { $slice: [1, 2] } }}
);
With this projection, I’m getting all the fields in the result. If I change the projection to something like { transactions: { $slice: [1, 2] }, _id: 1 }
, it will return only the transactions
and _id
. But I only want the transactions.
Update:
After some research, I found that this is possible with aggregate
method. What aggregate is doing, is basically recreating the projection based on previous stage on the pipeline. But still, I don’t know about the projection
on findOne
method.
const aggCursor = users.aggregate<TransactionsPagRes>([
{
$match: { _id: new ObjectId(id) },
},
{
$project: {
_id: 0,
transactions: { $slice: ['$transactions', 1, 2] },
},
},
]);
const result = await aggCursor.next();
2
Answers
If you are fine with
transactions
being returned, you need to blacklist other properties you don’t need like this:It will return info as:
If you want to have a flat array of user’s transactions, aggregation with $unwind is a way to go:
Returns:
MongoDB Playground.
Using the
$slice
operator does not appear to exclude other fields like a projection normally would, this seems to be unexpected behavior in MongoDB.A couple of choices to get the behavior you want:
Since an projection will never implicitly exclude _id, you will need to modify this anyway. To exclude all of the other fields, either explicitly exclude every field, or include a field that does not actually exist.
Playground
You will still need to explicitly exclude the _id, but would not need to reference a non-existent field to exclude the others
Playground