Свойство иерархии в базе данных графа
Я начинаю использовать neo4j. В моей базе данных графа у меня есть узлы Person
(посмотрите на "Джона" ниже) с надписями: Name
(Строка), Food
(положительное число). каждый Person
связано с другими Person
через отношения isFriendTo
, это имеет значение. Я использую граф DB только для того, чтобы найти кратчайший взвешенный путь между двумя людьми.
Кроме того, каждый день я проверяю каждый узел на графике, и если еда становится меньше 100, я предпринимаю некоторые действия.
Теперь, после некоторых улучшений, собственность Food
больше не достаточно для моего проекта. Поэтому я должен разделить это на три других более специфических свойства (положительное целое число): Vegetables
, Meat
а также Cereals
, Если сумма обоих трех меньше 100, я должен предпринять те же действия, что и раньше. Моя старая ситуация была как "Джон", единственные варианты, в которые я могу развить свой дизайн, это "Фред" или "Пол"?
Каким образом я могу спроектировать это? Должен ли я использовать в дополнение к neo4j что-то вроде MongoDB для представления иерархии?
Удаление свойства Food и добавление трех новых свойств кажется мне плохой практикой. Я должен спасти в другом месте, что эти 3 означают "еда"... и что если в будущем я добавлю другие виды продуктов? Где хранится информация о том, что значение 100 для проверки должно приходить из суммы Meat
, Vegetables
а также Cereals
? Наличие чего-то подобного решит мои сомнения, потому что я могу суммировать все элементы внутри food
:
{
"name": "Lucas",
"food": {
"meat": 40,
"vegetables": 30,
"cereals": 0
}
}
(Мне не нужно проходить через Food
а также Vegetables
в Person
, Просто нужно проверить, что сумма мяса, Вег. и зерновые меньше или больше 100.)
2 ответа
Похоже, вы путаете термины label и property.
Согласно вашей диаграмме, Person
кажется метка, разделяемая всеми вашими узлами, и Name/Food//Meat/Vegetables/Cererals
кажутся названиями свойств узлов.
Если мое понимание верно, то есть много подходов к обработке нескольких типов продуктов и получению общего количества на человека. Ниже приведено несколько примеров.
Вот один из подходов. Вы могли бы представить
Food
этикетка для уникальных узлов типа пищи:(:Food {type: 'Meat'}), (:Food {type: 'Vegetable'}), etc.
и каждый
Person
узел может иметьHAS_FOOD
отношения (сamount
свойство) к каждому соответствующему узлу Food (вместо внутреннего хранения свойств типа Food):(john:Person {Name: 'John'})-[:HAS_FOOD {amount: 140}]->(meat:Food {type: 'Meat'})
С помощью этой модели данных, чтобы найти все
Person
s с более чем 100 единицами пищи:MATCH (p:Person)-[r:HAS_FOOD]->() WITH p, SUM(r.amount) AS total WHERE total > 100 RETURN p;
Вот другой подход (который, вероятно, приведет к более быстрому поиску). Так как свойство neo4j не может иметь значение карты (в отличие от того, что вы показываете в JSON в нижней части вашего вопроса), каждый
Person
узел может иметьamount
а такжеfood
массивы, как это:(:Person {Name: 'Fred', amount: [50, 100], food: ['Meat','Vegetables']})
С помощью этой модели данных, чтобы найти все
Person
s с более чем 100 единицами пищи:MATCH (p:Person) WHERE REDUCE(s = 0, a IN p.amount | s + a) > 100 RETURN p;
[ОБНОВИТЬ]
Однако, делая обработку пищи (хороший каламбур, здесь), со вторым подходом может быть более громоздким и менее эффективным. Например, это один из способов получить количество мяса для
Fred
:MATCH (p:Person {Name: 'Fred'}) RETURN [i IN RANGE(0, SIZE(p.food)-1) WHERE p.food[i] = 'Meat' | p.amount[i]][0] AS meatAmt;
И, чтобы установить количество мяса для
Fred
до 123:MATCH (p:Person {Name: 'Fred'}) SET p.amount = [i IN RANGE(0, SIZE(p.food)-1) | CASE WHEN p.food[i] = 'Meat' THEN 123 ELSE p.amount[i]];
Итак, вот третий подход, который решает вашу проблему и гораздо лучше для обработки пищевых продуктов. Каждый узел Person может хранить количество продуктов непосредственно в виде свойств, например:
(:Person {Name: 'Fred', Meat: 50, Vegetables: 100, foodNames: ['Meat', 'Vegetables']})
С этой моделью данных
foodNames
массив позволяет перебирать свойства продуктов по имени. Итак, чтобы найти всех людей с более чем 100 единицами пищи:MATCH (p:Person) WHERE REDUCE(s = 0, n IN p.foodNames | s + p[n]) > 100 RETURN p;
И, чтобы получить количество мяса для
Fred
:MATCH (p:Person {Name: 'Fred'}) RETURN p.Meat AS meatAmt;
Чтобы установить количество мяса для
Fred
до 123:MATCH (p:Person {Name: 'Fred'}) SET p.Meat = 123;
В Neo4j метки похожи на метки, технической иерархии нет, и узел может иметь много меток.
Но если вы хотите сказать, что Food
является родителем Vegetables
, Meat
а также Cereals
с точки зрения вашего бизнеса, нет никаких проблем. У вас будет бизнес / семантическая иерархия.
Так что из моего POV, в вашем случае я бы добавил только новые метки на ваших узлах с меткой Food