Как лучше всего хранить вложенные наборы (например, деревья комментариев) в 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.
2 ответа
Думаю, идеального решения не существует - зависит от того, какие операции важнее для вашего приложения. Я считаю, что Silicon Alley Insider хранит комментарии, вложенные, например, в MongoDB. Это усложняет вопрос, о котором вы говорите.
Один из вариантов - сохранить на верхнем уровне сообщения список всех комментаторов в массиве. Думайте об этом как о денормализованных данных. Тогда можно будет легко найти все сообщения, в которых участвует определенный комментатор. Затем для детализации вы используете map / reduce или db.eval (), чтобы получить информацию о вложенном посте внутри.
Еще одно замечание: если вы имеете дело с одним документом, db.eval (), вероятно, легче, чем map / reduce. $ where также является вариантом, но может быть медленным, поэтому мне нравится упомянутый выше дополнительный «список комментаторов» - не так просто также проиндексировать этот массив (см. «Multikey» в документации).
В ссылке из сообщения dm Дуайт Мерриман упоминает использование ключа пути и выполнение совпадений с регулярным выражением
{
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 узлов.
Обновить
- следует остерегаться того, что в индексе может быть только один массив.
Будьте осторожны, используя объяснения в своих запросах
db.test.find ({путь: {$ in: ["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 является вашим корневым элементом или присутствует в большинстве ваших записей, тогда вы выполняете почти полное сканирование записей вместо эффективного запроса индекса.
Похожие вопросы
Связанные вопросы
Новые вопросы
mongodb
MongoDB - это масштабируемая, высокопроизводительная база данных NoSQL с открытым исходным кодом, ориентированная на документы. Он поддерживает большое количество языков и платформ разработки приложений. Вопросы по администрированию сервера можно задать на https://dba.stackexchange.com.