Агрегат MongoDB/Mongoose с поиском, вложенными массивами, сортировкой

Итак, это мои модели

let restaurantSchema = mongoose.Schema({
    name: 'string',
    restId: 'string',
    saved: {type: 'date', default: Date.now()}
})

let userSchema = mongoose.Schema({
    firstName: {type: 'string', default: ''},
    lastName: {type: 'string', default: ''},
    username: {
        type: 'string',
        required: true,
        unique: true
    },
    beenTo: [restaurantSchema],
    toGoTo: [restaurantSchema]
})

Я хочу сделать канал: «пользователь А хочет пойти в ресторан X, пользователь Б хочет пойти в ресторан Y, пользователь А был в ресторане Z» и т. д., все отсортировано в первую очередь по дате. поэтому я пытался использовать агрегат для создания документов, содержащих имя пользователя и название ресторана, а также метку времени, чтобы я мог сортировать все документы, независимо от имени пользователя, с помощью {'сохранено': -1 } или любой другой временной меткой.

Вот о том, что у меня есть до сих пор

User.aggregate([
    {$match: {username: {$ne: username}}},
    {$lookup: {
      from: "restaurants",
      localField: "beenTo.saved",
      foreignField: "saved",
      as: "savRest"
    }},
    {$project: {'savRest.saved': 1, 'savRest.name': 1, 'username': 1}},
    {$group: {
      _id: null,
      sav: {$push: '$savRest.saved'}
    }}
  ])

Учитывая массивы, я ожидаю, что в какой-то момент мне нужно будет размотать $, но я не уверен, где в конвейере это идет...

Судя по вашей схеме, ресторан является поддокументом пользовательского документа beenTo : [RestaurantSchema]. Это сохранит документ ресторана как поддокумент пользовательского объекта. В этом случае $lookup не будет работать так, как вы этого хотите. если вы хотите сохранить только идентификатор ресторана в массиве и ссылаться на фактический документ ресторана, вам нужно изменить свою схему: beenTo : [{type:mongoose.Schema.Types.Object, ref : 'Restaurant']. Если я неправильно понял ваш вопрос, пожалуйста, отредактируйте его, чтобы сделать его более понятным. Здесь немного запутанно.

Ravi Shankar Bharti 11.03.2019 12:04

Понимаю. Извините, если это сбивает с толку, я пытаюсь понять большую часть этого сам. В этом случае, предположим, я внес предложенное изменение в свою схему, смогу ли я получить желаемый результат?

Darryl Williams 12.03.2019 05:59

Да, вы можете, пожалуйста, скажите мне, что именно вы хотите, и я помогу вам. чего вы пытаетесь достичь с помощью конвейера агрегации?

Ravi Shankar Bharti 12.03.2019 06:07

В конечном итоге я хочу отсортировать все записи во всех (других) массивах пользователей beTo и toGoTo по дате, начиная с самой последней.

Darryl Williams 13.03.2019 05:48

Итак, вам нужны все пользователи, кроме данного пользователя, поэтому вы написали: username : {$ne : username} верно?.

Ravi Shankar Bharti 13.03.2019 06:03
Использование JavaScript и MongoDB
Использование JavaScript и MongoDB
Сегодня я собираюсь вкратце рассказать о прототипах в JavaScript, а также представить и объяснить вам работу с базой данных MongoDB.
1
5
1 163
1

Ответы 1

Я думаю, вам нужно реструктурировать свой schema и включить в пользовательский документ только ресторан _id, а не хранить весь ресторан doc как sub-document.

Пожалуйста, измените свой userSchema на это:

let userSchema = mongoose.Schema({
    firstName: {type: 'string', default: ''},
    lastName: {type: 'string', default: ''},
    username: {
        type: 'string',
        required: true,
        unique: true
    },
    beenTo: [{type : mongoose.Schema.Types.ObjectId,ref : "Restaurant"}],
    toGoTo: [{type : mongoose.Schema.Types.ObjectId,ref : "Restaurant"}]
})

Посмотрите изменение, которое я внес в вашу схему: вместо сохранения документа ресторана мы будем хранить только его _id (ObjectId).

А чтобы получить все рестораны и отсортировать их, вы можете сделать следующее:

User.aggregate([
    {$match: {username: {$ne: username}}},
    // need to unwind the array, before we can do a $lookup over it
    {$unwind : "beenTo"},
    {$lookup: {
      from: "restaurants",
      localField: "beenTo",
      foreignField: "_id",
      as: "beenTo"
    }},

    //need to unwind again, as $lookup returns an array
    {$unwind : "beenTo"},

    //sort the result by beenTo.saved (date) first.
    {$sort : {"$beenTo.saved" : -1}},

    //after sorting, group all the docs and created sorted beenTo array
    {
        $group : {
            _id : "$_id",
            firstName : {$first : "$firstName"},
            lastName : {$first : "$lastName"},
            username : {$first : "$username"},
            beenTo : {$push : "$beenTo"},
            toGoTo : {$first : "$toGoTo"}
        }
    },

    //after sorting of beenTo, we can go for sorting of toGoTo array, 
    // we will follow the same procedure again.
    {$unwind : "toGoTo"},
    {$lookup: {
      from: "restaurants",
      localField: "toGoTo",
      foreignField: "_id",
      as: "toGoTo"
    }},

    //need to unwind again, as $lookup returns an array
    {$unwind : "toGoTo"},

    {$sort : {"$toGoTo.saved" : -1}},
    {
        $group : {
            _id : "$_id",
            firstName : {$first : "$firstName"},
            lastName : {$first : "$lastName"},
            username : {$first : "$username"},
            beenTo : {$first : "$beenTo"}
            toGoTo : {$push : "$toGoTo"},
        }
    }
  ])

Объяснение конвейера агрегации:

  1. $match: требуется для соответствия всем выбранным документам.
  2. $unwind: нам нужно раскрутить массив (beenTo), прежде чем мы сможем выполнить поиск по нему.
  3. $lookup : заполнил уважаемый документ ресторана из коллекции ресторанов.
  4. $unwind : $lookup возвращает массив, нам нужно снова преобразовать его в объект.
  5. $sort: отсортировать все документы по beenTo.saved значению
  6. $group: сгруппируйте все документы на основе _id пользователей и поместите все beenTo документы в свой массив. (Теперь у нас есть массив beenTo, заполненный документами ресторана и отсортированный)
  7. $unwind : Далее нам нужно раскрутить массив toGoTo, чтобы мы могли выполнить его поиск
  8. $lookup: для заполнения соответствующих документов из коллекции ресторана.
  9. $unwind : Опять нам нужно раскрутиться, так как $lookup возвращает массив
  10. $sort: отсортировать все документы в соответствии с полем toGoTo.saved.
  11. $group: сгруппируйте все документы на основе пользователей _id и поместите все документы toGoTo в свой массив, а beenTo останется прежним.

Следовательно, в конце 11-го шага все документы с beenTo и toGoTo будут заполнены и отсортированы по дате сохранения.

Я надеюсь, что это помогает вам.

Примечание: при добавлении ресторанов в массив beenTo и toGoTo в документе пользователя нажимайте только restaurant's _id, а не весь doc.

Другие вопросы по теме