2015-11-08 4 views
0

У меня есть схема, как это:сортировать по вложенной стоимости объекта в MongoDB

{ 
    tags:[ 
     { 
     id:"t1", 
     score:70 
     }, 
     { 
     id:"t1", 
     score:60 
     } 
    ] 
} 

Я хочу, чтобы отсортировать запрос на tag.id поиска, чтобы получить отсортированный по соответствующему счету. Поэтому, если я ищу db.collection.find ({tags.id: "t1"}). Sort ({tags.score: -1}), он сортируется по счету объекта «t1», а не другим тегам.

Любое предложение?

+0

Возможно, возник вопрос более ясный, если образец массива содержал другие значения, а не только «t1» и, возможно, несколько документов и ожидаемый результат. Но я думаю, что вижу, к чему вы клоните. –

ответ

1

Если вам нужно вычислить что-то подобное во время выполнения, с «фильтруется» контента из массива, определяющего порядок сортировки, то вам лучше сделать что-то с .aggregate(), чтобы изменить форму и определить значение сортировки так:

db.collection.aggregate([ 
    // Pre-filter the array elements 
    { "$project": { 
     "tags": 1, 
     "score": { 
      "$setDifference": [ 
       { "$map": { 
        "input": "$tags", 
        "as": "tag", 
        "in": { 
         "$cond": [ 
          { "$eq": [ "$$el.id", "t1" ] }, 
          "$$el.score", 
          false 
         ] 
        } 
       }}, 
       [false] 
      ] 
     } 
    }}, 
    // Unwind to denormalize 
    { "$unwind": "$score" }, 
    // Group back the "max" score 
    { "$group": { 
     "_id": "$_id", 
     "tags": { "$first": "$tags" }, 
     "score": { "$max": "$score" } 
    }}, 
    // Sort descending by score 
    { "$sort": { "score": -1 } } 
]) 

Если первая часть трубопровода используется для «предварительного фильтра» содержание массива (а также сохраняя исходное поле) для просто эти значения «score», где id равен «t1». Это делается путем обработки $map, которая применяет условие к каждому элементу через $cond, чтобы определить, следует ли возвращать «счет» для этого элемента или false.

$setDifference операция делает сравнение с одним массивом [false] элемента, который эффективно удаляет любые false значения, возвращенные из $map. В качестве «набора» это также удаляет повторяющиеся записи, но для цели сортировки здесь это хорошо.

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

Тогда речь идет только о применении $sort по определенному значению для оформления документов. Естественно, если вы хотите, чтобы это было наоборот, используйте $min и сортируйте в порядке возрастания.

Конечно, добавьте этап $match в начало, если все, что вам действительно нужно, это документы, которые на самом деле содержат значения «t1» для id в тегах. Но эта часть имеет наименьшее значение для сортировки по отфильтрованным результатам, которые вы хотите достичь.

Альтернативой вычислению является выполнение всего этого при записи записей в массив в документах. Вид сумбурно, но это идет что-то вроде этого:

db.collection.update(
    { "_id": docId }, 
    { 
     "$push": { "tags": { "id": "t1", "score": 60 } }, 
     "$max": { "maxt1score": 60 }, 
     "$min": { "mint1score": 60 } 
    } 
) 

Здесь оператор $max обновления только устанавливает значение указанного поля, если новое значение больше, чем существующее значение или в противном случае свойства еще не существует. Обратный случай относится к $min, где только если оно меньше, чем оно будет заменено новым значением.

Это, конечно, иметь эффект добавления различных дополнительных свойств документов, но конечный результат сортировки значительно упрощается:

db.collection.find().sort({ "maxt1score": -1 }) 

И это будет работать намного быстрее, чем вычисление с агрегацией трубопровод.

Так что рассмотрите принципы дизайна. Структурированные данные в массивах, в которых вы хотите отфильтровать и парные результаты для сортировки, вычисляют во время выполнения, чтобы определить, какое значение нужно сортировать. Добавление дополнительных свойств в документ по адресу .update() означает, что вы можете просто ссылаться на эти свойства, чтобы напрямую сортировать результаты.

+0

Спасибо за объяснение. Второй подход не подходит для моего случая, потому что у меня может быть 50-100 вариантов тегов. Хотя это действительно эффективно. Совокупная функция дала мне ошибку, что el не определен, и когда вы поворачиваете его в $ el, он не показывает результат, я что-то здесь не вижу? – MKoosej

+0

Работал, как ожидалось, с небольшим редактированием в вашем ответе. – MKoosej

+0

Как я могу вернуть весь документ в группу? следует ли добавлять их один за другим в проекции? – MKoosej

0

В соответствии с упомянутым выше описанием запрос

db.collection.find({tags.id:"t1"}).sort({tags.score:-1}) 

будет фильтровать документы, состоящие из тега ID 1 и возвращает результаты, отсортированных по счету в порядке убывания.

Просьба также представить подробное описание вместе с примером документа и ожидаемые результаты вывода

+0

Справедливо уверен, что в вопросе OP четко указано, что «сортировка» предназначена для применения к значениям в массиве, соответствующем только «t1». Это не будет сделано и будет соответствовать только документы, содержащие «t1», и не ограничивает сортировку как таковую. По крайней мере, если бы синтаксис «точечной нотации» был правильным здесь и не был незаконным. Но это еще не ответ на вопрос, который был задан. –

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