Skip to content

Feature: Support per-pair multiplicity in multi-FROM-TO CREATE REL TABLE declarations #517

@RP38

Description

@RP38

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
);

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions