The aim of this page is to create a knowledge management software using the graph database neo4j (and its query language Cypher).
The approach is object-orientated, i.e. the nodes are classes and instances.
Class nodes are violet. Instance nodes are brown. Subclass arrows are red. Instance arrows are gray.
Knowledge is represented by questions and answers.
Questions come from classes, and they are shown in blue. Answers typically come from instances, and they are shown in green.
There are two questions, that connect the classes car and person:
Who is responsible for the car? There must be two people responsible for each car. One person can be responsible for up to five cars.
Who is the driver? There might be no driver. If there is one, it must be exactly one person, who drives exactly one car.
The cardinalities are not yet part of the code. The aim is, that the software will highlight unanswered questions for a given node.
If the question "Who is responsible for Dodge?" were answered only by "Alice", the question for a second responsible person would still be highlighted.
(The question for the driver would be shown less prominently, because a driver is not required.)
diagram
browser version
create
// class nodesCREATE(Car:Class{name:'car'})CREATE(Person:Class{name:'person'})// instance nodesCREATE(Alice:Inst{name:'Alice'})CREATE(Bob:Inst{name:'Bob'})CREATE(Chris:Inst{name:'Chris'})CREATE(Dodge:Inst{name:'Dodge'})CREATE// instance relations(Dodge)-[:INST]->(Car),(Alice)-[:INST]->(Person),(Bob)-[:INST]->(Person),(Chris)-[:INST]->(Person),// questions(Car)-[:QUEST{name:'responsible'}]->(Person),(Car)-[:QUEST{name:'driver'}]->(Person),// answers(Dodge)-[:ANS{quest:'responsible'}]->(Alice),(Dodge)-[:ANS{quest:'responsible'}]->(Bob),(Dodge)-[:ANS{quest:'driver'}]->(Chris);
All questions are asked by classes, and all answers are given by instances.
diagram
browser version
create
// class nodesCREATE(City:Class{name:'city'})CREATE(Country:Class{name:'country'})CREATE(Sea:Class{name:'sea'})CREATE(PortCity:Class{name:'port city'})// instance nodesCREATE(LaRochelle:Inst{name:'La Rochelle'})CREATE(Marseille:Inst{name:'Marseille'})CREATE(France:Inst{name:'France'})CREATE(Atlantic:Inst{name:'Atlantic'})CREATE(Mediterranean:Inst{name:'Mediterranean'})CREATE// subclass relations(PortCity)-[:SUB]->(City),// instance relations(LaRochelle)-[:INST]->(PortCity),(Marseille)-[:INST]->(PortCity),(France)-[:INST]->(Country),(Atlantic)-[:INST]->(Sea),(Mediterranean)-[:INST]->(Sea),// questions(City)-[:QUEST{name:'city part of country'}]->(Country),(Country)-[:QUEST{name:'country access to sea'}]->(Sea),(Country)-[:QUEST{name:'country borders country'}]->(Country),(PortCity)-[:QUEST{name:'port city access to sea'}]->(Sea),// answers(LaRochelle)-[:ANS{quest:'city part of country'}]->(France),(Marseille)-[:ANS{quest:'city part of country'}]->(France),(LaRochelle)-[:ANS{quest:'port city access to sea'}]->(Atlantic),(Marseille)-[:ANS{quest:'port city access to sea'}]->(Mediterranean),(France)-[:ANS{quest:'country access to sea'}]->(Atlantic),(France)-[:ANS{quest:'country access to sea'}]->(Mediterranean);
Now the class French port city is introduced. It answers the question asked by city. The instances inherit the answer.
diagram
browser version
create
// class nodesCREATE(City:Class{name:'city'})CREATE(Country:Class{name:'country'})CREATE(Sea:Class{name:'sea'})CREATE(PortCity:Class{name:'port city'})CREATE(FrenchPortCity:Class{name:'French port city'})// instance nodesCREATE(LaRochelle:Inst{name:'La Rochelle'})CREATE(Marseille:Inst{name:'Marseille'})CREATE(France:Inst{name:'France'})CREATE(Atlantic:Inst{name:'Atlantic'})CREATE(Mediterranean:Inst{name:'Mediterranean'})CREATE// subclass relations(PortCity)-[:SUB]->(City),(FrenchPortCity)-[:SUB]->(PortCity),// instance relations(LaRochelle)-[:INST]->(FrenchPortCity),(Marseille)-[:INST]->(FrenchPortCity),(France)-[:INST]->(Country),(Atlantic)-[:INST]->(Sea),(Mediterranean)-[:INST]->(Sea),// questions(City)-[:QUEST{name:'city part of country'}]->(Country),(Country)-[:QUEST{name:'country access to sea'}]->(Sea),(Country)-[:QUEST{name:'country borders country'}]->(Country),(PortCity)-[:QUEST{name:'port city access to sea'}]->(Sea),// answers(FrenchPortCity)-[:ANS{quest:'city part of country'}]->(France),(LaRochelle)-[:ANS{quest:'port city access to sea'}]->(Atlantic),(Marseille)-[:ANS{quest:'port city access to sea'}]->(Mediterranean),(France)-[:ANS{quest:'country access to sea'}]->(Atlantic),(France)-[:ANS{quest:'country access to sea'}]->(Mediterranean);
╒═════════════════════════╕
│quest.name │
╞═════════════════════════╡
│"port city access to sea"│
├─────────────────────────┤
│"city part of country" │
└─────────────────────────┘
╒═════════════════════════╤═══════════════╕
│ans.quest │ansInst.name │
╞═════════════════════════╪═══════════════╡
│"port city access to sea"│"Mediterranean"│
├─────────────────────────┼───────────────┤
│"city part of country" │"France" │
└─────────────────────────┴───────────────┘
The Q&A are reified into nodes. This allows an explicit relationship between them, shown by yellow arrows.
This will also allow questions connected to more than two classes – or to only one.
// class nodesCREATE(City:Class{name:'city'})CREATE(Country:Class{name:'country'})CREATE(Sea:Class{name:'sea'})CREATE(PortCity:Class{name:'port city'})// instance nodesCREATE(LaRochelle:Inst{name:'La Rochelle'})CREATE(Marseille:Inst{name:'Marseille'})CREATE(France:Inst{name:'France'})CREATE(Atlantic:Inst{name:'Atlantic'})CREATE(Mediterranean:Inst{name:'Mediterranean'})// subclass relationsCREATE(PortCity)-[:SUB]->(City)// instance relationsCREATE(LaRochelle)-[:INST]->(PortCity)CREATE(Marseille)-[:INST]->(PortCity)CREATE(France)-[:INST]->(Country)CREATE(Atlantic)-[:INST]->(Sea)CREATE(Mediterranean)-[:INST]->(Sea)/////////////////////////////////////////////////////////////////////////////// question nodesCREATE(CityPartCountry:Quest{name:'city part of country'})CREATE(CountryAccessSea:Quest{name:'country access to sea'})CREATE(CountryBordersCountry:Quest{name:'country borders country'})CREATE(PortCityAccessSea:Quest{name:'port city access to sea'})// question relationsCREATE(CityPartCountry)-[:QUEST{name:'city'}]->(City)CREATE(CityPartCountry)-[:QUEST{name:'country'}]->(Country)CREATE(CountryAccessSea)-[:QUEST{name:'country'}]->(Country)CREATE(CountryAccessSea)-[:QUEST{name:'sea'}]->(Sea)CREATE(CountryBordersCountry)-[:QUEST{name:'country'}]->(Country)CREATE(CountryBordersCountry)-[:QUEST{name:'country'}]->(Country)CREATE(PortCityAccessSea)-[:QUEST{name:'port city'}]->(PortCity)CREATE(PortCityAccessSea)-[:QUEST{name:'sea'}]->(Sea)
questions and answers
diagram
create
The class, instance and question nodes have been created with the query above.
This one fetches the instance and question nodes with MATCH, and creates everything related to the answers.
// instance nodesMATCH(LaRochelle:Inst{name:'La Rochelle'})MATCH(Marseille:Inst{name:'Marseille'})MATCH(France:Inst{name:'France'})MATCH(Atlantic:Inst{name:'Atlantic'})MATCH(Mediterranean:Inst{name:'Mediterranean'})// question nodesMATCH(CityPartCountry:Quest{name:'city part of country'})MATCH(CountryAccessSea:Quest{name:'country access to sea'})MATCH(CountryBordersCountry:Quest{name:'country borders country'})MATCH(PortCityAccessSea:Quest{name:'port city access to sea'})/////////////////////////////////////////////////////////////////////////////// answer nodesCREATE(LaRochellePartFrance:Ans)CREATE(MarseillePartFrance:Ans)CREATE(LaRochelleAccessAtlantic:Ans)CREATE(MarseilleAccessMediterranean:Ans)CREATE(FranceAccessAtlantic:Ans)CREATE(FranceAccessMediterranean:Ans)// answer question relationsCREATE(LaRochellePartFrance)-[:AQ]->(CityPartCountry)CREATE(MarseillePartFrance)-[:AQ]->(CityPartCountry)CREATE(FranceAccessAtlantic)-[:AQ]->(CountryAccessSea)CREATE(FranceAccessMediterranean)-[:AQ]->(CountryAccessSea)CREATE(LaRochelleAccessAtlantic)-[:AQ]->(PortCityAccessSea)CREATE(MarseilleAccessMediterranean)-[:AQ]->(PortCityAccessSea)// answer relationsCREATE(LaRochellePartFrance)-[:ANS{name:'city'}]->(LaRochelle)CREATE(LaRochellePartFrance)-[:ANS{name:'country'}]->(France)CREATE(MarseillePartFrance)-[:ANS{name:'city'}]->(Marseille)CREATE(MarseillePartFrance)-[:ANS{name:'country'}]->(France)CREATE(FranceAccessAtlantic)-[:ANS{name:'country'}]->(France)CREATE(FranceAccessAtlantic)-[:ANS{name:'sea'}]->(Atlantic)CREATE(FranceAccessMediterranean)-[:ANS{name:'country'}]->(France)CREATE(FranceAccessMediterranean)-[:ANS{name:'sea'}]->(Mediterranean)CREATE(LaRochelleAccessAtlantic)-[:ANS{name:'port city'}]->(LaRochelle)CREATE(LaRochelleAccessAtlantic)-[:ANS{name:'sea'}]->(Atlantic)CREATE(MarseilleAccessMediterranean)-[:ANS{name:'port city'}]->(Marseille)CREATE(MarseilleAccessMediterranean)-[:ANS{name:'sea'}]->(Mediterranean)
And here the subclass French port city is added:
diagram
create
// class nodesCREATE(City:Class{name:'city'})CREATE(Country:Class{name:'country'})CREATE(Sea:Class{name:'sea'})CREATE(PortCity:Class{name:'port city'})CREATE(FrenchPortCity:Class{name:'French port city'})// instance nodesCREATE(LaRochelle:Inst{name:'La Rochelle'})CREATE(Marseille:Inst{name:'Marseille'})CREATE(France:Inst{name:'France'})CREATE(Atlantic:Inst{name:'Atlantic'})CREATE(Mediterranean:Inst{name:'Mediterranean'})// subclass relationsCREATE(PortCity)-[:SUB]->(City)CREATE(FrenchPortCity)-[:SUB]->(PortCity)// instance relationsCREATE(LaRochelle)-[:INST]->(FrenchPortCity)CREATE(Marseille)-[:INST]->(FrenchPortCity)CREATE(France)-[:INST]->(Country)CREATE(Atlantic)-[:INST]->(Sea)CREATE(Mediterranean)-[:INST]->(Sea)/////////////////////////////////////////////////////////////////////////////// question nodesCREATE(CityPartCountry:Quest{name:'city part of country'})CREATE(CountryAccessSea:Quest{name:'country access to sea'})CREATE(CountryBordersCountry:Quest{name:'country borders country'})CREATE(PortCityAccessSea:Quest{name:'port city access to sea'})// answer nodesCREATE(FrenchPortCityPartFrance:Ans)CREATE(LaRochelleAccessAtlantic:Ans)CREATE(MarseilleAccessMediterranean:Ans)CREATE(FranceAccessAtlantic:Ans)CREATE(FranceAccessMediterranean:Ans)// question relationsCREATE(CityPartCountry)-[:QUEST{name:'city'}]->(City)CREATE(CityPartCountry)-[:QUEST{name:'country'}]->(Country)CREATE(CountryAccessSea)-[:QUEST{name:'country'}]->(Country)CREATE(CountryAccessSea)-[:QUEST{name:'sea'}]->(Sea)CREATE(CountryBordersCountry)-[:QUEST{name:'country'}]->(Country)CREATE(CountryBordersCountry)-[:QUEST{name:'country'}]->(Country)CREATE(PortCityAccessSea)-[:QUEST{name:'port city'}]->(PortCity)CREATE(PortCityAccessSea)-[:QUEST{name:'sea'}]->(Sea)// answer question relationsCREATE(FrenchPortCityPartFrance)-[:AQ]->(CityPartCountry)CREATE(FranceAccessAtlantic)-[:AQ]->(CountryAccessSea)CREATE(FranceAccessMediterranean)-[:AQ]->(CountryAccessSea)CREATE(LaRochelleAccessAtlantic)-[:AQ]->(PortCityAccessSea)CREATE(MarseilleAccessMediterranean)-[:AQ]->(PortCityAccessSea)// answer relationsCREATE(FrenchPortCityPartFrance)-[:ANS{name:'city'}]->(FrenchPortCity)CREATE(FrenchPortCityPartFrance)-[:ANS{name:'country'}]->(France)CREATE(FranceAccessAtlantic)-[:ANS{name:'country'}]->(France)CREATE(FranceAccessAtlantic)-[:ANS{name:'sea'}]->(Atlantic)CREATE(FranceAccessMediterranean)-[:ANS{name:'country'}]->(France)CREATE(FranceAccessMediterranean)-[:ANS{name:'sea'}]->(Mediterranean)CREATE(LaRochelleAccessAtlantic)-[:ANS{name:'port city'}]->(LaRochelle)CREATE(LaRochelleAccessAtlantic)-[:ANS{name:'sea'}]->(Atlantic)CREATE(MarseilleAccessMediterranean)-[:ANS{name:'port city'}]->(Marseille)CREATE(MarseilleAccessMediterranean)-[:ANS{name:'sea'}]->(Mediterranean)
╒═════════════════════════╕
│quest.name │
╞═════════════════════════╡
│"port city access to sea"│
├─────────────────────────┤
│"city part of country" │
└─────────────────────────┘
╒═════════════════════════╤═══════════════╕
│questNode.name │farNode.name │
╞═════════════════════════╪═══════════════╡
│"port city access to sea"│"Mediterranean"│
├─────────────────────────┼───────────────┤
│"city part of country" │"France" │
└─────────────────────────┴───────────────┘