DataSync data model

DataSync stores everything with two primitives: entities (objects) and relationships (links between two objects). Both are typed by classes. Users, channels, and memberships are built-in classes, so everything on this page applies to them too.

A class types each entity and relationship. In a wishlist example, the entity class Product v1 types the entity product-sneaker-42. The relationship class Wishlist v1 types the relationship wishlist-alice-sneaker, which links entity A (product-sneaker-42) to entity B (user-alice, an instance of User v1).

Entities​

A DataSync entity is a JSON object made up of system fields and a payload field that holds your application data. Every entity is an instance of exactly one entity class, and records which version of that class it currently pins to. The class name is fixed at creation, while the version can be changed later.

Don't confuse this with an SDK entity

An entity here is a stored server-side record. An SDK entity is a local handle you create on the client, with no stored state of its own.

The entity endpoints are Get entities, Get entity, Create entity, Update entity, Patch entity, and Delete entity.

System fields on every entity:

FieldPurpose
idThe entity's identifier
statusA free-form application lifecycle string, 1 to 100 characters
eTagOptimistic concurrency marker
createdAtCreation timestamp
updatedAtLast modification timestamp
expiresAtExpiry timestamp
entityClassThe name of the entity class this instance belongs to
entityClassVersionThe class version this instance pins to
entityClassLevelWhether that class is Global or SubKey (keyset-level)

The payload field sits alongside these system fields at the same level, not nested inside them. It is where your application data lives.

You can supply your own id on create, or let the server generate a UUID for you. An id must be 1 to 255 characters and can't contain whitespace, commas, colons, asterisks, forward slashes, backslashes, or control characters. It must be unique within the keyset: supplying an id that is already taken is rejected with a 409 Conflict.

An id becomes the name of the channel the object's events publish to, so the IDs you pick decide what a client can subscribe to. Periods are allowed, which means dot-namespaced IDs such as product.sneaker-42 can be followed as a group with Wildcard Subscribe.

Relationships​

A relationship is a typed link between two entities, entity A and entity B, with its own payload field. Every relationship is an instance of exactly one relationship class.

The relationship endpoints are Get relationships, Get relationship, Create relationship, Update relationship, Patch relationship, and Delete relationship.

System fields on every relationship:

FieldPurpose
idThe relationship's identifier
entityAIdThe linked entity A's ID, immutable after creation
entityBIdThe linked entity B's ID, immutable after creation
statusA free-form application lifecycle string, 1 to 100 characters
eTagOptimistic concurrency marker
createdAtCreation timestamp
updatedAtLast modification timestamp
expiresAtExpiry timestamp
relationshipClassThe name of the relationship class this instance belongs to
relationshipClassVersionThe class version this instance pins to
Memberships name their sides

A membership is a relationship, but it exposes its two sides as channelId and userId rather than the generic entityAId and entityBId. The channel is entity A and the user is entity B. Refer to Built-in types.

Relationship classes declare a cardinality, and DataSync enforces it when a relationship is created:

  • One-to-one: an entity can participate in at most one relationship instance of the class, on either side.
  • One-to-many: an entity is bound to whichever side it first appears on for that class. Once bound, it can appear on that same side any number of times, but never on the other side.
  • Many-to-many: no side constraints. The same entity can appear on either side any number of times. Only the exact ordered pair must be unique within the class.

A relationship that violates its class's cardinality is rejected with a 409 Conflict. Both linked entities must already exist, or the request is rejected with a 404 Not Found.

A relationship expires when the earlier of its two entities' expiry times arrives, computed once when the relationship is created.

Classes​

A class is a versioned type: a name plus an integer version. Multiple versions of the same class can coexist. Each instance records which version it currently pins to. You choose the version at creation, and you can move an instance to another existing version of the same class by setting entityClassVersion or relationshipClassVersion on an update or a partial update. The class name itself can't change.

Every class is described by two independent things: what it types (an entity or a relationship) and who owns it (PubNub or you). These are separate axes, so every class has one answer on each.

Entity classes and relationship classes​

  • An entity class types a stored object. Entity classes support inheritance, so a class you define can extend a Global class and add its own fields. Read one back with Get entity class by ID.
  • A relationship class types a link between two entities. Relationship classes declare a cardinality and can restrict which entity class is allowed on each side. They don't support inheritance. Read one back with Get relationship class by name and version.

Global classes and SubKey classes​

  • Global classes (entityClassLevel: Global) are seeded by PubNub and shared. The built-in User, Channel, and Membership classes are Global and can't be modified.
  • SubKey classes (entityClassLevel: SubKey), also called keyset-level classes, are the ones you define on your own keyset.
Global (PubNub-owned)SubKey (keyset-level, yours)
Entity classUser, ChannelYour custom classes
Relationship classMembershipYour custom classes

Classes are managed with the Admin API. Entity classes have their own endpoints: list, read, create, replace, and delete. Relationship classes have the same set: list, read, create, replace, and delete.

Built-in classes​

User and Channel are Global entity classes, and Membership is a Global relationship class linking a channel to a user. When you create a user, a channel, or a membership, the API sets the class for you. Refer to Built-in types for the operational details.

Payloads​

The payload is the value of the payload field on an entity or relationship. It is a free-form, arbitrarily nested JSON object holding your application data, stored as you send it. It must be a JSON object: an array or a scalar is rejected.

What is enforced comes from the class's property declarations. A value at a declared property path must be a scalar of the declared type, and a non-nullable property must be present. Violations return a 400. Fields you haven't declared are stored as-is and aren't checked.

Individual payload fields are addressable by JSON Pointer (RFC 6901). A pointer starts at the whole object, where payload sits alongside the system fields, so the price on a product entity is /payload/price. This same coordinate space is used for partial updates and for declaring class properties. Refer to Schemas and Data operations.

Declare the fields you filter on

Only payload fields declared as class properties with a filtering mode other than none are searchable. If you plan to filter or sort on a field, declare it as a property on the class first. Refer to Schemas.

Was this page useful?

Last updated on