diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 496c91e..e2cdef8 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -8,12 +8,12 @@ jobs: test: strategy: matrix: - go-version: [1.20.x, 1.21.x, 1.22.x] + go-version: [1.23.x, 1.24.x, 1.25.x] platform: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.platform }} steps: - name: Install Go - uses: actions/setup-go@v1 + uses: actions/setup-go@v5 with: go-version: ${{ matrix.go-version }} - name: Checkout code diff --git a/document.go b/document.go index bc91c6c..e3fd488 100644 --- a/document.go +++ b/document.go @@ -124,3 +124,11 @@ type SynonymDocument interface { // The provided visitor function is called for each synonym field. VisitSynonymFields(visitor SynonymFieldVisitor) } + +// NestedDocument is a document that contains other documents inside it. +type NestedDocument interface { + Document + // VisitNestedDocuments allows iteration over all nested documents in the document. + // The provided visitor function is called for each nested document. + VisitNestedDocuments(visitor func(doc Document)) +} diff --git a/index.go b/index.go index 12d907e..005d721 100644 --- a/index.go +++ b/index.go @@ -17,6 +17,8 @@ package index import ( "bytes" "context" + "encoding/binary" + "fmt" "reflect" ) @@ -185,17 +187,46 @@ func (tfv *TermFieldVector) Size() int { len(tfv.Field) + len(tfv.ArrayPositions)*sizeOfUint64 } -// IndexInternalID is an opaque document identifier interal to the index impl +// IndexInternalID is an opaque document identifier internal to the index impl type IndexInternalID []byte +// NewIndexInternalID encodes a uint64 into an 8-byte big-endian ID, reusing `buf` when possible. +func NewIndexInternalID(buf []byte, in uint64) IndexInternalID { + if len(buf) != 8 { + if cap(buf) >= 8 { + buf = buf[0:8] + } else { + buf = make([]byte, 8) + } + } + binary.BigEndian.PutUint64(buf, in) + return buf +} + +// NewIndexInternalIDFrom creates a new IndexInternalID by copying from `other`, reusing `buf` when possible. +func NewIndexInternalIDFrom(buf IndexInternalID, other IndexInternalID) IndexInternalID { + buf = buf[:0] + return append(buf, other...) +} + +// Equals checks if two IndexInternalID values are equal. func (id IndexInternalID) Equals(other IndexInternalID) bool { return id.Compare(other) == 0 } +// Compare compares two IndexInternalID values, inherently comparing the encoded uint64 values. func (id IndexInternalID) Compare(other IndexInternalID) int { return bytes.Compare(id, other) } +// Value returns the uint64 value encoded in the IndexInternalID. +func (id IndexInternalID) Value() (uint64, error) { + if len(id) != 8 { + return 0, fmt.Errorf("wrong len for IndexInternalID: %q", id) + } + return binary.BigEndian.Uint64(id), nil +} + type TermFieldDoc struct { Term string ID IndexInternalID @@ -391,3 +422,48 @@ type IndexInsightsReader interface { // cluster densities (or cardinalities) CentroidCardinalities(field string, limit int, descending bool) (cenCards []CentroidCardinality, err error) } + +// ----------------------------------------------------------------------------- +// NestedReader is an extended index reader that supports hierarchical document structures. +type NestedReader interface { + IndexReader + // Ancestors returns the ancestral chain for a given document ID in the index. + // For nested documents, this method retrieves all parent documents in the hierarchy + // leading up to the root document ID. + Ancestors(id IndexInternalID, prealloc []AncestorID) ([]AncestorID, error) +} + +// AncestorID represents the identifier of an ancestor document in an ancestor chain. +type AncestorID uint64 + +// NewAncestorID creates a new AncestorID from the given uint64 value. +func NewAncestorID(val uint64) AncestorID { + return AncestorID(val) +} + +// Compare compares two AncestorID values. +func (a AncestorID) Compare(b AncestorID) int { + switch { + case a < b: + return -1 + case a > b: + return 1 + default: + return 0 + } +} + +// Equals checks if two AncestorID values are equal. +func (a AncestorID) Equals(b AncestorID) bool { + return a == b +} + +// Add returns a new AncestorID by adding the given uint64 value to the current AncestorID. +func (a AncestorID) Add(n uint64) AncestorID { + return AncestorID(uint64(a) + n) +} + +// ToIndexInternalID converts the AncestorID to an IndexInternalID. +func (a AncestorID) ToIndexInternalID(prealloc IndexInternalID) IndexInternalID { + return NewIndexInternalID(prealloc, uint64(a)) +}