2016-02-13 3 views
2

В моем Метеоре приложении, у меня есть коллекция документов с массивом поддокументов, которые выглядят так:Сортировка по максимальному полю массива, по возрастанию или убыванию

/* 1 */ 
{ 
    "_id" : "5xF9iDTj3reLDKNHh", 
    "name" : "Lorem ipsum", 
    "revisions" : [ 
     { 
      "number" : 0, 
      "comment" : "Dolor sit amet", 
      "created" : ISODate("2016-02-11T01:22:45.588Z") 
     } 
    ], 
    "number" : 1 
} 

/* 2 */ 
{ 
    "_id" : "qTF8kEphNoB3eTNRA", 
    "name" : "Consecitur quinam", 
    "revisions" : [ 
     { 
      "comment" : "Hoste ad poderiquem", 
      "number" : 1, 
      "created" : ISODate("2016-02-11T23:25:46.033Z") 
     }, 
     { 
      "number" : 0, 
      "comment" : "Fagor questibilus", 
      "created" : ISODate("2016-02-11T01:22:45.588Z") 
     } 
    ], 
    "number" : 2 
} 

То, что я хочу сделать, это запрос этого сбором и сортировать результат, полученный на максимальная дата в поле поля revisions. Что-то, чего я еще не смог снять. Некоторые ограничения у меня есть:

  • Просто сортировка по revisions.created не разрезает его, потому что дата, используемая в коллекции, зависит от направления сортировки. Я должен использовать максимальную дату в наборе независимо от порядка сортировки.
  • Я не могу полагаться на манипуляции после запроса на несортированный результирующий набор, поэтому это должно быть сделано с помощью соответствующего запроса или агрегации с помощью базы данных.
  • Нет гарантии, что массив revisions будет предварительно отсортирован.
  • В некоторых документах могут быть дополнительные поля, и они должны быть готовы, так что будьте осторожны с $project.
  • Метеора еще используют MongoDB 2.6, новые функции API не годится :(
+1

На оболочке это будет 'db.coll.find (yourQuery) .sort ({" revisions.created ": - 1})'. Я не имею ни малейшего понятия об метеоре, поэтому вам нужно перевести его самостоятельно. –

+0

@Markus Это работает, когда я сортирую нисходящий, но не восходящий. Затем он использует минимум в наборе. – edgerunner

+1

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

ответ

3

Основная проблема с тем, что вы спрашиваете здесь не сводится к тому, что данные о котором идет речь в «массиве », и поэтому есть некоторые базовые предположения, сделанные MongoDB относительно того, как это обрабатывается.

Если вы применили сортировку в порядке« по убыванию », MongoDB выполнит именно то, что вы просите, и отсортируйте документы по« крупнейшим "значение указанного поля в массиве:

.sort({ "revisions.created": -1)) 

Но если вместо этого вы сортируете в порядке «возрастания», то, конечно, обратное верно и учитывается «наименьшее» значение.

.sort({ "revisions.created": 1 }) 

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

Collection.aggregate([ 
    { "$unwind": "$revisions" }, 
    { "$group": { 
     "_id": "$_id", 
     "name": { "$first": "$name" }, 
     "revisions": { "$push": "$revisions" }, 
     "number": { "$first": "$number" } 
     "maxDate": { "$max": "$revisions.created" } 
    }}, 
    { "$sort": { "maxDate": 1 } 
]) 

Или в лучшем случае с MongoDB 3.2, где $max могут быть применены непосредственно к выражению массива:

Collection.aggregate([ 
    { "$project": { 
     "name": 1, 
     "revisions": 1, 
     "number": 1, 
     "maxDate": { 
      "$max": { 
       "$map": { 
        "input": "$revisions", 
        "as": "el", 
        "in": "$$el.created" 
       } 
      } 
     } 
    }}, 
    { "$sort": { "maxDate": 1 } } 
]) 

Но на самом деле оба они не так уж велики, даже если подход MongoDB 3.2 имеет меньшие накладные расходы, чем то, что доступно для предыдущих версий, но все равно не так хорошо, как вы можете получить с точки зрения производительности из-за необходимости проходить через данных и выработать значение для сортировки.

Так что для Лучшее исполнение «всегда» сохраняет такие данные, которые вам понадобятся «снаружи» массива. Для этого существует оператор $max "update", который заменит только значение в документе «если» предоставленное значение «больше» уже имеющегося значения. я.е:

Collection.update(
    { "_id": "qTF8kEphNoB3eTNRA" }, 
    { 
     "$push": { 
      "revisions": { "created": new Date("2016-02-01") }    
     }, 
     "$max": { "maxDate": new Date("2016-02-01") } 
    } 
) 

Это означает, что значение, которое вы хотите будет «всегда» уже присутствовать в документе с ожидаемым значением, так что это только сейчас простой вопрос сортировки по этому полю:

.sort({ "maxDate": 1 }) 

Итак, для моих денег я бы поехал с существующими данными с любым из доступных предложений .aggregate() и использовал эти результаты, чтобы обновить каждую доккумент, чтобы она содержала поле «maxDate». Затем измените кодировку всех дополнений и ревизий данных массива, чтобы применить это $max "update" к каждому изменению.

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


В любом случае, принимая во внимание выше приложенное дату пример, который «меньше, чем» другие максимальные сроки, присутствующие вернутся за мной во всех формах:

{ 
     "_id" : "5xF9iDTj3reLDKNHh", 
     "name" : "Lorem ipsum", 
     "revisions" : [ 
       { 
         "number" : 0, 
         "comment" : "Dolor sit amet", 
         "created" : ISODate("2016-02-11T01:22:45.588Z") 
       } 
     ], 
     "number" : 1, 
     "maxDate" : ISODate("2016-02-11T01:22:45.588Z") 
} 
{ 
     "_id" : "qTF8kEphNoB3eTNRA", 
     "name" : "Consecitur quinam", 
     "revisions" : [ 
       { 
         "comment" : "Hoste ad poderiquem", 
         "number" : 1, 
         "created" : ISODate("2016-02-11T23:25:46.033Z") 
       }, 
       { 
         "number" : 0, 
         "comment" : "Fagor questibilus", 
         "created" : ISODate("2016-02-11T01:22:45.588Z") 
       }, 
       { 
         "created" : ISODate("2016-02-01T00:00:00Z") 
       } 
     ], 
     "number" : 2, 
     "maxDate" : ISODate("2016-02-11T23:25:46.033Z") 
} 

, правильно размещает первый документ в верхней части порядка сортировки с учетом «maxDate».

+2

Спасибо за подробный ответ. Одна из проблем заключается в том, что каждый раз, когда добавляется новая ревизия, нужно также помнить об обновлении поля «maxDate», иначе он сломается. Если есть способ ее автоматизировать, это будет приемлемо. – edgerunner

+0

@edgerunner Это в основном то, что я говорю здесь. Так ли это '$ push' или' $ set' на существующем элементе (и действительно только если «созданный» фактически изменился там), тогда вы также примените '$ max'. к значению 'maxDate' для документа. Да, это больше работы. Вы можете добавить некоторый код, чтобы просто добавить этот «ключ» к любому объекту обновления, если вам это нужно, но не заставлять себя выполнять другую операцию на одном и том же пути «maxDate» (не для этого нужно для этой цели). Компромисс здесь делает это для каждого обновления или поочередно работает с агрегатом по каждому запросу. –

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