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
| Operation | Description | Time Complexity |
|---|---|---|
rdflist_push | Add element to front (in-place) | O(1) |
rdflist_pop | Remove element from front (in-place) | O(1) |
rdflist_append | Add element to end | O(n) |
rdflist_insert | Insert at specific position | O(n) |
rdflist_drop | Remove element at position | O(n) |
rdflist_clear | Remove all elements | O(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
WOQL.lib().rdflist_push(consSubject, value)Parameters
| Parameter | Type | Description |
|---|---|---|
consSubject | string | The list head identifier |
value | any | Value to add (use WOQL.string() for strings) |
Example
// 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
// 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
WOQL.lib().rdflist_pop(consSubject, valueVar)Parameters
| Parameter | Type | Description |
|---|---|---|
consSubject | string | The list head identifier |
valueVar | string | Variable to bind the removed value |
Example
// 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
WOQL.lib().rdflist_append(consSubject, value, newCellVar)Parameters
| Parameter | Type | Description |
|---|---|---|
consSubject | string | The list head identifier |
value | any | Value to append |
newCellVar | string | Variable to bind the new cell ID |
Example
// 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
WOQL.lib().rdflist_insert(consSubject, position, value)Parameters
| Parameter | Type | Description |
|---|---|---|
consSubject | string | The list head identifier |
position | number | Index where to insert (0-based) |
value | any | Value to insert |
Examples
// 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
// 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
WOQL.lib().rdflist_drop(consSubject, position)Parameters
| Parameter | Type | Description |
|---|---|---|
consSubject | string | The list head identifier |
position | number | Index of element to remove (0-based) |
Examples
// 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
WOQL.lib().rdflist_clear(consSubject, emptyListVar)Parameters
| Parameter | Type | Description |
|---|---|---|
consSubject | string | The list head identifier |
emptyListVar | string | Variable to bind the empty list (rdf:nil) |
Example
// 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); // 0Important: Document References
When clearing a list that contains document references, only the list structure is deleted, not the referenced documents:
// 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 therePattern: Delete All Referenced Documents and Clear List
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
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
- RDF List Operations Overview - Main guide
- List Creation Operations - Creating lists
- List Access Operations - Reading list elements
- List Transformation Operations - Reordering lists
- Integration Tests - Complete test examples