2016-01-27 3 views
0

Может быть, я собираюсь идти против зерна здесь, но у меня есть структурированные данные, где поток сообщений живет внутри документа и всех сообщений живет внутри встроенного документа. (не массив поддокумента)mongoDB: Как получить последние поля встроенного документа (non-array)

Я хотел бы иметь возможность сортировать и ограничивать встроенный документ по метке времени.

Например, второй документ довольно большой, поэтому я хотел бы получить только последние 10 сообщений (или w/e) между Бобом и мной.

{ 
    "_id" : ObjectId("2bjbkjb4234j134124"), 
    "messages" : { 
     "56a7b13f24236dea1247cdc7" : { 
      "authorName" : "Nick", 
      "timestamp" : 1.453699391078E12, 
      "message" : "Hello" 
     }, 
     ... 5 more messages 
    } 

}, 
{ 
    "_id" : ObjectId("3e11kjb4234j134172"), 
    "messages" : { 
     "5727b13f24236dea1247ced8" : { 
      "authorName" : "Bob", 
      "timestamp" : 1.2353453455078E12, 
      "message" : "Sup!" 
     }, 
     ... 50,000 messages 
    } 

} 

Вопрос:

Есть ли способ, чтобы сделать эквивалент сортировки, ограничение и вернуться, но на встроенном документе (как выше сообщений)?

+1

Не с дизайном выше, если вы не реструктурируете схему, чтобы вы могли вставлять вложенные документы в массив, а не в хэш-ключ, лучшим вариантом будет MapReduce. – chridam

+0

Действительно оцените быстрый ответ. Было бы грустно бросать хеш-ключи :(Я думал, что они будут более быстрыми для обновлений в реальном времени. –

ответ

1

Вы действительно должны использовать массивы здесь, как с помощью именованных ключей объекта действительно нелогичным, как в основном работает база данных.

Помимо основных проблем с запросами, таких как, возможно, поиск всего содержимого автором «Боб» в коллекции (что просто с массивами), у вас есть аналогичные проблемы с «грубой силой» при поиске «последних 10 ». Не говоря уже о том, что «не-массив» становится действительно субъективным, что на самом деле «последняя десятка».

Даже если принять, что эти «ключи» на самом деле являются теми же сгенерированными значениями значений MongoDB ObjectID (поэтому они являются монотонными и всегда увеличиваются по стоимости), для разработки порядка сортировки требуется грубая силовая обработка JavaScript без какой-либо помощи из индексов сбора или природного массива индексных позиций:

db.collection.mapReduce(
    function() { 
     var messages = this.messages; 
     var newMessages = Object.keys(this.messages).sort().slice(-10).map(
      function(id) { 
       return messages[id]; 
      } 
     ); 

     emit(this._id,{ "messages": newMessages }); 
    }, 
    function() {}, // not really reducing anything here 
    { "out": { "inline": 1 } } 
) 

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

В основном Уродливый!, и очень плохой дизайн. Также просто используйте mapReduce для этого, это единственный метод (посредством обработки JavaScript) изменения структуры возвращаемого документа. Логика может также выполняться на клиенте, с единственным преимуществом удаления нежелательного контента перед отправкой по сетевому соединению.

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

{ 
    "_id" : ObjectId("2bjbkjb4234j134124"), 
    "messages" : [ 
     { 
      "_id": "56a7b13f24236dea1247cdc7", 
      "authorName" : "Nick", 
      "timestamp" : 1.453699391078E12, 
      "message" : "Hello" 
     }, 
     // etc 
    ] 
} 

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

db.collection.update(
    { "messages._id": "56a7b13f24236dea1247cdc7" }, 
    { "$set": { 
     "messages.$.message": "something new", 
     "messages.$.timestamp": aNewValue 
    }} 
) 

Добавление элементов в массивы с помощью $push также имеет то преимущество, все «новейшие» записи добавляются к концу e по умолчанию.Так что, если не изменить это (и не изменять и, таким образом, хотят последнюю метку времени), то все, что вам нужно сделать, это $slice «уже массив», без дальнейших жонглирования:

db.collection.find(
    {}, 
    { "messages": { "$slice": -10 } } 
) 

Если вы действительно хотите модифицированную такое поле как «timestamp», чтобы повлиять на упорядочение, тогда вы можете просто сохранить этот способ, используя $sort модификатор до $push. Это может даже применить к модифицированным элементам массива с помощью простого применения массовых операций:

var bulk = db.collection.initializeOrderedBulkOp(); 

// Update the matched element 
bulk.find({ 
    "_id": ObjectId("2bjbkjb4234j134124"), 
    "messages._id": "56a7b13f24236dea1247cdc7" 
}).updateOne({ 
    "$set": { 
     "messages.$.message": "something new", 
     "messages.$.timestamp": aNewValue 
    } 
}); 

// Sort the array on timestamp 
bulk.find({ 
    "_id": ObjectId("2bjbkjb4234j134124"), 
    "messages._id": "56a7b13f24236dea1247cdc7" 
}).updateOne({ 
    "$push": { "messages": { "$each": [], "$sort": { "timestamp": 1 } } } 
}) 

// Send and receive from server 
bulk.execute(); 

Который в то время как это эффективно два утверждения обновления (так как вы не можете изменить тот же путь документа с двумя утверждениями оператора в одной операции обновления), он все еще работает как единый запрос и ответ на сервер и, следовательно, довольно эффективен.

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

db.collection.aggregate([ 
    { "$match": ObjectId("2bjbkjb4234j134124") }, 
    { "$unwind": "$messages" }, 
    { "$sort": { "messages.timestamp": -1 } }, // in reverse order with $limit 
    { "$limit": 10 }, 
    { "$group": { 
     "_id": "$_id", 
     "messages": { "$push": "$messages" } 
    }} 
]) 

Или даже супер фантазии на несколько документов с новыми MongoDB 3.2 операторами:

db.collection.aggregate([ 
    { "$unwind": "$messages" }, 
    { "$sort": { "_id": 1, "messages.timestamp": 1 } }, 
    { "$group": { 
     "_id": "$_id", 
     "messages": { "$push": "$messages" } 
    }}, 
    { "$project": { 
     "messages": { "$slice": [ "$messages", -10 ] } 
    }} 
]) 

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

  1. Быть «массив», а не вложены в именованных ключей объекта

  2. В идеале хранятся в порядке наиболее распространенных случая использования для доступа на чтение.

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

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

+0

Вау, мне нужно будет изучить этот пост, чтобы понять все, что вы сделали, но я могу сказать, что великое понимание было Я буду следить за тем, как это получилось. Тем временем я надеюсь, что этот пост получит голосов, которых он заслуживает, потому что он должен быть видимым. –

+0

Еще раз спасибо @BlakesSeven Этот пост заставил меня вернуться и переосмыслить все о моей структуре! –

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