RDF List Modification Operations

This guide covers operations for modifying RDF lists. These operations allow you to add, remove, and manage list elements.

Use the operators together for various scenarios. To operate a list like a stack, use the push and pop operators. To create a queue, use the append and pop operator (append to the end of the list is O(n) in performance and pop is o(1)).

What You'll Learn

  • How to add elements to the front or back of a list
  • How to remove elements from a list
  • How to insert elements at specific positions
  • How to clear an entire list

Operations Summary

OperationDescriptionTime Complexity
rdflist_pushAdd element to front (in-place)O(1)
rdflist_popRemove element from front (in-place)O(1)
rdflist_appendAdd element to endO(n)
rdflist_insertInsert at specific positionO(n)
rdflist_dropRemove element at positionO(n)
rdflist_clearRemove all elementsO(n)

rdflist_push

Adds a new element to the front of a list. This is an in-place operation that modifies the list structure at the same head IRI. Useful for creating stacks or queues.

Syntax

Example: JavaScript
WOQL.lib().rdflist_push(consSubject, value)

Parameters

ParameterTypeDescription
consSubjectstringThe list head identifier
valueanyValue to add (use WOQL.string() for strings)

Example

Example: JavaScript
// Before: [A, B, C]
const pushQuery = WOQL.lib().rdflist_push(listHead, WOQL.string("Z"));
const result = await client.query(pushQuery);
// After: [Z, A, B, C]

// Verify the change
const peekQuery = WOQL.lib().rdflist_peek(listHead, "v:first");
const peekResult = await client.query(peekQuery);
console.log(peekResult.bindings[0]["first"]["@value"]); // "Z"

Building Lists with Multiple Pushes

Example: JavaScript
// Create a list by pushing elements (results in reverse order)
const createQuery = WOQL.and(
  WOQL.idgen_random("terminusdb://data/Cons/", "v:list"),
  WOQL.add_triple("v:list", "rdf:type", "rdf:List"),
  WOQL.add_triple("v:list", "rdf:first", WOQL.string("First")),
  WOQL.add_triple("v:list", "rdf:rest", "rdf:nil")
);

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

// Push more elements
await client.query(WOQL.lib().rdflist_push(listHead, WOQL.string("Second")));
await client.query(WOQL.lib().rdflist_push(listHead, WOQL.string("Third")));

// List is now: [Third, Second, First]

Use Cases

  • Stack operations: Implement LIFO (Last In, First Out) structures
  • Prepending: Add items to the beginning efficiently
  • Building lists: Create lists by pushing elements

rdflist_pop

Removes and returns the first element from a list. This is an in-place operation, which means that the list (the Cons) reference from where it is referenced stays the same, such as from a document.

Syntax

Example: JavaScript
WOQL.lib().rdflist_pop(consSubject, valueVar)

Parameters

ParameterTypeDescription
consSubjectstringThe list head identifier
valueVarstringVariable to bind the removed value

Example

Example: JavaScript
// Before: [X, Y, Z]
const popQuery = WOQL.lib().rdflist_pop(listHead, "v:popped");
const result = await client.query(popQuery);
// After: [Y, Z]

const popped = result.bindings[0]["popped"]["@value"];
console.log(`Removed: ${popped}`); // "X"

// Verify the list changed
const lengthQuery = WOQL.lib().rdflist_length(listHead, "v:len");
const lengthResult = await client.query(lengthQuery);
console.log(`Length: ${lengthResult.bindings[0]["len"]["@value"]}`); // "2"

Use Cases

  • Stack operations: Implement pop for LIFO structures
  • Queue processing: Remove items from front
  • Destructuring: Extract head and work with tail

rdflist_append

Adds an element to the end of a list. Creates a new cons cell and links it to the end of the list. Useful for appending elements to a FIFO queue.

Syntax

Example: JavaScript
WOQL.lib().rdflist_append(consSubject, value, newCellVar)

Parameters

ParameterTypeDescription
consSubjectstringThe list head identifier
valueanyValue to append
newCellVarstringVariable to bind the new cell ID

Example

Example: JavaScript
// Before: [A, B, C]
const appendQuery = WOQL.lib().rdflist_append(
  listHead,
  WOQL.string("D"),
  "v:new_cell"
);
const result = await client.query(appendQuery);
// After: [A, B, C, D]

// The operation deletes the old nil reference and creates a new cell
console.log(`Inserts: ${result.inserts}`);
console.log(`Deletes: ${result.deletes}`);

Use Cases

  • Queue operations: Add items to the back (FIFO)
  • Building ordered lists: Maintain insertion order
  • Extending lists: Add items without changing existing order

rdflist_insert

Inserts an element at a specific position in the list. Uses 0-based indexing.

Syntax

Example: JavaScript
WOQL.lib().rdflist_insert(consSubject, position, value)

Parameters

ParameterTypeDescription
consSubjectstringThe list head identifier
positionnumberIndex where to insert (0-based)
valueanyValue to insert

Examples

Example: JavaScript
// List: [A, B, C]

// Insert at position 0 (head): [X, A, B, C]
await client.query(
  WOQL.lib().rdflist_insert(listHead, 0, WOQL.string("X"))
);

// Insert at position 1 (after head): [A, Y, B, C]
await client.query(
  WOQL.lib().rdflist_insert(listHead, 1, WOQL.string("Y"))
);

// Insert at position 2 (middle): [A, B, Z, C]
await client.query(
  WOQL.lib().rdflist_insert(listHead, 2, WOQL.string("Z"))
);

Verification Example

Example: JavaScript
// Create list: [A, B, C]
const createQuery = 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("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("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("C")),
  WOQL.add_triple("v:cell3", "rdf:rest", "rdf:nil")
);

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

// Insert "X" at position 0
await client.query(
  WOQL.lib().rdflist_insert(listHead, 0, WOQL.string("X"))
);

// Verify: should be [X, A, B, C]
const readQuery = WOQL.lib().rdflist_member(listHead, "v:val");
const readResult = await client.query(readQuery);
const values = readResult.bindings.map(b => b["val"]["@value"]);
console.log(values); // ["X", "A", "B", "C"]

rdflist_drop

Removes an element at a specific position from the list. Uses 0-based indexing. Remember to also delete subdocuments using delete_document() if needed by using rdflist_slice() to query them.

Syntax

Example: JavaScript
WOQL.lib().rdflist_drop(consSubject, position)

Parameters

ParameterTypeDescription
consSubjectstringThe list head identifier
positionnumberIndex of element to remove (0-based)

Examples

Example: JavaScript
// List: [A, B, C]

// Drop at position 0 (head): [B, C]
await client.query(WOQL.lib().rdflist_drop(listHead, 0));

// Drop at position 1 (middle): [A, C]
await client.query(WOQL.lib().rdflist_drop(listHead, 1));

// Drop at position 2 (last): [A, B]
await client.query(WOQL.lib().rdflist_drop(listHead, 2));

Use Cases

  • Removal by index: Delete specific items
  • Filtering: Remove unwanted elements
  • Trimming: Remove elements from specific positions

rdflist_clear

Removes all elements from a list, deleting all cons cells and returning rdf:nil.

Syntax

Example: JavaScript
WOQL.lib().rdflist_clear(consSubject, emptyListVar)

Parameters

ParameterTypeDescription
consSubjectstringThe list head identifier
emptyListVarstringVariable to bind the empty list (rdf:nil)

Example

Example: JavaScript
// Clear a list
const clearQuery = WOQL.lib().rdflist_clear(listHead, "v:empty");
const result = await client.query(clearQuery);

const emptyList = result.bindings[0]["empty"];
console.log(emptyList); // "rdf:nil"
console.log(`Deleted ${result.deletes} triples`);

// Verify the old cons cells are gone
const verifyQuery = WOQL.triple(listHead, "v:pred", "v:obj");
const verifyResult = await client.query(verifyQuery);
console.log(verifyResult.bindings.length); // 0

Important: Document References

When clearing a list that contains document references, only the list structure is deleted, not the referenced documents:

Example: JavaScript
// Create tasks
await client.addDocument([
  { "@type": "Task", "@id": "Task/1", title: "Task 1" },
  { "@type": "Task", "@id": "Task/2", title: "Task 2" }
]);

// Build list of task references
// ... (list contains Task/1 and Task/2)

// Clear the list
await client.query(WOQL.lib().rdflist_clear(listHead, "v:empty"));

// Tasks still exist!
const task1 = await client.getDocument({ id: "Task/1" });
console.log(task1.title); // "Task 1" - still there

Pattern: Delete All Referenced Documents and Clear List

Example: JavaScript
const deleteAndClear = WOQL.and(
  // Get the list head
  WOQL.triple("Project/1", "tasks", "v:listHead"),
  
  // Delete all documents in the list
  WOQL.group_by(
    [],
    "v:task",
    "v:tasks",
    WOQL.and(
      WOQL.lib().rdflist_member("v:listHead", "v:task"),
      WOQL.delete_document("v:task")
    )
  ),
  
  // Clear the list structure
  WOQL.lib().rdflist_clear("v:listHead", "v:empty"),
  
  // Update the parent reference to point to empty list
  WOQL.delete_triple("Project/1", "tasks", "v:listHead"),
  WOQL.add_triple("Project/1", "tasks", "v:empty")
);

await client.query(deleteAndClear);

Complete Example: Task Queue

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

async function taskQueueDemo(client) {
  // Create initial queue with one task
  const createQueue = WOQL.and(
    WOQL.idgen_random("terminusdb://data/Cons/", "v:queue"),
    WOQL.add_triple("v:queue", "rdf:type", "rdf:List"),
    WOQL.add_triple("v:queue", "rdf:first", WOQL.string("Initial Task")),
    WOQL.add_triple("v:queue", "rdf:rest", "rdf:nil")
  );
  
  const result = await client.query(createQueue);
  const queue = result.bindings[0]["queue"];
  
  // Add tasks to the back (queue behavior)
  await client.query(
    WOQL.lib().rdflist_append(queue, WOQL.string("Task 2"), "v:cell")
  );
  await client.query(
    WOQL.lib().rdflist_append(queue, WOQL.string("Task 3"), "v:cell")
  );
  
  console.log("Queue after appends:");
  const allTasks = await client.query(
    WOQL.lib().rdflist_list(queue, "v:tasks")
  );
  console.log(allTasks.bindings[0]["tasks"].map(t => t["@value"]));
  // ["Initial Task", "Task 2", "Task 3"]
  
  // Process (pop) from the front
  const popResult = await client.query(
    WOQL.lib().rdflist_pop(queue, "v:task")
  );
  console.log(`Processing: ${popResult.bindings[0]["task"]["@value"]}`);
  // "Initial Task"
  
  // Insert high-priority task at position 0
  await client.query(
    WOQL.lib().rdflist_insert(queue, 0, WOQL.string("URGENT"))
  );
  
  console.log("Queue after urgent insert:");
  const finalTasks = await client.query(
    WOQL.lib().rdflist_list(queue, "v:tasks")
  );
  console.log(finalTasks.bindings[0]["tasks"].map(t => t["@value"]));
  // ["URGENT", "Task 2", "Task 3"]
  
  // Clear the queue when done
  await client.query(WOQL.lib().rdflist_clear(queue, "v:empty"));
  console.log("Queue cleared");
}

Read More

Was this helpful?