Essential Data Models

by Dimitrie Stefanescu
4 min read. Posted on Tue, Oct 3, 2017

The following goes hand in hand with the Schema section in the Speckle Api Docs.

Data Models

Speckle operates with two core data models, Objects and Streams. Alongside, we have UserAppClients (or Clients) and Users, but we will not cover them in this document.

Streams

Basics

Streams (DataStreams) are just a collection of objects. A stream has three important fields:

  • the streamId string
  • the objects array
  • the layers array

The streamId is a short, url friendly, id that the stream is known by. It is also the name of the websocket “room” where all announcements about the state/mutations of this stream happen.

The objects array is an ordered list of SpeckleObjects _ids (see below for a description of objects in speckle). It may contain duplicates and traces of nuts.

The optional layer array holds in a list of _SpeckleLayer_s.

// Voila a layer:
{
  "name": "string",
  "guid": "string",
  "orderIndex": 0,
  "startIndex": 0,
  "objectCount": 0,
  "topology": "0;0;0;0-2 0;0;0;1-2",
  "properties": {
    //...
  }
} 

A layer’s startIndex and objectCount determine, against the stream’s object list, which object is on what layer.

Inheritance & Versioning

Furthermore, streams may have a parent and an array of children. These enable two versioning models, for example:

  • A Sender Client (A) can save its current state as an option (A1). Subsequent updates will continue to be logged at A, and not against his children (A1, A2, … An).
  • A Sender Client (A) can clone himself into (A1) and log all subsequent updates to the newly created stream (A1).

In both scenarios, Stream A will have A1 in his children array, and A1 will have A as his parent.

The corresponding API method is StreamClone.

Objects

Speckle objects all inherit from a base model, the SpeckleObject, which enforces a few default properties, namely:

  • Type
  • Hash
  • ApplicationId
  • Properties […]

Object Type

The type field is the discriminator. It determines which subtype the specific instance is part of. It is enforced on the schema model on the server side, and currently it has to be part of the following enum:

type: {
    type: String,
    enum: [ 'Null', 'Boolean', 'Number', 'String', 'Interval', 'Interval2d', 'Point', 'Vector', 'Plane', 'Line', 'Rectangle', 'Circle', 'Box', 'Polyline', 'Curve', 'Mesh', 'Brep', 'Annotation' ],
    default: 'Null'
  }

Object Hash

The hash is a unique value, generated by the application specific converter, that is unique to that object. This hash is extremely important to be correctly generated, as objects with the same hash will not be saved twice in the database.

There is also a secondary hash, called GeometryHash, which describes just the geometry values of that specific object, without its properties field, which is described below.

Object Properties

The properties field of an object is essentially implemented as a key value store of type Dictionary<string, object>, where you define the keys as well as their values.

Anything can go in there, essentially describing your own data format on the go. Here’s a super small example of a truncated SpeckleBrep:

"speckleObject": {
    "_id": "59c0e81f0385f87cf2362cfc",
    "properties": {
      "Area": 9424.77796076938,
      "Volume": 141371.66999307554,
      "FacadeLayer": "SW01-32",
      "Planarity": 0.32,
      "Costing": {
        "base": 150,
        "unit": "$/m2"
      },
      "Centroid": {
        "type": "Point",
        "value": [
          3.088012320065082e-15,
          -7.645600799106988e-15,
          24.999999999999993
        ],
        "hash": "1849b3754e13fba0095274e51f779ba3",
        "geometryHash": "Point.9153ad925fa1",
        "properties": {
          "nestedValue": "ABC",
          "otherNestedValue": 25.43,
          // ...
        }
      },
      // ...
    },
    "hash": "3d67fd7575ada47d73eb649113724399",
    "type": "Brep"
}

How do you create all these amazing properties, I hear you asking? Well wait no longer, read on here!

Some things to note:

  • You can have SpeckleObjects inside SpeckleObjects: the Centroid is actually a SpecklePoint.
  • Nested properties: you are not restricted to a flat structure.
  • You can query the API after these properties, so you can, with no effort, get things like type=Polyline & Area>20 & Area < 100 & Level = 4.02 &sort = Area.

Here’s a live query example:

https://s003.speckle.works/api/streams/S1GUSjojW/objects?type=Circle&radius>10&radius<14&fields=type,radius,normal.value

Notice the everything after the ? sign: that’s a query:

  • Object type should be Circle.
  • Radius should be > 10 and < 14
  • Return only the type, radius and normal.value fields

More about queries (and how they’re useful) in a different tutorial. That should wrap it up for this one.

Having trouble? Found a bug? Have a question? Join the discussion below our join our slack channel.