A knowledge graph diff shows which facts changed between two versions of a graph: what was added, what was removed and which values are different. You use it to review a change before it goes live, to find out what a data load did, or to copy a change to another branch.
This page explains why graph diffs are harder than text diffs, and the two common ways to show them. Then it shows a short TerminusDB example that diffs two branches, diffs two commits and applies a diff as a patch.
Try it in TerminusDB
TerminusDB has a diff endpoint for any two branches, commits or documents. For the full format, see JSON Diff and Patch.
Why diffing a graph is harder than diffing text
A text diff compares lines. The line order is fixed, so the tool can line up the two files and mark the lines that are different.
A graph has no line order. You can write the same graph to a file in many orders and in many formats. If you export a graph two times and diff the files as text, you can get a large diff even if no fact changed.
There are more problems:
- Identity. To compare a node, you must find the same node in the other version. With stable IDs (IRIs) this is easy. With blank nodes it is not, because a blank node has no name that stays the same between exports.
- Size. A graph can have millions of facts. A diff tool must find the changed ones without a slow compare of everything.
- Meaning. A change of one value often shows up as one removed triple and one added triple. That is correct, but it is hard for a person to read.
So a good graph diff compares facts, not files. It matches nodes by ID and shows the change in a form that people can review.
Triple diff vs document diff
There are two common ways to show a graph diff.
A triple diff lists the triples that were added and the triples that were removed. This is close to how many RDF stores keep data. The RDF Patch format from Apache Jena records changes this way, with add and delete rows. It is exact and simple to apply, but a value change is two rows that you must read together.
A document diff groups the triples by the thing they describe, such as one person or one product. It shows each changed field with its old and new value. This is easier to review, but it needs a schema or a rule that says which triples belong to which document.
| Triple diff | Document diff | |
|---|---|---|
| Unit of change | One triple | One field of one document |
| A value change shows as | One removal and one addition | One swap, with old and new value |
| Can be applied as a patch | Yes | Yes, with a patch tool |
| Easy for people to review | For small changes | Yes |
| Needs a schema | No | Usually (not for raw JSON) |
TerminusDB uses both levels. It stores each commit as a delta layer of added and removed triples. The diff endpoint returns the change as a document diff: one entry per changed document, with an operation such as SwapValue for each changed field. See Diff and Patch Operations for the client options.
Diff two branches in TerminusDB (example)
The example uses the HTTP API on a local TerminusDB at localhost:6363 with the default admin:root login. It is a tiny graph: one person who works for one organization.
1. Set up a small graph
Create a database, a schema and the first facts. Each write is a commit.
Loading…Expected output:
{
"@type": "api:DbCreateResponse",
"api:status": "api:success"
}Loading…Expected output:
[ "Organization", "Person" ]
Loading…Expected output:
[ "Organization/acme", "Person/alice" ]
2. Make a branch and change a fact
Loading…Expected output:
{
"@type": "api:BranchResponse",
"api:status": "api:success"
}Loading…Expected output:
[ "Person/alice" ]
3. Diff the branches
Send the two branch names. The diff lists each changed document, and in it each changed field.
Loading…Expected output:
[
{
"@id": "Person/alice",
"role": {
"@after": "CTO",
"@before": "Engineer",
"@op": "SwapValue"
}
}
]To diff only one document, add "document_id". The JSON Diff and Patch page lists all the request forms and options, such as keep and unfold.
Diff two commits
You can also diff any two commits, not only branch heads. Get the commit IDs from the log of the update branch. Each entry has an identifier:
Loading…Put two identifiers in the diff request, in place of the branch names:
curl -u admin:root -X POST "http://localhost:6363/api/diff/admin/MyDatabase" \
-H "Content-Type: application/json" \
-d '{"before_data_version": "<older identifier>", "after_data_version": "<newer identifier>"}'If you compare the "Add first facts" commit with the "New role for Alice" commit, you get the same SwapValue for Person/alice. The audit tutorial uses commit diffs to find out who changed a value and when.
Turn a diff into a patch and apply it
The diff output is also a patch. You can send it to the patch endpoint to make the same change on another branch. Here we apply it to main. This makes a new commit on main with its own author and message.
Loading…Expected output:
[ "Person/alice" ]
The call returns the compressed IDs of the documents it changed. If the @before value does not match the document on the branch, the call fails with HTTP 409 and nothing is written. You can also remove entries from the patch before you send it, or add Insert and Delete operations. See the Patch Endpoint page for the formats.
To take all the changes from a branch at once, use a merge instead. The merge how-to shows how, and how to handle conflicts.
When you are done, delete the demo database:
Loading…Using diffs for review and CI
A diff is JSON, so a program can read it. Some ways to use it:
- Review a change. Make the change on a branch, show the diff to a person, and merge only after they agree.
- Check a data load. After an import on a branch, diff it against
main. If the diff touches documents or fields that it should not, stop before you merge. - Test in CI. Run a job that loads data on a branch, diffs it, and fails if the diff is empty or larger than you expect.
- Move part of a change. Keep only some entries of a diff, and apply them as a patch to another branch.
When this is not the right tool
A document diff may not be what you need if:
- Your data is plain RDF files with no schema, and you only want to compare two files. A triple-level tool, such as
rdfdiffin Apache Jena, may fit better. - You only need to know that something changed, not what. Compare commit IDs instead.
- You want the imperative RFC 6902 JavaScript Object Notation (JSON) file patch diff format
Next steps
- JSON Diff and Patch — the full diff and patch format
- Diff and Patch Operations — diffs with the JavaScript client
- Patch Endpoint — apply patches to documents in a branch
- Merge How-To — merge branches and handle conflicts
- Knowledge Graph Versioning — ways to keep the past versions of a graph
Frequently asked questions
What is a knowledge graph diff?
It is a list of the facts that are different between two versions of a knowledge graph. It tells you what was added, what was removed and which values changed.
How do I compare two versions of a graph?
You need both versions, and a way to match the same node in each version. Then you compare the facts on each node. In TerminusDB, you send two branch names or two commit IDs to the diff endpoint.
What is the difference between a triple diff and a document diff?
A triple diff lists the triples that were added and removed. A document diff groups the changes by document and field, and shows the old and new value side by side. Both describe the same change at a different level.
Why are blank nodes a problem for graph diffs?
A blank node has no stable name. When you compare two files, the tool must find out which blank node in one file matches which blank node in the other. This can make a small change look like a large one.
Can I apply a diff to another branch?
Yes, in TerminusDB. The output of the diff endpoint is a patch that the patch endpoint accepts. If a document no longer matches what the patch expects, the call fails with a conflict and nothing is written.