Не знаю, почему вы думали, что $expr
требовалось в начальном $match
, но на самом деле:
const getResults = () => {
const now = Date.now();
const twoWeeksAgo = new Date(now - (1000 * 60 * 60 * 24 * 7 * 2 ));
const fiveWeeksAgo = new Date(now - (1000 * 60 * 60 * 24 * 7 * 5 ));
// as long a mongoDriverCollectionReference points to a "Collection" object
// for the "users" collection
return mongoDriverCollectionReference.aggregate([
// No $expr, since you can actually use an index. $expr cannot do that
{ "$match": {
"$or": [
// Active and "logged in"/created in the last 2 weeks
{
"isActive": true,
"$or": [
{ "lastLogin": { "$gte": twoWeeksAgo } },
{ "createdOn": { "$gte": twoWeeksAgo } }
]
},
// Also want those who...
// Not Active and "logged in"/created in the last 5 weeks
// we'll "tag" them later
{
"isActive": false,
"$or": [
{ "lastLogin": { "$gte": fiveWeeksAgo } },
{ "createdOn": { "$gte": fiveWeeksAgo } }
]
}
]
}},
// Now we do the "conditional" stuff, just to return a matching result or not
{ "$lookup": {
"from": _.get(Config, `env.collection.projects`), // there are a lot cleaner ways to register models than this
"let": {
"uuid": {
"$cond": {
"if": "$isActive", // this is boolean afterall
"then": null, // don't really want to match
"else": "$uuid" // Okay to match the 5 week results
}
}
},
"pipeline": [
// Nothing complex here as null will return nothing. Just do $in for the array
{ "$match": { "$in": [ "$$uuid", "$users.uuid" ] } },
// Don't really need the detail, so just reduce any matches to one result of [null]
{ "$group": { "_id": null } }
],
"as": "projects"
}},
// Now test if the $lookup returned something where it mattered
{ "$match": {
"$or": [
{ "active": true }, // remember we selected the active ones already
{
"projects.0": { "$exists": false } // So now we only need to know the "inactive" returned no array result.
}
]
}}
]).toArray(); // returns a Promise
};
Это довольно просто, как вычисляемые выраженияvia $expr
на самом деле очень плохо, а не то, что вы хотите на первом этапе конвейера.Также «не то, что вам нужно» , поскольку createdOn
и lastLogin
действительно не должны были быть объединены в массив для $allElementsTrue
, который просто был бы AND условие, в котором вы описали логику, действительно означало бы ИЛИ .Так что $or
здесь просто отлично.
Так же, как и $or
при разделении условий для isActive
из true/false
.Опять же, это или"две недели" ИЛИ"пять недель".И это, безусловно, не требует $expr
, поскольку стандартное сопоставление диапазонов неравенства работает нормально и использует «индекс».
Тогда вы действительно просто хотите сделать «условным» вещей в let
для $lookup
вместо вашего "существует ли оно" мышления.Все, что вам действительно нужно знать (поскольку выбор диапазона дат фактически уже сделан), это то, является ли active
сейчас true
или false
.Где это active
(имеется в виду, что по вашей логике вас не волнуют проекты), просто установите $$uuid
, использованный в $match
стадии конвейера, в значение null
, чтобы оно не совпадало, и$lookup
возвращает пустой массив .Где false
(также уже соответствует условиям даты из более ранних), тогда вы используете фактическое значение и «объединяетесь» (где, конечно, есть проекты).
Тогда просто сохранить * 1068 просто* пользователи, а затем только тестируют оставшиеся false
значения для active
, чтобы увидеть, действительно ли массив "projects"
из $lookup
действительно что-то возвращает.Если этого не произошло, то у них просто нет проектов.
Вероятно, следует отметить, что, поскольку users
является "массивом" в коллекции projects
, вы используете $in
для условия $match
для одного значения массива.
Обратите внимание, что для краткости мы можем использовать $group
внутри внутреннего конвейера, чтобы вернуть только один результат, а не много совпадений с фактическими проектами.Вас не волнует содержание или "количество" , но просто если один был возвращен или ничто .Снова следуя представленной логике.
Это дает вам желаемые результаты, и это делает это эффективным способом и фактически использует индексы там, где они доступны.
Также return await
определенно не делаетто, что вы думаете, это делает, и на самом деле это предупреждающее сообщение ESLint (я предлагаю вам включить ESLint в вашем проекте), так как это не разумно.На самом деле он ничего не делает, так как в любом случае вам нужно было бы await getResults()
(в соответствии с именованием примера), поскольку ключевое слово await
- это не "магия" , а просто способ красивее написания then()
.Надеюсь, что будет легче понять, как только вы поймете, что такое async/await
для синтаксически.