Unique, cardinality-many attributes; why not?

I’d like to move a recent discussion in the Clojurians Slack to this forum for further thinking (and to avoid being lost).

As the title says, why are cardinality-many attributes not allowed to be unique? It does not appear to be a technical limitation, but a semantic choice. There is a very simple, minimal example of why cardinality-many unique attributes are completely natural semantically: user emails.

{:db/ident       :user/email
 :db/unique      :db.unique/identity
 :db/valueType   :db.type/string
 :db/cardinality :db.cardinality/many}

In my opinion, this schema definition perfectly matches reality. Real users have several email addresses (likely with different company domains) that all logically resolve to the same entity. Working around this uniqueness limitation requires introducing “joining entities” to express the same semantic, which I would argue brings with it a fair amount of accidental complexity.

Are there any plans to support unique, cardinality-many attributes, or has datomic made a firm semantic choice here?

As an aside, the above schema definition does in fact work on Datomic Cloud (tested on version 512-8806), however @marshall recommended against this use-case as it’s not technically supported.

2 Likes

Consider another example: URL slugs. It’s common to generate slugs which uniquely identify a team or organization, but we also would like to allow clients to change them. Obviously old URLs need to continue functioning, and so retracting the old slug does not seem appropriate. Semantically this feels like a unique-identity, card-many attribute.

As indicated in the documentation, cardinality-many :unique/identity attributes are not supported and we have no current plans to support them.

Thanks @marshall. I’d appreciate the opportunity for this to be reconsidered, or at least further discussed. I feel that my above use-cases are valid, and their solutions remain unsatisfying with the existing uniqueness limitations.

Technically speaking, it seems this could be constrained in the same way as multiple card-one-unique attributes in a single transaction. For card-many-unique attributes I can see three important cases:

  • None of the given values resolve to an entity (and so we create a new entity)
  • Some of the values resolve to a single entity, and the others do not resolve (we associate the unresolved values with this entity)
  • The values resolve to multiple entities (which results in a conflict, and the tx is aborted)

I recently added support for this to datascript, which is admittedly a completely separate project, but it does show off a possible implementation.