Fork me on GitHub

Data Model

An article by Gaspard Bucher

Base classes

All content in zena (pages, images, documents, etc) is a sub-class of the main class ‘Node’. The following diagram shows the inheritance diagram for the base classes:


The arrows mean “is a”. So the Image is a Document (behaves like a document) and the Document is a Node.

This means that when searching for Documents, you will find images as well and when searching for Pages, you will find sections and projects together with simple pages.

To identify the classes inside SQLiss, we use the class path kpath. This is defined as the first letter of all classes in the inheritance hierarchy. For example:

Node: N
Document: ND
Image: NDI
TextDocument: NDT
Project: NPP

You can use this attribute in filters to load documents without images for example:

<r:documents where='kpath not like "NDI%"'>...</r:documents>

For the techie amongst you, we store all the data for nodes and sub-classes in a single table called nodes using single table inheritance.

Section and Project scoping

These two classes (and their sub-classes) can be used to scope data (e.g. “nodes in project”). This is an important feature to better organize your data. For example, if you are managing contracts with clients, it is a good idea to make the Contract class a sub-class of “Project” so that you can find every class of element belonging to a Contract without having to recurse through all childs, no matter how deeply hidden the node is inside the site’s and contract’s project page hierarchy.

This works because the “Project” class propagates “project_id” to all it’s descendants and “Section” propagates “section_id”.

Contract "xyz"     (id = 34)    <= Contract is a sub-class of Project
  +-- sub-page     (project_id = 34)
  +-- other page   (project_id = 34)
     +-- deep      (project_id = 34)
     +-- sub-proj  (project_id = 34, id = 45) <= sub-class of Project
       +-- deeper  (project_id = 45)

You can then make direct queries for objects inside a given project. For example:

From any page, this shows the latest images in the same project as the current page.
<ul do='images in project order by updated_at desc limit 5'>
  <li do='img' mode='std'/>

The difference to using the “in sub_nodes” scope scope is that “in project” will not return the nodes belonging to child projects.

Important attributes for nodes table

Attributes to secure access to the node (none can be directly used in zafu) :

site_id, rgroup_id, wgroup_id, dgroup_id, user_id

Attributes to locate the element in “space” and time :

sqlparent_id, position, project_id, section_id, created_at, updated_at, publish_from, max_status

max_status is the maximal status of the node’s versions (published if any is already published, etc).

Main tables

The content itself is not stored in nodes but in another table called versions. The versions are used for each language of the site and to keep old redactions when new text is published. See unknown link for details.


Main zena tables.

The content of attatched documents is not stored in versions, because it would be silly to make an exact copy of an image just because the text describing it changes or because the text is translated. Documents are stored in the filesystem and are referenced with the “attachments” table.

Attributes are stored inside the versions tables as a single Json encoded blob. This make dynamic properties very fast to access and display.

Virtual classes

To model your site specific data, you can create your own classes which will be inserted inside this diagram. For example you might need a ‘List’ class for pages that should display nice lists of other pages:


Inheritance diagram for a virtual class.

In this example, the List objects will behave like pages. The kpath would be NPL. See virtual classes for details.

Data Model Design

This is heavily related to Indexing and Searching.