Вопрос, который иногда возникает при обсуждении моделирования графовых данных, заключается в том, как вы будете моделировать суб / супертипы .
По моему опыту, есть две причины, почему мы можем захотеть сделать это:
- Чтобы убедиться, что определенные свойства существуют на битах данных
- Чтобы написать детализацию запросов на основе этих типов
В настоящее время первый не встроен в Neo4j, и вы сможете достичь этого только путем подключения некоторого кода в обработчике события транзакции перед фиксацией, поэтому мы сосредоточимся на последнем.
Типичный пример, используемый для демонстрации того, как проектировать подтипы, — это царство животных, и мне удалось найти набор данных из Луизвилля, Службы животных Кентукки, который мы можем использовать.
В этом случае подтипы используются для представления типа животного, группы пород и породы. Затем у нас также есть «реальные данные» с точки зрения реальных собак, находящихся под присмотром животных.
В результате мы получаем два графика в одном — модель и метамодель:
Запрос шифра для создания этого графа выглядит так:
LOAD CSV WITH HEADERS FROM "file:/Users/markneedham/projects/neo4j-subtypes/data/dogs.csv" AS line MERGE (animalType:AnimalType {name: "Dog"}) MERGE (breedGroup:BreedGroup {name: line.BreedGroup}) MERGE (breed:Breed {name: line.PrimaryBreed}) MERGE (animal:Animal {id: line.TagIdentity, primaryColour: line.PrimaryColour, size: line.Size}) MERGE (animalType)<-[:PARENT]-(breedGroup) MERGE (breedGroup)<-[:PARENT]-(breed) MERGE (breed)<-[:PARENT]-(animal)
Затем мы могли бы написать простой запрос, чтобы узнать, сколько у нас собак:
MATCH (animalType:AnimalType)<-[:PARENT*]-(animal) RETURN animalType, COUNT(*) AS animals ORDER BY animals DESC
==> +--------------------------------+ ==> | animalType | animals | ==> +--------------------------------+ ==> | Node[89]{name:"Dog"} | 131 | ==> +--------------------------------+ ==> 1 row
Или мы могли бы написать немного более сложный запрос, чтобы найти количество животных на каждом уровне нашей иерархии типов:
MATCH path = (animalType:AnimalType)<-[:PARENT]-(breedGroup)<-[:PARENT*]-(animal) RETURN [node IN nodes(path) | node.name][..-1] AS breed, COUNT(*) AS animals ORDER BY animals DESC LIMIT 5
==> +-----------------------------------------------------+ ==> | breed | animals | ==> +-----------------------------------------------------+ ==> | ["Dog","SETTER/RETRIEVE","LABRADOR RETR"] | 15 | ==> | ["Dog","SETTER/RETRIEVE","GOLDEN RETR"] | 13 | ==> | ["Dog","POODLE","POODLE MIN"] | 10 | ==> | ["Dog","TERRIER","MIN PINSCHER"] | 9 | ==> | ["Dog","SHEPHERD","WELSH CORGI CAR"] | 6 | ==> +-----------------------------------------------------+ ==> 5 rows
Затем мы могли бы решить добавить подграф упражнения, который указывает, сколько упражнений требует каждый тип собаки:
MATCH (breedGroup:BreedGroup) WHERE breedGroup.name IN ["SETTER/RETRIEVE", "POODLE"] MERGE (exercise:Exercise {type: "2 hours hard exercise"}) MERGE (exercise)<-[:REQUIRES_EXERCISE]-(breedGroup);
MATCH (breedGroup:BreedGroup) WHERE breedGroup.name IN ["TERRIER", "SHEPHERD"] MERGE (exercise:Exercise {type: "1 hour gentle exercise"}) MERGE (exercise)<-[:REQUIRES_EXERCISE]-(breedGroup);
Затем мы можем запросить это, чтобы выяснить, какие собаки должны выйти на 2 часа тяжелых упражнений:
MATCH (exercise:Exercise {type: "2 hours hard exercise"})<-[:REQUIRES_EXERCISE]-()<-[:PARENT*]-(dog) WHERE NOT (dog)<-[:PARENT]-() RETURN dog LIMIT 10
==> +-----------------------------------------------------------+ ==> | dog | ==> +-----------------------------------------------------------+ ==> | Node[541]{id:"664427",primaryColour:"BLACK",size:"SMALL"} | ==> | Node[542]{id:"543787",primaryColour:"BLACK",size:"SMALL"} | ==> | Node[543]{id:"584021",primaryColour:"BLACK",size:"SMALL"} | ==> | Node[544]{id:"584022",primaryColour:"BLACK",size:"SMALL"} | ==> | Node[545]{id:"664430",primaryColour:"BLACK",size:"SMALL"} | ==> | Node[546]{id:"535176",primaryColour:"BLACK",size:"SMALL"} | ==> | Node[567]{id:"613557",primaryColour:"WHITE",size:"SMALL"} | ==> | Node[568]{id:"531376",primaryColour:"WHITE",size:"SMALL"} | ==> | Node[569]{id:"613567",primaryColour:"WHITE",size:"SMALL"} | ==> | Node[570]{id:"531379",primaryColour:"WHITE",size:"SMALL"} | ==> +-----------------------------------------------------------+ ==> 10 rows
В этом запросе мы удостоверились, что вернули только собак, а не породы, проверив, что нет никаких РОДИЛЬНЫХ отношений. В качестве альтернативы мы могли бы отфильтровать на этикетке животных …
MATCH (exercise:Exercise {type: "2 hours hard exercise"})<-[:REQUIRES_EXERCISE]-()<-[:PARENT*]-(dog:Animal) RETURN dog LIMIT 10
или если бы мы хотели выводить собак только для упражнений, возможно, у нас была бы метка Dog на соответствующих узлах.
Людям часто любопытно, почему у меток нет супер / подтипов между ними, но я склонен использовать метки для простой категоризации — что-нибудь более сложное, и мы можем также использовать встроенную мощь графовой модели!
Код на GitHub , если вы захотите играть с ним.