WOQL RDF List Operations

RDF Lists (rdf:List) are linked list data structures in RDF that use rdf:first, rdf:rest, and rdf:nil predicates. TerminusDB provides a comprehensive WOQL library for manipulating these structures efficiently.

What You'll Learn

By the end of this guide, you'll understand:

  • How RDF lists are structured internally
  • How to create, read, and modify RDF lists using WOQL
  • Best practices for list operations in TerminusDB

Understanding RDF List Structure

An RDF list is a chain of "cons cells" where each cell contains:

  • rdf:first → the value at this position
  • rdf:rest → pointer to the next cell (or rdf:nil for the end)
Example: Text
List: ["First", "Second", "Third"]

Structure:
Cell1 → rdf:type = rdf:List
     → rdf:first = "First"
     → rdf:rest = Cell2

Cell2 → rdf:type = rdf:List
     → rdf:first = "Second"
     → rdf:rest = Cell3

Cell3 → rdf:type = rdf:List
     → rdf:first = "Third"
     → rdf:rest = rdf:nil

Operations Overview

Quick Start Example

Here's a complete example demonstrating common list operations:

Example: JavaScript
import { WOQLClient, WOQL } from "@terminusdb/terminusdb-client";

const client = new WOQLClient("http://localhost:6363", {
  user: "admin",
  organization: "admin",
  key: "root"
});

// Create a list manually, independent of documents
const createList = WOQL.and(
  WOQL.idgen_random("terminusdb://data/Cons/", "v:cell1"),
  WOQL.idgen_random("terminusdb://data/Cons/", "v:cell2"),
  WOQL.idgen_random("terminusdb://data/Cons/", "v:cell3"),
  
  WOQL.add_triple("v:cell1", "rdf:type", "rdf:List"),
  WOQL.add_triple("v:cell1", "rdf:first", WOQL.string("Task A")),
  WOQL.add_triple("v:cell1", "rdf:rest", "v:cell2"),
  
  WOQL.add_triple("v:cell2", "rdf:type", "rdf:List"),
  WOQL.add_triple("v:cell2", "rdf:first", WOQL.string("Task B")),
  WOQL.add_triple("v:cell2", "rdf:rest", "v:cell3"),
  
  WOQL.add_triple("v:cell3", "rdf:type", "rdf:List"),
  WOQL.add_triple("v:cell3", "rdf:first", WOQL.string("Task C")),
  WOQL.add_triple("v:cell3", "rdf:rest", "rdf:nil")
);

const result = await client.query(createList);
const listHead = result.bindings[0]["cell1"];

// Read all values
const readAll = WOQL.lib().rdflist_member(listHead, "v:value");
const values = await client.query(readAll);
// Returns: ["Task A", "Task B", "Task C"]

// Get list length
const getLength = WOQL.lib().rdflist_length(listHead, "v:count");
const lengthResult = await client.query(getLength);
// Returns: 3

Detailed Guides

Explore each category of operations in detail:

List Creation

Learn how to create empty lists and check if lists are empty.

List Access

Learn how to read elements from lists using peek, member, length, list, and slice operations.

List Modification

Learn how to add, remove, and modify list elements with push, pop, append, insert, drop, and clear.

List Transformation

Learn how to reorder lists with swap and reverse operations.

Performance Characteristics

OperationTime ComplexityDescription
rdflist_peekO(1)Direct access to first element
rdflist_pushO(1)Add to front (in-place modification)
rdflist_popO(1)Remove from front (in-place modification)
rdflist_memberO(n)Single traversal
rdflist_lengthO(n)Single traversal with count
rdflist_appendO(n)Must traverse to find end
rdflist_insertO(n)Traverse to position
rdflist_dropO(n)Traverse to position
rdflist_clearO(n)Delete all cells
rdflist_reverseO(n)In-place reversal
rdflist_swapO(n)Traverse to both positions

Best Practices

Use Library Functions

Prefer library functions over manual triple manipulation:

Example: JavaScript
// Good - use library functions to ensure accurate operations
WOQL.lib().rdflist_push(listHead, WOQL.string("New Item"))

// Avoid manual manipulation as ensuring accuracy is hard
WOQL.and(
  WOQL.idgen_random(...),
  WOQL.add_triple(...),
  // ... many lines
)

Descriptive Variable Names

Use descriptive variable names with the v: prefix, or use the WOQL.vars() function:

Example: JavaScript
// Good
WOQL.lib().rdflist_member("List/tasks", "v:task_title")

// Less clear
WOQL.lib().rdflist_member("List/tasks", "v:x")

Error Handling

Always check results for empty bindings:

Example: JavaScript
const result = await client.query(query);
if (!result?.bindings || result.bindings.length === 0) {
  // Handle empty result
}

Atomic Operations

Group related list operations in a single query for atomicity for ACID transactions:

Example: JavaScript
// Atomic operation - both succeed or both fail
WOQL.and(
  WOQL.lib().rdflist_pop("List/queue", "v:item"),
  WOQL.lib().rdflist_append("List/processed", "v:item", "v:new_cell")
)

Working with Document References

Lists can contain references to documents:

Example: JavaScript
// Schema with list of document references
const schema = {
  "@type": "Class",
  "@id": "Playlist",
  "@key": { "@type": "Random" },
  name: "xsd:string",
  songs: { "@type": "List", "@class": "Song" }
};

// Read songs with their details
const songsQuery = WOQL.and(
  WOQL.lib().rdflist_member(playlistHead, "v:song_id"),
  WOQL.triple("v:song_id", "title", "v:title"),
  WOQL.triple("v:song_id", "artist", "v:artist")
);

Read More

Was this helpful?