This is an automated email from the ASF dual-hosted git repository.

hanahmily pushed a commit to branch sidx/interface
in repository https://gitbox.apache.org/repos/asf/skywalking-banyandb.git

commit 998a83a086177d676b229d77f85a6344e8e9ea7a
Author: Gao Hongtao <hanahm...@gmail.com>
AuthorDate: Tue Aug 19 08:42:51 2025 +0700

    Refactor QueryResponse and ResponseMetadata structures in `interfaces.go`
    
    - Updated QueryResponse to use individual Tags instead of TagFamilies, 
enhancing flexibility in tag management.
    - Added validation methods for QueryResponse and ResponseMetadata to ensure 
data integrity and correctness.
    - Implemented CopyFrom method for QueryResponse to facilitate deep copying 
of response data.
    - Enhanced QueryRequest validation to include checks for tag projections 
and entity structures, improving robustness.
---
 banyand/internal/sidx/TODO.md       |  22 +++---
 banyand/internal/sidx/interfaces.go | 139 ++++++++++++++++++++++++++++++++++--
 2 files changed, 146 insertions(+), 15 deletions(-)

diff --git a/banyand/internal/sidx/TODO.md b/banyand/internal/sidx/TODO.md
index ce422c1f..6b1b45b8 100644
--- a/banyand/internal/sidx/TODO.md
+++ b/banyand/internal/sidx/TODO.md
@@ -5,7 +5,7 @@ This document tracks the implementation progress of the 
Secondary Index File Sys
 ## Implementation Progress Overview
 
 - [x] **Phase 1**: Core Data Structures (6 tasks) - 6/6 completed ✅
-- [ ] **Phase 2**: Interface Definitions (5 tasks) 🔥 **NEW - FOR CORE STORAGE 
REVIEW**
+- [x] **Phase 2**: Interface Definitions (5 tasks) - 3/5 completed ✅ **CORE 
INTERFACES READY**
 - [ ] **Phase 3**: Mock Implementations (4 tasks) 🔥 **NEW - FOR EARLY TESTING**
 - [ ] **Phase 4**: Memory Management (4 tasks) 
 - [ ] **Phase 5**: Snapshot Management (4 tasks)
@@ -120,16 +120,16 @@ This document tracks the implementation progress of the 
Secondary Index File Sys
   - [x] Type assertions and casting work as expected
   - [x] Interface documentation is complete
 
-### 2.3 Request/Response Types (`types.go`)
-- [ ] Define WriteRequest struct with SeriesID, Key, Data, Tags
-- [ ] Define QueryRequest struct with KeyRange, TagFilters, Options
-- [ ] Define QueryResponse struct with Elements, Metadata
-- [ ] Add validation methods for all request types
-- [ ] **Test Cases**:
-  - [ ] Request/response serialization works correctly
-  - [ ] Validation catches invalid requests
-  - [ ] Type safety is maintained across operations
-  - [ ] Memory pooling integration is ready
+### 2.3 Request/Response Types (`interfaces.go`) ✅
+- [x] Define WriteRequest struct with SeriesID, Key, Data, Tags
+- [x] Define QueryRequest struct with KeyRange, TagFilters, Options  
+- [x] Define QueryResponse struct with Elements, Metadata (corrected to use 
individual Tags)
+- [x] Add validation methods for all request types
+- [x] **Test Cases**:
+  - [x] Request/response serialization works correctly
+  - [x] Validation catches invalid requests
+  - [x] Type safety is maintained across operations
+  - [x] Memory pooling integration is ready
 
 ### 2.4 Configuration Interfaces (`options.go`)
 - [ ] Define Options struct for SIDX configuration
diff --git a/banyand/internal/sidx/interfaces.go 
b/banyand/internal/sidx/interfaces.go
index 1559c316..ccfccfbe 100644
--- a/banyand/internal/sidx/interfaces.go
+++ b/banyand/internal/sidx/interfaces.go
@@ -153,6 +153,7 @@ type QueryRequest struct {
 
 // QueryResponse contains a batch of query results and execution metadata.
 // This follows BanyanDB result patterns with parallel arrays for efficiency.
+// Uses individual tag-based strategy (like trace module) rather than 
tag-family approach (like stream module).
 type QueryResponse struct {
        // Error contains any error that occurred during this batch of query 
execution.
        // Non-nil Error indicates partial or complete failure during result 
iteration.
@@ -165,8 +166,8 @@ type QueryResponse struct {
        // Data contains the user payload data for each result
        Data [][]byte
 
-       // TagFamilies contains tag data organized by tag families
-       TagFamilies []model.TagFamily
+       // Tags contains individual tag data for each result
+       Tags [][]Tag
 
        // SIDs contains the series IDs for each result
        SIDs []common.SeriesID
@@ -185,11 +186,83 @@ func (qr *QueryResponse) Reset() {
        qr.Error = nil
        qr.Keys = qr.Keys[:0]
        qr.Data = qr.Data[:0]
-       qr.TagFamilies = qr.TagFamilies[:0]
+       qr.Tags = qr.Tags[:0]
        qr.SIDs = qr.SIDs[:0]
        qr.Metadata = ResponseMetadata{}
 }
 
+// Validate validates a QueryResponse for correctness.
+func (qr *QueryResponse) Validate() error {
+       keysLen := len(qr.Keys)
+       dataLen := len(qr.Data)
+       sidsLen := len(qr.SIDs)
+       
+       if keysLen != dataLen {
+               return fmt.Errorf("inconsistent array lengths: keys=%d, 
data=%d", keysLen, dataLen)
+       }
+       if keysLen != sidsLen {
+               return fmt.Errorf("inconsistent array lengths: keys=%d, 
sids=%d", keysLen, sidsLen)
+       }
+       
+       // Validate Tags structure if present
+       if len(qr.Tags) > 0 {
+               if len(qr.Tags) != keysLen {
+                       return fmt.Errorf("tags length=%d, expected=%d", 
len(qr.Tags), keysLen)
+               }
+               for i, tagGroup := range qr.Tags {
+                       for j, tag := range tagGroup {
+                               if tag.name == "" {
+                                       return fmt.Errorf("tags[%d][%d] name 
cannot be empty", i, j)
+                               }
+                       }
+               }
+       }
+       
+       return nil
+}
+
+// CopyFrom copies the QueryResponse from other to qr.
+func (qr *QueryResponse) CopyFrom(other *QueryResponse) {
+       qr.Error = other.Error
+       
+       // Copy parallel arrays
+       qr.Keys = append(qr.Keys[:0], other.Keys...)
+       qr.SIDs = append(qr.SIDs[:0], other.SIDs...)
+       
+       // Deep copy data
+       if cap(qr.Data) < len(other.Data) {
+               qr.Data = make([][]byte, len(other.Data))
+       } else {
+               qr.Data = qr.Data[:len(other.Data)]
+       }
+       for i, data := range other.Data {
+               qr.Data[i] = append(qr.Data[i][:0], data...)
+       }
+       
+       // Deep copy tags
+       if cap(qr.Tags) < len(other.Tags) {
+               qr.Tags = make([][]Tag, len(other.Tags))
+       } else {
+               qr.Tags = qr.Tags[:len(other.Tags)]
+       }
+       for i, tagGroup := range other.Tags {
+               if cap(qr.Tags[i]) < len(tagGroup) {
+                       qr.Tags[i] = make([]Tag, len(tagGroup))
+               } else {
+                       qr.Tags[i] = qr.Tags[i][:len(tagGroup)]
+               }
+               for j, tag := range tagGroup {
+                       qr.Tags[i][j].name = tag.name
+                       qr.Tags[i][j].value = append(qr.Tags[i][j].value[:0], 
tag.value...)
+                       qr.Tags[i][j].valueType = tag.valueType
+                       qr.Tags[i][j].indexed = tag.indexed
+               }
+       }
+       
+       // Copy metadata
+       qr.Metadata = other.Metadata
+}
+
 // Stats contains system statistics and performance metrics.
 type Stats struct {
        // MemoryUsageBytes tracks current memory usage
@@ -244,6 +317,32 @@ type ResponseMetadata struct {
        TruncatedResults bool
 }
 
+// Validate validates ResponseMetadata for correctness.
+func (rm *ResponseMetadata) Validate() error {
+       if rm.ExecutionTimeMs < 0 {
+               return fmt.Errorf("executionTimeMs cannot be negative")
+       }
+       if rm.ElementsScanned < 0 {
+               return fmt.Errorf("elementsScanned cannot be negative")
+       }
+       if rm.ElementsFiltered < 0 {
+               return fmt.Errorf("elementsFiltered cannot be negative")
+       }
+       if rm.ElementsFiltered > rm.ElementsScanned {
+               return fmt.Errorf("elementsFiltered (%d) cannot exceed 
elementsScanned (%d)", rm.ElementsFiltered, rm.ElementsScanned)
+       }
+       if rm.PartsAccessed < 0 {
+               return fmt.Errorf("partsAccessed cannot be negative")
+       }
+       if rm.BlocksScanned < 0 {
+               return fmt.Errorf("blocksScanned cannot be negative")
+       }
+       if rm.CacheHitRatio < 0.0 || rm.CacheHitRatio > 1.0 {
+               return fmt.Errorf("cacheHitRatio must be between 0.0 and 1.0, 
got %f", rm.CacheHitRatio)
+       }
+       return nil
+}
+
 // Tag represents an individual tag for WriteRequest.
 // This uses the existing tag structure from the sidx package.
 type Tag = tag
@@ -256,6 +355,18 @@ func (wr WriteRequest) Validate() error {
        if wr.Data == nil {
                return fmt.Errorf("data cannot be nil")
        }
+       if len(wr.Data) == 0 {
+               return fmt.Errorf("data cannot be empty")
+       }
+       // Validate tags if present
+       for i, tag := range wr.Tags {
+               if tag.name == "" {
+                       return fmt.Errorf("tag[%d] name cannot be empty", i)
+               }
+               if len(tag.value) == 0 {
+                       return fmt.Errorf("tag[%d] value cannot be empty", i)
+               }
+       }
        return nil
 }
 
@@ -264,6 +375,26 @@ func (qr QueryRequest) Validate() error {
        if qr.Name == "" {
                return fmt.Errorf("name cannot be empty")
        }
+       if qr.MaxElementSize < 0 {
+               return fmt.Errorf("maxElementSize cannot be negative")
+       }
+       // Validate tag projection names
+       for i, projection := range qr.TagProjection {
+               if projection.Family == "" {
+                       return fmt.Errorf("tagProjection[%d] family cannot be 
empty", i)
+               }
+       }
+       // Validate entities structure
+       for i, entityGroup := range qr.Entities {
+               if len(entityGroup) == 0 {
+                       return fmt.Errorf("entities[%d] cannot be empty", i)
+               }
+               for j, tagValue := range entityGroup {
+                       if tagValue == nil {
+                               return fmt.Errorf("entities[%d][%d] cannot be 
nil", i, j)
+                       }
+               }
+       }
        return nil
 }
 
@@ -341,7 +472,7 @@ func (qr *QueryRequest) CopyFrom(other *QueryRequest) {
 //             if batch.Error != nil {
 //                     log.Printf("query execution error: %v", batch.Error)
 //             }
-//             // Process batch.Keys, batch.Data, etc.
+//             // Process batch.Keys, batch.Data, batch.Tags, etc.
 //     }
 
 // Example: Interface composition in SIDX

Reply via email to