Как лучше всего хранить вложенные наборы (например, деревья комментариев) в 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.

8
Valentin Golev 6 Дек 2009 в 01:49

2 ответа

Лучший ответ

Думаю, идеального решения не существует - зависит от того, какие операции важнее для вашего приложения. Я считаю, что Silicon Alley Insider хранит комментарии, вложенные, например, в MongoDB. Это усложняет вопрос, о котором вы говорите.

Один из вариантов - сохранить на верхнем уровне сообщения список всех комментаторов в массиве. Думайте об этом как о денормализованных данных. Тогда можно будет легко найти все сообщения, в которых участвует определенный комментатор. Затем для детализации вы используете map / reduce или db.eval (), чтобы получить информацию о вложенном посте внутри.

Еще одно замечание: если вы имеете дело с одним документом, db.eval (), вероятно, легче, чем map / reduce. $ where также является вариантом, но может быть медленным, поэтому мне нравится упомянутый выше дополнительный «список комментаторов» - не так просто также проиндексировать этот массив (см. «Multikey» в документации).

Смотрите также:

3
dm. 6 Дек 2009 в 18:01

В ссылке из сообщения 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 является вашим корневым элементом или присутствует в большинстве ваших записей, тогда вы выполняете почти полное сканирование записей вместо эффективного запроса индекса.

2
Pete Brumm 14 Июн 2011 в 18:22