Extend history replay for mapped sequence items

This commit is contained in:
2026-10-23 17:55:45 +00:00
parent 12fdf0d331
commit 7b04dc6a50
4 changed files with 274 additions and 14 deletions

View File

@@ -50,7 +50,7 @@ type historyApplication struct {
value *document.Node
origin Origin
sequences []sequenceHistoryContribution
sequencesByPath map[string]sequenceHistoryContribution
sequencesByPath map[string]sequenceHistoryContributionIndex
}
func cloneHistoryApplications(values []historyApplication) []historyApplication {
@@ -92,7 +92,14 @@ func walkHistoryApplication(application historyApplication, visit func(local, ef
for index, child := range node.Sequence {
effectiveIndex := index
if projected {
effectiveIndex += contribution.offset
if contribution.mode == sequenceHistoryMapped {
effectiveIndex = contribution.outputForInput(index)
if effectiveIndex < 0 {
continue
}
} else {
effectiveIndex += contribution.offset
}
}
walk(child, local.Index(index), effective.Index(effectiveIndex))
}
@@ -112,7 +119,18 @@ func historyApplicationLocalPath(application historyApplication, effective Path)
for _, segment := range relative.segments {
if contribution, found := sequenceHistoryAt(application.sequencesByPath, local); found {
index, valid := schemaIndex(segment)
if !valid || segment.kind == pathSegmentKey || index < contribution.offset || index >= contribution.offset+contribution.length {
if !valid || segment.kind == pathSegmentKey {
return Path{}, false
}
if contribution.mode == sequenceHistoryMapped {
inputIndex := contribution.inputForOutput(index)
if inputIndex < 0 {
return Path{}, false
}
local = local.Index(inputIndex)
continue
}
if index < contribution.offset || index >= contribution.offset+contribution.length {
return Path{}, false
}
local = local.Index(index - contribution.offset)
@@ -149,6 +167,16 @@ func applicationOperationPath(application historyApplication, query Path, lookup
return container, true
}
}
indexed, _ := sequenceHistoryAt(application.sequencesByPath, contribution.path)
if indexed.mode == sequenceHistoryMapped && len(query.segments) > len(container.segments) {
outputIndex, valid := schemaIndex(query.segments[len(container.segments)])
_, replaced := indexed.replacedOutputs[outputIndex]
if valid && replaced {
if _, present := documentAtPathWithLookup(application.value, local, lookup); !ok || !present {
return container.Index(outputIndex), true
}
}
}
if !ok {
continue
}
@@ -213,7 +241,7 @@ func mergeHistoryDocument(target **document.Node, incoming *document.Node, index
mergeHistoryDocumentAtPath(target, incoming, Path{}, index, nil)
}
func mergeHistoryDocumentAtPath(target **document.Node, incoming *document.Node, path Path, index *mappingIndex, sequences map[string]sequenceHistoryContribution) {
func mergeHistoryDocumentAtPath(target **document.Node, incoming *document.Node, path Path, index *mappingIndex, sequences map[string]sequenceHistoryContributionIndex) {
if incoming == nil {
return
}
@@ -238,23 +266,71 @@ func mergeHistoryDocumentAtPath(target **document.Node, incoming *document.Node,
}
}
func sequenceHistoryAt(contributions map[string]sequenceHistoryContribution, path Path) (sequenceHistoryContribution, bool) {
func sequenceHistoryAt(contributions map[string]sequenceHistoryContributionIndex, path Path) (sequenceHistoryContributionIndex, bool) {
contribution, found := contributions[path.String()]
return contribution, found
}
func indexSequenceHistoryContributions(values []sequenceHistoryContribution) map[string]sequenceHistoryContribution {
type sequenceHistoryContributionIndex struct {
sequenceHistoryContribution
outputIndexes []int
inputIndexes []int
replacedOutputs map[int]struct{}
}
func indexSequenceHistoryContributions(values []sequenceHistoryContribution) map[string]sequenceHistoryContributionIndex {
if len(values) == 0 {
return nil
}
index := make(map[string]sequenceHistoryContribution, len(values))
index := make(map[string]sequenceHistoryContributionIndex, len(values))
for _, value := range values {
index[value.path.String()] = value
contribution := sequenceHistoryContributionIndex{sequenceHistoryContribution: value}
if value.mode == sequenceHistoryMapped {
maxOutput := -1
for _, projection := range value.items {
if projection.outputIndex > maxOutput {
maxOutput = projection.outputIndex
}
}
contribution.outputIndexes = make([]int, len(value.items))
for inputIndex := range contribution.outputIndexes {
contribution.outputIndexes[inputIndex] = -1
}
contribution.inputIndexes = make([]int, maxOutput+1)
for outputIndex := range contribution.inputIndexes {
contribution.inputIndexes[outputIndex] = -1
}
for _, projection := range value.items {
if projection.inputIndex >= 0 && projection.inputIndex < len(contribution.outputIndexes) && projection.outputIndex >= 0 && projection.outputIndex < len(contribution.inputIndexes) {
contribution.outputIndexes[projection.inputIndex] = projection.outputIndex
contribution.inputIndexes[projection.outputIndex] = projection.inputIndex
}
}
contribution.replacedOutputs = make(map[int]struct{}, len(value.replacements))
for _, replacement := range value.replacements {
contribution.replacedOutputs[replacement.outputIndex] = struct{}{}
}
}
index[value.path.String()] = contribution
}
return index
}
func applySequenceHistory(target **document.Node, incoming *document.Node, contribution sequenceHistoryContribution) {
func (contribution sequenceHistoryContributionIndex) outputForInput(inputIndex int) int {
if inputIndex < 0 || inputIndex >= len(contribution.outputIndexes) {
return -1
}
return contribution.outputIndexes[inputIndex]
}
func (contribution sequenceHistoryContributionIndex) inputForOutput(outputIndex int) int {
if outputIndex < 0 || outputIndex >= len(contribution.inputIndexes) {
return -1
}
return contribution.inputIndexes[outputIndex]
}
func applySequenceHistory(target **document.Node, incoming *document.Node, contribution sequenceHistoryContributionIndex) {
if incoming == nil || incoming.Kind != document.Sequence {
return
}
@@ -262,6 +338,18 @@ func applySequenceHistory(target **document.Node, incoming *document.Node, contr
*target = document.Clone(incoming)
return
}
if contribution.mode == sequenceHistoryMapped {
for _, projection := range contribution.items {
if projection.inputIndex < 0 || projection.inputIndex >= len(incoming.Sequence) || projection.outputIndex < 0 {
continue
}
for len((*target).Sequence) <= projection.outputIndex {
(*target).Sequence = append((*target).Sequence, nil)
}
(*target).Sequence[projection.outputIndex] = document.Clone(incoming.Sequence[projection.inputIndex])
}
return
}
for _, item := range incoming.Sequence {
(*target).Sequence = append((*target).Sequence, document.Clone(item))
}
@@ -275,6 +363,8 @@ func cloneSequenceHistoryContributions(values []sequenceHistoryContribution) []s
for index, value := range values {
cloned[index] = value
cloned[index].path = value.path.clone()
cloned[index].items = append([]sequenceItemProjection(nil), value.items...)
cloned[index].replacements = append([]sequenceItemReplacement(nil), value.replacements...)
}
return cloned
}