feat: Aggregate netted branch diff in merge preview (include_changes)#1434
Open
bplatz wants to merge 3 commits into
Open
feat: Aggregate netted branch diff in merge preview (include_changes)#1434bplatz wants to merge 3 commits into
bplatz wants to merge 3 commits into
Conversation
Add an opt-in aggregate change set to Fluree::merge_preview: the source side's ancestor..head flakes folded per fact (subject, predicate, object, datatype, graph, lang, list index), with internally-cancelling assert/retract pairs removed — a git-diff-style rollup for merge-request review UIs. - fluree-db-novelty: NetChangeAccumulator netting fold and compute_delta_keys_and_changes, sharing one source-chain walk between conflict keys and the change set. - fluree-db-api: MergePreviewOpts gains include_changes, max_changes (flake-counted cap, cut at subject boundaries; 0 = stats-only), and changes_after_subject (cursor over IRI-ordered subjects). ChangeSummary carries exact assert/retract/subject counts regardless of truncation. - fluree-db-server: matching query params on GET /merge-preview with a 5,000 hard cap on max_changes. - fluree-db-cli: branch diff --changes / --max-changes / --stat / --changes-after for local, tracked, and remote modes. The change set is strategy-independent (raw source-vs-ancestor delta, before conflict resolution) and documents the net-commit-effect contract: re-asserts of pre-range values net as asserts because the merge applies them.
External apps can shell out to 'fluree branch diff --json' instead of calling the merge-preview endpoint directly; document the three routing modes (remote, tracked, local), the shared MergePreview JSON shape, and the --stat vs --max-changes-0 cap convention.
aaj3f
approved these changes
Jul 6, 2026
Comment on lines
+675
to
+681
| // Group by full subject IRI; BTreeMap gives the deterministic subject | ||
| // order that pagination cursors rely on. | ||
| let mut by_subject: BTreeMap<String, Vec<Flake>> = BTreeMap::new(); | ||
| for f in net_flakes { | ||
| let subject = compactor.decode_sid(&f.s)?; | ||
| by_subject.entry(subject).or_default().push(f); | ||
| } |
Contributor
There was a problem hiding this comment.
compactor.decode_sid(&f.s) runs once per flake during grouping, so a subject with K changed facts decodes its IRI K times, and every flake beyond max_changes is still decoded before being dropped (O(net_flakes) format! allocations). Consider grouping by &Sid (cheap, no alloc) into an FxHashMap, then decoding each distinct subject once and sorting by decoded IRI. Bounded, off the query hot path — a micro-opt, though, so take it or leave it
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds an opt-in aggregate change set to
Fluree::merge_preview— a git-diff-style rollup of the net facts a merge would apply, for merge-request review UIs (Fluree Solo). A branch with 40 commits that ultimately changes 12 facts now reviews as 12 facts instead of 40 commit-by-commit event logs.Closes the
include_changesfeature request.What's included
Netting engine (
fluree-db-novelty)NetChangeAccumulator: folds the source chain per full fact identity(graph, s, p, o, dt, lang, list-index)— deliberately notFlake's ownEq/Hash, which ignores graph, and stricter than(s,p,o,dt,g), which would collide language-tagged strings and list positions.compute_delta_keys_and_changes: one commit-chain walk serves both conflict keys and the change set, so requesting conflicts + changes never replays the source side twice.API (
fluree-db-api)MergePreviewOptsgainsinclude_changes,max_changes,changes_after_subject.MergePreview.changes(ChangeSummary) carries exactassert_count/retract_count/subject_count(never truncated), subject-grouped resolved-flake entries ordered by full IRI,truncated, andnext_cursor.HTTP (
fluree-db-server)GET /merge-previewgainsinclude_changes,max_changes(hard-capped at 5,000), andchanges_after_subjectquery params.CLI (
fluree-db-cli)branch diff --changes / --max-changes / --stat / --changes-afterin local, tracked, and remote modes.Semantics
conflicts.details.max_changescounts flakes but cuts at subject boundaries — a subject's diff is never split across pages; the first subject of a page is returned whole even if it alone exceeds the cap, so pagination always progresses.max_changes=0is a cheap diff-stats mode (exact counts, no payload, no source-state load).Docs
docs/api/endpoints.md: params,changesresponse object, examples.docs/cli/server-integration.md: Merge Preview Contract rule 11 (netting/pagination semantics for alternate server implementations).docs/getting-started/rust-api.md:include_changesexample (also fixed two pre-existing non-compilingMergePreviewOptsliterals).Testing
--all-features --all-targets -D warningsclean on all four touched crates.