Description
Currently AshNeo4j writes up to 3 labels on CREATE: [domain_label, module_label, label]. For a resource extending a fragment the label is the fragment's base type (e.g. :Relationship), giving [:Provider, :DefinedSimpleRelationship, :Relationship].
Neo4j natively optimises up to 4 labels. We want to use the 4th slot for a domain-level category label, derived from a domain fragment module name — the same convention used for module_label on resources.
defmodule Telco do
use Spark.Dsl.Fragment, of: Ash.Domain
# no DSL blocks needed — the module name is the label
end
defmodule Provider do
use Ash.Domain, fragments: [Telco]
end
Telco → :Telco. No explicit declaration required. Consistent with how every other label in the system is derived from its module name.
The resource fragment continues to declare its base type label explicitly (because the module name and the type label can diverge — RelationshipBase vs :Relationship):
defmodule RelationshipBase do
use Spark.Dsl.Fragment, of: Ash.Resource, data_layer: AshNeo4j.DataLayer
neo4j do
label :Relationship
end
end
With both fragments in place, a node for Provider.DefinedSimpleRelationship gets all 4 labels on CREATE:
[:Provider, :DefinedSimpleRelationship, :Relationship, :Telco]
What we need
- Detect domain fragments on the resource's domain at compile time (in a persister)
- Derive the category label from the fragment module's short name
- Write it as a 4th label on CREATE alongside the existing three
- No new DSL blocks required on the domain fragment itself
Why it matters
Writing all available labels gives the graph multiple valid domain:resource pairs. Direct graph queries can traverse by base type ([:Telco, :Relationship]) while the data layer always reads using the most concrete pair ([:Provider, :DefinedSimpleRelationship]). The graph is semantically rich rather than shaped by framework limitations.
Neo4j natively optimises up to 4 labels, so this design fits the storage model exactly.
Description
Currently AshNeo4j writes up to 3 labels on CREATE:
[domain_label, module_label, label]. For a resource extending a fragment the label is the fragment's base type (e.g.:Relationship), giving[:Provider, :DefinedSimpleRelationship, :Relationship].Neo4j natively optimises up to 4 labels. We want to use the 4th slot for a domain-level category label, derived from a domain fragment module name — the same convention used for
module_labelon resources.Telco→:Telco. No explicit declaration required. Consistent with how every other label in the system is derived from its module name.The resource fragment continues to declare its base type label explicitly (because the module name and the type label can diverge —
RelationshipBasevs:Relationship):With both fragments in place, a node for
Provider.DefinedSimpleRelationshipgets all 4 labels on CREATE:[:Provider, :DefinedSimpleRelationship, :Relationship, :Telco]What we need
Why it matters
Writing all available labels gives the graph multiple valid
domain:resourcepairs. Direct graph queries can traverse by base type ([:Telco, :Relationship]) while the data layer always reads using the most concrete pair ([:Provider, :DefinedSimpleRelationship]). The graph is semantically rich rather than shaped by framework limitations.Neo4j natively optimises up to 4 labels, so this design fits the storage model exactly.