2015-06-06 2 views
1

У меня есть коллекция MongoDB, Опросы с приведенной ниже схемеКак сделать агрегацию мангуста с вложенными документами массива

{ 
"options" : [ 
       { 
       "_id" : Object Id, 
       "option" : String, 
       "votes" : [ Object Id ] // object ids of users who voted 
       },..... 
      ] 
} 

Предположим, у меня есть USERID пользователя в узле JS, которому я хочу отправить это Информация. Моя задача состоит в том, чтобы

(1) включает дополнительное поле в вышеуказанном объекте JSON (который я получить с помощью мангуста).

в

"myVote": option._id

Мне нужно найти option._id, для которых

опции [someIndex] .votes содержит USERID

(2) изменить существующее поле «голосов» в каждом варианте для представления количества голосов по определенному варианту, как может видно в примере

Пример:

{ 
    "options" : [ 
       { 
       "_id" : 1, 
       "option" : "A", 
       "votes" : [ 1,2,3 ] 
       }, 
       { 
       "_id" : 2, 
       "option" : "B", 
       "votes" : [ 5 ] 
       }, 
       { 
       "_id" : 3, 
       "option" : "C", 
       "votes" : [ ] 
       } 
      ] 
} 

Так что, если я пользователь с идентификатором пользователя = 5 хочет видеть опрос, то мне нужно послать следующую информацию:

Ожидаемый результат:

{ 
    "my_vote" : 2,   // user with id 5 voted on option with id 2 
    "options" : [ 
       { 
       "_id" : 1, 
       "option" : "A", 
       "votes" : 3    //num of votes on option "A" 
       }, 
       { 
       "_id" : 2, 
       "option" : "B", 
       "votes" : 1    //num of votes on option "B" 
       }, 
       { 
       "_id" : 3, 
       "option" : "C", 
       "votes" : 0   //num of votes on option "C" 
       } 
      ] 
} 
+1

Хех? Что ты хочешь? Попробуйте добавить то, что вы пробовали, и ожидаемый результат. –

+0

Пример и результат здесь выглядят весьма несовместимыми с исходной структурой, опубликованной. Также как «id = 5» даже играет в это как результат? Также принято и вежливо «тегировать» человека, комментирующего вас, запрашивая дополнительную информацию, когда вы решите добавить его. Помогает людям понять, что вы делаете. –

+1

@ user3561036 sorry abt not tagging before, Ожидаемый результат на самом деле json, который я хочу отправить клиенту. Я думаю, что это может быть достигнуто с использованием структуры агрегации, но не в состоянии написать точный запрос, поскольку я новичок в этом –

ответ

1

Так как это был вопрос, что вы на самом деле спросили, что не было ни действительно предусмотрены в текущем приеме ответа, а также то, что он делает некоторые ненужные вещи, есть другой подход:

var userId = 5; // A variable to work into the submitted pipeline 

db.sample.aggregate([ 
    { "$unwind": "$options" }, 
    { "$group": { 
     "_id": "$_id", 
     "my_vote": { "$min": { 
      "$cond": [ 
       { "$setIsSubset": [ [userId], "$options.votes" ] }, 
       "$options._id", 
       false 
      ] 
     }}, 
     "options": { "$push": { 
      "_id": "$options._id", 
      "option": "$options.option", 
      "votes": { "$size": "$options.votes" } 
     }} 
    }} 
]) 

Что, конечно, даст вам выход за каждый документ, как это:

{ 
    "_id" : ObjectId("5573a0a8b67e246aba2b4b6e"), 
    "my_vote" : 2, 
    "options" : [ 
      { 
        "_id" : 1, 
        "option" : "A", 
        "votes" : 3 
      }, 
      { 
        "_id" : 2, 
        "option" : "B", 
        "votes" : 1 
      }, 
      { 
        "_id" : 3, 
        "option" : "C", 
        "votes" : 0 
      } 
    ] 
} 

Так что вы делаете здесь, используя $unwind для того, чтобы разбить массив для проверки первой. На следующем этапе $group (и только на другом этапе, который вам нужен) используются операторы и $push для перестройки.

Внутри каждой из этих операций, то $cond операция проверяет содержимое массива с помощью $setIsSubset и либо возвращает значение согласованного _id или false. При восстановлении внутреннего элемента массива укажите все элементы, а не только документ верхнего уровня в аргументах $push, и используйте оператор $size для подсчета элементов в массиве.

Вы также упоминаете ссылку на другой вопрос о работе с пустым массивом с $unwind. Оператор $size выполнит правильные действия, поэтому не требуется $unwind и спроектировать «фиктивное» значение, в котором массив пуст в этом случае.


Гранд примечание, если вы на самом деле «агрегирование» по документам он вообще бы посоветовал сделать эту операцию в коде клиента, а не структуры агрегации. Использование $unwind эффективно создает новый документ в конвейере агрегации для каждого элемента массива, содержащегося в каждом документе, что создает значительные накладные расходы.

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


Если вы действительно должны сохраняться, что обработка сервера является способом сделать это, то это, вероятно, наиболее эффективное использование $map вместо:

db.sample.aggregate([ 
    { "$project": { 
     "my_vote": { 
      "$setDifference": [ 
       { "$map": { 
        "input": "$options", 
        "as": "o", 
        "in": { "$cond": [ 
         { "$setIsSubset": [ [userId], "$$o.votes" ] }, 
         "$$o._id", 
         false 
        ]} 
       }}, 
       [false] 
      ] 
     }, 
     "options": { "$map": { 
      "input": "$options", 
      "as": "o", 
      "in": { 
       "_id": "$$o._id", 
       "option": "$$o.option", 
       "votes": { "$size": "$$o.votes" } 
      } 
     }} 
    }} 
]) 

Так что это просто «проектов» Переработанные результаты для каждого документа. my_vote не то же самое, хотя, так как это единственный элемент массива (или возможно несколько соответствий), что структура агрегации не хватает операторов, чтобы свести к элементу массива, не без дополнительной служебной:

{ 
    "_id" : ObjectId("5573a0a8b67e246aba2b4b6e"), 
    "options" : [ 
      { 
        "_id" : 1, 
        "option" : "A", 
        "votes" : 3 
      }, 
      { 
        "_id" : 2, 
        "option" : "B", 
        "votes" : 1 
      }, 
      { 
        "_id" : 3, 
        "option" : "C", 
        "votes" : 0 
      } 
    ], 
    "my_vote" : [ 
      2 
    ] 
} 
0

Check out this question.

Это не то же самое, но в любом случае вы не можете делать то, что вы просите, без нескольких запросов. Я бы изменил JSON, вы сразу вернетесь, так как вы просто показываете дополнительную информацию, которая уже содержится в результате запроса.

  1. Сохраните userID, на который вы запрашиваете.
  2. Возьмите результаты вашего запроса (массив параметров в объекте), выполните поиск по votes каждого элемента массива.
  3. Когда вы нашли правильный голос, прикрепите _id (возможно, добавьте 'n/a', если вы не найдете голоса).

Написать функцию, которая делает 2 и 3, и вы можете просто передать его userID, и получить обратно новый объект с myVote прилагается.

Я не думаю, что это будет медленнее, чем выполнение другого запроса в Mongoose.

Смежные вопросы