API
Python
Description
Summary
Allow the multiplicity constraint (ONE_ONE, ONE_MANY, MANY_ONE, MANY_MANY) to be specified per FROM ... TO ... clause when a REL table declares multiple node-pair connections, rather than once for the whole table.
Use case
When a single logical relationship spans multiple node-pair combinations, the natural cardinality often differs per pair. For example, a Located relationship might be:
User → City: many-to-one (a user lives in exactly one city)
Shop → City: many-to-many (a shop chain has shops in many cities)
Event → City: one-to-one (an event happens in exactly one city, and a city hosts at most one such event at a time)
Today these can't be modeled as a single REL table with the right constraints — the multiplicity slot applies uniformly to all pairs.
Current behavior
The grammar's kU_FromToConnection rule accepts only FROM <name> TO <name> with no trailing modifiers, and the constraint slot in kU_CreateRelTable lives once at the table level, after all properties. Attempting per-pair multiplicity fails to parse:
CREATE REL TABLE Located(
FROM User TO City MANY_ONE,
FROM Shop TO City MANY_MANY,
since DATE
);
→ Parser exception extraneous input 'ONE_ONE' expecting {')', ','} (line: 1, offset: 40).
Workaround
Declaring separate REL tables (Located_UserCity, Located_ShopCity, …) works for the constraints, but the cost is real:
- Table names must be unique, so there's no shared label.
- Queries that should target "any Located edge" need either an enumerated list or a
label(r) IN [...] / label(r) STARTS WITH '…' filter.
- The naming-convention discipline lives in application code rather than the schema.
Why this should be tractable
A multi-FROM-TO REL table is already expanded internally into one child rel table per pair, and multiplicity already drives the physical storage layout at the child level (x_to_one → columnar, x_to_many → rel-list, per the storage internals notes inherited from Kuzu). So the engine can already carry a different multiplicity per child; the gap is purely at the grammar / DDL surface.
Proposed syntax
Extend kU_FromToConnection to optionally accept a trailing multiplicity keyword:
kU_FromToConnection
: FROM SP oC_SchemaName SP TO SP oC_SchemaName ( SP kU_Multiplicity )? ;
Behavior:
- If per-pair multiplicity is specified on any pair, it wins for that child table.
- The existing table-level multiplicity slot remains as a default applied to any pair that didn't specify one (backward-compatible — existing schemas keep working unchanged).
- Mixing both (table-level default + per-pair overrides) should be allowed.
Example:
CREATE REL TABLE Located(
FROM User TO City MANY_ONE,
FROM Shop TO City MANY_MANY,
FROM Event TO City ONE_ONE,
since DATE
);
API
Python
Description
Summary
Allow the multiplicity constraint (
ONE_ONE,ONE_MANY,MANY_ONE,MANY_MANY) to be specified perFROM ... TO ...clause when a REL table declares multiple node-pair connections, rather than once for the whole table.Use case
When a single logical relationship spans multiple node-pair combinations, the natural cardinality often differs per pair. For example, a
Locatedrelationship might be:User → City: many-to-one (a user lives in exactly one city)Shop → City: many-to-many (a shop chain has shops in many cities)Event → City: one-to-one (an event happens in exactly one city, and a city hosts at most one such event at a time)Today these can't be modeled as a single REL table with the right constraints — the multiplicity slot applies uniformly to all pairs.
Current behavior
The grammar's
kU_FromToConnectionrule accepts onlyFROM <name> TO <name>with no trailing modifiers, and the constraint slot inkU_CreateRelTablelives once at the table level, after all properties. Attempting per-pair multiplicity fails to parse:→ Parser exception
extraneous input 'ONE_ONE' expecting {')', ','} (line: 1, offset: 40).Workaround
Declaring separate REL tables (
Located_UserCity,Located_ShopCity, …) works for the constraints, but the cost is real:label(r) IN [...]/label(r) STARTS WITH '…'filter.Why this should be tractable
A multi-FROM-TO REL table is already expanded internally into one child rel table per pair, and multiplicity already drives the physical storage layout at the child level (
x_to_one→ columnar,x_to_many→ rel-list, per the storage internals notes inherited from Kuzu). So the engine can already carry a different multiplicity per child; the gap is purely at the grammar / DDL surface.Proposed syntax
Extend
kU_FromToConnectionto optionally accept a trailing multiplicity keyword:Behavior:
Example: