Index collection prefixes for path correspondence
This commit is contained in:
@@ -13,28 +13,45 @@ type collectionCorrespondence struct {
|
||||
}
|
||||
|
||||
type resolutionCorrespondence struct {
|
||||
collections map[string]collectionCorrespondence
|
||||
collections map[string]collectionCorrespondence
|
||||
prefix pathPrefixIndex
|
||||
indexedCollections []string
|
||||
}
|
||||
|
||||
func (c resolutionCorrespondence) clone() resolutionCorrespondence {
|
||||
result := resolutionCorrespondence{collections: make(map[string]collectionCorrespondence, len(c.collections))}
|
||||
for key, collection := range c.collections {
|
||||
var result resolutionCorrespondence
|
||||
for _, collection := range c.collections {
|
||||
collection.path = collection.path.clone()
|
||||
collection.entryToFinal = append([]int(nil), collection.entryToFinal...)
|
||||
collection.finalToEntry = append([]int(nil), collection.finalToEntry...)
|
||||
result.collections[key] = collection
|
||||
result.indexCollection(collection)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (c *resolutionCorrespondence) indexCollection(collection collectionCorrespondence) {
|
||||
if c.collections == nil {
|
||||
c.collections = make(map[string]collectionCorrespondence)
|
||||
}
|
||||
pointer := collection.path.String()
|
||||
c.collections[pointer] = collection
|
||||
if collection.eligible {
|
||||
c.prefix.insert(pointer, len(c.indexedCollections))
|
||||
c.indexedCollections = append(c.indexedCollections, pointer)
|
||||
}
|
||||
}
|
||||
|
||||
func buildResolutionCorrespondence(entry, final *document.Node, policy sequencePolicy) resolutionCorrespondence {
|
||||
result := resolutionCorrespondence{collections: make(map[string]collectionCorrespondence)}
|
||||
var result resolutionCorrespondence
|
||||
if policy.selectorTrie == nil {
|
||||
return result
|
||||
}
|
||||
var indexes mappingIndex
|
||||
var visit func(*document.Node, *document.Node, Path)
|
||||
visit = func(before, after *document.Node, path Path) {
|
||||
if fields, registered := policy.identityFieldsAt(path); registered &&
|
||||
((before != nil && before.Kind == document.Sequence) || (after != nil && after.Kind == document.Sequence)) {
|
||||
result.collections[path.String()] = matchResolutionCollection(before, after, path, fields)
|
||||
result.indexCollection(matchResolutionCollection(before, after, path, fields))
|
||||
return
|
||||
}
|
||||
if before != nil && before.Kind == document.Mapping {
|
||||
@@ -94,64 +111,55 @@ func matchResolutionCollection(before, after *document.Node, path Path, fields [
|
||||
// entryPath translates a surviving final record prefix; additions have no entry.
|
||||
// Paths outside eligible collections and wholly absent final positions stay positional.
|
||||
func (c resolutionCorrespondence) entryPath(path Path) (Path, bool) {
|
||||
if !path.valid() {
|
||||
return Path{}, false
|
||||
}
|
||||
for depth := 0; depth < len(path.segments); depth++ {
|
||||
prefix := Path{segments: path.segments[:depth]}
|
||||
collection, found := c.collections[prefix.String()]
|
||||
if !found || !collection.eligible {
|
||||
continue
|
||||
}
|
||||
compatible := true
|
||||
for i, segment := range collection.path.segments {
|
||||
if segment.kind == pathSegmentKey && path.segments[i].kind == pathSegmentIndex {
|
||||
compatible = false
|
||||
break
|
||||
}
|
||||
}
|
||||
index, ok := schemaIndex(path.segments[depth])
|
||||
if !compatible || !ok || index >= len(collection.finalToEntry) {
|
||||
continue
|
||||
}
|
||||
previous := collection.finalToEntry[index]
|
||||
if previous < 0 {
|
||||
return Path{}, false
|
||||
}
|
||||
translated := path.clone()
|
||||
translated.segments[depth] = pathSegment{kind: pathSegmentIndex, index: previous}
|
||||
return translated, true
|
||||
}
|
||||
return path.clone(), true
|
||||
return c.translate(path, false)
|
||||
}
|
||||
|
||||
// finalPath translates historical record descendants without matching removals.
|
||||
func (c resolutionCorrespondence) finalPath(path Path) (Path, bool) {
|
||||
return c.translate(path, true)
|
||||
}
|
||||
|
||||
func (c resolutionCorrespondence) translate(path Path, toFinal bool) (Path, bool) {
|
||||
if !path.valid() {
|
||||
return Path{}, false
|
||||
}
|
||||
for depth := 0; depth < len(path.segments); depth++ {
|
||||
prefix := Path{segments: path.segments[:depth]}
|
||||
collection, found := c.collections[prefix.String()]
|
||||
if !found || !collection.eligible {
|
||||
continue
|
||||
translated := path.clone()
|
||||
if len(c.indexedCollections) == 0 {
|
||||
return translated, true
|
||||
}
|
||||
matched, handled := true, false
|
||||
c.prefix.visitAncestors(path.String(), func(id int) {
|
||||
if handled {
|
||||
return
|
||||
}
|
||||
collection := c.collections[c.indexedCollections[id]]
|
||||
depth := len(collection.path.segments)
|
||||
if depth >= len(path.segments) {
|
||||
return
|
||||
}
|
||||
for i, segment := range collection.path.segments {
|
||||
if segment.kind == pathSegmentKey && path.segments[i].kind == pathSegmentIndex {
|
||||
return path.clone(), true
|
||||
return
|
||||
}
|
||||
}
|
||||
index, ok := schemaIndex(path.segments[depth])
|
||||
if !ok || index >= len(collection.entryToFinal) {
|
||||
continue
|
||||
indices := collection.finalToEntry
|
||||
if toFinal {
|
||||
indices = collection.entryToFinal
|
||||
}
|
||||
current := collection.entryToFinal[index]
|
||||
if !ok || index >= len(indices) {
|
||||
return
|
||||
}
|
||||
handled = true
|
||||
current := indices[index]
|
||||
if current < 0 {
|
||||
return Path{}, false
|
||||
matched = false
|
||||
return
|
||||
}
|
||||
translated := path.clone()
|
||||
translated.segments[depth] = pathSegment{kind: pathSegmentIndex, index: current}
|
||||
return translated, true
|
||||
})
|
||||
if !matched {
|
||||
return Path{}, false
|
||||
}
|
||||
return path.clone(), true
|
||||
return translated, true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user