问题
I'm starting to use neo4j.
In my graph database I've the nodes Person
(look at "John" below), with the labels: Name
(string), Food
(positive integer). Each Person
is connected with other Person
through the relationship isFriendTo
, that has a value.
I use the graph DB only to find the shortest weighted path between two person.
Also, every day I check every node in the graph, and if the food go under value of 100, I take some actions.
Now, after some improvements, the property Food
is no more enough for my project. So I have to split this in three other more specific property (positive integer): Vegetables
, Meat
and Cereals
. If the sum of both three go under 100, I've to take the same actions as before.
My old situation was as "John", the only options to which I can evolve my design is to "Fred" or "Paul"?
In which way can I design this? Should I use in addition to neo4j something like MongoDB to represent hierarchy?
Removing the property Food and add the three new properties seems like a bad practices to me. I've to save elsewhere that this 3 means "food"... and what about if in the future will I add others types of foods? Where do I store the knowledge that the value 100 to check, must come from the sum of Meat
, Vegetables
and Cereals
?
Having something like this, will solve my doubt because I can sum all items inside food
:
{
"name": "Lucas",
"food": {
"meat": 40,
"vegetables": 30,
"cereals": 0
}
}
(I don't need to traverse the connections from Food
and Vegetables
to Person
. Just need to check that the sum of Meat, Veg. and Cereals is lesser or greater 100.)
回答1:
It seems that you are confusing the terms label and property.
According to your diagram, Person
seems to be the label shared by all your nodes, and Name/Food//Meat/Vegetables/Cererals
seem to be the names of node properties.
If my understanding is correct, then there are many approaches to handling multiple food type amounts, and getting a total per person. Below are a couple of examples.
Here is one approach. You could introduce the
Food
label for unique food type nodes:(:Food {type: 'Meat'}), (:Food {type: 'Vegetable'}), etc.
and each
Person
node can have aHAS_FOOD
relationship (with anamount
property) to each relevant Food node (instead of storing the food type properties internally):(john:Person {Name: 'John'})-[:HAS_FOOD {amount: 140}]->(meat:Food {type: 'Meat'})
With this data model, to find all
Person
s with more than 100 units of food:MATCH (p:Person)-[r:HAS_FOOD]->() WITH p, SUM(r.amount) AS total WHERE total > 100 RETURN p;
Here is another approach (which will likely result in faster searches). Since a neo4j property cannot have a map value (contrary to what you show in the JSON near the bottom of your question), each
Person
node could haveamount
andfood
arrays, like this:(:Person {Name: 'Fred', amount: [50, 100], food: ['Meat','Vegetables']})
With this data model, to find all
Person
s with more than 100 units of food:MATCH (p:Person) WHERE REDUCE(s = 0, a IN p.amount | s + a) > 100 RETURN p;
[UPDATE]
However, doing food processing (nice pun, here), with second approach can be more cumbersome and less efficient. For example, this is one way to get the amount of Meat for
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;
And, to set the amount of Meat for
Fred
to 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]];
So, here is a 3rd approach that solves your issue AND is much better for performing food processing. Each Person node could store food amounts directly as properties, like this:
(:Person {Name: 'Fred', Meat: 50, Vegetables: 100, foodNames: ['Meat', 'Vegetables']})
With this data model, the
foodNames
array allows you to iterate through the food properties by name. So, to find all Persons with more than 100 units of food:MATCH (p:Person) WHERE REDUCE(s = 0, n IN p.foodNames | s + p[n]) > 100 RETURN p;
And, to get the amount of Meat for
Fred
:MATCH (p:Person {Name: 'Fred'}) RETURN p.Meat AS meatAmt;
To set the amount of Meat for
Fred
to 123:MATCH (p:Person {Name: 'Fred'}) SET p.Meat = 123;
回答2:
On Neo4j, labels are like tags, there is no technical hierarchy, and a node can has many labels.
But if you want to says that Food
is the parent of Vegetables
, Meat
and Cereals
from your business perspective, there is no prob. You will have a business/semantic hierarchy.
So from my POV, in your case I would only add the new labels on your nodes with the label Food
来源:https://stackoverflow.com/questions/51023869/hierarchy-property-in-graph-database