Вопрос
Каковы наилучшие методы хранения вложенных наборов (например, деревьев комментариев) в MongoDB?
Я имею в виду, что у каждого комментария может быть родительский комментарий и дочерние комментарии (ответы).
Храните их вот так:
{
title: "Hello",
body: "Please comment me!",
comments: [
{
author: "Peter",
text: "Hi there",
answers: [
{
author: "Peter",
text: "Hi there",
answers: [
{ author: "Ivan", text: "Hi there" },
{ author: "Nicholas", text: "Hi there" }
]
},
{ author: "Ivan", text: "Hi there" },
{ author: "Nicholas", text: "Hi there" },
]
},
{ author: "Ivan", text: "Hi there" },
{ author: "Nicholas", text: "Hi there" },
]
}
это не круто, потому что мы не можем, например, запросить "все сообщения, которые прокомментировал Питер" без map / reduce.
Решение
Я думаю, что идеального решения не существует - зависит от того, какие операции более важны для вашего приложения.Я полагаю, что Silicon Alley Insider хранит комментарии, вложенные, например, в MongoDB.Это действительно усложняет запрос, о котором вы упомянули.
Один из вариантов - сохранить на верхнем уровне в публикации список всех комментаторов в массиве.Думайте об этом как о денормализованных данных.Тогда можно легко найти все сообщения, в которых участвует определенный комментатор.Затем для детализации вы используете map / reduce или db.eval(), чтобы получить вложенную информацию о записи внутри.
Еще одно замечание - если вы имеете дело с одним документом, db.eval(), вероятно, имеет меньший вес, чем map / reduce .$where также является опцией, но может быть медленным, поэтому мне нравится дополнительный "список комментаторов", упомянутый выше, - этот массив также легко проиндексировать (см. "Multikey" в документах).
Смотрите также:http://groups.google.com/group/mongodb-user/browse_thread/thread/df8250573c91f75a/e880d9c57e343b52?lnk=gst&q=trees#e880d9c57e343b52
Другие советы
В ссылке из сообщения dm Дуайт Мерриман упоминает использование ключа path и выполнение совпадений регулярных выражений
{
path : "a.b.c.d.e.f"
}
Другим способом сделать это было бы с помощью массивов
{
path : ["a", "b", "c", "d", "e", "f"]
}
db.test.ensureIndex({path: 1})
это должно сделать это довольно быстро.
если каждый узел может находиться только в одном пути, то вам не нужно будет беспокоиться о том, где он находится в списке
db.test.find({path: "a"})
нашел бы всех детей "а"
Вместо имен путей я бы, вероятно, использовал _id узлов.
Обновить
- одна вещь, с которой следует быть осторожным, - это то, что индекс может содержать только один массив.
Будьте осторожны, используя explain в своих запросах
db.test.find({путь:{$в:["a", "b"]})
дает вам
db.test.find({path: {$in: ["a", "b"]}}).explain()
{
"cursor" : "BtreeCursor path_1 multi",
"nscanned" : 2,
"nscannedObjects" : 2,
"n" : 1,
"millis" : 0,
"nYields" : 0,
"nChunkSkips" : 0,
"isMultiKey" : true,
"indexOnly" : false,
"indexBounds" : {
"path" : [
[
"a",
"a"
],
[
"b",
"b"
]
]
}
}
но
db.test.find({path: {$all: ["a", "b"]}}).explain()
{
"cursor" : "BtreeCursor path_1",
"nscanned" : 1,
"nscannedObjects" : 1,
"n" : 1,
"millis" : 0,
"nYields" : 0,
"nChunkSkips" : 0,
"isMultiKey" : true,
"indexOnly" : false,
"indexBounds" : {
"path" : [
[
"a",
"a"
]
]
}
}
использует только первый элемент, а затем сканирует все совпадающие результаты для b.
Если a является вашим корневым элементом или присутствует в большинстве ваших записей, то вы выполняете почти полное сканирование записей вместо эффективного индексного запроса.