图数据库中的层次结构 属性

hierarchy property in graph database

我开始使用 neo4j。 在我的图形数据库中,我有节点 Person(请看下面的 "John"),带有标签:Name(字符串)、Food(正整数)。每个 Person 通过关系 isFriendTo 与其他 Person 相连,具有一个值。 我使用图数据库只是为了找到两个人之间的最短加权路径。

此外,我每天都会检查图中的每个节点,如果食物低于 100,我就会采取一些措施。

现在,经过一些改进,属性 Food 对我的项目来说已经不够用了。所以我必须将其拆分为其他三个更具体的 属性(正整数):VegetablesMeatCereals。如果三者之和低于 100,我必须采取与之前相同的行动。 我以前的情况是 "John",我可以将我的设计发展到的唯一选择是 "Fred" 或 "Paul"?

我可以用什么方式设计这个?除了 neo4j 之外,我还应该使用 MongoDB 之类的东西来表示层次结构吗?

删除 属性 食物并添加三个新属性对我来说似乎是一种不好的做法。我必须在其他地方保存这 3 意味着 "food"... 如果将来我会添加其他类型的食物呢?我在哪里存储要检查的值 100 必须来自 MeatVegetablesCereals 之和的知识? 有了这样的东西,将解决我的疑问,因为我可以对 food:

中的所有项目求和
{
  "name": "Lucas",
  "food": {
    "meat": 40,
    "vegetables": 30,
    "cereals": 0
  }
}

(我不需要遍历FoodVegetablesPerson的连接。只需要检查Meat,Veg。和Cereals的总和较小或大于 100。)

在Neo4j上,标签就像标签一样,没有技术层次,一个节点可以有很多标签。

但是如果你想说FoodVegetablesMeatCereals的父级,从你的业务角度来说,是没有问题的。您将拥有 business/semantic 层次结构。

所以从我的 POV 来看,在你的情况下,我只会在你的节点上添加标签为 Food

的新标签

您似乎混淆了术语 label and property

根据您的图表,Person 似乎是您所有节点共享的 label,而 Name/Food//Meat/Vegetables/Cererals 似乎是节点的名称 属性.

如果我的理解是正确的,那么有很多方法可以处理多种食物类型的数量,并获得每人的总数。下面是几个例子。

  1. 这是一种方法。您可以为 unique 食物类型节点引入 Food 标签:

    (:Food {type: 'Meat'}), (:Food {type: 'Vegetable'}), etc.
    

    并且每个 Person 节点可以与每个相关的 Food 节点具有 HAS_FOOD 关系(具有 amount 属性)(而不是在内部存储食物类型属性):

    (john:Person {Name: 'John'})-[:HAS_FOOD {amount: 140}]->(meat:Food {type: 'Meat'})
    

    使用此数据模型,查找所有 Person 的食物超过 100 单位:

    MATCH (p:Person)-[r:HAS_FOOD]->()
    WITH p, SUM(r.amount) AS total
    WHERE total > 100
    RETURN p;
    
  2. 这是另一种方法(可能会加快搜索速度)。由于 neo4j 属性 不能有地图值(与您在问题底部附近的 JSON 中显示的相反),每个 Person 节点可以有 amountfood 数组,像这样:

    (:Person {Name: 'Fred', amount: [50, 100], food: ['Meat','Vegetables']})
    

    使用此数据模型,查找所有 Person 的食物超过 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]];
    
  3. 因此,这是解决您的问题并且更适合执行食品加工的第三种方法。每个 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;