Извлечение иерархических / вложенных данных из CouchDB - PullRequest
6 голосов
/ 25 мая 2011

Я довольно новичок в couchDB, и даже после прочтения (последний архив, который сейчас удален) http://wiki.apache.org/couchdb/How_to_store_hierarchical_data (через «Сохранить полный путь к каждому узлу в качестве атрибута в документе этого узла») это еще не нажал только пока.

Вместо использования шаблона полного пути, как описано в вики, я надеюсь отслеживать дочерние элементы как массив UUID, а родительский - как единый UUID. Я склоняюсь к этому шаблону, чтобы сохранить порядок детей по их позициям в массиве children.

Вот некоторые образцы документов на диване, в ведрах могут содержаться ведра и предметы, в предметах могут быть только другие предметы. (UUIDs сокращены для ясности):

{_id: 3944
 name: "top level bucket with two items"
 type: "bucket",
 parent: null
 children: [8989, 4839]
}
{_id: 8989
 name: "second level item with no sub items"
 type: "item"
 parent: 3944
}
{
 _id: 4839
 name: "second level bucket with one item"
 type: "bucket",
 parent: 3944
 children: [5694]
}
{
 _id: 5694
 name: "third level item (has one sub item)"
 type: "item",
 parent: 4839,
 children: [5390]
}
{
 _id: 5390
 name: "fourth level item"
 type: "item"
 parent: 5694
}

Можно ли искать документ по встроенному идентификатору документа в функции карты?

function(doc) {
    if(doc.type == "bucket" || doc.type == "item")
        emit(doc, null); // still working on my key value output structure
        if(doc.children) {
            for(var i in doc.children) {
                // can i look up a document here using ids from the children array?
                doc.children[i]; // psuedo code
                emit(); // the retrieved document would be emitted here
            }
        }
     }
}   

В идеальном мире конечный вывод JSON будет выглядеть примерно так.

{"_id":3944,
 "name":"top level bucket with two items",
 "type":"bucket",
 "parent":"",
 "children":[
     {"_id":8989, "name":"second level item with no sub items", "type":"item", "parent":3944},
     {"_id": 4839, "name":"second level bucket with one item", "type":"bucket", "parent":3944, "children":[
         {"_id":5694", "name":"third level item (has one sub item)", "type":"item", "parent": 4839, "children":[
             {"_id":5390, "name":"fourth level item", "type":"item", "parent":5694}
         ]}
     ]}
 ]
}

Ответы [ 2 ]

7 голосов
/ 26 мая 2011

Можете ли вы вывести древовидную структуру из представления? Нет. Запросы представления CouchDB возвращают список значений, нет способа заставить их выводить что-либо, кроме списка. Итак, вы должны иметь дело с вашей картой, возвращая список всех потомков данного сегмента.

Однако вы можете подключить функцию пост-обработки _list после самого представления, чтобы превратить этот список обратно во вложенную структуру. Это возможно, если ваши значения знают _id своего родителя & mdash; алгоритм довольно прост, просто задайте другой вопрос, если он доставляет вам неприятности.

Можете ли вы получить документ по его идентификатору в функции карты? Нет. Невозможно получить документ по его идентификатору из CouchDB. Запрос должен исходить из приложения, либо в форме стандартного GET идентификатора документа, либо путем добавления include_docs=true к запросу просмотра.

Техническая причина этого довольно проста: CouchDB запускает функцию карты только при изменении документа. Если документу A было разрешено получать документ B, то отправленные данные станут недействительными при изменении B.

Можете ли вы вывести всех потомков без сохранения списка родителей каждого узла? Нет. Функции карт CouchDB выдают набор пар ключ-значение-идентификатор для каждого документа в базе данных, поэтому соответствие между ключ и идентификатор должны быть определены на основе одного документа.

Если у вас есть четырехуровневая древовидная структура A -> B -> C -> D, но вы только сообщаете узлу о своих родителях и дочерних элементах, то ни один из вышеперечисленных узлов не знает, что D является потомком A, поэтому вы не будете быть в состоянии выдать идентификатор D с ключом, основанным на A, и, таким образом, он не будет виден на выходе.

Итак, у вас есть три варианта:

  • Получите только три уровня (это возможно, потому что B знает, что C является потомком A), и захватите дополнительные уровни, снова выполнив запрос.
  • Каким-то образом хранить список потомков каждого узла в узле (это дорого).
  • Хранить список родителей каждого узла в узле.
6 голосов
/ 26 мая 2011

Общее обсуждение можно найти на вики-сайте CouchDB .

У меня нет времени проверять это прямо сейчас, однако ваша функция карты должна выглядеть примерно так:

function(doc) {
    if (doc.type === "bucket" || doc.type === "item")
        emit([ doc._id, -1 ], 1);
        if (doc.children) {
            for (var i = 0, child_id; child_id = doc.children[i]; ++i) {
                emit([ doc._id, i ], { _id: child_id });
            }
        }
    }
}

Вы должны запросить его с помощью include_docs=true, чтобы получить документы, как описано в документации CouchDB : если ваша функция карты выдает значение объекта, которое имеет {'_id': XXX}, и вы запрашиваете представление с include_docs=true параметр, то CouchDB будет извлекать документ с идентификатором XXX, а не документ, который был обработан для создания пары ключ / значение.

Добавьте startkey=["3944"]&endkey["3944",{}], чтобы получить только документ с идентификатором "3944" с его дочерними элементами.

РЕДАКТИРОВАТЬ: посмотрите этот вопрос для более подробной информации.

...