Entities

A Datomic entity provides a lazy, associative view of all the information that can be reached from a Datomic entity id.

The Entity interface provides associative access to

  • all the attribute/value pairs associated with an entity id E
  • all other entities reachable as values V from E
  • all other entities that can reach E through their values V

Nagivation to other entities is recursive, so with thoroughly connected data such a social network graph it may be possible to navigate an entire dataset starting with a single entity.

The Entity interface is completely generic, and can navigate any and all Datomic data.

Basics

The examples below refer to the following example datoms concerning entity 42, a.k.a. Jane Doe.

entities-basics.png

The Database.entity method will return an Entity, given an entity id or lookup ref:

jane = db.entity(42)

Given an entity, you can then navigate to any of that entity's attributes:

// returns "Jane"
firstName = jane.get(":person/firstName")

The special attribute :db/id provides access to the entity's own id:

// returns 42
id = jane.get(":db/id")

If an attribute points to another entity through a cardinality-one attribute, get will return another Entity instance:

address = jane.get(":person/address")

If an attributes points to another entity through a cardinality-many attribute, get will return a Set of entity instances. The following example returns all the entities that Jane likes:

// empty, given the example data
peopleJaneLikes = jane.get(":person/likes")

If you precede an attribute's local name with an underscore (_), get will navigate backwards, returning the Set of entities that point to the current entity. The following example returns all the entities who like Jane:

// returns set containing John
peopleWhoLikeJane = jane.get(":person/_likes")

Entities do not "exist" in any particular place; they are merely an associative view of all the datoms about some E at a point in time. If there are no facts currently available about an entity, Database.entity will return an empty entity, having only a :db/id.

Laziness and Caching

Entity attributes are accessed lazily as you request them. Once you access a particular attribute, the attribute will be cached for the lifetime of that entity instance.

The Entity.keySet method will return the keys available for an entity, without caching their values.

The Entity.touch method is a convenience that will access and cache all the attributes of an entity. In addition, touch will also access and cache the attributes of any other entities reachable through an attribute whose schema definition includes :isComponent true.

As an example, consider a database of orders, line items, and products. A line item is a component of an order. A product is related to an order, but is not a component of the order. In such a database, touching an order will access the order's attributes, and recursively access all the order's line items. Touching an order will not access the line items' products.

The touch method is a convenience, suitable for interactive exploration, or for when you know that you will need all of an entity's information. When this is not the case, it can be more efficient to be explicit, and ask for precisely what you need.

Note that none of the entity APIs mandate any communication with a server. Entities are lazy values, backed by a lazy database value in an application process. In particular, entities are not subject to the N+1 select problem, which is an artifact of client/server query architectures.

Entities and Time

Entities know the point in time on which they are based. Given an entity, you can access the database on which the entity is based

db = entity.db()

From the database you can then find the database's basisT, the transactions that contributed to the entity, etc.

Entities represent only a point in time, not the database across time. Notice that the "across time" aspect of the database vanishes when making entities.

entities-time.png

The point-in-time nature of entities means that entities can be derived from any point-in-time view of a database, i.e. the return values of Connection.db, Database.asOf, Database.since, and Database.with.

Entities cannot be derived from with any time-spanning view of a database, i.e. the return value of Database.history.

Usage Considerations

Entities are always consistent.

Entities are lazy, immutable values. They are thread safe and do not require coordination with any other concurrent uses of the same information.

Entities are based on a point in time of the database. Navigation from an entity will always and only reach other entities with that same time basis, allowing extended calculations to be performed without coordination.

Entities are not a mapping layer between databases and application code. Entities are a direct, mechanical translation from database information to associative application access.

Entities are not suitable for existence tests, which should typically be performed via lookup of a unique identity.