<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Oliver</title>
    <description>The latest articles on DEV Community by Oliver (@oj423).</description>
    <link>https://dev.to/oj423</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F718895%2F127631a0-0dfc-47f7-88b2-647c39f49f6a.jpeg</url>
      <title>DEV Community: Oliver</title>
      <link>https://dev.to/oj423</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/oj423"/>
    <language>en</language>
    <item>
      <title>Building a vector database sidecar for semantic search &amp; entity resolution</title>
      <dc:creator>Oliver</dc:creator>
      <pubDate>Wed, 21 Jun 2023 10:23:17 +0000</pubDate>
      <link>https://dev.to/terminusdb/building-a-vector-database-sidecar-for-semantic-search-entity-resolution-2gmc</link>
      <guid>https://dev.to/terminusdb/building-a-vector-database-sidecar-for-semantic-search-entity-resolution-2gmc</guid>
      <description>&lt;p&gt;Vector databases are currently all the rage in the tech world, and it isn't just hype. Vector search has become ever more critical due to artificial intelligence advances which make use of &lt;em&gt;vector embeddings&lt;/em&gt;. These vector embeddings are vector representations of word embeddings, &lt;a href="https://en.wikipedia.org/wiki/Sentence_embedding"&gt;sentences&lt;/a&gt;, or documents that provide &lt;em&gt;semantic similarity&lt;/em&gt; for semantically close inputs by simply looking at a distance metric between the vectors.&lt;/p&gt;

&lt;p&gt;The canonical example from &lt;a href="https://en.wikipedia.org/wiki/Word2vec"&gt;word2vec&lt;/a&gt; in which the embedding of the word "king" was very near the resulting vector from the vectors of the words "queen", "man", and "woman" when arranged in the following formula:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    king - man + woman ≈ queen
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The fact that this works has always seemed amazing to me, but it works even for fairly large documents provided our embedding space is of sufficiently high dimension. With modern deep learning methods, you can get excellent embeddings of complex documents.&lt;/p&gt;

&lt;p&gt;For TerminusDB we needed a way to leverage these sorts of embeddings for the following tasks that our users have asked for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Full-text search&lt;/li&gt;
&lt;li&gt;Entity resolution (finding other documents which are likely the same for deduplication)&lt;/li&gt;
&lt;li&gt;Similarity search (for related content or for recommender systems)&lt;/li&gt;
&lt;li&gt;Clustering&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We decided to prototype using OpenAI's embeddings, but in order to get the rest of the features we required a vector database.&lt;/p&gt;

&lt;p&gt;We needed a few unusual features, including the ability to do incremental indexing, and the ability to index the basis of commits, so we know precisely what commit an index applies to. This allows us to put indexing into our CI workflows.&lt;/p&gt;

&lt;p&gt;A versioned open-source vector database doesn't exist in the wild. So we wrote one!&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing a vector database
&lt;/h2&gt;

&lt;p&gt;A vector database is a store of vectors with the ability to compare any two vectors using some metric. The metric can be a lot of different things such as &lt;a href="https://en.wikipedia.org/wiki/Euclidean_distance"&gt;Euclidean distance&lt;/a&gt;, &lt;a href="https://en.wikipedia.org/wiki/Cosine_similarity"&gt;Cosine similarity&lt;/a&gt;, &lt;a href="https://en.wikipedia.org/wiki/Taxicab_geometry"&gt;Taxicab geometry&lt;/a&gt;, or really anything which obeys the triangle inequality rules required to define a metric space.&lt;/p&gt;

&lt;p&gt;In order to make this fast, you need to have some sort of indexing structure to quickly find candidates that are already close for comparison. Otherwise, many operations will need to compare with every single thing in the database, every time.&lt;/p&gt;

&lt;p&gt;There are many approaches to indexing vector spaces, but we went with the HNSW (Hierarchical Navigable Small World) graph. &lt;a href="https://arxiv.org/abs/1603.09320"&gt;See Malkov and Yashunin&lt;/a&gt;. HNSW is easy to understand and provides good performance in both low and high dimensions, so is flexible. Most importantly there is a very clear open-source implementation that we found - &lt;a href="https://github.com/rust-cv/hnsw"&gt;HNSW for Rust Computer Vision&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Storing the vectors
&lt;/h2&gt;

&lt;p&gt;Vectors are stored in a &lt;em&gt;domain&lt;/em&gt;. This helps to separate different vector stores that do not need to describe the same vectors. For TerminusDB we have many different commits that all pertain to the same vectors, so it's important that we put them all into the same domain.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;            Page
            0            1         2...
            ———————————————————————
Vectors:   | 0 [......]  2 [......]
           | 1 [......]  3 [......]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The vector store is page based, where each buffer is designed to map cleanly to the operating system pages but fit the vectors we use closely. We assign each vector an index and then we can map from the index to the appropriate page and offset.&lt;/p&gt;

&lt;p&gt;Inside the HNSW index, we refer to a &lt;code&gt;LoadedVec&lt;/code&gt;. This ensures that the page lives in a buffer, currently loaded so we can perform our metric comparisons on the vectors of interest.&lt;/p&gt;

&lt;p&gt;As soon as the last &lt;code&gt;LoadedVec&lt;/code&gt; drops from a buffer, the buffer can be added back into a buffer pool to be used to load a new page.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a versioned index
&lt;/h2&gt;

&lt;p&gt;We build an HNSW structure for each (domain + commit) pair. If starting a new index, we start with an empty HNSW. If starting an incremental index from a previous commit, we load the old HNSW from the previous commit and then begin our indexing operations.&lt;/p&gt;

&lt;p&gt;What is new versus what is old is all kept in TerminusDB, which knows how to find changes between commits and can submit them to the vector database indexer. The indexer only needs to know the operations it is being asked to perform (i.e. Insert, Delete, Replace).&lt;/p&gt;

&lt;p&gt;We maintain the indexes themselves in an LRU pool that allows us to load on demand or use a cache if the index is already in memory. Since we only perform destructive operations at commits, this caching is always coherent.&lt;/p&gt;

&lt;p&gt;When we save the index, we serialize the structure with the raw vector index as a stand-in for the &lt;code&gt;LoadedVec&lt;/code&gt; which helps to keep the index small.&lt;/p&gt;

&lt;p&gt;In the future, we would like to use some of the tricks we have learned in TerminusDB to keep layers of an index around, so new layers can be added without requiring each incremental index to add a duplicate when serializing. However, the indexes have proved small enough compared to the vectors we are storing that it has not mattered much.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;NOTE: While we currently do incremental indexing, we have yet to implement the delete and replace operations (there are only so many hours in a week!). I've read the literature on HNSW and it seems that it is not yet well described.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We have a design for the delete and replace operations that we think will work well with HNSW and wanted to explain in case any technical people have ideas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If we are in an upper layer of the HNSW, then simply ignore the deletion - it should not matter much as most vectors are not in upper layers, and ones that are, are only for navigation&lt;/li&gt;
&lt;li&gt;If we are in the zero layer but not in an above layer, delete the node from the index, while trying to replace links between all neighbors of the deleted link according to closeness.&lt;/li&gt;
&lt;li&gt;If we are in the zero layer but also above, mark the node as deleted, and use it for navigation but do not store this node in the candidate pool.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Finding embeddings
&lt;/h2&gt;

&lt;p&gt;We use OpenAI to define our embeddings, and after an indexing request is made to TerminusDB, we feed each of the documents to OpenAI which returns lists of float vectors in JSON.&lt;/p&gt;

&lt;p&gt;It turns out that the embeddings are quite sensitive to context. We tried initially just submitting TerminusDB JSON documents and the results were not fantastic.&lt;/p&gt;

&lt;p&gt;However, we found that if we define a GraphQL query + Handlebars template, we can create very high-quality embeddings. For &lt;code&gt;People&lt;/code&gt; in Star Wars, this pair, which is defined in our schema, looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"embedding"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"query($id: ID){ People(id : $id) { birth_year, created, desc, edited, eye_color, gender, hair_colors, height, homeworld { label }, label, mass, skin_colors, species { label }, url } }"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"template"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The person's name is {{label}}.{{#if desc}} They are described with the following synopsis: {{#each desc}} *{{this}} {{/each}}.{{/if}}{{#if gender}} Their gender is {{gender}}.{{/if}}{{#if hair_colors}} They have the following hair colours: {{hair_colors}}.{{/if}}{{#if mass}} They have a mass of {{mass}}.{{/if}}{{#if skin_colors}} Their skin colours are {{skin_colors}}.{{/if}}{{#if species}} Their species is {{species.label}}.{{/if}}{{#if homeworld}} Their homeworld is {{homeworld.label}}.{{/if}}"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The meaning of each field in the &lt;code&gt;People&lt;/code&gt; object is rendered as text which helps OpenAI understand what we mean, providing much better semantics.&lt;/p&gt;

&lt;p&gt;Ultimately it would be nice if we could guess these sentences from a combination of our schema documentation and the schema structure, which is probably also possible using AI chat! But for now, this works brilliantly and does not require much technical sophistication.&lt;/p&gt;

&lt;h2&gt;
  
  
  Indexing Star Wars
&lt;/h2&gt;

&lt;p&gt;So what happens when we actually run this thing? Well, we tried it out on our Star Wars data product to see what would happen.&lt;/p&gt;

&lt;p&gt;First, we fire off an index request, and our indexer obtains the information from TerminusDB:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl 'localhost:8080/index?commit=o2uq7k1mrun1vp4urktmw55962vlpto&amp;amp;domain=admin/star_wars'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This returns with a task-id which we can use to poll an endpoint for completion.&lt;/p&gt;

&lt;p&gt;The index file and vector files for the domain &lt;code&gt;admin/star_wars&lt;/code&gt; and the commit &lt;code&gt;o2uq7k1mrun1vp4urktmw55962vlpto&lt;/code&gt; come out as:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;admin%2Fstar_wars@o2uq7k1mrun1vp4urktmw55962vlpto.hnsw&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;and&lt;/p&gt;

&lt;p&gt;&lt;code&gt;admin%2Fstar_wars.vecs&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We can now ask the semantic index server about our documents at the specified commit.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl 'localhost:8080/search?commit=o2uq7k1mrun1vp4urktmw55962vlpto&amp;amp;domain=admin/star_wars' -d "Who are the squid people"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We get back a number of results as JSON which look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"terminusdb:///star-wars/Species/8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"distance"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;0.09396297&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But what is the embedding string we used to produce this result? This is how the text rendered for the &lt;code&gt;Species/8&lt;/code&gt; id:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"The species name is Mon Calamari. They have the following hair colours: 
none. Their skin colours are red, blue, brown, magenta. They speak the 
Mon Calamarian language."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Amazing! Notice that it never says squid anywhere! There is some pretty amazing work being done by our embeddings here.&lt;/p&gt;

&lt;p&gt;Let's have another go:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl 'localhost:8080/search?commit=o2uq7k1mrun1vp4urktmw55962vlpto&amp;amp;domain=admin/star_wars' -d "Wise old man"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"The person's name is Yoda. They are described with the following synopsis:
Yoda is a fictional character in the Star Wars franchise created by George 
Lucas, first appearing in the 1980 film The Empire Strikes Back. In the 
original films, he trains Luke Skywalker to fight against the Galactic 
Empire. In the prequel films, he serves as the Grand Master of the Jedi 
Order and as a high-ranking general of Clone Troopers in the Clone Wars. 
Following his death in Return of the Jedi at the age of 900, Yoda was the 
oldest living character in the Star Wars franchise in canon, until the 
introduction of Maz Kanata in Star Wars: The Force Awakens. Their gender 
is male. They have the following hair colours: white. They have a mass of 
17. Their skin colours are green."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Incredible! While we do say "oldest" in the text, we don't say "wise" or "man"!&lt;/p&gt;

&lt;p&gt;I hope you can see how this could be helpful for you in getting high-quality semantic indexing of your data!&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;We have also added endpoints to find neighboring documents and to find duplicates that search the entire corpus. The latter was used on some benchmarks and has performed admirably. We hope to show the results of these experiments here soon.&lt;/p&gt;

&lt;p&gt;While there are really great vector databases out there in the wild, such as Pinecone, we want to have a sidecar that integrates well with TerminusDB and which can be used for less technical users who care about content primarily and are not going to be spinning up their own vector database.&lt;/p&gt;

&lt;p&gt;We are really excited about the potential of this &lt;a href="https://github.com/terminusdb-labs/terminusdb-semantic-indexer"&gt;VectorLink&lt;/a&gt;, and would love people to have a look at what we have so far! Please forgive us a bit for the relatively sparse error handling. We're working furiously on it!&lt;/p&gt;

&lt;h2&gt;
  
  
  Update - July 2023
&lt;/h2&gt;

&lt;p&gt;We're pleased to let you know that VectorLink is now available in TerminusCMS. Here are some useful links -&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://terminusdb.com/vectorlink/"&gt;VectorLink Semantic Indexer Overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://terminusdb.com/vectorlink/entity-resolution-solution/"&gt;Entity Resolution&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://terminusdb.com/vectorlink/semantic-search/"&gt;Semantic Search&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://terminusdb.com/vectorlink/clustering/"&gt;Clustering&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://terminusdb.com/docs/use-vectorlink/"&gt;User Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dashboard.terminusdb.com"&gt;Sign Up and Play for Free&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>vectordatabase</category>
      <category>ai</category>
      <category>database</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Schema Migration for Graph Databases</title>
      <dc:creator>Oliver</dc:creator>
      <pubDate>Tue, 16 May 2023 13:24:52 +0000</pubDate>
      <link>https://dev.to/terminusdb/schema-migration-for-graph-databases-15p6</link>
      <guid>https://dev.to/terminusdb/schema-migration-for-graph-databases-15p6</guid>
      <description>&lt;p&gt;Data is always changing – that’s why we have databases in the first place! And it is not just the content that changes, but also the shape of the data.&lt;/p&gt;

&lt;p&gt;I spend a lot of time thinking about change management for data – how to make change requests with approval workflows function smoothly, allowing users to update their content and see what it would look like in practice before putting their data into production.&lt;/p&gt;

&lt;p&gt;But part of the data lifecycle is managing these changes to the data model itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Schema Migration in Graph Databases
&lt;/h2&gt;

&lt;p&gt;In a normal RDBMS there is a command language of “schema” operations that can be performed. You can add or drop tables, add or drop foreign keys, and add or drop columns from your tables.&lt;/p&gt;

&lt;p&gt;Similar things are needed in a graph database, where you will need to add and drop edges in the graph, drop node types, add new node types, and alter keys.&lt;/p&gt;

&lt;p&gt;However, when it comes to change management with branches and merges, things get a little bit trickier. To make things work smoothly, we need to keep track of how we got where we are.&lt;/p&gt;

&lt;h2&gt;
  
  
  Schema Migrations as First-Class Citizens
&lt;/h2&gt;

&lt;p&gt;In the latest version of TerminusDB, we have added tracking of schema migration to each commit. This schema migration information can be used to ensure that branches can be made comparable so that we can do change requests between them.&lt;/p&gt;

&lt;p&gt;In addition, this will later allow us to have model products that can be updated in ways that downstream users of the model products can track the schema alterations without having to figure out how to alter their data.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;em&gt;Weakening&lt;/em&gt; and Strengthening of Schemas
&lt;/h2&gt;

&lt;p&gt;We can split the kinds of schema changes into two types: weakening and strengthening.&lt;/p&gt;

&lt;p&gt;A weakening operation is backward compatible. It changes the schema in such a way that it can not invalidate data that already exists, and does not require any change to be made to existing data.&lt;/p&gt;

&lt;p&gt;In an SQL world this might be something like adding a new table, or adding a nullable column to a table.&lt;/p&gt;

&lt;p&gt;In TerminusDB it means operations such as adding optional or set properties to a class, or adding new classes, or extending an enum with new values.&lt;/p&gt;

&lt;p&gt;One additional interesting point about these operations is that the operations can be inferred. We can guess from the addition of a new schema document, or update of a schema document that it is a weakening operation, which means you don’t have to explicitly migrate your schema, but can just edit the schema directly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Weakening Inference
&lt;/h2&gt;

&lt;p&gt;So how might we infer a schema migration? Let’s demonstrate with the command line tool.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terminusdb db create admin/m
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'{ "@type" : "Class", "@id" : "A", "a": "xsd:string" }'&lt;/span&gt; | ./terminusdb doc insert admin/m &lt;span class="nt"&gt;-g&lt;/span&gt; schema
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have created a new database (called m), and added one type of class A to the schema.&lt;/p&gt;

&lt;p&gt;If we look at the log of this (with the verbose flag) we can see the inferred migration reported.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terminusdb log admin/m &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You’ll get something along the lines of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wrhzet2rzenaiud9dc7nw03gbgwfsof
&lt;span class="nt"&gt;--------------------------------&lt;/span&gt;
Date: 2023-04-21T11:24:49+00:00
Author: admin
Message: cli: document insert
Migration:
&lt;span class="o"&gt;[&lt;/span&gt;
    &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"@type"&lt;/span&gt;:&lt;span class="s2"&gt;"CreateClass"&lt;/span&gt;,
    &lt;span class="s2"&gt;"class_document"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"@id"&lt;/span&gt;:&lt;span class="s2"&gt;"A"&lt;/span&gt;, &lt;span class="s2"&gt;"@type"&lt;/span&gt;:&lt;span class="s2"&gt;"Class"&lt;/span&gt;, &lt;span class="s2"&gt;"a"&lt;/span&gt;:&lt;span class="s2"&gt;"xsd:string"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The migration operation to obtain this schema has been inferred.&lt;/p&gt;

&lt;p&gt;In addition we can make a slightly more complex change to the schema which is also a weakening, such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'{ "@type" : "Class", "@id" : "A", "a" : "xsd:string", "b" : { "@type" : "Optional", "@class" : "xsd:boolean" }}'&lt;/span&gt; | terminusdb doc replace admin/m &lt;span class="nt"&gt;-g&lt;/span&gt; schema
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The most recent commit is given from the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terminusdb log admin/m &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It will now be along the lines of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cezsjyywg7o1r2f1fj6otvod5ka6zqa
&lt;span class="nt"&gt;--------------------------------&lt;/span&gt;
Date: 2023-04-21T11:26:35+00:00
Author: admin
Message: cli: document replace
Migration:
&lt;span class="o"&gt;[&lt;/span&gt;
    &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"@type"&lt;/span&gt;:&lt;span class="s2"&gt;"CreateClassProperty"&lt;/span&gt;,
    &lt;span class="s2"&gt;"class"&lt;/span&gt;:&lt;span class="s2"&gt;"A"&lt;/span&gt;,
    &lt;span class="s2"&gt;"property"&lt;/span&gt;:&lt;span class="s2"&gt;"b"&lt;/span&gt;,
    &lt;span class="s2"&gt;"type"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"@class"&lt;/span&gt;:&lt;span class="s2"&gt;"xsd:boolean"&lt;/span&gt;, &lt;span class="s2"&gt;"@type"&lt;/span&gt;:&lt;span class="s2"&gt;"Optional"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This migration operation tells the system that we updated the class A with a weakening operation.&lt;/p&gt;

&lt;p&gt;Strengthening operations are also possible implicitly using inference but we have not exposed this feature yet as it is still experimental (it can potentially change your instance data!)&lt;/p&gt;

&lt;h2&gt;
  
  
  Explicit Migration
&lt;/h2&gt;

&lt;p&gt;It is also possible to perform explicit migrations, whether strengthening or weakening, by stating them explicitly through the API or via the CLI.&lt;/p&gt;

&lt;p&gt;First, lets add a bit of data to illustrate.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'{ "a" : "foo", "b": true }'&lt;/span&gt; | terminusdb doc insert admin/m
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is now legal because of our above alterations which introduced the b property.&lt;/p&gt;

&lt;p&gt;Now, to add a new required property c to our class A which is an integer, we can write the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terminusdb migration admin/m &lt;span class="nt"&gt;--operations&lt;/span&gt; &lt;span class="s1"&gt;'[{"@type" : "CreateClassProperty", "class" : "A", "property" : "c", "type" : "xsd:integer", "default" : 0 }]'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This returns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"instance_operations"&lt;/span&gt;:1, &lt;span class="s2"&gt;"schema_operations"&lt;/span&gt;:1&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Meaning that we changed the data in the database.&lt;/p&gt;

&lt;p&gt;To see this we can get the documents back out:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terminusdb doc get admin/m
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which returns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"@id"&lt;/span&gt;:&lt;span class="s2"&gt;"A/86b5201b5e907ad916ed650b7656828a8f89bea12edf50ecfd341cd290194695"&lt;/span&gt;,&lt;span class="s2"&gt;"@type"&lt;/span&gt;:&lt;span class="s2"&gt;"A"&lt;/span&gt;,&lt;span class="s2"&gt;"a"&lt;/span&gt;:&lt;span class="s2"&gt;"foo"&lt;/span&gt;,&lt;span class="s2"&gt;"b"&lt;/span&gt;:true,&lt;span class="s2"&gt;"c"&lt;/span&gt;:&lt;span class="s2"&gt;"0"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With a new default value of 0 (represented as a string, unfortunately, since it is in fact an unbounded integer that causes problems with many programming languages if not represented as a string).&lt;/p&gt;

&lt;h2&gt;
  
  
  Targeting a Migration
&lt;/h2&gt;

&lt;p&gt;This is pretty great in itself as we can migrate the shape of our data even when we have instance data, but it’s especially important when doing change-request-type workflows.&lt;/p&gt;

&lt;p&gt;For instance, if I branch the database now (branching with an implicit base of main):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terminusdb branch create admin/m/local/branch/other
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I can now update the main branch again as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'{ "@type" : "Class", "@id" : "B", "b": "xsd:string" }'&lt;/span&gt; | ./terminusdb doc insert admin/m &lt;span class="nt"&gt;-g&lt;/span&gt; schema
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now my other branch has difficulties comparing with the main branch since the schema is not symmetric.&lt;/p&gt;

&lt;p&gt;To alleviate this problem I can simply target the schema of the branch in main by writing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terminusdb migration admin/m/local/branch/other &lt;span class="nt"&gt;-t&lt;/span&gt; admin/m
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now if I look at the schema, I’ll see that it has been made symmetric, including adding default values on a required property!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terminusdb doc get admin/m/local/branch/other
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Returning:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"@id"&lt;/span&gt;:&lt;span class="s2"&gt;"A/86b5201b5e907ad916ed650b7656828a8f89bea12edf50ecfd341cd290194695"&lt;/span&gt;,&lt;span class="s2"&gt;"@type"&lt;/span&gt;:&lt;span class="s2"&gt;"A"&lt;/span&gt;,&lt;span class="s2"&gt;"a"&lt;/span&gt;:&lt;span class="s2"&gt;"foo"&lt;/span&gt;,&lt;span class="s2"&gt;"b"&lt;/span&gt;:true,&lt;span class="s2"&gt;"c"&lt;/span&gt;:&lt;span class="s2"&gt;"0"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Of course this requires that we have migrations the entire way along, so we’ll be adding a mode that forces all schema updates (via the document interface or otherwise) to have a valid migration, or throw an error.&lt;/p&gt;

&lt;p&gt;And for model products that are meant to be imported as schema components for other data products, this should also be required.&lt;/p&gt;

&lt;p&gt;However if you keep to explicit migrations for schema changes, or restrict to weakening operations, you should always be able to move your branches along with you.&lt;/p&gt;

&lt;p&gt;Hope you enjoy the new-found model flexibility!&lt;/p&gt;

</description>
      <category>database</category>
      <category>schema</category>
      <category>data</category>
      <category>news</category>
    </item>
    <item>
      <title>Stories from over yonder</title>
      <dc:creator>Oliver</dc:creator>
      <pubDate>Tue, 16 May 2023 13:14:25 +0000</pubDate>
      <link>https://dev.to/terminusdb/stories-from-over-yonder-42di</link>
      <guid>https://dev.to/terminusdb/stories-from-over-yonder-42di</guid>
      <description>&lt;p&gt;We don't post all of our blogs on Dev.to, mainly because we're a small team and it's tough to create original content for lots of different sites. I thought I'd provide a quick overview of what we've posted recently over on our blog. &lt;/p&gt;

&lt;h2&gt;
  
  
  Reframing Data Strategy: The Era of Quality Data - Unlocking Success Through Prioritizing Accuracy
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Synopsis:&lt;/em&gt; Move beyond the hype of "big data" and embrace the paradigm shift towards prioritizing quality over quantity. This insightful article sheds light on the paramount importance of good data and its impact on businesses. Discover why accuracy, consistency, relevance, and timeliness are the cornerstones of good data, enabling reliable decision-making. Explore the pitfalls of relying solely on big data, including complexity, data gaps, and biased analysis. The article advocates for a transformative mindset that places a premium on quality data, urging business leaders to proactively align their strategies with this new approach. By fostering meaningful dialogue between data producers and consumers, organizations can ensure data aligns with business objectives. Embracing quality data empowers businesses to unleash the true potential of their data, enabling informed and impactful decisions. Join the movement and make quality data the cornerstone of your organization's success.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://terminusdb.com/blog/good-data-not-big-data/"&gt;Good data, not big data&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Redefining Collaboration in Database Management: Unleashing the Potential of TerminusCMS
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Synopsis:&lt;/em&gt; Experience the game-changing capabilities of TerminusCMS as it redefines the landscape of database collaboration. This article unveils the cutting-edge features that empower developers and end-users alike, placing flexibility and collaboration at the forefront. With the revolutionary integration of branches and change request workflows, data and content changes become a seamless process, involving humans at every step. TerminusCMS revolutionizes supply chain management, ensuring regulatory compliance, empowering research teams, and supercharging application features. Whether you're a developer, compliance professional, researcher, or app creator, TerminusCMS offers a transformative platform for accurate, reproducible, and collaborative data management. Embrace the future of database collaboration and witness the remarkable impact TerminusCMS can have on your work.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://terminusdb.com/blog/database-collaboration-keep-humans-in-the-loop/"&gt;Database collaboration&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Revolutionize SBOM Management with Headless CMS
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Synopsis:&lt;/em&gt; Dive into the world of &lt;a href="https://terminusdb.com"&gt;headless content management systems&lt;/a&gt; (CMS) and unlock the true power of managing your software bill of materials (SBOM). This captivating article delves into the crucial role SBOMs play in businesses, from compliance and risk management to supply chain relationships and product development. Discover how a headless CMS empowers you to streamline SBOM processes by providing a centralized platform, automated data collection, task management applications, and exceptional customer service support. With a special focus on TerminusCMS, explore its game-changing features, including its graph data structure, flexible schema modeling, change request workflows, and dynamic query capabilities. By embracing SBOMs and harnessing the advantages they offer in terms of security, compliance, and innovation, your business will soar to unprecedented heights.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://terminusdb.com/blog/sbom-with-headless-cms/"&gt;Manage your SBOM with Headless CMS&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Elevate Your Data Management: Empowering Business Critical Functions with Data Layer Content Moderation
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Synopsis:&lt;/em&gt; Embark on a transformative journey with TerminusCMS, an innovative content management system that revolutionizes data and content moderation. This captivating blog uncovers the pivotal role of change request workflows at the data layer and their profound impact on critical business functions such as forecasting, product testing, and regulatory compliance. Immerse yourself in the advanced capabilities of TerminusCMS, including immutable data, version control, and collaborative features, which guarantee unparalleled accuracy, accountability, and transparency. Gain insights into the crucial role of content moderation at the data layer, fostering data integrity, optimizing workflows, and ensuring compliance with regulations.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://terminusdb.com/blog/database-collaboration-keep-humans-in-the-loop/"&gt;Content Moderation at the Data Layer&lt;/a&gt;&lt;/p&gt;

</description>
      <category>news</category>
    </item>
    <item>
      <title>How we built a low-code application for straight-through processing of insurance claims in a week</title>
      <dc:creator>Oliver</dc:creator>
      <pubDate>Wed, 26 Apr 2023 09:11:22 +0000</pubDate>
      <link>https://dev.to/terminusdb/how-we-built-a-low-code-application-for-straight-through-processing-of-insurance-claims-in-a-week-1epm</link>
      <guid>https://dev.to/terminusdb/how-we-built-a-low-code-application-for-straight-through-processing-of-insurance-claims-in-a-week-1epm</guid>
      <description>&lt;p&gt;Can you build an app for straight through processing of insurance claims, including UI for necessary human interventions in a week?&lt;/p&gt;

&lt;p&gt;The answer, surprisingly, &lt;strong&gt;is yes&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Of course, with software, it’s very &lt;a href="https://bigthink.com/neuropsych/task-completion-study/"&gt;hard to tell how long code takes&lt;/a&gt; unless you try. So we built one. The secret to our success was twofold:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We had a semantic description of our domain, making the building of the UI almost completely automatic, and enabling logical description of entities&lt;/li&gt;
&lt;li&gt;We used a declarative logical description of the states of the insurance claim&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So both the core logic of transitioning an insurance policy through states and the UI fell out of careful modeling of the problem domain. This is what declarative software design is supposed to do for us, and it worked so well that it even surprised me!&lt;/p&gt;

&lt;p&gt;Best of all, it’s very easy to customize, because it is a very low-code application, with most of the logic in the description. I expect this approach can be used in a lot of FinTech applications and would love to have the opportunity to try it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo App
&lt;/h2&gt;

&lt;p&gt;We focused only on one use case: life insurance policies to demonstrate straight through processing of insurance claims with human intervention. Of course, our app doesn’t do everything that you might want in a fully-fledged insurance claims application.&lt;/p&gt;

&lt;p&gt;However, it does do a surprising amount considering the time it took to put together, and with a bit of elbow grease it seems to me it could easily be a product in itself, and it could easily be grown out to deal with a much larger assortment of claims.&lt;/p&gt;

&lt;h2&gt;
  
  
  Modeling Insurance Claims
&lt;/h2&gt;

&lt;p&gt;The first port of call is to model the objects of interest in claims processing. This is the semantic part that is going to drive the rest of the application.&lt;/p&gt;

&lt;p&gt;Our demo app has the following elements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Countries&lt;/li&gt;
&lt;li&gt;Claim Cases&lt;/li&gt;
&lt;li&gt;Policies&lt;/li&gt;
&lt;li&gt;Beneficiaries&lt;/li&gt;
&lt;li&gt;Death Certificates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It also has a couple of enumerated types:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Refunds (Sum Assured, Premium or Denied)&lt;/li&gt;
&lt;li&gt;Cause of Death (Natural, Manslaughter, Murder, Accidental and Suicide)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The entire claim case model &lt;a href="https://github.com/GavinMendelGleason/blog/blob/main/assets/claims.json"&gt;can be seen here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Right out of the box, with this in place, we can get a default UI that looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--k-aGG7Zx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3uh1qyt95mu8jgmrhl7x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--k-aGG7Zx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3uh1qyt95mu8jgmrhl7x.png" alt="A screenshot of the TerminusCMS backend showing the curation tools that are automatically generated" width="800" height="551"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is what TerminusCMS gives us just by modeling our application domain. A nearly fully functional UI, which can then be customized quickly by passing JavaScript configuration objects in React.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding Declarative States
&lt;/h2&gt;

&lt;p&gt;To make our straight-through processing system, we model the states of our system based on whether the object we are interested in meets certain constraints. That way the state of the object is defined by the content of the object, and changing the content of the object (for instance, supplying more information, or correcting information) will change it to a new state.&lt;/p&gt;

&lt;p&gt;Let us take a concrete example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"@comment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Claim is eligible for a premium refund"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"@given"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"death_certificate_for_claim"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"date_of_death"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"@var"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DateOfDeath"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"policy_of_claim"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"moratorium_end"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"@var"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"MoratoriumEnd"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"moratorium_start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"@var"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"MoratoriumStart"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"@has"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"@ge"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"@var"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"MoratoriumStart"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"@le"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"@var"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"MoratoriumEnd"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"@with"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DateOfDeath"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"@noneOf"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"ReferralForAssessment"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"ReferralForService"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"ClaimClosed"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"@on"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ClaimCase"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PremiumRefund"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Restriction"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What we are doing here is defining what claims are immediately eligible for a premium refund. The conditions which place a claim in this state are described declaratively. This is a business logical description that helps us move to the appropriate state &lt;em&gt;automatically&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;First, we must check to find the death date associated with the claims case, then we look for the start and end of the moratorium written into the policy.&lt;/p&gt;

&lt;p&gt;Next, we check a condition, is the date of death between the start of the moratorium, and the end? If so, the person is only eligible for the refund of premiums made to date, and can not avail of the sum assured.&lt;/p&gt;

&lt;p&gt;In addition, however, we have to check that the status of the record is safe in the sense that all necessary associated information has matched the conditions which place it into a &lt;em&gt;referral for assessment&lt;/em&gt; or &lt;em&gt;referral for service&lt;/em&gt;. These are conditions such as an unusual cause of death (perhaps murder), or failure for the death certificate to match policy details.&lt;/p&gt;

&lt;p&gt;All of these other conditions are likewise written declaratively (as you can see from the above schema).&lt;/p&gt;

&lt;p&gt;We also want to exclude those records for which the claim has already been closed (either due to a prior payout, or rejection).&lt;/p&gt;

&lt;h2&gt;
  
  
  Straight Through Processing Actions and the UI
&lt;/h2&gt;

&lt;p&gt;With these definitions which incorporate the relevant business logic, we can turn it into a fully-fledged application. We build React components automatically and expose a rich GraphQL interface that allows you to pull objects which meet a given &lt;em&gt;restriction&lt;/em&gt; status easily.&lt;/p&gt;

&lt;p&gt;If a premium payout status is reached, for instance, then we can have an &lt;em&gt;automated&lt;/em&gt; procedure for payment, which then places the object in a closed status if successful. In fact, there may be many automatic procedures that take place, each moving the object into a further state which can ultimately result in the closure of the claim case or movement into some human interaction.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2YFFxavn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fn46anb49soz6qa59dr8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2YFFxavn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fn46anb49soz6qa59dr8.png" alt="A screenshot of the insurance claims demo app we build for an insurance customer" width="800" height="460"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If an insurance claim is &lt;em&gt;referred for servicing&lt;/em&gt; or some other state that requires human intervention, it will appear in a dashboard automatically.&lt;/p&gt;

&lt;p&gt;The dashboard only refers to the &lt;em&gt;state&lt;/em&gt; of the claim it would like to retrieve, in this case, &lt;em&gt;referred for servicing&lt;/em&gt;. All of the query logic is entirely in the model. This means that as business rules change, nothing needs to change about the UI. Changes to the UI are only required when new insurance claim states are defined, saving time and reducing complexity.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CcwnagMS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kij3jm066y1rgce7abfm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CcwnagMS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kij3jm066y1rgce7abfm.png" alt="A screenshot of the insurance claims application showing user notices informing them why the claim is in a certain state" width="800" height="460"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When a user views a particular case state in the dashboard, we can tell the reason that it ended up in this state. This helps the human operator understand what needs to be altered in order to place the claim in a new state that no longer requires servicing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Declarative domain modeling is exceptionally powerful as a means of writing low-code applications and implementing rules-driven functionality such as straight through processing of insurance claims.&lt;/p&gt;

&lt;p&gt;Low code brings big benefits itself. It means easier maintenance, faster development time and more flexible business logic. This reduces costs of development and maintenance and improves agility, which is extremely important to respond to customer needs and competitors.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>database</category>
      <category>development</category>
      <category>backend</category>
    </item>
    <item>
      <title>Messing around with GraphQL queries with a critical asset management dataset</title>
      <dc:creator>Oliver</dc:creator>
      <pubDate>Tue, 18 Apr 2023 13:40:04 +0000</pubDate>
      <link>https://dev.to/terminusdb/messing-around-with-graphql-queries-with-a-critical-asset-management-dataset-25d6</link>
      <guid>https://dev.to/terminusdb/messing-around-with-graphql-queries-with-a-critical-asset-management-dataset-25d6</guid>
      <description>&lt;p&gt;I wanted to show the simplicity of TerminusCMS’ GraphQL query ability and the work our dev team has achieved to make queries lightning-fast and very simple to construct.&lt;/p&gt;

&lt;p&gt;I would consider myself technically inept, so anyone with a modicum of technical skill will breeze through these examples.&lt;/p&gt;

&lt;p&gt;The example I’m using is the CAMS (Critical Asset Management System) project that you can clone from the &lt;a href="https://dashboard.terminusdb.com"&gt;TerminusCMS dashboard&lt;/a&gt;. It has a schema and dummy data based around the Caribbean Island of Dominica. It is a copy of the backend for the &lt;a href="https://climateresilient.world/"&gt;critical asset management system project&lt;/a&gt; we worked on with the UN and ARISE.&lt;/p&gt;

&lt;p&gt;The examples use assets and the relationships between them. They have dependency relationships between them. It also looks at the asset owner. Here’s a rough diagram showing the focus.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AcweRCUI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1d8xhma8iodn6q1sa5zp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AcweRCUI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1d8xhma8iodn6q1sa5zp.png" alt="A diagram showing the dependency relationships between assets, and the asset owner" width="524" height="344"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hypothetically, I’m a dev and I want to build an application for first responders to communicate asset failures.&lt;/p&gt;

&lt;p&gt;Firstly, I want to find all the critical assets associated with my region’s communications.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;Asset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;assetType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Communications&lt;/span&gt;&lt;span class="p"&gt;}})&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;asset_identifier&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here I’ve filtered by assetType using equals communications. Read our docs for &lt;a href="https://terminusdb.com/docs/guides/how-to-guides/query-using-graphql/filter-with-graphql"&gt;more information on filtering&lt;/a&gt;. The above query produces these JSON results -&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Asset"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"asset_identifier"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Communication tower near Good Hope "&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"asset_identifier"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Petro Caribe Radio Tower"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can filter and display any document property. The GraphQL schema for properties, filters and limits is constructed automatically from your project's schema. This includes paths too, so you can establish the relationships in your query.&lt;/p&gt;

&lt;p&gt;For example, I now want to see which assets are dependent on my comms assets.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;Asset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;assetType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Communications&lt;/span&gt;&lt;span class="p"&gt;}})&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;asset_identifier&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="n"&gt;_depends_on_of_DependencyRelation&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;dependent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using Ctrl C, I can get a list of document classes to specify, this includes the link properties that create the edges in the graph. Here’s a look at the schema builder UI showing the asset document class — you can see it has dependency relationships as properties -&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MOVebi7---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tgfrwmpr6gt4szafcw06.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MOVebi7---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tgfrwmpr6gt4szafcw06.JPG" alt="A close up view of the TerminusCMS dashboard and the schema builder UI displaying the dependency relationship properties of the asset document class" width="800" height="544"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The above query produces a list of your comms assets with those that are dependent on them -&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Asset"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"asset_identifier"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Communication tower near Good Hope "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"_depends_on_of_DependencyRelation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"dependent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Mahaut River Health Centre"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"dependent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"La Plaine Police Station"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"dependent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"La Plaine Police Station"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"dependent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Colihaut Primary School"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"asset_identifier"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Petro Caribe Radio Tower"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"_depends_on_of_DependencyRelation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"dependent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Princess Margaret Hospital "&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"dependent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Castle bruce Electrical substation"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"dependent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Portsmouth Town Council"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In order to make my application useful. I need to include the owners of each asset so first responders can relay important information.&lt;/p&gt;

&lt;p&gt;The owner document class is added as a link property to an asset and it is specified as a set to allow more than one owner.&lt;/p&gt;

&lt;p&gt;I simply add the owner document class and the contact properties I want to see. Again I can press Ctrl C to get a list of properties, or use the handy autofill.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;Asset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;assetType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Communications&lt;/span&gt;&lt;span class="p"&gt;}})&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;asset_identifier&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;_depends_on_of_DependencyRelation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;dependent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;owner&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="n"&gt;last_name&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="n"&gt;email_address&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="n"&gt;phone_number&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gets me back the assets dependent on my comms assets and the names and contact details for all those that need to be communicated to -&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Asset"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"asset_identifier"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Communication tower near Good Hope "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"_depends_on_of_DependencyRelation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"dependent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Mahaut River Health Centre"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"owner"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Sue"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"last_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Sandez"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"email_address"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sue@dashcom.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"phone_number"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"21358394"&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"dependent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"La Plaine Police Station"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"owner"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Amanda"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"last_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Christensen"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"email_address"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"amanda@vlink.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"phone_number"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"324987392"&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"dependent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"La Plaine Police Station"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"owner"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Amanda"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"last_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Christensen"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"email_address"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"amanda@vlink.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"phone_number"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"324987392"&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"dependent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Colihaut Primary School"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"owner"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Ted"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"last_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bigs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"email_address"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tbigs@bbn.org"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"phone_number"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2344859"&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"asset_identifier"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Petro Caribe Radio Tower"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"_depends_on_of_DependencyRelation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"dependent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Princess Margaret Hospital "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"owner"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Ted"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"last_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bigs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"email_address"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tbigs@bbn.org"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"phone_number"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2344859"&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"dependent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Castle bruce Electrical substation"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"owner"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Eliza"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"last_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Albert"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"email_address"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eliza@vvv.org"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"phone_number"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"32449234"&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"dependent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Portsmouth Town Council"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"owner"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Oliver"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"last_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Little"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"email_address"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"olittle@comms.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"phone_number"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"12384957684"&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Have a go yourself and &lt;a href="https://dashboard.terminusdb.com/"&gt;sign up for TerminusCMS&lt;/a&gt; for free. Clone a project and see how easy it is to query using GraphQL.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mPI37xvF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ike77cwep4xygqrom64x.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mPI37xvF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ike77cwep4xygqrom64x.jpg" alt="Screenshot of the TerminusCMS dashboard and the area where you can clone a demo project to experiment with." width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>graphql</category>
      <category>tutorial</category>
      <category>backend</category>
    </item>
    <item>
      <title>The Content Revolution</title>
      <dc:creator>Oliver</dc:creator>
      <pubDate>Thu, 16 Feb 2023 15:49:14 +0000</pubDate>
      <link>https://dev.to/terminusdb/the-content-revolution-1ca8</link>
      <guid>https://dev.to/terminusdb/the-content-revolution-1ca8</guid>
      <description>&lt;h2&gt;
  
  
  Building the Headless Content Management System of the Future
&lt;/h2&gt;

&lt;p&gt;Myself and team just finished preparing our new cloud headless CMS offering, TerminusCMS. It feels like a pretty long journey getting here, but we feel like we have the basis of a truly next-generation CMS for devs.&lt;/p&gt;

&lt;p&gt;We live in the information age, and unsurprisingly that means a lot of what we do with computers is about marshalling, finding or transforming data.&lt;/p&gt;

&lt;p&gt;Each domain which IT serves involves creating digital assets which record information to be enriched, served up, or curated over time. And much of this data is highly interconnected.&lt;/p&gt;

&lt;p&gt;Moreover, it is being loaded, curated and enriched by a combination of humans and computers. The entire business is fundamentally cybernetic. We need knowledge graph management systems which include easy-to-use interfaces for human data entry, review and curation at the same time that they facilitate the same from a computer. These systems have to be scalable and distributed.&lt;/p&gt;

&lt;p&gt;And as AI grows in importance, the importance of well-structured data will simply grow. AIs perform better when fed well-structured data and they will be able to help us create the search engines of the future. It will make expert systems something more like asking an expert, and less like a tool for an expert.&lt;/p&gt;

&lt;p&gt;Our infrastructure for creating and managing the data world is still young, so the question is, how is it going to change? We have databases, including graph databases, and we have headless content management systems, what more could we ask for?&lt;/p&gt;

&lt;p&gt;As a practitioner in managing data, I developed strong opinions on the matter and made some big bets on what I thought was important by making TerminusDB.&lt;/p&gt;

&lt;p&gt;What is currently missing in our data management solutions? I think it essentially boils down to the need for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Continuous integration / Continuous development (CI/CD):&lt;/strong&gt; data is always in a pipeline.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Document Graph structured content:&lt;/strong&gt; Content always relates to other content and we need to manage content with both people and machines.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Discoverability:&lt;/strong&gt; We require powerful query and search (Drupal / WordPress aren’t going to cut it)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these ideas are completely new. In fact, all of them are widely used in their respective domains, but they aren’t currently being used together for data management.&lt;/p&gt;

&lt;p&gt;To hit these functional requirements we need a headless, schema-driven knowledge graph management system with external transactions for CI/CD… right, what does that mean… Let’s unpack that…&lt;/p&gt;

&lt;h2&gt;
  
  
  CI/CD for Data
&lt;/h2&gt;

&lt;p&gt;Transactions in databases tend to be focused around the central server. This makes a lot of sense when you want high-performance ACID transactions and a single source of truth.&lt;/p&gt;

&lt;p&gt;However, there are more kinds of transactions than the text-book “decrement a bank account” variety. Content management by humans is often more natural to think about with transactions which could be open for a long time.&lt;/p&gt;

&lt;p&gt;In software engineering, we already do this with code. Change requests, ala Git, are external transactions. What do I mean by external transaction? It’s a transaction id which associates a state, which can be used with many individuals or microservices to perform a pipeline of updates on the request branch – and which finally completes only after everything is in order.&lt;/p&gt;

&lt;p&gt;To do this we need a state which sticks around and which can refer to other states. We need a graph of states and their relationships. Basically, we need a git-like architecture for our database.&lt;/p&gt;

&lt;p&gt;A typical workflow with a change request might be:&lt;/p&gt;

&lt;p&gt;(I’ll use cN to refer to the names of individual commits)&lt;/p&gt;

&lt;h3&gt;
  
  
  1. I branch from main with a change request branch which I name &lt;code&gt;dev&lt;/code&gt;. Initially we share a commit &lt;code&gt;c0&lt;/code&gt;.
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;       c0
main – ∘
       |
      dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I make some commits to this branch, adding data by human data entry with a front end I’ve designed to make data entry easy for my problem domain.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;       c0
main – ∘
        ↘
  dev –  ∘
         cd1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. A computer programme sees this change request update and enriches my added documents, by performing named entity recognition on the text, enhancing searchability.
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;       c0
main – ∘
        ↘      dev
         ∘      |
         cd1 → cd2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. A spell checker runs on cd2 highlighting errors, which I think fixes.
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;       c0
main – ∘
        ↘
         ∘ → ∘ → ∘ – dev
        cd1 cd2 cd3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Now, my cd3 gets a green light from all of the linting operations that run on commits in CRs. It’s ready for a human review. An editor sees it, likes it, and decides to merge it into main. However, main has moved on… some other changes have taken place (perhaps another curator or maybe an automatic ingestion). These need to be merged back into dev.
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                        main
       c0          c1    |
       ∘ →→→→→→→→→ ∘  →  ∘
        ↘            ↘ ↗
          ∘ → ∘ → ∘ → ∘ – dev
         cd1 cd2 cd3  cd4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everything in this process is already possible with TerminusCMS. However, there is more yet to be done.&lt;/p&gt;

&lt;h2&gt;
  
  
  Document Graphs
&lt;/h2&gt;

&lt;p&gt;Structured documents are widely used by both computers and humans. These include everything from Excel and Word documents which are more human-focused, to &lt;a href="https://en.wikipedia.org/wiki/Protocol_Buffers"&gt;Protocol Buffers&lt;/a&gt; and JSON which use documents which are more machine focused.&lt;/p&gt;

&lt;p&gt;HTML’s great innovation was to introduce into this idea of documents, the concept of the hyperlink. This would allow documents to refer to other documents.&lt;/p&gt;

&lt;p&gt;HTML however was focused on rendering (Mark-up) rather than structured content. This means that HTML actually makes poor documents for machine manipulation.&lt;/p&gt;

&lt;p&gt;In TerminusDB we have allowed structured content to use references to other documents, which gives us the power of both the graph, and document orientation, and allows machines to process documents more easily. Rendering can be thought of more as a publishing step of processing the structured knowledge graph data, and less as something which is built directly into the structured content.&lt;/p&gt;

&lt;p&gt;It is also possible to mix-and-match, keeping the content as mark-up (or markdown) while having more structured accompanying information as well – as in the info-boxes on Wikipedia. For instance, a Politician might have a description renderable as markdown, but positions that could show up in an info-box, be searched easily, or processed by other machines.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"@type"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Class"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"@id"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Position"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"party"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"xsd:string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"xsd:date"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"end"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"xsd:date"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"position"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"xsd:date"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"@type"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Class"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"@id"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Politician"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Markdown"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"positions"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"@type"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Set"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"@class"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Position"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"@type"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Class"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"@id"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Markdown"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"@metadata"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"render_as"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"markdown"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"body"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"xsd:string"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Empowering Cybernetic Cyborgs
&lt;/h2&gt;

&lt;p&gt;This cybernetic approach is designed based on iterative processes, and bio-mechanical. Ultimately we hope to do more to facilitate this approach to data management to make it more scalable and more external.&lt;/p&gt;

&lt;p&gt;Right now, we are adding information to commits about what objects changed to facilitate queries over histories, but we’d also like to be able to retain information about read-sets. A read-set is all of the data which you read that was necessary to make a transaction work. This information can allow you to reorder transactions while retaining the Isolation part of &lt;a href="https://en.wikipedia.org/wiki/ACID"&gt;A.C.I.D&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This could allow you to have long-running transactions that can commit out of order with others, retaining isolation, as long as none of the information required for the completion invalidates the information. It also makes it the responsibility of the committing transaction to know whether or not this is the case, taking the load off of the central server, which can not in principle know what precise isolation level is acceptable.&lt;/p&gt;

&lt;p&gt;To give a case in point, I could have a user class as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"@type"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Class"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"@id"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"User"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"xsd:string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"xsd:string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"credits"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"xsd:integer"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we might want to perform a transaction that looks up the number of credits, finds them to be 20, and then commits the transaction if so. If the granularity of isolation is, however, the object, then people changing the user’s name will conflict. This is almost certainly not what is meant, but the DBMS can’t know this. With &lt;em&gt;read-sets&lt;/em&gt;, you could.&lt;/p&gt;

&lt;p&gt;In fact, you could even have amended transactions which just tack on a bit on the end to make things consistent. For instance, you could decide that the User needs &amp;gt;20 credits since 20 will be deducted. One starts with the read of 30 credits, comes back to commit and finds 25, and hence the committed number should be 5, which we could paste together in an amended transaction and complete.&lt;/p&gt;

&lt;p&gt;This is a form of automated conflict resolution, and while not all sorts of conflicts can be resolved without aborting the transaction, it is possible to build logic around things like greater/less-than etc which would work.&lt;/p&gt;

&lt;p&gt;Further, it’s entirely possible to ingrate CRDT data types into this model to get a mixed view, when you really don’t care about the ordering of transactions on a field or the exact state of the original or final read.&lt;/p&gt;

&lt;p&gt;The approach of read-sets and external transactions are very general and therefore will be a powerful mechanism for sharing state not only among humans but also among micro-services.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;TerminusCMS is our latest attempt to put up a cloud service which exposes some of the power of TerminusDB in a way that non-technical users can enjoy, while also giving the full headless API to developers. The tool is open source, and free to use on our cloud for even quite large databases, so we hope you give it a try and give your feedback!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://terminusdb.com/terminuscms/"&gt;TerminusCMS overview&lt;/a&gt;&lt;br&gt;
&lt;a href="https://terminusdb.com/features/"&gt;TerminusCMS features&lt;/a&gt;&lt;br&gt;
&lt;a href="https://dashboard.terminusdb.com"&gt;Free sign up&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Written by TerminusDB CTO Gavin Mendel Gleason&lt;/em&gt;&lt;/p&gt;

</description>
      <category>news</category>
      <category>opensource</category>
      <category>showdev</category>
      <category>writing</category>
    </item>
    <item>
      <title>Putting the Graph in GraphQL Query</title>
      <dc:creator>Oliver</dc:creator>
      <pubDate>Wed, 08 Feb 2023 14:35:43 +0000</pubDate>
      <link>https://dev.to/terminusdb/putting-the-graph-in-graphql-query-4b09</link>
      <guid>https://dev.to/terminusdb/putting-the-graph-in-graphql-query-4b09</guid>
      <description>&lt;p&gt;From the name, one would think a GraphQL query would be a graph query language. But then one would be incorrect. GraphQL is neither particularly graph-oriented nor is it a query language.&lt;/p&gt;

&lt;p&gt;Don’t get me wrong, GraphQL is great, it’s just not exactly what it says on the tin. In reality, GraphQL is a sort of API killer. A way to avoid having to write a bazillion HTTP endpoints, and instead have a well-structured, schema-aware approach to getting information in and out of a service.&lt;/p&gt;

&lt;p&gt;This is extremely useful, reducing development time for new APIs and, by having well-defined schemas which actually check for the correctness of the objects being communicated, averting many errors and regressions.&lt;/p&gt;

&lt;p&gt;Yet wouldn’t it be great if GraphQL was also about graphs and a query language?&lt;/p&gt;

&lt;p&gt;Well, that’s certainly what we thought at TerminusDB! We’ve recently implemented a suite of features which allows you to query a TerminusDB project using GrahpQL in such a way that deep linking can be discovered.&lt;/p&gt;

&lt;h2&gt;
  
  
  GraphQL Back Links
&lt;/h2&gt;

&lt;p&gt;In a typical document in TerminusDB, you specify classes and their fields. This might look as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"@type"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Class"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"@id"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Person"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"xsd:string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"pet"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"@type"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Set"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"@class"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Pet"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"friend"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"@type"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Set"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"@class"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Friend"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"@type"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Class"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"@id"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Pet"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"@abstract"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"xsd:string"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"@type"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Class"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"@id"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Dog"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"@inherits"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Pet"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;TerminusDB automagically constructs a GraphQL schema derived from the project’s schema, which allows us to query it. A GraphQL query might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;query&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;Person&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;pet&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And we might get back a response along the lines of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Person"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Joe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"pet"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Mimi"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Fido"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But supposing I find Mimi wandering the streets. How can I get back to the owner of Mimi? We defined our edge only in one direction. Yet in TerminusDB going in the reverse direction from an object is actually a cheap operation.&lt;/p&gt;

&lt;p&gt;In order to achieve this, we have enriched the GraphQL schema with back-links which automatically give you the reverse edges of every object field.&lt;/p&gt;

&lt;p&gt;We can perform the reverse GraphQL query as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;query&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;Pet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;eq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Mimi"&lt;/span&gt;&lt;span class="p"&gt;}})&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;_pet_of_Person&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This query asks for a pet, whose name is Mimi, and obtains the name field, but also a link to who they are a pet of: &lt;code&gt;_pet_of_Person&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This field name is constructed automagically, and the language tends to work well using the genetive construction, but most importantly you can clearly see the type of the object from which it comes from. This is because reverse links could come from many different objects, all having the same field name, so it is important to disambiguate.&lt;/p&gt;

&lt;p&gt;Our GraphQL query result using my current database state is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Pet"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Mimi"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"_pet_of_Person"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Joe"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  GraphQL Path Queries
&lt;/h2&gt;

&lt;p&gt;Ok, well that is certainly handy since we often need to use these sorts of relationships in two directions, but what about graphs? Can we weave our way through the graph to find what we want? Can we do this with a GraphQL query?&lt;/p&gt;

&lt;p&gt;TerminusDB adds path queries to our GraphQL schema to obtain precisely this.&lt;/p&gt;

&lt;p&gt;A path query is a regular expression pattern describing the edges that it would like to follow in a graph. The full GraphQL path query specification can be found here.&lt;/p&gt;

&lt;p&gt;For those familiar with regular expressions it shouldn’t be too difficult to pick up but even if new to the concept, it’s fairly intuitive.&lt;/p&gt;

&lt;p&gt;Basically, I can write down the name of an edge: &lt;code&gt;"friend"&lt;/code&gt; then you say how many times you want to follow this edge. If you want to follow it precisely once, we can leave it alone, if you want to follow it zero or more times, you write: &lt;code&gt;"friend*"&lt;/code&gt;. If you want at least one hop, but any number greater you can write: &lt;code&gt;"friend+"&lt;/code&gt;. If you want to follow it between 1 and 3 times, you can write: &lt;code&gt;"friend{1,3}"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If I want to go backwards on an edge (similarly to the back links above) I can use a directional modifier: &lt;code&gt;"&amp;lt;friend"&lt;/code&gt;, and again I can modify this with the number of hops.&lt;/p&gt;

&lt;p&gt;Let’s say we want to find everyone who has a friend with a pet named Mimi.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;query&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;Pet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;eq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Mimi"&lt;/span&gt;&lt;span class="p"&gt;}})&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;_path_to_Person&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;pet,(friend|&amp;lt;friend)+"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In English, we might write this query as: “Get me all of the friends of the owner of Mimi, whether they believe they are this person’s friend, or the person considers them a friend, i.e. friendship here is not automatically symmetric, so we have to look at it from both perspectives.&lt;/p&gt;

&lt;p&gt;The answer we get back is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Pet"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"_path_to_Person"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Candy"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Doug"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we’re really talking about exploring the graph!&lt;/p&gt;

&lt;h2&gt;
  
  
  Future Directions
&lt;/h2&gt;

&lt;p&gt;We already have a nice GraphQL interface for querying, but we intend to build out the mutation capabilities as well. We also want to have derived edges which are GraphQL queries that are the result of a &lt;a href="https://terminusdb.com/blog/the-power-of-web-object-query-language/"&gt;WOQL&lt;/a&gt; query internally. This would allow us to expose very sophisticated queries which might make use of aggregation etc. at the level of GraphQL.&lt;/p&gt;

&lt;p&gt;GraphQL has really simplified access to data for front-end developers and those performing analytics. It’s great to be able to expose it in TerminusDB. For us, the fit feels very natural and perhaps best of all: we have a very high-performance GraphQL!&lt;/p&gt;

&lt;p&gt;You should try it out. You can either use TerminusDB by downloading it from &lt;a href="https://github.com/terminusdb/terminusdb/"&gt;our repositories&lt;/a&gt;, or you sign up for a free TerminusCMS account at &lt;a href="https://dashboard.terminusdb.com/"&gt;dashboard.terminusdb.com&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>graphql</category>
      <category>opensource</category>
      <category>news</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Data Modelling &amp; Collaboration for Change Makers - Do Good With DFRNT</title>
      <dc:creator>Oliver</dc:creator>
      <pubDate>Fri, 27 Jan 2023 11:06:51 +0000</pubDate>
      <link>https://dev.to/terminusdb/data-modelling-collaboration-for-change-makers-do-good-with-dfrnt-3f8</link>
      <guid>https://dev.to/terminusdb/data-modelling-collaboration-for-change-makers-do-good-with-dfrnt-3f8</guid>
      <description>&lt;p&gt;Last week saw the launch of the SaaS version of DFRNT, a tool to model and build data products. Built on top of TerminusDB, DFRNT gives digital architects the means to capture, navigate, and visualise complex graph data structures with accuracy and ease. &lt;/p&gt;

&lt;p&gt;Having used DFRNT and had the pleasure of a demo from product creator &lt;a href="https://www.linkedin.com/in/hoijnet/" rel="noopener noreferrer"&gt;Philippe Höij&lt;/a&gt;, we wanted to do a quick write-up of DFRNT as we know many of our readers have a keen interest in ontologies, data modelling and graph visualisation.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is DFRNT and who is it for?
&lt;/h2&gt;

&lt;p&gt;DFRNT is a browser-based tool for digital architects and an advanced data/ontology modeller with graph visualisation. There is a free version, a SaaS version that includes hosting of TerminusDB graph data products, and a version for self-hosted (on localhost) TerminusDB instances.&lt;/p&gt;

&lt;p&gt;It provides access to an advanced bounded knowledge graph model which is fully type-checked, follows RDF semantics, and exposes both Datalog and JSON-LD document APIs for schema, queries, and updates. The model scales from personal knowledge graphs, all the way up to enterprise data product use cases.&lt;/p&gt;

&lt;p&gt;The product has been developed to do good. To help teams collaborate. To confront global issues such as climate change, global pandemic responses, and biodiversity challenges. And to make a positive difference to societal forces like healthcare, economic equality, and racism. A tool to help build shared data structures necessary to navigate the complexities changemakers face making an impact at scale.&lt;/p&gt;

&lt;p&gt;Creating change for such huge issues is no easy task. It requires collaboration, thought, and experimentation. A place for academics, business leaders, philanthropists, and digital architects to hypothesise, visualise, share, and plan. These challenges are not predictable, but with a focus on a shared vision and expected outcomes, the chances of success are greatly enhanced. The DFRNT launch brings core tools to address graph data collaboration where complexity is high, and to allow explorers to express graph data freely in the way it should be shaped. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“DFRNT exists to enable digital architects, changemakers and data scientists to collaboratively author, visualise and connect advanced data structures for change initiatives; and to derive new insights through the multiple perspectives offered by dynamic knowledge graph canvases.”&lt;br&gt;
Philippe Höij, founder of DFRNT&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  DFRNT for Business
&lt;/h3&gt;

&lt;p&gt;DFRNT helps businesses do good too. The challenges organisations face are complex. Cybersecurity, risk assessments, carbon emissions, and regulatory compliance are costly headaches, relationship-heavy, and difficult to understand. &lt;/p&gt;

&lt;p&gt;Digital architects, data scientists, and changemakers routinely have to resort to their corporate-imposed collaboration toolbox of Powerpoint, Excel and whiteboard applications offered centrally. It is a big stretch to call these graph tools and using inappropriate tooling makes solving problems that involve exploration and ideation based on hyperconnected datasets extremely challenging. &lt;/p&gt;

&lt;p&gt;To help these experts work well, they need multiple perspectives (canvases), data enrichment, and advanced query engines. A significant reason for Philippe to build DFRNT was to build a tool to serve his own needs as a digital and security architect.&lt;/p&gt;

&lt;p&gt;Here’s an example of DFRNT in action, using a risk assessment model aimed at helping a business’ cybersecurity department plan and mitigate security vulnerabilities and cyber threats. The data product view features the ability to describe data products using markdown to present the risk model cleanly and informatively to business users.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu8qov5lunsh2qmbvmgmo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu8qov5lunsh2qmbvmgmo.png" alt="Image description" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The risk model is then modelled using types and traits, modelled data structures attached to the types as properties. The example below is of an advanced type definition that uses both multiple inheritance and property composition (traits) for how a qualitative risk JSON-LD type may be modelled. Additionally, there is a recognized markdown data structure that enables strings to be recognized as markdown and rendered, which enables risk details to be expressed in formatted prose and be part of the data model. Risk data is then rendered together with inbound and outbound links to other elements of the graph to easily navigate the graph from a data exploration perspective.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fes5003jttmi07blcuvhe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fes5003jttmi07blcuvhe.png" alt="Image description" width="800" height="541"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The final image shows the canvas feature of DFRNT. Select data to add to a canvas for graph visualisation. Be as granular as you like, for example view the relationships between a single entity or take a look at the bigger picture and see the model as a whole. With the ability to save canvases and download them as images to use elsewhere, this is a great feature to share and understand the structure of the problem being solved.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo41o9j8ukchoqzf85gjz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo41o9j8ukchoqzf85gjz.png" alt="Image description" width="800" height="512"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Need for DFRNT
&lt;/h2&gt;

&lt;p&gt;Relational databases and SQL are the most popular type of database and query language. They are well-suited for organising and managing structured data in an efficient manner, however they are not well-suited for certain types of tasks, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Working in a model driven approach with embedded schema and embedded semantics.&lt;/li&gt;
&lt;li&gt;Performing complex mathematical computations or data analysis.
Handling real-time data streams or handling large numbers of concurrent users.&lt;/li&gt;
&lt;li&gt;Handling graph or hierarchical data.&lt;/li&gt;
&lt;li&gt;Enabling hierarchical grouping of buckets that are of the same kind.&lt;/li&gt;
&lt;li&gt;Making it easy to build thin apps that leverage the semantic model directly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These types of tasks are often at the heart of the problems that DFRNT is helping to solve. A better solution for these tasks is an RDF graph database that exposes schema-ful/typed data structures.&lt;/p&gt;

&lt;p&gt;Predictions for exponential growth of graph database adoption has been a feature of many ‘future trends’ reports, but this growth has never quite hit the forecast heights. Part of this problem has been a high barrier to entry. One of the main barriers to entry is data modelling. Graph databases require a different approach to data modelling, which can be challenging for developers who are used to working with relational databases. &lt;/p&gt;

&lt;p&gt;Despite the increasing popularity and maturity of graph databases, the lack of adequate modelling tools for them remains a major hindrance to their wider adoption, leaving many businesses and people struggling to harness the full potential of this powerful technology. &lt;/p&gt;

&lt;p&gt;What has been needed is the business-orientated TerminusDB-style closed-world interpretation of OWL, with strongly typed, discernable, and predictably structured graph data. Additionally, using a document-orientated style to express the graph makes taxonomies and ontologies easy to formulate and attach data properties and structures to. Data instances become clearly expressed in a schema with semantic descriptions. &lt;/p&gt;

&lt;p&gt;DFRNT is a service that makes this expressive TerminusDB way of modelling easy to use, we find this a remarkable feat. It radically simplifies data and ontology modelling and gives a whole new generation of digital architects powerful tools, and makes it even easier for those already in the field.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where TerminusDB fits in
&lt;/h2&gt;

&lt;p&gt;TerminusDB is used as DFRNT’s engine for graph data product instances. &lt;/p&gt;

&lt;p&gt;TerminusDB is an open-source (Apache 2.0) data model and hosting platform, professionally maintained by the TerminusDB team so you don’t have to. &lt;/p&gt;

&lt;p&gt;It features a secure version control system for collaborative data modelling, including branching for experimentation, prototyping and enhanced resilience. Any changes made are tracked with accuracy and security.&lt;/p&gt;

&lt;p&gt;These ‘git-for-data’ features were essential for DFRNT to provide a truly collaborative data product builder with several innovations to make cross-data product collaboration easy. Data developers can create a branch (shallow copy) of the main data branch and later “rebase” their changes on top of updates made later in the main (official) branch. Pull and push to remote branches are also elegantly implemented without the usual key-handling complexities. Errors and mistakes can be fixed quickly by branching an older version to retrieve old data, or rolling back to a known good version of the data product, just like working with code, &lt;/p&gt;

&lt;p&gt;DFRNT provides peace of mind knowing that your database remains secure and up to date no matter what happens during development and editing processes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get started with DFRNT
&lt;/h2&gt;

&lt;p&gt;Getting started with DFRNT is quick and easy. If you, or if you know of other fellow digital enthusiasts who want a better way to model data for complex problems, give it a go, their details are:&lt;/p&gt;

&lt;p&gt;Visit &lt;a href="https://dfrnt.com/" rel="noopener noreferrer"&gt;dfrnt.com&lt;/a&gt;.&lt;br&gt;
Follow DFRNT’s progress on &lt;a href="https://twitter.com/DFRNT_com" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; and &lt;a href="https://www.linkedin.com/company/dfrnt/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>cybersecurity</category>
      <category>dataanalysis</category>
      <category>networking</category>
      <category>learning</category>
    </item>
    <item>
      <title>A look ahead to our open-source headless content and knowledge management system</title>
      <dc:creator>Oliver</dc:creator>
      <pubDate>Tue, 10 Jan 2023 13:28:03 +0000</pubDate>
      <link>https://dev.to/terminusdb/a-look-ahead-to-our-open-source-headless-content-and-knowledge-management-system-50b4</link>
      <guid>https://dev.to/terminusdb/a-look-ahead-to-our-open-source-headless-content-and-knowledge-management-system-50b4</guid>
      <description>&lt;p&gt;In a little under a month, we will be launching TerminusCMS, an open-source headless CMS to provide businesses with composable architectures and true organization-wide knowledge management. &lt;/p&gt;

&lt;p&gt;TerminusCMS is not like existing headless CMS services such as Contentful which are primarily aimed at brochure websites and e-commerce stores. Our headless CMS is developer-first and is designed to enable engineers to build composable architectures and knowledge management systems for complex environments.&lt;/p&gt;

&lt;p&gt;The purpose of this article is to provide you with a better understanding of what TerminusCMS is and where it will fit within an organization. If TerminusCMS resonates with your project requirements, please &lt;a href="https://terminusdb.com/contact/" rel="noopener noreferrer"&gt;contact us&lt;/a&gt; to discuss your needs to get in the door first.&lt;/p&gt;

&lt;h2&gt;
  
  
  A brief history about how we got here
&lt;/h2&gt;

&lt;p&gt;Like with most startups, there have been a number of pivots in recent years as we seek our market fit. &lt;a href="https://terminusdb.com/blog/terminusdb-v10-1-0/" rel="noopener noreferrer"&gt;TerminusDB started life&lt;/a&gt; as a schema-supported graph database aimed at providing DataOps to engineers with our Git-like collaboration model. As we evolved and developed new features such as the &lt;a href="https://terminusdb.com/blog/terminusdb-v10-1-0/" rel="noopener noreferrer"&gt;version 4.0&lt;/a&gt; Document Interface we inadvertently began to broach the CMS space, at the time we even wrote in our community Discord server – &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;‘The document interface is basically a "CMS in a box" for terminus data objects. The documents page allows you to filter and order documents of different types, create documents of any type through a simple form, view documents as either JSON-LD or a simple web page, and edit, delete, create and update them with no code.’&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://terminusdb.com/blog/terminusdb-v10-1-0/" rel="noopener noreferrer"&gt;Version 10 of TerminusDB&lt;/a&gt; saw us move edge closer to becoming a headless CMS provider as users built collaborative and complex applications with TerminusDB and TerminusX as the backend. Following an increase in user activity in the headless space both within our community and the types of applications being built, we decided in June 2022 to focus our efforts to become the leading headless CMS provider giving organizations a compostable architecture for business-wide knowledge management.&lt;/p&gt;

&lt;p&gt;The next two weeks will see us integrate the past six month’s development into TerminusX and make TerminusCMS available as a Docker compose, complete with cloneable demos to play with.&lt;/p&gt;

&lt;h2&gt;
  
  
  Standard headless CMS features
&lt;/h2&gt;

&lt;p&gt;TerminusDB is and will continue to be available for those looking to build with our &lt;a href="https://terminusdb.com/products/terminusdb/" rel="noopener noreferrer"&gt;document graph database&lt;/a&gt;. TerminusCMS will be available as a separate Docker compose, it will come with new CMS features, that include –&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Schema model builder – develop with code or user interface&lt;/li&gt;
&lt;li&gt;GraphQL &amp;amp; REST API endpoints&lt;/li&gt;
&lt;li&gt;Admin UI&lt;/li&gt;
&lt;li&gt;Change requests workflows&lt;/li&gt;
&lt;li&gt;Media library&lt;/li&gt;
&lt;li&gt;User authentication and management&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are the standard features you would expect from any headless CMS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Features for composable architectures and knowledge management
&lt;/h2&gt;

&lt;p&gt;What sets TerminusCMS apart from other headless CMS providers are several features that have been in our arsenal for years and extended upon. Built from the database up, TerminusCMS gives devs many features at the database layer without being reliant on third-party data stores or troublesome plugins. The exciting features include –&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Schema as code&lt;/strong&gt; – Many CMS providers have invested in slick UIs to build content models and schemas. UIs are great for domains, but from a developer’s perspective, and when schema becomes complicated, are cumbersome and slow. It also causes issues when migrating or cloning schemas. TerminusCMS lets devs build with code to work quickly and efficiently.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Semantically enriched and standards-based CMS&lt;/strong&gt; – Under the hood of TerminusCMS is an RDF graph database that connects JSON documents into a knowledge graph. Developers can model content, asset, and data with relationships to provide context, discoverability, and advanced analytical capabilities.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Bridging data and content silos&lt;/strong&gt; – Headless CMS normally provides storage for content and assets, useful for websites. However, headless CMS can be used for so much more. For instance, using CMS as a composable architecture and knowledge management system necessitates the need to include often hidden transactional and operational data. TerminusCMS helps to bridge the gap between content and data to provide greater power to front-end developers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Custom workflows&lt;/strong&gt; – Approval workflows and pipelines are built into the database layer and can be completely customized to suit the needs of your organization and the individual working methodologies of your teams. Set specific workflows by user role or content/data type to tailor the way of working to your exact needs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Analytics engine&lt;/strong&gt; – We have integrated our datalog query language into GraphQL so you can use GraphQL as a full-blown graph query language. The connected graph of content, assets, and data can be analyzed with powerful GraphQL queries to make discovery and analytics available across your content infrastructure. With the advancements seen with OpenAI, get your content and data AI ready from the start.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Immutable version control &amp;amp; collaboration&lt;/strong&gt; – Our bread and butter from the very beginning. TerminusCMS is &lt;a href="https://terminusdb.com/blog/9-reasons-why-you-need-an-immutable-database/" rel="noopener noreferrer"&gt;immutable&lt;/a&gt; and has Git-like collaboration features allowing branching, cloning, merging, diff, time-travel, and non-lock collaboration. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Authentication from database to front-end&lt;/strong&gt; – Robust authentication is handled in the database layer so there is no need to integrate other auth providers into front-end apps making it easier and safer to control access.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What are the ideal use cases for TerminusCMS?
&lt;/h2&gt;

&lt;p&gt;TerminusCMS is aimed at large organizations with complex infrastructures. An ideal business is one that wants to understand and manage organization-wide knowledge by integrating interdepartmental data, processes, and content and then making it discoverable and useable internally and externally.&lt;/p&gt;

&lt;p&gt;As it’s early days, we don’t have a definitive list of sectors that TerminusCMS is aimed at, but we have a number of customers using TerminusDB as a headless backend for an array of applications and services and a number waiting to get started with TerminusCMS.&lt;/p&gt;

&lt;p&gt;Our primary focus, in the beginning, will be to work with developers and consultants who either work for or have large organizations as clients. Much of our development has been driven by the feedback from developers and consultants working on similar projects, and we see TerminusCMS as a perfect fit for –&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Manufacturing and Engineering&lt;/strong&gt; – Manufacturers and engineering companies are rich in data, products, and processes. Information about machine performance, product specifications, the supply chain, and distribution channels is spread across different platforms and made available to the relevant people in unconnected ways, causing many issues. TerminusCMS helps to connect disparate teams, provide leadership with a holistic view of operations, and serve relevant data and content to stakeholders.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Pharma &amp;amp; Biotech&lt;/strong&gt; – Pharmaceutical and biotech companies are complex operations. From discovery to launch, there are vast amounts of data, documentation, and content required for researchers, medical professionals, patients, and legislative bodies. TerminusCMS aims to help improve the flow of important data and content between departments, legislators, and patients, by making information discoverable and useable to surface relevant information to stakeholders. It is an auditable CMS for medical companies that need stringent governance.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;CMS for Compliance&lt;/strong&gt; – Cybersecurity is at the forefront of policymakers across the world. Ransomware and various other attacks have left major institutions in disarray. Both the EU and the US have plans for rigorous legislation to improve security and reduce the risk of cyber threats. TerminusCMS’s immutability and versioning are ideal for maintaining software bill of materials and vulnerability reports to ensure compliance and to be able to operate in these regions when legislation forces companies to take action.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The list of sectors that can benefit from TerminusCMS and its ability to develop a composable architecture that scales is by no means complete. Other ideas we’ve surfaced include semantically tagged training data for technical documentation so AI can serve customer documentation requirements, interactive blogs where users can branch and create their own twist of the content, and large-scale knowledge bases built collaboratively such as big research projects. The list could go on, and we haven’t even included the basics of brochure websites and e-commerce stores.&lt;/p&gt;

&lt;p&gt;Our goal is to provide developers with a headless CMS that makes their jobs easier. No more plugin architectures, no more security flaws, and no more dread of being dragged into a CMS project. Less time firefighting, more time building great things.&lt;/p&gt;

&lt;p&gt;If you think of areas where TerminusCMS would sit nicely, we’d love to hear from you, we’re a small team so ideas are always welcomed warmly. If you’ve got projects that you think TerminusCMS could help you with, &lt;a href="https://terminusdb.com/contact/" rel="noopener noreferrer"&gt;get in touch&lt;/a&gt; and we can provide you with a demo prior to its release.&lt;/p&gt;

</description>
      <category>discuss</category>
    </item>
    <item>
      <title>2022 - A Year In Review</title>
      <dc:creator>Oliver</dc:creator>
      <pubDate>Tue, 20 Dec 2022 16:03:35 +0000</pubDate>
      <link>https://dev.to/terminusdb/2022-a-year-in-review-3afj</link>
      <guid>https://dev.to/terminusdb/2022-a-year-in-review-3afj</guid>
      <description>&lt;p&gt;This year has gone by so fast. I know, I know, we say that EVERY year, and yet every year it’s true that the year in question really seems to have gone by a lot faster than the previous years. So much has happened in 2022, and there is so much to look forward to in 2023. However, all too often, we can get caught up in the rush of life and all the plans we have for the future, so much so that we lose track of just how far we have already come. &lt;/p&gt;

&lt;p&gt;As a tech start-up, we are almost always iterating, pushing out new features, and working on new products and services. TerminusDB has exciting plans for next year, but we are going to take a little moment to look back at all that’s happened in 2022. What better time to do a retrospective than at the end of the year, right? Here’s what happened, ICYMI: &lt;/p&gt;

&lt;h2&gt;
  
  
  Community and Collaboration
&lt;/h2&gt;

&lt;p&gt;The Terminator community is now 674 people strong! From modest beginnings to now nearly 700 people. We are delighted to be able to share this journey with you and look forward to more growth and success. As always, we are grateful for the feedback and welcome all questions. If you’re new to TerminusDB and haven’t joined our Discord community yet, you can do so &lt;a href="https://discord.com/invite/Gvdqw97"&gt;here&lt;/a&gt;. It’s a great space to chat all things TerminusDB. It’s also a great space for discussions on all things data and databases, so if that’s your vibe, we’re your tribe. &lt;/p&gt;

&lt;p&gt;As a result of our growing community and of being open source, we’ve been fortunate to have made friends that we have been able to collaborate with over the years. One of those projects has been on DFRNT with Philippe Hoij. If we’re being perfectly honest, this collaboration started in 2021, but has really come into full force and fruition this year. DFRNT is a SaaS platform that enables visual graph data collaboration while leveraging TerminusDB. DFRNT provides graph data product hosting, visualization and modeling, with data product hosting maintained by us over at TerminusDB. You can find out more &lt;a href="https://dfrnt.com/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Also this year, community member, Fidel Thomet, demonstrated how TerminusDB is being used as a collaborative research database to support the &lt;a href="https://uclab.fh-potsdam.de/projects/amazonia-future-lab/"&gt;Amazonia Future Lab project&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The purpose of the project is to derive knowledge from indigenous communities and collections spread across various cultural institutions. Collaborative working is at the very heart of the project and is essential to understand how certain artifacts were used, to create new interpretations of cultural heritage, and improve the representation of indigenous perspectives. The connected knowledge will be publicly accessible via interactive formats to explore and contribute to. The tools created will be made freely available to be used or further developed by other communities and institutions that are engaged in the field of participatory cultural education.&lt;/p&gt;

&lt;p&gt;To see how you might be able to use TerminusDB for a project of your own, have a look at our &lt;a href="https://terminusdb.com/use-cases/"&gt;use cases&lt;/a&gt;, or &lt;a href="https://terminusdb.com/contact/"&gt;get in touch&lt;/a&gt; with us.&lt;/p&gt;

&lt;h2&gt;
  
  
  Climate Action
&lt;/h2&gt;

&lt;p&gt;In July 2021, TerminusDB received an interesting email. It was to chat about how TerminusDB might be able to help with the development of a critical asset management tool to help governments be better prepared for disaster management and resilience in the face of climate change. After months of conversation with key stakeholders, and some furious development on our part, the CAMS MVP was launched, with a Spanish version of the tool made available shortly thereafter. CAMS is a critical asset management system developed as part of a voluntary effort, led by ARISE-US, a private-sector advisory group to the United Nations Office for Disaster Risk Reduction (UNDRR), and TerminusDB.&lt;/p&gt;

&lt;p&gt;The project is open source and focused on providing a data-driven system to help islands, cities, and communities become disaster resilient. It does this by storing all critical asset data and the asset dependencies. CAMS is a data-driven mobile and desktop application. Government officials and first responders use it to plan and respond to disasters. Learn more about the project &lt;a href="https://climateresilient.world/"&gt;here&lt;/a&gt;, and see how you can contribute. &lt;/p&gt;

&lt;h2&gt;
  
  
  Compliance
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OrDP8hka--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fwvr4kxz84cp5g4gg9tw.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OrDP8hka--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fwvr4kxz84cp5g4gg9tw.jpg" alt="A Dobby Soc 2 meme - it went over my head, no idea who Dobby is" width="749" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We achieved SOC 2 compliance! While some of our team are still somewhat recovering from the trauma, we did it all for you! The SOC 2 compliance certification is the most widely recognized method for sharing independently reviewed information about a service organization’s internal controls. The accreditation means that TerminusDB and TerminusX meet or exceed the levels of security required for service providers storing customer data in the cloud.&lt;/p&gt;

&lt;p&gt;While we have always taken data security and privacy very seriously – we are after all, a database company – having SOC 2 certification lets us demonstrate to new and existing customers these matters are a priority to us.&lt;/p&gt;

&lt;h2&gt;
  
  
  New Features and New Releases
&lt;/h2&gt;

&lt;p&gt;There have been plenty of new features, along with a few releases this year.&lt;/p&gt;

&lt;h3&gt;
  
  
  TerminusDB CLI
&lt;/h3&gt;

&lt;p&gt;If you’re signed up for our newsletters, you’d have received some updates on the TerminusDB CLI for Push / Pull / Clone. TerminusDB is designed to be a distributed database with a collaboration model. The building blocks of that model are revision control, diff, and push/pull/clone. It is essentially meant to be like Git, but for data. The foundation of a distributed database with a collaboration model are Revision Control, Diff, and Push/Pull/Clone. Read more about the TerminusDB CLI, and the building blocks of a distributed database with a collaboration model &lt;a href="https://terminusdb.com/blog/distributed-database-with-collaboration-model/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  TerminusDB 10.1: The Mule
&lt;/h2&gt;

&lt;p&gt;We had a need for speed, so we released TerminusDB 10.1, which demonstrated significant improvements to the speed and performance of the document interface, including much improved WOQL integration.&lt;/p&gt;

&lt;p&gt;The Mule Release – a homage to Asimov’s Foundation series, of course – was faster, more robust, and packed with new features to make building knowledge graphs and data-intensive applications easier and quicker. Some of the noteworthy elements in the new release were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Type inference&lt;/li&gt;
&lt;li&gt;Unconstrained and untypechecked JSON documents and subdocuments&lt;/li&gt;
&lt;li&gt;ID capture&lt;/li&gt;
&lt;li&gt;Document UI SDK&lt;/li&gt;
&lt;li&gt;JSON Diff &amp;amp; Patch&lt;/li&gt;
&lt;li&gt;Applying a patch from any two&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The release also included improvements to the CLI, Oauth2 configuration for SSO, and more features you can read more in detail about &lt;a href="https://terminusdb.com/blog/the-mule-release/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  More Speed with TerminusDB 10.1.5
&lt;/h3&gt;

&lt;p&gt;This version of TerminusDB included some significant performance improvements, including a 10x speedup of document retrieval and a 10x improvement in insertion speed. More details can be found &lt;a href="https://terminusdb.com/blog/terminusdb-10-1-5-release/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;GraphQL is here&lt;br&gt;
This is something that we’ve been working on for the better part of this year, and we were excited to announce that it finally happened. The latest TerminusDB release came with GraphQL to further improve the developer experience. Your data product schemas are automatically loaded in GraphQL for speed and convenience. GraphQL means you can now use most programming languages to work with TerminusDB. This release was a big step toward TerminusDB becoming a headless CMS and managing content in a knowledge graph. You can read more about our plans for &lt;a href="https://terminusdb.com/blog/terminusdb-a-headless-cms/"&gt;headless CMS here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Company and Celebrations
&lt;/h2&gt;

&lt;p&gt;Over the course of the pandemic, we became rather good at throwing virtual parties and celebrations - all complete with themed quizzes created by the ultimate quiz master, i.e., me.&lt;/p&gt;

&lt;p&gt;Jokes aside, 2022 was the first time in a long time we all finally got together in person for a week’s worth of planning - in beautiful Vienna to boot! Many of the new features and developments you’ve been seeing over the course of the year have been a result of all we discussed during that week’s worth of planning. We had an internal retrospective recently, as part of one of our discovery sessions, and almost everything we had planned in Vienna has been implemented.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Nsc6XtiO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c61zq7cnxfu6kk9jwtw3.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Nsc6XtiO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c61zq7cnxfu6kk9jwtw3.jpg" alt="The TerminusDB team posing for a picture in Vienna" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As per usual, we focused all too much on all the things that are yet to be done and all the plans we have for the future. Still, I think we should be very proud of what we’ve accomplished this year. I, personally, am always in awe of our team. We are small in number, but strong in might. We ended this year of work with a lovely (virtual) Christmas party, and would like to take this opportunity to wish all of you who are along on this ride with us a very Happy Christmas and a joyous New Year.&lt;/p&gt;

&lt;p&gt;See you in 2023 with lots more exciting stuff coming your way.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--42peMykr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7brwfene5zsua0qksf6n.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--42peMykr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7brwfene5zsua0qksf6n.jpg" alt="The TerminusDB team on Zoom at the Christmas Quiz" width="880" height="432"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>startup</category>
      <category>review</category>
      <category>database</category>
      <category>watercooler</category>
    </item>
    <item>
      <title>TerminusDB - Now with GraphQL</title>
      <dc:creator>Oliver</dc:creator>
      <pubDate>Thu, 17 Nov 2022 14:20:12 +0000</pubDate>
      <link>https://dev.to/terminusdb/terminusdb-now-with-graphql-1njo</link>
      <guid>https://dev.to/terminusdb/terminusdb-now-with-graphql-1njo</guid>
      <description>&lt;p&gt;For those of you that don't know, TerminusDB is a document graph database with Git-like superpowers. It stores data as JSON documents and the schema language connects these into a graph. You get the best of both worlds, the convenience of JSON with the query power of Graph.&lt;/p&gt;

&lt;p&gt;The latest version of TerminusDB has been released which means TerminusDB now comes with GraphQL to improve the developer experience. Your data product schemas are automatically loaded in GraphQL for speed and convenience. GraphQL means you can now use any programming language to work with TerminusDB.&lt;/p&gt;

&lt;p&gt;Enhancements include – &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GraphQL endpoint at localhost:6363/api/graphql/ORG/DB&lt;/li&gt;
&lt;li&gt;GraphiQL endpoint for testing available at localhost:6363/graphiql/ORG/DB&lt;/li&gt;
&lt;li&gt;GraphQL automatic loading of Schema in GraphQL schema&lt;/li&gt;
&lt;li&gt;GraphQL search and retrieval&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/terminusdb/terminusdb/releases/tag/v10.1.8"&gt;Head on over to GitHub&lt;/a&gt; for a full list of enhancements and bug fixes.&lt;/p&gt;

&lt;p&gt;If you want to learn more, take a look at our &lt;a href="https://terminusdb.com/docs/guides/reference-guides/graphql_query"&gt;GraphQL docs&lt;/a&gt; or &lt;a href="https://terminusdb.com/docs/"&gt;install the latest version of TerminusDB&lt;/a&gt; to experiment and play.&lt;/p&gt;

&lt;p&gt;Excitingly, this release is a big step toward our TerminusDB becoming a headless CMS and connecting content in a knowledge graph. You can read more about our plans for &lt;a href="https://terminusdb.com/blog/terminusdb-a-headless-cms/"&gt;headless CMS&lt;/a&gt; here.&lt;/p&gt;

&lt;p&gt;Here’s a quick example of how GraphQL works in TerminusDB – &lt;/p&gt;

&lt;p&gt;Using the following TerminusDB schema:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"@type"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Class"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"@id"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Person"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"xsd:string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"dob"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"xsd:dateTime"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"friend"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"@type"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Set"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"@class"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Person"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;TerminusDB will generate the following GraphQL class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="err"&gt;"""&lt;/span&gt;&lt;span class="n"&gt;skip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;elements&lt;/span&gt;&lt;span class="err"&gt;"""&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Int&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="err"&gt;"""&lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;elements&lt;/span&gt;&lt;span class="err"&gt;"""&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Int&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Person_Filter&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="err"&gt;"""&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;by&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;given&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="err"&gt;"""&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;orderBy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Person_Ordering&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;!]!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Person&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;dob&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;friend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="err"&gt;"""&lt;/span&gt;&lt;span class="n"&gt;skip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;elements&lt;/span&gt;&lt;span class="err"&gt;"""&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Int&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="err"&gt;"""&lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;elements&lt;/span&gt;&lt;span class="err"&gt;"""&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Int&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Person_Filter&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="err"&gt;"""&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;by&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;given&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="err"&gt;"""&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;orderBy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Person_Ordering&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;!]!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can then query this using the &lt;a href="https://terminusdb.com/docs/guides/reference-guides/graphql_query/connecting_to_graphql"&gt;GraphQL endpoint&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  We’ve got more planned
&lt;/h2&gt;

&lt;p&gt;TerminusDB automatically generates fields in GraphQL based on classes and their properties in the data product schema. Here you can use arguments such as id, offset, limit, and order by, as well as filters such as time comparisons and for strings, and, or, not. All the filter options are also automatically generated for you.&lt;/p&gt;

&lt;p&gt;We’re not stopping here though, we are working to integrate WOQL into GraphQL so that you can use GraphQL for complex path queries to make building graph-based applications even easier.&lt;/p&gt;

&lt;p&gt;Our goal is to be the smoothest and most powerful GraphQL experience around.&lt;/p&gt;

&lt;p&gt;We hope you join us on this journey.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other resources that you might like
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://terminusdb.com/blog/graphql-rdf-bridge-using-star-wars-dataset/"&gt;RDF &amp;amp; GraphQL Bridge using a Star Wars Dataset&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://terminusdb.com/blog/implement-graphql-in-fourteen-days/"&gt;How we implemented GraphQL in 14 days&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/terminusdb/terminusdb-tutorials/blob/master/terminusBlog"&gt;Tutorial – Build a blog-focused CMS with TerminusDB &amp;amp; GraphQL&lt;/a&gt;&lt;/p&gt;

</description>
      <category>graphql</category>
      <category>database</category>
      <category>news</category>
      <category>showdev</category>
    </item>
    <item>
      <title>TerminusDB Internals - Part 1 - Graph Representation</title>
      <dc:creator>Oliver</dc:creator>
      <pubDate>Thu, 17 Nov 2022 12:06:03 +0000</pubDate>
      <link>https://dev.to/terminusdb/terminusdb-internals-part-1-graph-representation-3f6p</link>
      <guid>https://dev.to/terminusdb/terminusdb-internals-part-1-graph-representation-3f6p</guid>
      <description>&lt;p&gt;When we were designing TerminusDB we wanted to make a graph database which would allow curation of large complex graphs where the search mode (we’ll talk a bit about modes in a minute) was not yet known. In playing with some large graphs (for instance a database of all Polish companies since the mid 1990s) we came to the following conclusion:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Graphs, due to their highly interconnected nature, are hard to segment&lt;/li&gt;
&lt;li&gt;This makes paging, and network communication, potentially very costly when edges are traversed across segments&lt;/li&gt;
&lt;li&gt;Main memory size can accommodate many very large graphs even for enterprise scale problems.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Point two is especially hard to overcome. If you start paging in a graph, because of the very random nature of graphs, we can get extremely varied pages. This could easily lead to thrashing. Since disk access times on fast SSD are one to two orders of magnitude slower than DRAM we’re talking perhaps 100 times worse performance. For networks this climbs to three orders of magnitude.&lt;/p&gt;

&lt;p&gt;These considerations let us to speculate that we could build an in-memory graph database if we were careful to ensure that we were sparing with memory use.&lt;/p&gt;

&lt;p&gt;In addition to this in-memory advantage, having low memory overhead has additional benefits for collaboration, in that we can send deltas around in a compact format.&lt;/p&gt;

&lt;p&gt;Size matters, and smaller is better.&lt;/p&gt;

&lt;p&gt;It’s important to note, that by in-memory we mean that there is no database paging, we do not move parts of the database into and out of memory. It does not mean that we are not writing to disk. TerminusDB is both ACID and persistent, so there is no risk of data loss for completed transactions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Relational indexing
&lt;/h2&gt;

&lt;p&gt;In typical databases, we ask questions of relations. Relations are typically built from tables of facts together with some operations which allow us to join relations. In addition we often specify particular projections of these relations by asking for certain constraints to hold.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;Orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OrderID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Customers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CustomerName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OrderDate&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Orders&lt;/span&gt;
&lt;span class="k"&gt;INNER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;Customers&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;Orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CustomerID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Customers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CustomerID&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;Orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OrderDate&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;2010&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;04&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;01&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Create a new relation by joining two relations, Customers and Orders on the customer ID field and project to a relation in which all dates are more recent than 2010-04-01 using the WHERE restriction.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In order to make these queries efficient we will often introduce indexing on the fields which we want to join or restrict. If we do not do this, then we might have to scan through each &lt;code&gt;CustomerID&lt;/code&gt; in the one table to see if it is in the second table. A scan means we look at each record. If the size of &lt;code&gt;Customers&lt;/code&gt; is &lt;code&gt;n&lt;/code&gt; (&lt;code&gt;|Customers| =n&lt;/code&gt;) and &lt;code&gt;Orders&lt;/code&gt; is &lt;code&gt;m&lt;/code&gt; (&lt;code&gt;|Orders| = m&lt;/code&gt;), then this would result in a number of operations on the order of &lt;code&gt;n*m&lt;/code&gt;. Similarly, if we have not indexed the date field, we will have to compare the date for each record that we produce.&lt;/p&gt;

&lt;p&gt;With an index, we can make access log like, by creating a tree which makes it possible to access the data without a scan. Our date restriction will allow us to start our search in the right place in the relation which will make our effective m smaller (which we might call m', and the order of the problem shrinks to n * log(m').&lt;/p&gt;

&lt;p&gt;If &lt;code&gt;n&lt;/code&gt; and &lt;code&gt;m&lt;/code&gt; are big numbers, then this is a big deal. If we have more than one join, we can easily see how unindexed relations could lead to numbers which spiral out of control and are effectively impossible to query.&lt;/p&gt;

&lt;h2&gt;
  
  
  Graph Databases
&lt;/h2&gt;

&lt;p&gt;In a graph database, we remove a good deal of this complexity by having exactly one type of relation, the edge. And since we want to be able to explore the graph very freely, we will need to index everything. This will let us join to create paths through the graph. Everything is a self join of a single relation in a graph, the edge relation.&lt;/p&gt;

&lt;p&gt;For a search we can think of querying our relation in any of the possible modes. A mode means specifying what we know, versus what we need to look for. We write a &lt;code&gt;-&lt;/code&gt; for anything we don’t know, and a &lt;code&gt;+&lt;/code&gt; for anything we do. We can write our modes as a triple such as &lt;code&gt;(+,+,-)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Graph modes include:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(-,-,-)
(-,-,+)
(-,+,-)
(-,+,+)
(+,-,-)
(+,-,+)
(+,+,-)
(+,+,+)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;…so eight query modes that we might want to support.&lt;/p&gt;

&lt;p&gt;Now, some of these might be more common than others, and therefore we might want to make them faster. We’ll talk about that later when we get into the guts of our index.&lt;/p&gt;

&lt;p&gt;By way of example, let’s think of a data point named &lt;code&gt;Joe&lt;/code&gt;. If we wanted to see everything that &lt;code&gt;Joe&lt;/code&gt; is connected to, we might say (in &lt;a href="https://terminusdb.com/blog/the-power-of-web-object-query-language/" rel="noopener noreferrer"&gt;WOQL&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Vars&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;p&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;triple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Joe&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This has mode &lt;code&gt;(+,-,-)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If we wanted to see everything that was connected to everything that Joe was connected to, we might say:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Vars&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;p&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;q&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;y&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;and&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;triple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Joe&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nf"&gt;triple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we start with &lt;code&gt;(+,-,-)&lt;/code&gt; and it might at first appear that we have &lt;code&gt;(-,-,-)&lt;/code&gt; for our second search. However, we can get the results back from the first search to reduce this to &lt;code&gt;(+,-,-)&lt;/code&gt; meaning we can consider a projection of the relation speeding things up considerably.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sparing Use of Memory
&lt;/h2&gt;

&lt;p&gt;Doing all of this indexing makes query very flexible. We can weave relationships out of the graph and instead of having all sorts of different tables with different shapes we can just have one big table.&lt;/p&gt;

&lt;p&gt;This is also potentially very fast if everything is indexed and we can do restrictions quickly.&lt;/p&gt;

&lt;p&gt;But it also threatens to make things very big. How many indexes will we need to build all of this? The mode &lt;code&gt;(-,-,-)&lt;/code&gt; is just the whole relation so doesn’t need an index. For &lt;code&gt;(+,+,+)&lt;/code&gt; we can probably permit ourselves a scan on one of the fields as we’ve already restricted heavily. What can we get away with?&lt;/p&gt;

&lt;p&gt;Indexes are trees that help us get log like behaviour out of our searches. Trees often mean adding lots of pointers. Pointers can actually end up being a substantial amount of the size of an indexed database as we create the many layers of the tree.&lt;/p&gt;

&lt;p&gt;If our database grows too great in size, we threaten to hit the size of main memory. We can often add more memory, but the more parsimonious our representation, the more databases will fit into a given memory size. It therefore pays handsomely to be small.&lt;/p&gt;

&lt;p&gt;However, we can solve this problem with a family of data structures known as succinct data structures. These try to keep fast, log-like access modes available, while keeping storage space close to the size of the data itself.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Dictionary
&lt;/h3&gt;

&lt;p&gt;The first point of entry is the dictionary. Given a name (such as &lt;code&gt;Joe&lt;/code&gt;) we want to come up with an internal identifier which consists of an unsigned 64 bit integer. This will be our internal name for &lt;code&gt;Joe&lt;/code&gt;, which takes a fixed and relatively small amount of space.&lt;/p&gt;

&lt;p&gt;Essentially we need a table which goes from integers to names and back again.&lt;/p&gt;

&lt;p&gt;In TerminusDB we do this with a data structure known as a &lt;a href="https://en.wikipedia.org/wiki/Incremental_encoding" rel="noopener noreferrer"&gt;Front coded dictionary&lt;/a&gt;. This structure is particularly good when your identifiers are likely to share prefixes. Since all named identifiers in TerminusDB are &lt;a href="https://en.wikipedia.org/wiki/Incremental_encoding" rel="noopener noreferrer"&gt;IRIs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If we have a block size of eight entries, a block might look as follows:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Input&lt;/th&gt;
&lt;th&gt;Common prefix&lt;/th&gt;
&lt;th&gt;Compressed output&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;myxa&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;0 myxa&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;myxophyta&lt;/td&gt;
&lt;td&gt;'myx'&lt;/td&gt;
&lt;td&gt;3 opyta&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;myxopod&lt;/td&gt;
&lt;td&gt;'myxop'&lt;/td&gt;
&lt;td&gt;5 od&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;nab&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;0 nab&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;nabbed&lt;/td&gt;
&lt;td&gt;'nab'&lt;/td&gt;
&lt;td&gt;3 bed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;nabbing&lt;/td&gt;
&lt;td&gt;'nabb'&lt;/td&gt;
&lt;td&gt;4 ing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;nabit&lt;/td&gt;
&lt;td&gt;'nabit'&lt;/td&gt;
&lt;td&gt;3 it&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The basic idea is to store deltas between strings, allowing us to use less space to store all of our strings than would be required with a straight dictionary. The dictionary is organized in blocks with each block starting with a full, uncompressed entry, and all subsequent entries in the block are allowed to refer back to any element in the block to reuse parts of previous elements.&lt;/p&gt;

&lt;p&gt;As you can see, we get substantial compression here by reusing prior entries. But we have to start over with no common prefix every block size, and if we share nothing, we get a slight disimprovement (as we have to store a zero offset as a prefix to our compressed output).&lt;/p&gt;

&lt;p&gt;This gives reasonably good compression for typical IRIs, often between 40 and 80%.&lt;/p&gt;

&lt;p&gt;To get our ID, we simply need to know numerically which entry we are in the dictionary. Since we store the dictionary entries lexically sorted, we can find a specific entry using binary search. First we look up &lt;code&gt;Jim&lt;/code&gt; in the middle of the dictionary, if the first entry in our middle block is bigger than &lt;code&gt;Jim&lt;/code&gt;, we scan through the block, if we’re bigger than every entry of the block, we search in the middle of the second half of the dictionary. If we are less than &lt;code&gt;Jim&lt;/code&gt; we can search in the first half. Wash-rinse-repeat, we have access to our index using an access mode that guarantees we get our answer in a log of the size of the dictionary &lt;code&gt;O(log(n))&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;And going in reverse we can find the dictionary entry for a given integer in O(1) (constant) time. We do this by keeping around an index of block-offsets so we can quickly find which block we are in. The block offsets themselves are stored in a compressed data structure which has constant time access (in our case a Log Array, but it could be another data structure such as an &lt;a href="https://www.antoniomallia.it/sorted-integers-compression-with-elias-fano-encoding.html" rel="noopener noreferrer"&gt;Elias-Fano encoding&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;For our graphs we have three dictionaries. One for nodes, which form the subjects/objects, one for predicates or edges and one for values.&lt;/p&gt;

&lt;p&gt;The first shared dictionary allows us to use the same subject id for an object which is convenient in following chains. Predicates, or the edge name, is treated specially as it is seldom necessary to look them up in a chain, so they can reside in their own name space, can can use an overlapping range of integer ids with the nodes.&lt;/p&gt;

&lt;p&gt;We also represent our data in a value dictionary. These are represented as a specific offset above the numbers used for nodes, which we add to the number of our dictionary entry to translate to and from id-space.&lt;/p&gt;

&lt;p&gt;Note, that to store data effectively here using a dictionary with front encoding, it is important to store everything lexically. We’ll address lexical encodings of data types in another future blog.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Adjacency List
&lt;/h3&gt;

&lt;p&gt;Once we have our id, we need to see how we are connected. Let’s imagine we are starting with &lt;code&gt;Joan&lt;/code&gt; and our access mode is &lt;code&gt;(+,-,-)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Perhaps there are only two people in our database for the moment, and our graph looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Jim -address-&amp;gt; "12 Mulberry Lane"
Jim -dob-&amp;gt; "1963-01-03"
Jim -friend-&amp;gt; Jim
Jim -friend-&amp;gt; Joan
Jim -name-&amp;gt; "Jim-Bob McGee"
Joan -address-&amp;gt; "3 Builders street, house number 25, apartment number 12"
Joan -dob-&amp;gt; "1985-03-12"
Joan -name-&amp;gt; "Joan Doe"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our node dictionary is &lt;code&gt;{'Jim':1,'Joan':2}&lt;/code&gt; our predicate dictionary is: &lt;code&gt;{address:1,dob:2,friend:3,name:4}&lt;/code&gt; and our value dictionary is: &lt;code&gt;{'12 Mulberry Lane':1,'3 Builders street, house number 25, apartment number 12':2,'1963-01-03':3,'1985-03-12':4,'Jim-Bob McGree':5,'Joan Doe':6}&lt;/code&gt;. Note that we still have to apply an offset of 2 (the size of our node dictionary) to our value dictionary to get back and forth between id space and our dictionary entry number for values. Also, all dictionaries are 1 indexed rather than zero. We will treat zero as a special identifier representing emptiness.&lt;/p&gt;

&lt;p&gt;To find out what predicates are associated with &lt;code&gt;Joan&lt;/code&gt; we need to look them up in an adjacency list. Internal to TerminusDB this is represented with a pair of data structures. A log-array and a bit index. The log array stores the ids of our associated predicates, and the bit index tells us which predicates are associated with which subject.&lt;/p&gt;

&lt;p&gt;For instance, to look up &lt;code&gt;Joan&lt;/code&gt;, we first look up the second entry (Joan’s id is 2). This is done with our succinct data structure, the bit index.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Conceptual Structure
&lt;/h3&gt;

&lt;p&gt;Our bit index looks conceptually as follows:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Subjects&lt;/th&gt;
&lt;th&gt;Subject Id&lt;/th&gt;
&lt;th&gt;Bit Index&lt;/th&gt;
&lt;th&gt;SP Array&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Jim&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Jane&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The bit index tells us what subject is associated with which entry of the SP Array (using select), and the SP Array holds the predicate id which is available to that subject. The index of the SP Array is conceptually an Subject-Pair identifier, refering to a concrete subject (available by asking the rank of the bit-index, the population count of 1s at the specific offset) and simply by returning the value of the SP Array at that index.&lt;/p&gt;

&lt;p&gt;Making the rank and select operations fast for bit-indexes is its own subject which we will talk about in a moment. For now it’s just important to know why we want them to be fast.&lt;/p&gt;

&lt;p&gt;We have the following the concrete representations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SP BitIndex: 1110110
SP Array: [1,2,3,4,1,2,4]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can ask for the &lt;code&gt;(subject_id - 1)&lt;/code&gt;th 0 of our bit-index to find out which section of the bit index we are interested in. So for instance, if we want to know what Jane is connected to, we look up the index of the 1st 0, add one, and we find our subject-predicate starting index. In this case that index is 5.&lt;/p&gt;

&lt;p&gt;We can then iterate over this vector up until we reach the next 0 in our bit index, and these are all the SP pairs associated with Jane. In other words, the indexes into the SP array of 5 through 7 are associated with the Subject 2, and have predicates 1, 2 and 4.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                 Jane's bits in the bit index
                      | | |
SP BitIndex:  1 1 1 0 1 1 0
SP Array:    [1,2,3,4,1,2,4]
                      | | |
                      Jane's associated predicates:
                        address (1), dob (2), name (4)
                        ...but not friend(3)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, supposing we have an SP index, how do we find the object associated? Since each predicate can point to multiple objects, we will use the same trick again. In our case we only have one object for each element of the SP Array, Here, we use the Object Log Array.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SP BitIndex:  1 1 1 0 1 1 0
SP Array:    [1,2,3,4,1,2,4]
O BitIndex:   0 0 1 0 0 0 0 0
Object Array [3,5,1,2,7,4,6,8]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ok, so what object is associated with Jane’s name? First, we look up the name to see that it is predicate id 4. We know that Jane starts at index 5, so we can scan through the SP vector until we get a 4. We remember this index, which is 7.&lt;/p&gt;

&lt;p&gt;We can now ask for the index of the (7-1=6)th zero in the Object BitIndex and add one to get 7. The number at this index is 8. Since &lt;code&gt;8 &amp;gt; 2&lt;/code&gt;, the max number for nodes, we know this is a value. We can subtract 2, get 6, and look up the number in the value dictionary, to retrieve “Joan Doe” (6). Success!&lt;/p&gt;

&lt;p&gt;Notice that there is one 1 in the Object BitIndex. This corresponds with the fact that the predicate friend (3) has two elements in the Object Array. The 3 in the SP array corresponds with friend. It is at index 3. When we look up the (3-1)th zero in the Object index we get a 1, we add one to get 2, see that there is a 1 in this position, and know that there is more than one value. We count the number of values (number of ones + 1), and get that there are two values. We can now iterate in the Object arroun from the index 3 to the index 4.&lt;/p&gt;

&lt;p&gt;The answers here are 1, and 2, both are less than or equal to our node count, so we are nodes. We can look up the node names in our dictionary and find it is Jim, and Joan!&lt;/p&gt;

&lt;h3&gt;
  
  
  Reversal of Fortune
&lt;/h3&gt;

&lt;p&gt;But how do we go backward from object id to SP? We need another data structure here. In this case it will be the inverted index of the Object Array.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;O_SP BitIndex:  0 0 0 0 0 0 0 0
O_SP Array     [2,2,0,5,1,4,3,6]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we know an object Id, for instance, we want to find the object associated with the name “Joan Doe” we simply look it up in the Object dictionary, get an id of 6, add two (the offset for nodes) to get 8. Now we look up where we are in the Object to SP bit index, by asking for the (8-1)th 0, and add 1 and find that we are at SP index of 6. If we look up this index (6) in the SP_array, we find that it corresponds with name (4), and we can count the number of zeros in the SP BitIndex up to this point to our Subject identifier which is 2, the id of Jane!&lt;/p&gt;

&lt;h3&gt;
  
  
  Bit Indexes
&lt;/h3&gt;

&lt;p&gt;Our bit index lets us perform two operations, &lt;code&gt;select&lt;/code&gt; and &lt;code&gt;rank&lt;/code&gt;. We were using them above informally to find the ith zero (&lt;code&gt;my_array.select(0,i)&lt;/code&gt;), and the population of zeros up to an index (&lt;code&gt;my_array.rank(0,i)&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Of course we could implement these operations trivially by scanning through a bit vector, but that would take time proportional to the number of elements &lt;code&gt;O(n)&lt;/code&gt;. To do it faster we need to keep some additional information.&lt;/p&gt;

&lt;p&gt;We can see here that our implementation in rust keeps around some book-keeping, a &lt;code&gt;BitArray&lt;/code&gt;, and &lt;code&gt;LogArray&lt;/code&gt; representing blocks and sblocks.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;BitIndex&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;BitArray&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;blocks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;LogArray&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;sblocks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;LogArray&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The bit array just contains the bits themselves. The blocks however, keep track of population counts so we know the rank, and the super blocks keep track of super population counts.&lt;/p&gt;

&lt;p&gt;For instance, for the bit sequence used above, blocks of size 3 and super blocks of size 6, we might have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BitArray:  1 1 1 0 1 1 0
Blocks:   |  3  |  2  |  0  |
SBlocks:  |     5     |     0    |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To find the number of 1s at index j, we simply make the calculation of the number of bits up to j in a block, and use the population counts from blocks and superblocks. In rust pseudocode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;BitArray&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_bits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0b1110110&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;block_index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;sblock_index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;block_index&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;block_rank&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;blocks&lt;/span&gt;&lt;span class="nf"&gt;.sum_rank&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;blocks_index&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;sblock_rank&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sblocks&lt;/span&gt;&lt;span class="nf"&gt;.sum_rank&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sblock_index&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;bits_rank&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="nf"&gt;.sum_rank&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;block_index&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;sbock_rank&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;block_rank&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;bits_rank&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here the &lt;code&gt;sum_rank&lt;/code&gt; function just counts the rank as a sum from the last superblock, last block or last bit sequence. This can be considered a constant time operation as each of these sizes is fixed and though we have to compute all three, three constant time operations is also constant.&lt;/p&gt;

&lt;p&gt;The rank of index 4 is now calculated with &lt;code&gt;block_index = 1&lt;/code&gt;, &lt;code&gt;sblock_index = 0&lt;/code&gt;. Therefore we take 5, subtract 2, and add 1, to get 4. Indeed the correct rank. The number of zeros is the index + 1 minus the population count of 1s, or 1.&lt;/p&gt;

&lt;p&gt;So rank is done, but what about select? It turns out we can implement select by doing a binary search with rank. Recall that select finds the &lt;code&gt;ith&lt;/code&gt; zero or one. To implement this we can simply start from the mid point, look at the rank, and keep subdividing until we get a select that matches. Since rank is &lt;code&gt;O(n)&lt;/code&gt;, this means that select, implemented in this way is &lt;code&gt;O(log(n))&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;If you’ve made it this far then congratulations, you’ve a lot of stamina! What you’ve learned so far is enough to make a graph with flexible query modes that can be used as a foundation for a database.&lt;/p&gt;

&lt;p&gt;All of these succinct structures are tightly packed and therefore optimised to be write once, read often. They aren’t well suited to mutable updates. In order to get these mutations, we’ll need some more machinery. &lt;a href="https://terminusdb.com/blog/terminusdb-internals-2/" rel="noopener noreferrer"&gt;In my next blog, we’ll dig into how that works&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This blog was written by TerminusDB CTO Gavin Mendel-Gleason and posted here on his behalf.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>beginners</category>
      <category>welcome</category>
    </item>
  </channel>
</rss>
