Как я могу использовать агрегацию MongoDB $lookup и $addFields для сравнения полей и возврата значения, если есть совпадение?

#mongodb #aggregate

Вопрос:

У меня есть 2 коллекции MongoDB, вот 2 примера моделей:

Коллекция пользователей:

 {
  _id: ObjectId,
  available: Boolean,
  province: String,
  city: String,
  [...]
}
 

Коллекция расстояний:

 {
  _id: ObjectId,
  start_province: String,
  start_city: String,
  end_province: String,
  end_city: String,
  distance: Number
}
 

Мне нужно найти пользователей , используя следующий запрос {available: true} , а затем использовать некоторые агрегаты для добавления для каждого пользователя документ расстояние поле, которое будет содержать значение, если существует соответствие между провинцией и городом пользователей и end_province / end_city от расстояния (start_province и start_city являются фиксированными значениями, определенными в JS, прежде чем этот стог).

Если есть совпадение, расстояние должно содержать значение расстояния, в противном случае 0 (или не определено).

Я думаю, что мне следует использовать $lookup и/или $addFields, но я еще недостаточно уверен в операторах агрегации, чтобы решить эту проблему.

Пример Результатов пользователей:

 [
  {
    _id: ObjectId,
    available: Boolean,
    province: String,
    city: String,
    [...],
    distance: Number
  },
  {...}
]
 

Спасибо за вашу помощь!


ИЗМЕНИТЬ: Добавление примеров данных по запросу:

 Users
[
  {name: 'John', available: true, province: 'Rome', city: 'Tivoli'},
  {name: 'Difool', available: true, province: 'Rome', city: 'Ostia'},
  {name: 'Paul', available: true, province: 'Rome', city: 'Rome'},
  {name: 'Andrew', available: false, province: 'Rome', city: 'Grottaferrata'}
]

Distances
[
  {start_province: 'Rome', start_city: 'Rome', end_province: 'Rome', end_city: 'Ostia', distance: 5},
  {start_province: 'Rome', start_city: 'Rome', end_province: 'Rome', end_city: 'Tivoli', distance: 8}
]
 

Ожидаемые результаты от Пользователей

 [
  {name: 'John', available: true, province: 'Rome', city: 'Tivoli', distance: 8},
  {name: 'Difool', available: true, province: 'Rome', city: 'Ostia', distance: 5},
  {name: 'Paul', available: true, province: 'Rome', city: 'Rome', distance: 0}
]
 

Запрос фильтра: { available: true }

Пользователь.провинция должна совпадать с расстоянием.end_province

User.city должен совпадать с Distance.end_city.

В противном случае расстояние должно возвращать 0 или не определено. Спасибо.

Комментарии:

1. если вы добавите примеры данных и ожидаемых данных, было бы полезно лучше понять и иметь возможность протестировать запрос.

2. Отредактировано по запросу, спасибо!

3. Редактирование было очень хорошим! надеюсь, я сделал то, что ты хотел

4. это выглядит идеально! Спасибо!

Ответ №1:

Запрос

  • фильтр для сохранения доступной
  • присоединяйтесь, если
    • Пользователь.провинция должна совпадать с расстоянием.end_province
    • User.city должен совпадать с Distance.end_city.
  • если не присоединено => расстояние=0, иначе получите расстояние от первого присоединенного документа
  • снимите значения расстояний(результат соединения)

*если у вас есть mongodb

Тестовый код здесь

 db.users.aggregate([
  {
    "$match": {
      "available": {
        "$eq": true
      }
    }
  },
  {
    "$lookup": {
      "from": "distances",
      "let": {
        "uprovince": "$province",
        "ucity": "$city"
      },
      "pipeline": [
        {
          "$match": {
            "$expr": {
              "$and": [
                {
                  "$eq": [
                    "$uprovince",
                    "$end_province"
                  ]
                },
                {
                  "$eq": [
                    "$ucity",
                    "$end_city"
                  ]
                }
              ]
            }
          }
        }
      ],
      "as": "distances"
    }
  },
  {
    "$set": {
      "distance": {
        "$cond": [
          {
            "$eq": [
              "$distances",
              []
            ]
          },
          0,
          {
            "$arrayElemAt": [
              "$distances.distance",
              0
            ]
          }
        ]
      }
    }
  },
  {
    "$unset": [
      "distances"
    ]
  }
])