Перво-наперво: убедитесь, что ваша $lookup
цель foreignField
проиндексирована.Затем начните с описания вашей характерной для данных характеристики: часто ли у одного пользователя много предпочтений?Или для проекта иметь много тегов?Диспропорция в размерах employees
и projects
также повлияет на производительность.
Теперь перейдем к экспериментам.
- Базовая линия (ваше решение).Не забудьте проиндексировать
projects.tags
!
db.employees.aggregate([
{$unwind: '$preferences'},
{$lookup: {
from: 'projects',
localField: 'preferences.tag',
foreignField: 'tags',
as: 'match'
}},
{$group: {
_id: {project: '$match.name', employee: '$name'},
score: {$sum: '$preferences.score'}
}},
{$unwind: '$_id.project'},
{$group: {
_id: {project: '$_id.project', employee: '$_id.employee'},
score: {$sum: '$score'}
}}
])
Избавление от одной
$group
стадии.Не забудьте проиндексировать
projects.tags
!
db.employees.aggregate([
{$unwind: '$preferences'},
{$lookup: {
from: 'projects',
localField: 'preferences.tag',
foreignField: 'tags',
as: 'match'
}},
{$unwind: '$match'},
{$group: {
_id: {project: '$match.name', employee: '$name'},
score: {$sum: '$preferences.score'}
}}
])
Избавление от
обеих $group
ступеней.Это будет работать
только , если
employees.preferences.tag
будет уникальным (будет повторяться несколько раз).Не забудьте проиндексировать
projects.tags
!
db.employees.aggregate([
{$lookup: {
from: 'projects',
localField: 'preferences.tag',
foreignField: 'tags',
as: 'match'}},
{$unwind: '$match'},
{$project: {
_id: 0,
employee: '$name',
project: '$match.name',
score: {$reduce: {
input: '$preferences',
initialValue: 0,
in: {$cond: [
{$in: ['$$this.tag', '$match.tags']},
{$add: ['$$this.score', '$$value']},
'$$value'
]}
}}
}}
])
Точно так же, как 3, но в обратном направлении.Это будет работать
только , если
employees.preferences.tag
будет уникальным (будет повторяться несколько раз).Не забудьте индексировать
employees.preferences.tag
!
db.projects.aggregate([
{$lookup: {
from: 'employees',
localField: 'tags',
foreignField: 'preferences.tag',
as: 'match'
}},
{$unwind: '$match'},
{$project: {
_id: 0,
employee: '$match.name',
project: '$name',
score: {$reduce: {
input: '$match.preferences',
initialValue: 0,
in: {$cond: [
{$in: ['$$this.tag', '$tags']},
{$add: ['$$this.score', '$$value']},
'$$value'
]}
}}
}}
])
и результаты.Протестировано на MongoDB, версия 4.0.10.Я подготовил базу данных с n
сотрудниками и проектами, по 1-7 предпочтений / тегов для каждого.
n | 10 | 100 | 1000 |
--|--------|--------|--------|
1 | 0.004s | 0.070s | 4.061s |
2 | 0.004s | 0.069s | 4.022s |
3 | 0.002s | 0.051s | 3.983s |
4 | 0.002s | 0.060s | 4.225s |
И, если мы нарушим размеры, у нас будет в 10 раз больше сотрудников, чем проектов (n
) ...
n | 10 | 100 | 500 |
--|--------|--------|--------|
1 | 0.038s | 0.674s | 19.42s |
2 | 0.036s | 0.672s | 17.91s |
3 | 0.017s | 0.482s | 10.42s |
4 | 0.018s | 0.497s | 12.13s |
И, если мы нарушим размеры, у нас будет в 10 раз больше проектов, чем сотрудников (n
) ...
n | 10 | 100 | 500 |
--|--------|--------|--------|
1 | 0.014s | 0.466s | 16.22s |
2 | 0.015s | 0.481s | 16.08s |
3 | 0.012s | 0.476s | 10.30s |
4 | 0.032s | 0.697s | 13.09s |
Как видите,Все это зависит.Оцените все это на своих данных и выберите лучшее решение.