Label inheritance#
A label can inherit from another label. The child is a separate label with its own id sequence and its own rows, but a pattern that names the parent also matches the child’s rows. That gives you a type hierarchy without giving up the ability to query the whole family in one pattern.
Inheritance is PostgreSQL table inheritance — a label is a table,
and INHERITS on a label becomes INHERITS on that table.
Declaring a hierarchy#
The examples on this page build their own small graph rather than using
the example dataset, because inheritance is easier to
see on a hierarchy than on films and people. Run DROP GRAPH inh CASCADE; when you are done
with it.
CREATE GRAPH inh;
SET graph_path = inh;
CREATE VLABEL work;
CREATE VLABEL film INHERITS (work);
CREATE VLABEL series INHERITS (work);
CREATE (:film {title: 'A'}), (:series {title: 'B'}), (:work {title: 'C'});
Naming the parent matches every row in the family:
MATCH (n:work) RETURN label(n) AS label, n.title AS t ORDER BY t;
label | t
----------+-----
"film" | "A"
"series" | "B"
"work" | "C"
Naming a child matches only that child:
MATCH (n:film) RETURN label(n) AS label, n.title AS t;
label | t
--------+-----
"film" | "A"
Note what label(n) returns: the label the vertex was created with, never the parent. A
vertex belongs to exactly one label. “Matching the parent” is about which tables the pattern
scans, not about a vertex having two labels.
ONLY: excluding the children#
ONLY restricts a pattern to the named label itself:
MATCH (n:work ONLY) RETURN label(n) AS label, n.title AS t;
label | t
--------+-----
"work" | "C"
It works on edges too, and is written inside the brackets:
MATCH ()-[e:rel ONLY]->() RETURN count(e) AS n;
n
---
0
That zero is the useful case: rel has a child, sequel, and all the edges were created as
sequel, so rel ONLY matches none of them while plain rel matches all of them.
Edges inherit the same way#
CREATE ELABEL rel;
CREATE ELABEL sequel INHERITS (rel);
MATCH (a:film {title: 'A'}), (b:series {title: 'B'}) CREATE (a)-[:sequel]->(b);
MATCH ()-[e:rel]->() RETURN label(e) AS label;
label
----------
"sequel"
More than one parent#
A label may inherit from several labels at once:
CREATE VLABEL hybrid INHERITS (work, film);
The rows of hybrid are then matched by MATCH (n:work) and by MATCH (n:film).
What every graph already inherits#
Every label you create inherits from ag_vertex or ag_edge, even when you write no
INHERITS clause. That is why an unlabelled MATCH (n) matches everything. See
How a graph is stored.
What it costs#
A pattern on a parent scans the parent’s table and every table below it, so the plan grows
an Append over the family:
EXPLAIN (COSTS OFF) MATCH (n:work) RETURN n;
QUERY PLAN
------------------------------------
Result
-> Append
-> Seq Scan on work n_1
-> Seq Scan on film n_2
-> Seq Scan on series n_3
-> Seq Scan on hybrid n_4
ONLY takes the Append away:
EXPLAIN (COSTS OFF) MATCH (n:work ONLY) RETURN n;
QUERY PLAN
--------------------
Seq Scan on work n
Two things follow. A deep hierarchy makes the cheapest possible query, “give me the rows of
one label”, plan over more relations than it needs. So name the child, or add ONLY, when
you want one label’s rows and no others. And an index has to exist on each table that will be scanned: an
index on work does not index film’s rows. Indexes and
constraints covers building an index across a
hierarchy.
Note
Reach for inheritance when a query genuinely wants the whole family — a work that is
sometimes a film and sometimes a series. If every query names the child anyway, separate
labels are simpler and plan better.