FlureeLabs

Retractions

Retractions remove data from Fluree. While data is never truly deleted (it remains in history), retractions mark triples as no longer current.

What is a Retraction?

A retraction removes a triple from the current state:

  • The triple existed at some point (was asserted)
  • The retraction marks it as no longer true
  • Historical queries can still see the triple
  • Current queries don't see the triple

Basic Retraction

Remove a specific triple:

{
  "@context": {
    "ex": "http://example.org/ns/",
    "schema": "http://schema.org/"
  },
  "where": [
    { "@id": "ex:alice", "schema:age": "?age" }
  ],
  "delete": [
    { "@id": "ex:alice", "schema:age": "?age" }
  ]
}

This removes the age property from ex:alice.

Retract Specific Property

Remove a specific property value:

curl -X POST "http://localhost:8090/v1/fluree/update?ledger=mydb:main" \
  -H "Content-Type: application/json" \
  -d '{
    "@context": {
      "ex": "http://example.org/ns/",
      "schema": "http://schema.org/"
    },
    "where": [
      { "@id": "ex:alice", "schema:email": "alice.old@example.org" }
    ],
    "delete": [
      { "@id": "ex:alice", "schema:email": "alice.old@example.org" }
    ]
  }'

Retract All Values of a Property

Remove all values for a property:

{
  "where": [
    { "@id": "ex:alice", "schema:telephone": "?phone" }
  ],
  "delete": [
    { "@id": "ex:alice", "schema:telephone": "?phone" }
  ]
}

If ex:alice has multiple phone numbers, this removes them all.

Retract Multiple Properties

Remove several properties at once:

{
  "where": [
    { "@id": "ex:alice", "schema:email": "?email" },
    { "@id": "ex:alice", "schema:telephone": "?phone" },
    { "@id": "ex:alice", "ex:preferences": "?prefs" }
  ],
  "delete": [
    { "@id": "ex:alice", "schema:email": "?email" },
    { "@id": "ex:alice", "schema:telephone": "?phone" },
    { "@id": "ex:alice", "ex:preferences": "?prefs" }
  ]
}

Retract Entire Entity

Remove all triples for an entity:

{
  "where": [
    { "@id": "ex:alice", "?predicate": "?value" }
  ],
  "delete": [
    { "@id": "ex:alice", "?predicate": "?value" }
  ]
}

This finds all triples where ex:alice is the subject and retracts them all.

Result: Entity is "deleted" from current state (but remains in history).

Conditional Retractions

Retract only if conditions are met:

{
  "where": [
    { "@id": "?user", "@type": "schema:Person" },
    { "@id": "?user", "ex:lastLogin": "?lastLogin" },
    { "@id": "?user", "ex:status": "?status" }
  ],
  "filter": "?lastLogin < '2023-01-01' && ?status == 'inactive'",
  "delete": [
    { "@id": "?user", "?predicate": "?value" }
  ],
  "where": [
    { "@id": "?user", "?predicate": "?value" }
  ]
}

Removes all inactive users who haven't logged in since 2023.

Retract Relationships

Remove Single Relationship

{
  "where": [
    { "@id": "ex:alice", "schema:knows": "ex:bob" }
  ],
  "delete": [
    { "@id": "ex:alice", "schema:knows": "ex:bob" }
  ]
}

Remove All Relationships of a Type

{
  "where": [
    { "@id": "ex:alice", "schema:knows": "?person" }
  ],
  "delete": [
    { "@id": "ex:alice", "schema:knows": "?person" }
  ]
}

Bidirectional Relationship Removal

Remove relationship in both directions:

{
  "where": [
    { "@id": "ex:alice", "schema:knows": "ex:bob" },
    { "@id": "ex:bob", "schema:knows": "ex:alice" }
  ],
  "delete": [
    { "@id": "ex:alice", "schema:knows": "ex:bob" },
    { "@id": "ex:bob", "schema:knows": "ex:alice" }
  ]
}

Cascading Retractions

Retract an entity and all related entities:

{
  "where": [
    { "@id": "ex:order-123", "ex:items": "?item" },
    { "@id": "?item", "?itemPred": "?itemVal" },
    { "@id": "ex:order-123", "?orderPred": "?orderVal" }
  ],
  "delete": [
    { "@id": "?item", "?itemPred": "?itemVal" },
    { "@id": "ex:order-123", "?orderPred": "?orderVal" }
  ]
}

Deletes order and all its items.

Soft Delete vs Hard Retraction

Soft Delete (Recommended)

Mark as deleted without retracting:

{
  "where": [
    { "@id": "ex:alice", "ex:status": "?status" }
  ],
  "delete": [
    { "@id": "ex:alice", "ex:status": "?status" }
  ],
  "insert": [
    { "@id": "ex:alice", "ex:status": "deleted" },
    { "@id": "ex:alice", "ex:deletedAt": "2024-01-22T10:30:00Z" }
  ]
}

Benefits:

  • Easy to "undelete"
  • Audit trail of deletion
  • Can query deleted entities
  • Less impact on indexes

Hard Retraction

Retract all data:

{
  "where": [
    { "@id": "ex:alice", "?predicate": "?value" }
  ],
  "delete": [
    { "@id": "ex:alice", "?predicate": "?value" }
  ]
}

When to use:

  • Legal requirement to remove data
  • Sensitive data that must be removed
  • Test data cleanup

Note: Data still exists in history. For true deletion, see data purging operations.

Pattern-Based Retractions

Retract by Type

Remove all entities of a type:

{
  "where": [
    { "@id": "?entity", "@type": "ex:TempData" },
    { "@id": "?entity", "?predicate": "?value" }
  ],
  "delete": [
    { "@id": "?entity", "?predicate": "?value" }
  ]
}

Retract by Property Value

Remove entities with specific property:

{
  "where": [
    { "@id": "?entity", "ex:expired": true },
    { "@id": "?entity", "?predicate": "?value" }
  ],
  "delete": [
    { "@id": "?entity", "?predicate": "?value" }
  ]
}

Retraction Semantics

Idempotent

Retracting a non-existent triple is a no-op:

t=1: No triple exists
t=2: DELETE { ex:alice schema:age 30 }
     Result: No change (triple didn't exist)

No Cascading by Default

Retracting an entity doesn't automatically retract references to it:

t=1: ex:alice schema:worksFor ex:company-a
     ex:company-a schema:name "Acme"

t=2: DELETE all triples for ex:company-a

Result:
- ex:company-a properties are gone
- ex:alice schema:worksFor ex:company-a REMAINS
- Reference is now "dangling"

To cascade, explicitly match and delete references.

Time Travel and Retractions

Historical Queries See Retracted Data

# Current query (after retraction at t=5)
curl -X POST http://localhost:8090/v1/fluree/query \
  -d '{"from": "mydb:main", "select": ["?name"], ...}'
# Returns: [] (no results)

# Historical query (before retraction)
curl -X POST http://localhost:8090/v1/fluree/query \
  -d '{"from": "mydb:main@t:3", "select": ["?name"], ...}'
# Returns: [{"name": "Alice"}] (data visible)

History Shows Retractions

Query the history to see both assertions and retractions:

curl -X POST http://localhost:8090/v1/fluree/query \
  -d '{
    "@context": { "schema": "http://schema.org/" },
    "from": "mydb:main@t:1",
    "to": "mydb:main@t:latest",
    "select": ["?name", "?t", "?op"],
    "where": [
      { "@id": "ex:alice", "schema:name": { "@value": "?name", "@t": "?t", "@op": "?op" } }
    ],
    "orderBy": "?t"
  }'

Response:

[
  ["Alice", 1, true],
  ["Alice", 5, false]
]

The @t annotation captures the transaction time and @op binds a boolean — true for assertions, false for retractions (mirroring Flake.op on disk).

Error Handling

Common Errors

No Match (Not an Error):

{
  "where": [{ "@id": "ex:nonexistent", "schema:name": "?name" }],
  "delete": [{ "@id": "ex:nonexistent", "schema:name": "?name" }]
}

Result: No changes, no error.

Invalid Pattern:

{
  "error": "QueryError",
  "message": "Invalid WHERE pattern",
  "code": "INVALID_PATTERN"
}

Performance Considerations

Index Updates

Retractions update all indexes:

  • Each retracted triple updates SPOT, POST, OPST, PSOT
  • Large retractions can impact performance
  • Consider batch size for bulk deletions

Indexing Lag

Large retractions may increase indexing lag:

  • Monitor commit_t - index_t
  • Allow time for indexing between large retractions
  • Consider scheduling during low-traffic periods

Vacuum/Compaction

Eventually, consider compaction to reclaim space from retracted data (implementation-specific).

Best Practices

1. Use Soft Deletes

Prefer marking as deleted:

Good:

{
  "insert": [{ "@id": "ex:alice", "ex:status": "deleted" }]
}

Over:

{
  "delete": [{ "@id": "ex:alice", "?pred": "?val" }]
}

2. Add Audit Metadata

Include deletion metadata:

{
  "insert": [
    { "@id": "ex:alice", "ex:status": "deleted" },
    { "@id": "ex:alice", "ex:deletedAt": "2024-01-22T10:30:00Z" },
    { "@id": "ex:alice", "ex:deletedBy": "user-admin" },
    { "@id": "ex:alice", "ex:deleteReason": "User request" }
  ]
}

3. Be Specific in WHERE

Avoid accidentally retracting too much:

Good:

{
  "where": [{ "@id": "ex:alice", "schema:age": "?age" }],
  "delete": [{ "@id": "ex:alice", "schema:age": "?age" }]
}

Dangerous:

{
  "where": [{ "@id": "?entity", "schema:age": "?age" }],
  "delete": [{ "@id": "?entity", "?pred": "?val" }]
}

4. Test Retractions

Test on development data:

// Count before
const countBefore = await query('SELECT (COUNT(?e) as ?count) WHERE { ... }');

// Retract
await transact(retractionQuery);

// Count after
const countAfter = await query('SELECT (COUNT(?e) as ?count) WHERE { ... }');

console.log(`Retracted ${countBefore - countAfter} entities`);

5. Handle Cascading Explicitly

Don't rely on cascading—make it explicit:

{
  "where": [
    { "@id": "ex:order-123", "?pred": "?val" },
    { "@id": "?item", "ex:orderId": "ex:order-123" },
    { "@id": "?item", "?itemPred": "?itemVal" }
  ],
  "delete": [
    { "@id": "ex:order-123", "?pred": "?val" },
    { "@id": "?item", "?itemPred": "?itemVal" }
  ]
}

6. Document Deletion Logic

Comment deletion logic:

// Hard delete expired sessions older than 30 days
// - Finds all sessions with expired=true and oldDate
// - Retracts all properties
// - Logs count of deleted sessions
await retractExpiredSessions();

7. Monitor Impact

Track retraction metrics:

  • Count of retractions
  • Entities affected
  • Indexing lag after large retractions
  • Query performance impact

Data Privacy Compliance

GDPR "Right to be Forgotten"

For compliance, consider:

  1. Soft delete first (marks as deleted)
  2. Schedule purge (actual removal from history)
  3. Anonymize references (replace with pseudonymous ID)

Example:

{
  "where": [{ "@id": "ex:user-123", "?pred": "?val" }],
  "delete": [{ "@id": "ex:user-123", "?pred": "?val" }],
  "insert": [{
    "@id": "ex:user-123",
    "ex:anonymized": true,
    "ex:anonymizedAt": "2024-01-22T10:30:00Z"
  }]
}

Note: True purging from history requires administrative operations beyond standard retractions.

Related Documentation