-
-
Notifications
You must be signed in to change notification settings - Fork 7
Discourse
The simplest case for using Aspen is a simple relationship between two people or things, like:
Liz knows Jack.
Aspen doesn't know which of these are nodes and which are edges, so we have to tell it by adding parentheses ()
to indicate nodes and square brackets []
to indicate edges. These conventions are intentionally the same as Cypher.
(Liz) [knows] (Jack).
Let's think about what we can conclude from this statement:
- The strings of text
"Liz"
and"Jack"
are names. - Liz and Jack are people, so they should have a
:Person
label in Cypher. - If Liz knows Jack, Jack knows Liz as well, so the relationship "knows" is reciprocal.
However, Aspen doesn't know any of this automatically.
So, we need to tell Aspen:
- What attribute to assign the text
"Liz"
and"Jack"
(name
) - What kind of labels to apply to the nodes (
Person
) - That the relationship "knows" is a reciprocal (aka mutual) relationship
A label is the type or class of object: a Person, an Object, a Car, etc. See Neo4j documentation on Node Labels.
First, we need to tell Aspen that, when it encounters a simple node like (Liz)
, that it should assume it's a person. (If we don't, it's given the abstract label Entity
.)
# Discourse
default:
label: Person
By default, Aspen assumes that the given attribute is a name
. If we wanted to make it first_name
, we'd write:
# Discourse
default:
label: Person
attribute: first_name
When we run this, we get this Cypher:
MERGE (person_liz:Person { first_name: "Liz" })
MERGE (person_jack:Person { first_name: "Jack" })
MERGE (person_liz)-[:KNOWS]->(person_jack)
Nodes can be labeled or unlabeled. Unlabeled nodes are given the default label, whereas labeled nodes keep their labels.
Unlabeled node: (Liz)
Labeled node: (Show: TGS with Tracy Jordan)
Let's take a look at a narrative with labeled nodes.
(Jack) [works at] (Employer: Kabletown)
(Liz) [works at] (Show: TGS with Tracy Jordan)
Given the following line, what attributes should Kabletown
and TGS with Tracy Jordan
be set to?
Right now, they would both be assigned to name
. If we want to set Kabletown
as company_name
and TGS
as the show's title
, we add some more to the discourse.
default:
# ... lines omitted
attributes:
Employer: company_name
Show: title
Take a look at the arrow—it's pointing from Liz to Jack, suggesting that Liz knows Jack but Jack doesn't know Liz.
However, we want the relationship "knows" to be reciprocal, because if Liz knows Jack, we can assume that Jack knows Liz.
A note on reciprocal relationships:
In Neo4j, the convention is for reciprocal (also known as bidirectional or undirected) relationships to be represented by a directional relationship. Read more at GraphAware.
However, we want our resulting Cypher to show a reciprocal relationship so we can read the Cypher and understand the intent of the code.
If we wanted to show that we intend to create a reciprocal relationship, we'd write Cypher for "Liz knows Jack" as follows. Notice that there's no arrowhead—the relationship is "undirected".
MERGE (person_liz)-[:KNOWS]-(person_jack)
To get this reciprocality in Aspen, we list all the reciprocal relationships after the keyword reciprocal
or mutual
:
# Discourse
default:
label: Person
reciprocal: knows # this would also work with `mutual: knows`
----
# Narrative
(Liz) [knows] (Jack).
MERGE (person_liz:Person { first_name: "Liz" })
MERGE (person_jack:Person { first_name: "Jack" })
MERGE (person_liz)-[:KNOWS]-(person_jack) # Note the absence of pointed arrow caps (e.g. "->")
NOTE: Cypher renders a mutual relationship as undirected, which doesn't meaningfully affect structure or performance, but it's worth noting that other formats differentiate between mutual and undirected relationships. In those formats, mutual relationships point in
2
directions, while Cypher's undirected relationships point innull
or0
directions.
If we had multiple reciprocal relationships, we'd write:
reciprocal: knows, is friends with, is married to
This would ensure that [:KNOWS]
, [:IS_FRIENDS_WITH]
, and [:IS_MARRIED_TO]
would all be encoded as reciprocal/undirected relationships.
Writing freeform text using Aspen can easily lead to typos, and typos in your graph model are no fun.
Example: It would be really easy to write a node using the label
Persno
and not catch it. When you go to query your data forPerson
nodes, the one with thePersno
label won't even show up.
So, Aspen provides Protections that catches typos in node labels and edge names.
For example, to ensure that you can only have Person
and Employer
nodes, and that the only relationships allowed are knows
and works at
, write the following in your discourse.
allow_only:
nodes: Person, Employer
edges: knows, works at
You can write longer protection lists using list items, like this:
allow_only:
nodes:
- Person
- Employer
edges:
- knows
- works at
If you make a typo in your narrative, or try to use a label that isn't Person
or Employer
, you'll get an error.
- Installation
- Parts of an Aspen project
- Parts of an Aspen file
- Adding Data from other sources and in different formats
- Command Line Guide
- Design Philosophy: To come