Mongoose aggregate using $addFields and $match by added field - mongodb

I am trying to run the aggregate query in Mongo using $addFields and $match
.aggregate([
{
$addFields: {
level: { $sum: '$members.level' },
},
},
{
$match: {
level: { $gte: level }
},
},
{
$project: {
_id: 0,
logo: 1,
name: 1,
level: 1,
id: '$_id',
joinType: 1,
countryId: 1,
minimumJoinLevel: 1,
membersCount: { $size: '$members' },
},
},
])
The issue is that level is not an indexed field and has been calculated in the query
My question is: how I can run this query efficiently, avoid "COLLSCAN" and make it "IXSCAN" execution

Related

Unable to sort documents after grouping mongodb aggregation framework

so i am trying to sort notifications array of user by it's insertion date (i want the latest one on top) but it seems to be not working am i missing something ?
here's the template for data:
{
_id: "628ceeae3df06d49419f0bb4",
name: "John",
notifications: [
{_id: "someIdA", details: "xyz", dateTime: "1653321337762"},
{_id: "someIdB", details: "jkl", dateTime: "1653321337762"}
{_id: "someIdC", details: "abc", dateTime: "1653321321323"}
{_id: "someIdD", details: "lmn", dateTime: "1653123412341"}
]
}
and the aggregation pipeline that i'm trying:
const foundUser = await users.aggregate([
{
$match: {
_id: mongoose.Types.ObjectId(userId)
}
},
{
$project: {
notifications: 1,
_id: 0
}
},
{
$sort: {
_id: -1
}
}
])
First unwind the array of notifications array, sort the documents by notifications DateTime key, then group them by the user.
This is the mongo shell query:
db.collection.aggregate([
{
$match: {
_id: "628ceeae3df06d49419f0bb4"
}
},
{
$unwind: "$notifications"
},
{
$sort: {
"notifications.dateTime": -1
}
},
{
$group: {
_id: "$_id",
notifications: {
$push: "$notifications"
}
}
},
{
"$project": {
"notifications": 1,
"_id": 0
}
}
])
Here, is the working link Mongodb Playground

MongoDB Atlas search: sort by "searchScore"

I run the following aggregation pipeline and it works well:
[
{
$search: {
text: {
query: text,
path: 'name',
fuzzy: {
maxEdits: 2,
prefixLength: 0,
maxExpansions: 256,
},
},
},
},
{
$limit: 10,
},
{
$project: {
_id: 1,
name: 1,
score: { $meta: 'searchScore' },
},
},
]
I can see that the score-field is present in my result and is correct. Now I want to sort the documents retrieved by the $meta-property searchScore. However, adding this step at the end of the pipeline:
{
$sort: { score: { $meta: 'searchScore' }, _id: 1 },
}
Yields the following error:
MongoError: $meta sort by 'searchScore' metadata is not supported
How do I achieve this?
You need to add a new field that contains the result of $meta before sorting (which you already do) and then sort on that field, so you new pipeline would be:
...
{
$project: {
_id: 1,
name: 1,
score: { $meta: 'searchScore' }, // you are already adding the field here.
},
},
{
$sort: {
score: -1, // use the new computed field here.
_id: 1
}
}

MongoDB multiple levels embedded array query

I have a document like this:
{
_id: 1,
data: [
{
_id: 2,
rows: [
{
myFormat: [1,2,3,4]
},
{
myFormat: [1,1,1,1]
}
]
},
{
_id: 3,
rows: [
{
myFormat: [1,2,7,8]
},
{
myFormat: [1,1,1,1]
}
]
}
]
},
I want to get distinct myFormat values as a complete array.
For example: I need the result as: [1,2,3,4], [1,1,1,1], [1,2,7,8]
How can I write mongoDB query for this?
Thanks for the help.
Please try this, if every object in rows has only one field myFormat :
db.getCollection('yourCollection').distinct('data.rows')
Ref : mongoDB Distinct Values for a field
Or if you need it in an array & also objects in rows have multiple other fields, try this :
db.yourCollection.aggregate([{$project :{'data.rows.myFormat':1}},{ $unwind: '$data' }, { $unwind: '$data.rows' },
{ $group: { _id: '$data.rows.myFormat' } },
{ $group: { _id: '', distinctValues: { $push: '$_id' } } },
{ $project: { distinctValues: 1, _id: 0 } }])
Or else:
db.yourCollection.aggregate([{ $project: { values: '$data.rows.myFormat' } }, { $unwind: '$values' }, { $unwind: '$values' },
{ $group: { _id: '', distinctValues: { $addToSet: '$values' } } }, { $project: { distinctValues: 1, _id: 0 } }])
Above aggregation queries would get what you wanted, but those can be tedious on large datasets, try to run those and check if there is any slowness, if you're using for one-time then if needed you can consider using {allowDiskUse: true} & irrespective of one-time or not you need to check on whether to use preserveNullAndEmptyArrays:true or not.
Ref : allowDiskUse , $unwind preserveNullAndEmptyArrays

MongoDB sorting by condition

Hello guys i'm stuck in one condition where i need to sort the data that matches some conditions and then sort them by it's priority bases i.e
first i want to sort the data via it's priority in descending order and then sort them if priority is 1 (high) sort the document via createdAt field else via it's date field.
i currently create one aggregate by myself but it's not returning the correct data.
model.aggregate([
{
$match: {
jobId,
deprecated: false,
},
},
{
$project: {
document: "$$ROOT",
sort: {
$cond: {
if: {
$eq: ["$priority", 1],
},
then: "$createdAt",
else: "$date",
},
},
},
},
{
$sort: {
priority: -1,
sort: 1,
},
},
]);
hey guys thanks for your time i solve the issue i use
i just add one field in the $project due to i'm using document: "$$ROOT"
so it create document field and store all the data in it, and that is why priority field is not available,
i just add priority:1 in $project and it includes that field. below i post the working code may be it helps you if you also wants something like that.
model.aggregate([
{
$match: {
jobId: '5c501eed65816d61c6a0af77',
deprecated: false,
},
},
{
$project: {
document: "$$ROOT",
priority: 1,
sort: {
$cond: {
if: {
$eq: ["$priority", 1],
},
then: "$createdAt",
else: "$date",
},
},
},
},
{
$sort: {
priority: -1,
sort: 1,
},
},
])

Duplicate elements in a mongo db collection

Is there an quick efficient way to duplicate elements in a mongo db collections based on a property. In the example below, I am trying to duplicate the elements based on a jobId.
I am using Spring boot, so any example using Spring boot API would be even more helpful.
Original Collection
{ _id: 1, jobId: 1, product: "A"},
{ _id: 2, jobId: 1, product: "B"},
{ _id: 3, jobId: 1, product: "C"},
After duplication
{ _id: 1, jobId: 1, product: "A"},
{ _id: 2, jobId: 1, product: "B"},
{ _id: 3, jobId: 1, product: "C"},
{ _id: 4, jobId: 2, product: "A"},
{ _id: 5, jobId: 2, product: "B"},
{ _id: 6, jobId: 2, product: "C"},
You can use following aggregation:
db.col.aggregate([
{
$group: {
_id: null,
values: { $push: "$$ROOT" }
}
},
{
$addFields: {
size: { $size: "$values" },
range: { $range: [ 0, 3 ] }
}
},
{
$unwind: "$range"
},
{
$unwind: "$values"
},
{
$project: {
_id: { $add: [ "$values._id", { $multiply: [ "$range", "$size" ] } ] },
jobId: { $add: [ "$values.jobId", "$range" ] },
product: "$values.product",
}
},
{
$sort: {
_id: 1
}
},
{
$out: "outCollection"
}
])
The algorithm is quite simple here: we want to iterate over two sets:
first one defined by all items from your source collection (that's why I'm grouping by null)
second one defined artificially by $range operator. It will define how many times we want to multiply our collection (3 times in this example)
Double unwind generates as much documents as we need. Then the formula for each _id is following: _id = _id + range * size. Last step is just to redirect the aggregation output to your collection.