13

Maybe I'm still thinking sql but I'm having trouble writing the datomic schema for a simple blog. I don't really understand the :db/cardinality attribute and what it means.

In terms of this type of system, how do we model these relationships

  • The system supports multiple users
  • Each user may have many categories
  • Each user may have many articles
  • Each category may have many users
  • Each category may have many articles
  • Each article may have many comments
  • Each comment has one user
zcaudate
  • 13,998
  • 7
  • 64
  • 124
  • `In terms of this type of system, how do we model these relationships` not sure if you expect someone to create the whole corresponding Datomic schema for you...? "many-to-many relationships" is self-explanatory, no need for a business-specific example – deprecated Feb 12 '13 at 15:00
  • ` I don't really understand the :db/cardinality attribute and what it means` then that is your real problem now. There are plenty of learning resources, from the docs to InfoQ presentations to https://github.com/Datomic/day-of-datomic – deprecated Feb 12 '13 at 15:06

2 Answers2

28

Look at the following diagram and read the full code sample (schema, sample data and queries) at https://gist.github.com/a2ndrade/5651419. It should help you understand how to model data in Datomic.

Datomic Schema: Blog

Querying

Note that some relationships are not explicitly modeled because relationships in Datomic are bidirectional and because you can retrieve the rest of the information using simple Datalog queries. For example, the query:

(d/q '[:find ?cid ?c
   :in $ ?u
   :where
   [?uid :user/username ?u]
   [?aid :article/category ?cid]
   [?aid :article/author ?uid]
   [?cid :category/name ?c]]
 (d/db conn) "john.smith")

finds all the category ids -and their names- that a user ("john.smith") has written articles for.

Containment Relationships

An important modeling decision is to have articles point to comments and mark the relationship as :db/isComponent since comments should not exist on their own but as part of an article. Datomic will make sure to retract all comments associated with an article if the article itself is retracted.

Enforcing Business Rules

If you want to enforce application-specific consistency rules (e.g. articles and comments must have a author, comments must be of certain length, etc) you need to use database functions. They run inside the transactor and can atomically enforce arbitrary constrains, aborting transactions that don't comply with them.

a2ndrade
  • 2,403
  • 21
  • 19
6

Let's first look at the simpler case where you have a one to many relationship between to kinds of entities (in your case users and comments):

user ---- * comment

You can choose to model this by letting each comment point to exactly one user, by means of an attribute, say :comment/user of type :db.type/ref.

This would be a natural model, as a comment could have at most one user. We say that the cardinality is at most 1, ie. the count of values (in this case references to users) cannot exceed 1.

This can be specified in the schema with :db/cardinality :db.cardinality/one, which is in fact the default, so we do not have to spell it out explicitly.

Note that since Datomic entities are not typed, it is not possible to enforce an actual cardinality of 1, ie. any attribute can be absent. (Entities have implicit types through their actual attibutes. The maintenance and interpretation of these is entirely up to your application)

If on the other hand you want any comment to be applicable to more than one user, you have a many-to-many relationship:

user * ---- * comment

This can be achieved by allowing the attribute :comment/user to be of :db/cardinality :db.cardinality/many, ie. by allowing multiple references from comments to users.

In this way each user can be referenced by multiple comments and each comment can reference multiple users.

You could equally opt to have references of cardinality many on the users instead of on the comments.

I hope this is sufficiently clear to help getting you started :)

clojureman
  • 421
  • 3
  • 4