498b235461
Build and test / Build and test AMD64 Ubuntu 22.04 (push) Failing after 0s
Publish Builder / amazonlinux2023 (push) Failing after 1s
Build and test / UT for Go (push) Has been skipped
Publish KRTE Images / KRTE (push) Failing after 1s
Build and test / Integration Test (push) Has been skipped
Build and test / Upload Code Coverage (push) Has been skipped
Publish Builder / rockylinux9 (push) Failing after 1s
Publish Builder / ubuntu22.04 (push) Failing after 0s
Publish Builder / ubuntu24.04 (push) Failing after 0s
Publish Gpu Builder / publish-gpu-builder (push) Failing after 1s
Publish Test Images / PyTest (push) Failing after 0s
Build and test / UT for Cpp (push) Has been cancelled
3426 lines
130 KiB
Go
3426 lines
130 KiB
Go
// Licensed to the LF AI & Data foundation under one
|
||
// or more contributor license agreements. See the NOTICE file
|
||
// distributed with this work for additional information
|
||
// regarding copyright ownership. The ASF licenses this file
|
||
// to you under the Apache License, Version 2.0 (the
|
||
// "License"); you may not use this file except in compliance
|
||
// with the License. You may obtain a copy of the License at
|
||
//
|
||
// http://www.apache.org/licenses/LICENSE-2.0
|
||
//
|
||
// Unless required by applicable law or agreed to in writing, software
|
||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
// See the License for the specific language governing permissions and
|
||
// limitations under the License.
|
||
|
||
package queryutil
|
||
|
||
import (
|
||
"context"
|
||
"encoding/binary"
|
||
"fmt"
|
||
"math"
|
||
"testing"
|
||
|
||
"github.com/stretchr/testify/assert"
|
||
"github.com/stretchr/testify/require"
|
||
|
||
"github.com/milvus-io/milvus-proto/go-api/v3/commonpb"
|
||
"github.com/milvus-io/milvus-proto/go-api/v3/schemapb"
|
||
"github.com/milvus-io/milvus/pkg/v3/proto/internalpb"
|
||
)
|
||
|
||
// makeNullableSchema builds a minimal CollectionSchema marking the given
|
||
// fieldID as nullable with the specified DataType. For dense vectors, pass
|
||
// dim > 0; for sparse or scalars, pass dim = 0.
|
||
func makeNullableSchema(fieldID int64, dataType schemapb.DataType, dim int64) *schemapb.CollectionSchema {
|
||
fs := &schemapb.FieldSchema{
|
||
FieldID: fieldID,
|
||
DataType: dataType,
|
||
Nullable: true,
|
||
}
|
||
if dim > 0 {
|
||
fs.TypeParams = []*commonpb.KeyValuePair{
|
||
{Key: "dim", Value: fmt.Sprintf("%d", dim)},
|
||
}
|
||
}
|
||
return &schemapb.CollectionSchema{Fields: []*schemapb.FieldSchema{fs}}
|
||
}
|
||
|
||
func requireRangeSliceRetrieveResults(t *testing.T, result *internalpb.RetrieveResults, start, end int) *internalpb.RetrieveResults {
|
||
t.Helper()
|
||
sliced, err := rangeSliceRetrieveResults(result, start, end)
|
||
require.NoError(t, err)
|
||
return sliced
|
||
}
|
||
|
||
func requireRangeSliceFieldData(t *testing.T, fd *schemapb.FieldData, start, end int) *schemapb.FieldData {
|
||
t.Helper()
|
||
sliced, err := rangeSliceFieldData(fd, start, end)
|
||
require.NoError(t, err)
|
||
return sliced
|
||
}
|
||
|
||
func requireRangeSliceVectorField(t *testing.T, vf *schemapb.VectorField, start, end int, validData []bool) *schemapb.VectorField {
|
||
t.Helper()
|
||
sliced, err := rangeSliceVectorField(vf, start, end, validData)
|
||
require.NoError(t, err)
|
||
return sliced
|
||
}
|
||
|
||
func requireSliceFieldData(t *testing.T, fd *schemapb.FieldData, indices []int) *schemapb.FieldData {
|
||
t.Helper()
|
||
sliced, err := sliceFieldData(fd, indices)
|
||
require.NoError(t, err)
|
||
return sliced
|
||
}
|
||
|
||
func requireSliceVectorField(t *testing.T, vf *schemapb.VectorField, indices []int, validData []bool) *schemapb.VectorField {
|
||
t.Helper()
|
||
sliced, err := sliceVectorField(vf, indices, validData)
|
||
require.NoError(t, err)
|
||
return sliced
|
||
}
|
||
|
||
func TestGetRowCountNullableCompactVectorWithoutIDsUsesLogicalRows(t *testing.T) {
|
||
result := &internalpb.RetrieveResults{
|
||
FieldsData: []*schemapb.FieldData{
|
||
{
|
||
Type: schemapb.DataType_FloatVector,
|
||
FieldName: "nullable_vec",
|
||
FieldId: 100,
|
||
Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Dim: 2,
|
||
Data: &schemapb.VectorField_FloatVector{
|
||
FloatVector: &schemapb.FloatArray{Data: []float32{1, 2, 3, 4}},
|
||
},
|
||
}},
|
||
ValidData: []bool{true, false, true},
|
||
},
|
||
},
|
||
}
|
||
|
||
assert.Equal(t, 3, getRowCount(result))
|
||
}
|
||
|
||
// =========================================================================
|
||
// buildMergedFieldData: ValidData preservation
|
||
// =========================================================================
|
||
|
||
func TestBuildMergedFieldData_ValidData(t *testing.T) {
|
||
// Two results with nullable int64 fields
|
||
r1 := &internalpb.RetrieveResults{
|
||
Ids: &schemapb.IDs{IdField: &schemapb.IDs_IntId{IntId: &schemapb.LongArray{Data: []int64{1, 2}}}},
|
||
FieldsData: []*schemapb.FieldData{
|
||
{
|
||
Type: schemapb.DataType_Int64, FieldId: 100,
|
||
Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_LongData{LongData: &schemapb.LongArray{Data: []int64{10, 20}}},
|
||
}},
|
||
ValidData: []bool{true, false}, // row 0 valid, row 1 null
|
||
},
|
||
},
|
||
}
|
||
r2 := &internalpb.RetrieveResults{
|
||
Ids: &schemapb.IDs{IdField: &schemapb.IDs_IntId{IntId: &schemapb.LongArray{Data: []int64{3}}}},
|
||
FieldsData: []*schemapb.FieldData{
|
||
{
|
||
Type: schemapb.DataType_Int64, FieldId: 100,
|
||
Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_LongData{LongData: &schemapb.LongArray{Data: []int64{30}}},
|
||
}},
|
||
ValidData: []bool{true},
|
||
},
|
||
},
|
||
}
|
||
|
||
results := []*internalpb.RetrieveResults{r1, r2}
|
||
selectedRows := []rowRef{
|
||
{resultIdx: 0, rowIdx: 0}, // r1 row 0 (valid)
|
||
{resultIdx: 0, rowIdx: 1}, // r1 row 1 (null)
|
||
{resultIdx: 1, rowIdx: 0}, // r2 row 0 (valid)
|
||
}
|
||
|
||
merged, err := buildMergedRetrieveResults(results, selectedRows, makeNullableSchema(100, schemapb.DataType_Int64, 0))
|
||
require.NoError(t, err)
|
||
require.Len(t, merged.FieldsData, 1)
|
||
|
||
fd := merged.FieldsData[0]
|
||
assert.Equal(t, []bool{true, false, true}, fd.ValidData)
|
||
assert.Equal(t, []int64{10, 20, 30}, fd.GetScalars().GetLongData().GetData())
|
||
}
|
||
|
||
func TestBuildMergedFieldData_ValidData_MixedNullable(t *testing.T) {
|
||
// r1 has ValidData (nullable), r2 has explicit ValidData=[true] (all valid)
|
||
// This tests that explicit all-valid ValidData is preserved correctly.
|
||
r1 := &internalpb.RetrieveResults{
|
||
Ids: &schemapb.IDs{IdField: &schemapb.IDs_IntId{IntId: &schemapb.LongArray{Data: []int64{1}}}},
|
||
FieldsData: []*schemapb.FieldData{
|
||
{
|
||
Type: schemapb.DataType_Int64, FieldId: 100,
|
||
Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_LongData{LongData: &schemapb.LongArray{Data: []int64{10}}},
|
||
}},
|
||
ValidData: []bool{false}, // null
|
||
},
|
||
},
|
||
}
|
||
r2 := &internalpb.RetrieveResults{
|
||
Ids: &schemapb.IDs{IdField: &schemapb.IDs_IntId{IntId: &schemapb.LongArray{Data: []int64{2}}}},
|
||
FieldsData: []*schemapb.FieldData{
|
||
{
|
||
Type: schemapb.DataType_Int64, FieldId: 100,
|
||
Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_LongData{LongData: &schemapb.LongArray{Data: []int64{20}}},
|
||
}},
|
||
ValidData: []bool{true}, // explicit valid (segcore always includes ValidData for nullable fields)
|
||
},
|
||
},
|
||
}
|
||
|
||
results := []*internalpb.RetrieveResults{r1, r2}
|
||
selectedRows := []rowRef{
|
||
{resultIdx: 0, rowIdx: 0}, // r1 (null)
|
||
{resultIdx: 1, rowIdx: 0}, // r2 (valid=true)
|
||
}
|
||
|
||
merged, err := buildMergedRetrieveResults(results, selectedRows, makeNullableSchema(100, schemapb.DataType_Int64, 0))
|
||
require.NoError(t, err)
|
||
|
||
fd := merged.FieldsData[0]
|
||
assert.Equal(t, []bool{false, true}, fd.ValidData)
|
||
}
|
||
|
||
// =========================================================================
|
||
// buildMergedScalarField: ArrayData ElementType preservation
|
||
// =========================================================================
|
||
|
||
func TestBuildMergedScalarField_ArrayElementType(t *testing.T) {
|
||
r1 := &internalpb.RetrieveResults{
|
||
Ids: &schemapb.IDs{IdField: &schemapb.IDs_IntId{IntId: &schemapb.LongArray{Data: []int64{1, 2}}}},
|
||
FieldsData: []*schemapb.FieldData{
|
||
{
|
||
Type: schemapb.DataType_Array, FieldId: 200,
|
||
Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_ArrayData{ArrayData: &schemapb.ArrayArray{
|
||
Data: []*schemapb.ScalarField{
|
||
{Data: &schemapb.ScalarField_FloatData{FloatData: &schemapb.FloatArray{Data: []float32{1.1, 2.2}}}},
|
||
{Data: &schemapb.ScalarField_FloatData{FloatData: &schemapb.FloatArray{Data: []float32{3.3}}}},
|
||
},
|
||
ElementType: schemapb.DataType_Float,
|
||
}},
|
||
}},
|
||
},
|
||
},
|
||
}
|
||
|
||
results := []*internalpb.RetrieveResults{r1}
|
||
selectedRows := []rowRef{{resultIdx: 0, rowIdx: 0}, {resultIdx: 0, rowIdx: 1}}
|
||
|
||
merged, err := buildMergedRetrieveResults(results, selectedRows, nil)
|
||
require.NoError(t, err)
|
||
|
||
arrayField := merged.FieldsData[0].GetScalars().GetArrayData()
|
||
assert.Equal(t, schemapb.DataType_Float, arrayField.GetElementType())
|
||
assert.Len(t, arrayField.GetData(), 2)
|
||
}
|
||
|
||
// =========================================================================
|
||
// buildMergedScalarField: Geometry types
|
||
// =========================================================================
|
||
|
||
func TestBuildMergedScalarField_GeometryWktData(t *testing.T) {
|
||
r1 := &internalpb.RetrieveResults{
|
||
Ids: &schemapb.IDs{IdField: &schemapb.IDs_IntId{IntId: &schemapb.LongArray{Data: []int64{1, 2}}}},
|
||
FieldsData: []*schemapb.FieldData{
|
||
{
|
||
Type: schemapb.DataType_Geometry, FieldId: 300,
|
||
Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_GeometryWktData{GeometryWktData: &schemapb.GeometryWktArray{
|
||
Data: []string{"POINT(0 0)", "POINT(1 1)"},
|
||
}},
|
||
}},
|
||
},
|
||
},
|
||
}
|
||
|
||
results := []*internalpb.RetrieveResults{r1}
|
||
selectedRows := []rowRef{{resultIdx: 0, rowIdx: 1}}
|
||
|
||
merged, err := buildMergedRetrieveResults(results, selectedRows, nil)
|
||
require.NoError(t, err)
|
||
|
||
wktData := merged.FieldsData[0].GetScalars().GetGeometryWktData().GetData()
|
||
assert.Equal(t, []string{"POINT(1 1)"}, wktData)
|
||
}
|
||
|
||
func TestBuildMergedScalarField_GeometryData(t *testing.T) {
|
||
r1 := &internalpb.RetrieveResults{
|
||
Ids: &schemapb.IDs{IdField: &schemapb.IDs_IntId{IntId: &schemapb.LongArray{Data: []int64{1}}}},
|
||
FieldsData: []*schemapb.FieldData{
|
||
{
|
||
Type: schemapb.DataType_Geometry, FieldId: 300,
|
||
Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_GeometryData{GeometryData: &schemapb.GeometryArray{
|
||
Data: [][]byte{{0x01, 0x02}, {0x03, 0x04}},
|
||
}},
|
||
}},
|
||
},
|
||
},
|
||
}
|
||
|
||
results := []*internalpb.RetrieveResults{r1}
|
||
selectedRows := []rowRef{{resultIdx: 0, rowIdx: 0}}
|
||
|
||
merged, err := buildMergedRetrieveResults(results, selectedRows, nil)
|
||
require.NoError(t, err)
|
||
|
||
geoData := merged.FieldsData[0].GetScalars().GetGeometryData().GetData()
|
||
assert.Equal(t, [][]byte{{0x01, 0x02}}, geoData)
|
||
}
|
||
|
||
// =========================================================================
|
||
// buildMergedScalarField: Timestamptz and Mol types
|
||
// =========================================================================
|
||
|
||
func TestBuildMergedScalarField_TimestamptzData(t *testing.T) {
|
||
r1 := &internalpb.RetrieveResults{
|
||
Ids: &schemapb.IDs{IdField: &schemapb.IDs_IntId{IntId: &schemapb.LongArray{Data: []int64{1, 2}}}},
|
||
FieldsData: []*schemapb.FieldData{
|
||
{
|
||
Type: schemapb.DataType_Timestamptz, FieldId: 400,
|
||
Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_TimestamptzData{TimestamptzData: &schemapb.TimestamptzArray{
|
||
Data: []int64{1000, 2000},
|
||
}},
|
||
}},
|
||
},
|
||
},
|
||
}
|
||
|
||
results := []*internalpb.RetrieveResults{r1}
|
||
selectedRows := []rowRef{{resultIdx: 0, rowIdx: 0}, {resultIdx: 0, rowIdx: 1}}
|
||
|
||
merged, err := buildMergedRetrieveResults(results, selectedRows, nil)
|
||
require.NoError(t, err)
|
||
|
||
tsData := merged.FieldsData[0].GetScalars().GetTimestamptzData().GetData()
|
||
assert.Equal(t, []int64{1000, 2000}, tsData)
|
||
}
|
||
|
||
func TestBuildMergedScalarField_MolData(t *testing.T) {
|
||
r1 := &internalpb.RetrieveResults{
|
||
Ids: &schemapb.IDs{IdField: &schemapb.IDs_IntId{IntId: &schemapb.LongArray{Data: []int64{1}}}},
|
||
FieldsData: []*schemapb.FieldData{
|
||
{
|
||
Type: schemapb.DataType_Mol, FieldId: 500,
|
||
Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_MolData{MolData: &schemapb.MolArray{
|
||
Data: [][]byte{{0xAA, 0xBB}},
|
||
}},
|
||
}},
|
||
},
|
||
},
|
||
}
|
||
|
||
results := []*internalpb.RetrieveResults{r1}
|
||
selectedRows := []rowRef{{resultIdx: 0, rowIdx: 0}}
|
||
|
||
merged, err := buildMergedRetrieveResults(results, selectedRows, nil)
|
||
require.NoError(t, err)
|
||
|
||
molData := merged.FieldsData[0].GetScalars().GetMolData().GetData()
|
||
assert.Equal(t, [][]byte{{0xAA, 0xBB}}, molData)
|
||
}
|
||
|
||
// =========================================================================
|
||
// buildMergedVectorField: Int8Vector
|
||
// =========================================================================
|
||
|
||
func TestBuildMergedVectorField_Int8Vector(t *testing.T) {
|
||
dim := 4
|
||
r1 := &internalpb.RetrieveResults{
|
||
Ids: &schemapb.IDs{IdField: &schemapb.IDs_IntId{IntId: &schemapb.LongArray{Data: []int64{1, 2}}}},
|
||
FieldsData: []*schemapb.FieldData{
|
||
{
|
||
Type: schemapb.DataType_Int8Vector, FieldId: 600,
|
||
Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Dim: int64(dim),
|
||
Data: &schemapb.VectorField_Int8Vector{Int8Vector: []byte{1, 2, 3, 4, 5, 6, 7, 8}},
|
||
}},
|
||
},
|
||
},
|
||
}
|
||
|
||
results := []*internalpb.RetrieveResults{r1}
|
||
selectedRows := []rowRef{{resultIdx: 0, rowIdx: 1}} // select row 1 only
|
||
|
||
merged, err := buildMergedRetrieveResults(results, selectedRows, nil)
|
||
require.NoError(t, err)
|
||
|
||
vecData := merged.FieldsData[0].GetVectors().GetInt8Vector()
|
||
assert.Equal(t, []byte{5, 6, 7, 8}, vecData)
|
||
}
|
||
|
||
// =========================================================================
|
||
// rangeSliceScalarField: new types
|
||
// =========================================================================
|
||
|
||
func TestRangeSliceScalarField_ArrayElementType(t *testing.T) {
|
||
fd := &schemapb.FieldData{
|
||
Type: schemapb.DataType_Array, FieldId: 200,
|
||
Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_ArrayData{ArrayData: &schemapb.ArrayArray{
|
||
Data: []*schemapb.ScalarField{
|
||
{Data: &schemapb.ScalarField_IntData{IntData: &schemapb.IntArray{Data: []int32{1}}}},
|
||
{Data: &schemapb.ScalarField_IntData{IntData: &schemapb.IntArray{Data: []int32{2}}}},
|
||
{Data: &schemapb.ScalarField_IntData{IntData: &schemapb.IntArray{Data: []int32{3}}}},
|
||
},
|
||
ElementType: schemapb.DataType_Int32,
|
||
}},
|
||
}},
|
||
}
|
||
|
||
sliced := requireRangeSliceFieldData(t, fd, 1, 3)
|
||
arrayField := sliced.GetScalars().GetArrayData()
|
||
assert.Equal(t, schemapb.DataType_Int32, arrayField.GetElementType())
|
||
assert.Len(t, arrayField.GetData(), 2)
|
||
}
|
||
|
||
func TestRangeSliceScalarField_GeometryWkt(t *testing.T) {
|
||
fd := &schemapb.FieldData{
|
||
Type: schemapb.DataType_Geometry, FieldId: 300,
|
||
Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_GeometryWktData{GeometryWktData: &schemapb.GeometryWktArray{
|
||
Data: []string{"POINT(0 0)", "POINT(1 1)", "POINT(2 2)"},
|
||
}},
|
||
}},
|
||
}
|
||
|
||
sliced := requireRangeSliceFieldData(t, fd, 1, 3)
|
||
assert.Equal(t, []string{"POINT(1 1)", "POINT(2 2)"}, sliced.GetScalars().GetGeometryWktData().GetData())
|
||
}
|
||
|
||
func TestRangeSliceScalarField_Timestamptz(t *testing.T) {
|
||
fd := &schemapb.FieldData{
|
||
Type: schemapb.DataType_Timestamptz, FieldId: 400,
|
||
Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_TimestamptzData{TimestamptzData: &schemapb.TimestamptzArray{
|
||
Data: []int64{100, 200, 300},
|
||
}},
|
||
}},
|
||
}
|
||
|
||
sliced := requireRangeSliceFieldData(t, fd, 0, 2)
|
||
assert.Equal(t, []int64{100, 200}, sliced.GetScalars().GetTimestamptzData().GetData())
|
||
}
|
||
|
||
// =========================================================================
|
||
// sliceScalarField (index-based): new types
|
||
// =========================================================================
|
||
|
||
func TestSliceScalarField_ArrayElementType(t *testing.T) {
|
||
fd := &schemapb.FieldData{
|
||
Type: schemapb.DataType_Array, FieldId: 200,
|
||
Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_ArrayData{ArrayData: &schemapb.ArrayArray{
|
||
Data: []*schemapb.ScalarField{
|
||
{Data: &schemapb.ScalarField_LongData{LongData: &schemapb.LongArray{Data: []int64{10}}}},
|
||
{Data: &schemapb.ScalarField_LongData{LongData: &schemapb.LongArray{Data: []int64{20}}}},
|
||
{Data: &schemapb.ScalarField_LongData{LongData: &schemapb.LongArray{Data: []int64{30}}}},
|
||
},
|
||
ElementType: schemapb.DataType_Int64,
|
||
}},
|
||
}},
|
||
}
|
||
|
||
sliced := requireSliceFieldData(t, fd, []int{2, 0})
|
||
arrayField := sliced.GetScalars().GetArrayData()
|
||
assert.Equal(t, schemapb.DataType_Int64, arrayField.GetElementType())
|
||
assert.Len(t, arrayField.GetData(), 2)
|
||
assert.Equal(t, []int64{30}, arrayField.GetData()[0].GetLongData().GetData())
|
||
assert.Equal(t, []int64{10}, arrayField.GetData()[1].GetLongData().GetData())
|
||
}
|
||
|
||
func TestSliceScalarField_GeometryWkt(t *testing.T) {
|
||
fd := &schemapb.FieldData{
|
||
Type: schemapb.DataType_Geometry, FieldId: 300,
|
||
Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_GeometryWktData{GeometryWktData: &schemapb.GeometryWktArray{
|
||
Data: []string{"POINT(0 0)", "POINT(1 1)", "POINT(2 2)"},
|
||
}},
|
||
}},
|
||
}
|
||
|
||
sliced := requireSliceFieldData(t, fd, []int{2, 0})
|
||
assert.Equal(t, []string{"POINT(2 2)", "POINT(0 0)"}, sliced.GetScalars().GetGeometryWktData().GetData())
|
||
}
|
||
|
||
// =========================================================================
|
||
// calcFieldElementSize: new types
|
||
// =========================================================================
|
||
|
||
func TestCalcFieldElementSize_Geometry(t *testing.T) {
|
||
fd := &schemapb.FieldData{
|
||
Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_GeometryWktData{GeometryWktData: &schemapb.GeometryWktArray{
|
||
Data: []string{"POINT(0 0)", "LINESTRING(0 0, 1 1)"},
|
||
}},
|
||
}},
|
||
}
|
||
assert.Equal(t, int64(len("POINT(0 0)")), calcFieldElementSize(fd, 0))
|
||
assert.Equal(t, int64(len("LINESTRING(0 0, 1 1)")), calcFieldElementSize(fd, 1))
|
||
}
|
||
|
||
func TestCalcFieldElementSize_Timestamptz(t *testing.T) {
|
||
fd := &schemapb.FieldData{
|
||
Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_TimestamptzData{TimestamptzData: &schemapb.TimestamptzArray{
|
||
Data: []int64{1000},
|
||
}},
|
||
}},
|
||
}
|
||
assert.Equal(t, int64(8), calcFieldElementSize(fd, 0))
|
||
}
|
||
|
||
func TestCalcFieldElementSize_Int8Vector(t *testing.T) {
|
||
fd := &schemapb.FieldData{
|
||
Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Dim: 128,
|
||
Data: &schemapb.VectorField_Int8Vector{Int8Vector: make([]byte, 256)},
|
||
}},
|
||
}
|
||
assert.Equal(t, int64(128), calcFieldElementSize(fd, 0))
|
||
}
|
||
|
||
// =========================================================================
|
||
// ElementIndices propagation through merge
|
||
// =========================================================================
|
||
|
||
func TestBuildMergedRetrieveResults_ElementIndices(t *testing.T) {
|
||
r1 := &internalpb.RetrieveResults{
|
||
Ids: &schemapb.IDs{IdField: &schemapb.IDs_IntId{IntId: &schemapb.LongArray{Data: []int64{1, 2}}}},
|
||
FieldsData: []*schemapb.FieldData{
|
||
{
|
||
Type: schemapb.DataType_Int64, FieldId: 100,
|
||
Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_LongData{LongData: &schemapb.LongArray{Data: []int64{10, 20}}},
|
||
}},
|
||
},
|
||
},
|
||
ElementLevel: true,
|
||
ElementIndices: []*internalpb.ElementIndices{{Indices: []int32{0, 1}}, {Indices: []int32{2}}},
|
||
}
|
||
|
||
results := []*internalpb.RetrieveResults{r1}
|
||
selectedRows := []rowRef{{resultIdx: 0, rowIdx: 1}, {resultIdx: 0, rowIdx: 0}}
|
||
|
||
merged, err := buildMergedRetrieveResults(results, selectedRows, nil)
|
||
require.NoError(t, err)
|
||
|
||
assert.True(t, merged.GetElementLevel())
|
||
require.Len(t, merged.GetElementIndices(), 2)
|
||
assert.Equal(t, []int32{2}, merged.GetElementIndices()[0].GetIndices())
|
||
assert.Equal(t, []int32{0, 1}, merged.GetElementIndices()[1].GetIndices())
|
||
}
|
||
|
||
// =========================================================================
|
||
// rangeSliceRetrieveResults: ElementIndices propagation
|
||
// =========================================================================
|
||
|
||
func TestRangeSliceRetrieveResults_ElementIndices(t *testing.T) {
|
||
result := &internalpb.RetrieveResults{
|
||
Ids: &schemapb.IDs{IdField: &schemapb.IDs_IntId{IntId: &schemapb.LongArray{Data: []int64{1, 2, 3}}}},
|
||
FieldsData: []*schemapb.FieldData{
|
||
{
|
||
Type: schemapb.DataType_Int64, FieldId: 100,
|
||
Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_LongData{LongData: &schemapb.LongArray{Data: []int64{10, 20, 30}}},
|
||
}},
|
||
},
|
||
},
|
||
ElementLevel: true,
|
||
ElementIndices: []*internalpb.ElementIndices{
|
||
{Indices: []int32{0, 1}},
|
||
{Indices: []int32{2}},
|
||
{Indices: []int32{0}},
|
||
},
|
||
}
|
||
|
||
sliced := requireRangeSliceRetrieveResults(t, result, 1, 3)
|
||
assert.True(t, sliced.GetElementLevel())
|
||
require.Len(t, sliced.GetElementIndices(), 2)
|
||
assert.Equal(t, []int32{2}, sliced.GetElementIndices()[0].GetIndices())
|
||
assert.Equal(t, []int32{0}, sliced.GetElementIndices()[1].GetIndices())
|
||
}
|
||
|
||
// =========================================================================
|
||
// ConcatAndCheckPKOperator
|
||
// =========================================================================
|
||
|
||
func TestConcatAndCheckPKOperator_NoDuplicate(t *testing.T) {
|
||
op := NewConcatAndCheckPKOperator(nil)
|
||
ctx := context.Background()
|
||
|
||
r1 := &internalpb.RetrieveResults{
|
||
Ids: &schemapb.IDs{IdField: &schemapb.IDs_IntId{IntId: &schemapb.LongArray{Data: []int64{1, 2}}}},
|
||
FieldsData: []*schemapb.FieldData{
|
||
{
|
||
Type: schemapb.DataType_Int64, FieldId: 100,
|
||
Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_LongData{LongData: &schemapb.LongArray{Data: []int64{10, 20}}},
|
||
}},
|
||
},
|
||
},
|
||
}
|
||
r2 := &internalpb.RetrieveResults{
|
||
Ids: &schemapb.IDs{IdField: &schemapb.IDs_IntId{IntId: &schemapb.LongArray{Data: []int64{3, 4}}}},
|
||
FieldsData: []*schemapb.FieldData{
|
||
{
|
||
Type: schemapb.DataType_Int64, FieldId: 100,
|
||
Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_LongData{LongData: &schemapb.LongArray{Data: []int64{30, 40}}},
|
||
}},
|
||
},
|
||
},
|
||
}
|
||
|
||
outputs, err := op.Run(ctx, nil, []*internalpb.RetrieveResults{r1, r2})
|
||
require.NoError(t, err)
|
||
|
||
result := outputs[0].(*internalpb.RetrieveResults)
|
||
ids := result.GetIds().GetIntId().GetData()
|
||
// Concatenated in order: 1,2,3,4
|
||
assert.Equal(t, []int64{1, 2, 3, 4}, ids)
|
||
}
|
||
|
||
func TestConcatAndCheckPKOperator_DuplicateFails(t *testing.T) {
|
||
op := NewConcatAndCheckPKOperator(nil)
|
||
ctx := context.Background()
|
||
|
||
r1 := &internalpb.RetrieveResults{
|
||
Ids: &schemapb.IDs{IdField: &schemapb.IDs_IntId{IntId: &schemapb.LongArray{Data: []int64{1, 2}}}},
|
||
FieldsData: []*schemapb.FieldData{
|
||
{
|
||
Type: schemapb.DataType_Int64, FieldId: 100,
|
||
Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_LongData{LongData: &schemapb.LongArray{Data: []int64{10, 20}}},
|
||
}},
|
||
},
|
||
},
|
||
}
|
||
r2 := &internalpb.RetrieveResults{
|
||
Ids: &schemapb.IDs{IdField: &schemapb.IDs_IntId{IntId: &schemapb.LongArray{Data: []int64{2, 3}}}}, // PK=2 duplicate
|
||
FieldsData: []*schemapb.FieldData{
|
||
{
|
||
Type: schemapb.DataType_Int64, FieldId: 100,
|
||
Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_LongData{LongData: &schemapb.LongArray{Data: []int64{21, 30}}},
|
||
}},
|
||
},
|
||
},
|
||
}
|
||
|
||
_, err := op.Run(ctx, nil, []*internalpb.RetrieveResults{r1, r2})
|
||
require.Error(t, err)
|
||
assert.Contains(t, err.Error(), "duplicate PK")
|
||
}
|
||
|
||
// =========================================================================
|
||
// buildMergedRetrieveResults: common scalar types (LongData, DoubleData, StringData)
|
||
// =========================================================================
|
||
|
||
func TestBuildMergedFieldData_CommonScalarTypes(t *testing.T) {
|
||
// Merge two results with Int64 + Double + String fields
|
||
makeDoubleField := func(id int64, name string, vals []float64) *schemapb.FieldData {
|
||
return &schemapb.FieldData{
|
||
FieldId: id, FieldName: name, Type: schemapb.DataType_Double,
|
||
Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_DoubleData{DoubleData: &schemapb.DoubleArray{Data: vals}},
|
||
}},
|
||
}
|
||
}
|
||
makeStringField := func(id int64, name string, vals []string) *schemapb.FieldData {
|
||
return &schemapb.FieldData{
|
||
FieldId: id, FieldName: name, Type: schemapb.DataType_VarChar,
|
||
Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_StringData{StringData: &schemapb.StringArray{Data: vals}},
|
||
}},
|
||
}
|
||
}
|
||
|
||
r1 := &internalpb.RetrieveResults{
|
||
Ids: &schemapb.IDs{IdField: &schemapb.IDs_IntId{IntId: &schemapb.LongArray{Data: []int64{1, 2}}}},
|
||
FieldsData: []*schemapb.FieldData{
|
||
{FieldId: 100, Type: schemapb.DataType_Int64, Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_LongData{LongData: &schemapb.LongArray{Data: []int64{10, 20}}},
|
||
}}},
|
||
makeDoubleField(101, "price", []float64{1.1, 2.2}),
|
||
makeStringField(102, "name", []string{"alice", "bob"}),
|
||
},
|
||
}
|
||
r2 := &internalpb.RetrieveResults{
|
||
Ids: &schemapb.IDs{IdField: &schemapb.IDs_IntId{IntId: &schemapb.LongArray{Data: []int64{3}}}},
|
||
FieldsData: []*schemapb.FieldData{
|
||
{FieldId: 100, Type: schemapb.DataType_Int64, Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_LongData{LongData: &schemapb.LongArray{Data: []int64{30}}},
|
||
}}},
|
||
makeDoubleField(101, "price", []float64{3.3}),
|
||
makeStringField(102, "name", []string{"charlie"}),
|
||
},
|
||
}
|
||
|
||
selectedRows := []rowRef{{0, 0}, {1, 0}, {0, 1}} // r1[0], r2[0], r1[1]
|
||
merged, err := buildMergedRetrieveResults([]*internalpb.RetrieveResults{r1, r2}, selectedRows, nil)
|
||
require.NoError(t, err)
|
||
|
||
assert.Equal(t, []int64{1, 3, 2}, merged.GetIds().GetIntId().GetData())
|
||
assert.Equal(t, []int64{10, 30, 20}, merged.GetFieldsData()[0].GetScalars().GetLongData().GetData())
|
||
assert.Equal(t, []float64{1.1, 3.3, 2.2}, merged.GetFieldsData()[1].GetScalars().GetDoubleData().GetData())
|
||
assert.Equal(t, []string{"alice", "charlie", "bob"}, merged.GetFieldsData()[2].GetScalars().GetStringData().GetData())
|
||
}
|
||
|
||
// =========================================================================
|
||
// buildMergedRetrieveResults: FloatVector merge
|
||
// =========================================================================
|
||
|
||
func TestBuildMergedFieldData_FloatVector(t *testing.T) {
|
||
dim := 4
|
||
makeVecField := func(id int64, data []float32) *schemapb.FieldData {
|
||
return &schemapb.FieldData{
|
||
FieldId: id, Type: schemapb.DataType_FloatVector,
|
||
Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Dim: int64(dim),
|
||
Data: &schemapb.VectorField_FloatVector{FloatVector: &schemapb.FloatArray{Data: data}},
|
||
}},
|
||
}
|
||
}
|
||
|
||
r1 := &internalpb.RetrieveResults{
|
||
Ids: &schemapb.IDs{IdField: &schemapb.IDs_IntId{IntId: &schemapb.LongArray{Data: []int64{1, 2}}}},
|
||
FieldsData: []*schemapb.FieldData{
|
||
makeVecField(100, []float32{1, 2, 3, 4, 5, 6, 7, 8}), // 2 rows × dim=4
|
||
},
|
||
}
|
||
r2 := &internalpb.RetrieveResults{
|
||
Ids: &schemapb.IDs{IdField: &schemapb.IDs_IntId{IntId: &schemapb.LongArray{Data: []int64{3}}}},
|
||
FieldsData: []*schemapb.FieldData{
|
||
makeVecField(100, []float32{9, 10, 11, 12}), // 1 row × dim=4
|
||
},
|
||
}
|
||
|
||
selectedRows := []rowRef{{1, 0}, {0, 1}} // r2[0], r1[1]
|
||
merged, err := buildMergedRetrieveResults([]*internalpb.RetrieveResults{r1, r2}, selectedRows, nil)
|
||
require.NoError(t, err)
|
||
|
||
vecData := merged.GetFieldsData()[0].GetVectors().GetFloatVector().GetData()
|
||
assert.Equal(t, []float32{9, 10, 11, 12, 5, 6, 7, 8}, vecData)
|
||
}
|
||
|
||
// =========================================================================
|
||
// calcRowSize
|
||
// =========================================================================
|
||
|
||
func TestCalcRowSize_BasicTypes(t *testing.T) {
|
||
r := &internalpb.RetrieveResults{
|
||
Ids: &schemapb.IDs{IdField: &schemapb.IDs_IntId{IntId: &schemapb.LongArray{Data: []int64{1}}}},
|
||
FieldsData: []*schemapb.FieldData{
|
||
// Int64: 8 bytes
|
||
{FieldId: 100, Type: schemapb.DataType_Int64, Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_LongData{LongData: &schemapb.LongArray{Data: []int64{42}}},
|
||
}}},
|
||
// Double: 8 bytes
|
||
{FieldId: 101, Type: schemapb.DataType_Double, Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_DoubleData{DoubleData: &schemapb.DoubleArray{Data: []float64{3.14}}},
|
||
}}},
|
||
// String: len("hello") = 5 bytes
|
||
{FieldId: 102, Type: schemapb.DataType_VarChar, Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_StringData{StringData: &schemapb.StringArray{Data: []string{"hello"}}},
|
||
}}},
|
||
},
|
||
}
|
||
|
||
size := calcRowSize(r, 0)
|
||
assert.Equal(t, int64(8+8+5), size) // Int64(8) + Double(8) + String("hello"=5)
|
||
}
|
||
|
||
func TestCalcRowSize_EmptyString(t *testing.T) {
|
||
r := &internalpb.RetrieveResults{
|
||
FieldsData: []*schemapb.FieldData{
|
||
{FieldId: 100, Type: schemapb.DataType_VarChar, Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_StringData{StringData: &schemapb.StringArray{Data: []string{""}}},
|
||
}}},
|
||
},
|
||
}
|
||
size := calcRowSize(r, 0)
|
||
assert.Equal(t, int64(0), size)
|
||
}
|
||
|
||
func TestCalcRowSize_FloatVector(t *testing.T) {
|
||
dim := 8
|
||
r := &internalpb.RetrieveResults{
|
||
FieldsData: []*schemapb.FieldData{
|
||
{
|
||
FieldId: 100, Type: schemapb.DataType_FloatVector,
|
||
Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Dim: int64(dim),
|
||
Data: &schemapb.VectorField_FloatVector{FloatVector: &schemapb.FloatArray{Data: make([]float32, dim*2)}}, // 2 rows
|
||
}},
|
||
},
|
||
},
|
||
}
|
||
// FloatVector: dim * 4 bytes per row = 8 * 4 = 32
|
||
size := calcRowSize(r, 0)
|
||
assert.Equal(t, int64(dim*4), size)
|
||
}
|
||
|
||
func TestCalcRowSize_NullableDenseVectorUsesLogicalValidity(t *testing.T) {
|
||
tests := []struct {
|
||
name string
|
||
field *schemapb.FieldData
|
||
wantSize int64
|
||
}{
|
||
{
|
||
name: "float_vector",
|
||
field: &schemapb.FieldData{
|
||
Type: schemapb.DataType_FloatVector,
|
||
ValidData: []bool{false, true},
|
||
Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Dim: 2,
|
||
Data: &schemapb.VectorField_FloatVector{FloatVector: &schemapb.FloatArray{Data: []float32{1, 2}}},
|
||
}},
|
||
},
|
||
wantSize: 8,
|
||
},
|
||
{
|
||
name: "binary_vector",
|
||
field: &schemapb.FieldData{
|
||
Type: schemapb.DataType_BinaryVector,
|
||
ValidData: []bool{false, true},
|
||
Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Dim: 16,
|
||
Data: &schemapb.VectorField_BinaryVector{BinaryVector: []byte{0x01, 0x02}},
|
||
}},
|
||
},
|
||
wantSize: 2,
|
||
},
|
||
{
|
||
name: "float16_vector",
|
||
field: &schemapb.FieldData{
|
||
Type: schemapb.DataType_Float16Vector,
|
||
ValidData: []bool{false, true},
|
||
Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Dim: 2,
|
||
Data: &schemapb.VectorField_Float16Vector{Float16Vector: []byte{0x01, 0x02, 0x03, 0x04}},
|
||
}},
|
||
},
|
||
wantSize: 4,
|
||
},
|
||
{
|
||
name: "bfloat16_vector",
|
||
field: &schemapb.FieldData{
|
||
Type: schemapb.DataType_BFloat16Vector,
|
||
ValidData: []bool{false, true},
|
||
Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Dim: 2,
|
||
Data: &schemapb.VectorField_Bfloat16Vector{Bfloat16Vector: []byte{0x01, 0x02, 0x03, 0x04}},
|
||
}},
|
||
},
|
||
wantSize: 4,
|
||
},
|
||
{
|
||
name: "int8_vector",
|
||
field: &schemapb.FieldData{
|
||
Type: schemapb.DataType_Int8Vector,
|
||
ValidData: []bool{false, true},
|
||
Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Dim: 4,
|
||
Data: &schemapb.VectorField_Int8Vector{Int8Vector: []byte{0x01, 0x02, 0x03, 0x04}},
|
||
}},
|
||
},
|
||
wantSize: 4,
|
||
},
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run(tt.name, func(t *testing.T) {
|
||
r := &internalpb.RetrieveResults{FieldsData: []*schemapb.FieldData{tt.field}}
|
||
assert.Equal(t, int64(0), calcRowSize(r, 0))
|
||
assert.Equal(t, tt.wantSize, calcRowSize(r, 1))
|
||
})
|
||
}
|
||
}
|
||
|
||
func TestCalcRowSize_NullableSparseVectorUsesCompactMapping(t *testing.T) {
|
||
rowA := makeTestSparseVec(1, 0.5)
|
||
rowC := makeTestSparseVec(3, 1.5)
|
||
|
||
tests := []struct {
|
||
name string
|
||
validData []bool
|
||
contents [][]byte
|
||
wantSizes []int64
|
||
}{
|
||
{
|
||
name: "null_before_valid",
|
||
validData: []bool{false, true},
|
||
contents: [][]byte{rowA},
|
||
wantSizes: []int64{0, int64(len(rowA))},
|
||
},
|
||
{
|
||
name: "valid_null_valid",
|
||
validData: []bool{true, false, true},
|
||
contents: [][]byte{rowA, rowC},
|
||
wantSizes: []int64{int64(len(rowA)), 0, int64(len(rowC))},
|
||
},
|
||
{
|
||
name: "all_null",
|
||
validData: []bool{false, false},
|
||
contents: nil,
|
||
wantSizes: []int64{0, 0},
|
||
},
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run(tt.name, func(t *testing.T) {
|
||
r := &internalpb.RetrieveResults{
|
||
FieldsData: []*schemapb.FieldData{
|
||
{
|
||
Type: schemapb.DataType_SparseFloatVector,
|
||
ValidData: tt.validData,
|
||
Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Dim: 4,
|
||
Data: &schemapb.VectorField_SparseFloatVector{SparseFloatVector: &schemapb.SparseFloatArray{
|
||
Dim: 4,
|
||
Contents: tt.contents,
|
||
}},
|
||
}},
|
||
},
|
||
},
|
||
}
|
||
for rowIdx, want := range tt.wantSizes {
|
||
assert.Equal(t, want, calcRowSize(r, int64(rowIdx)))
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
func TestCalcRowSize_MultipleFieldTypes(t *testing.T) {
|
||
r := &internalpb.RetrieveResults{
|
||
FieldsData: []*schemapb.FieldData{
|
||
// Bool: 1 byte
|
||
{
|
||
FieldId: 100, Type: schemapb.DataType_Bool,
|
||
Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_BoolData{BoolData: &schemapb.BoolArray{Data: []bool{true}}},
|
||
}},
|
||
},
|
||
// Float: 4 bytes
|
||
{
|
||
FieldId: 101, Type: schemapb.DataType_Float,
|
||
Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_FloatData{FloatData: &schemapb.FloatArray{Data: []float32{1.0}}},
|
||
}},
|
||
},
|
||
// Int32: 4 bytes
|
||
{
|
||
FieldId: 102, Type: schemapb.DataType_Int32,
|
||
Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_IntData{IntData: &schemapb.IntArray{Data: []int32{42}}},
|
||
}},
|
||
},
|
||
},
|
||
}
|
||
size := calcRowSize(r, 0)
|
||
assert.Equal(t, int64(1+4+4), size) // Bool(1) + Float(4) + Int32(4)
|
||
}
|
||
|
||
// =========================================================================
|
||
// validateElementLevelConsistency
|
||
// =========================================================================
|
||
|
||
func TestValidateElementLevelConsistency_Consistent(t *testing.T) {
|
||
r1 := &internalpb.RetrieveResults{
|
||
Ids: &schemapb.IDs{IdField: &schemapb.IDs_IntId{IntId: &schemapb.LongArray{Data: []int64{1}}}},
|
||
ElementLevel: true,
|
||
ElementIndices: []*internalpb.ElementIndices{
|
||
{Indices: []int32{0, 1}},
|
||
},
|
||
}
|
||
r2 := &internalpb.RetrieveResults{
|
||
Ids: &schemapb.IDs{IdField: &schemapb.IDs_IntId{IntId: &schemapb.LongArray{Data: []int64{2}}}},
|
||
ElementLevel: true,
|
||
ElementIndices: []*internalpb.ElementIndices{
|
||
{Indices: []int32{3}},
|
||
},
|
||
}
|
||
err := validateElementLevelConsistency([]*internalpb.RetrieveResults{r1, r2}, nil)
|
||
assert.NoError(t, err)
|
||
}
|
||
|
||
// makeStructArrayFieldData creates a FieldData wrapping a StructArrayField with scalar sub-fields.
|
||
// Used by TestGetRowCount and slice/reconstruct tests below.
|
||
func makeStructArrayFieldData(fieldID int64, name string, subFields []*schemapb.FieldData) *schemapb.FieldData {
|
||
return &schemapb.FieldData{
|
||
Type: schemapb.DataType_Array,
|
||
FieldId: fieldID,
|
||
FieldName: name,
|
||
Field: &schemapb.FieldData_StructArrays{
|
||
StructArrays: &schemapb.StructArrayField{
|
||
Fields: subFields,
|
||
},
|
||
},
|
||
}
|
||
}
|
||
|
||
// =========================================================================
|
||
// rangeSliceStructArrayField
|
||
// =========================================================================
|
||
|
||
func TestRangeSliceStructArrayField(t *testing.T) {
|
||
sa := &schemapb.StructArrayField{
|
||
Fields: []*schemapb.FieldData{
|
||
{
|
||
Type: schemapb.DataType_Int64, FieldId: 101, FieldName: "age",
|
||
Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_LongData{LongData: &schemapb.LongArray{Data: []int64{10, 20, 30, 40}}},
|
||
}},
|
||
},
|
||
{
|
||
Type: schemapb.DataType_VarChar, FieldId: 102, FieldName: "name",
|
||
Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_StringData{StringData: &schemapb.StringArray{Data: []string{"a", "b", "c", "d"}}},
|
||
}},
|
||
},
|
||
},
|
||
}
|
||
|
||
sliced, err := rangeSliceStructArrayField(sa, 1, 3)
|
||
require.NoError(t, err)
|
||
require.Len(t, sliced.GetFields(), 2)
|
||
assert.Equal(t, []int64{20, 30}, sliced.GetFields()[0].GetScalars().GetLongData().GetData())
|
||
assert.Equal(t, []string{"b", "c"}, sliced.GetFields()[1].GetScalars().GetStringData().GetData())
|
||
}
|
||
|
||
func TestRangeSliceStructArrayField_Nil(t *testing.T) {
|
||
sliced, err := rangeSliceStructArrayField(nil, 0, 1)
|
||
require.NoError(t, err)
|
||
assert.Nil(t, sliced)
|
||
}
|
||
|
||
// =========================================================================
|
||
// sliceStructArrayField
|
||
// =========================================================================
|
||
|
||
func TestSliceStructArrayField(t *testing.T) {
|
||
sa := &schemapb.StructArrayField{
|
||
Fields: []*schemapb.FieldData{
|
||
{
|
||
Type: schemapb.DataType_Int64, FieldId: 101, FieldName: "val",
|
||
Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_LongData{LongData: &schemapb.LongArray{Data: []int64{100, 200, 300, 400}}},
|
||
}},
|
||
},
|
||
{
|
||
Type: schemapb.DataType_VarChar, FieldId: 102, FieldName: "tag",
|
||
Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_StringData{StringData: &schemapb.StringArray{Data: []string{"x", "y", "z", "w"}}},
|
||
}},
|
||
},
|
||
},
|
||
}
|
||
|
||
// Select indices 3, 0, 2 (out of order)
|
||
sliced, err := sliceStructArrayField(sa, []int{3, 0, 2})
|
||
require.NoError(t, err)
|
||
require.Len(t, sliced.GetFields(), 2)
|
||
assert.Equal(t, []int64{400, 100, 300}, sliced.GetFields()[0].GetScalars().GetLongData().GetData())
|
||
assert.Equal(t, []string{"w", "x", "z"}, sliced.GetFields()[1].GetScalars().GetStringData().GetData())
|
||
}
|
||
|
||
func TestSliceStructArrayField_Nil(t *testing.T) {
|
||
sliced, err := sliceStructArrayField(nil, []int{0})
|
||
require.NoError(t, err)
|
||
assert.Nil(t, sliced)
|
||
}
|
||
|
||
// =========================================================================
|
||
// buildMergedVectorField: VectorArray (used in StructArray vector sub-fields)
|
||
// =========================================================================
|
||
|
||
func TestBuildMergedVectorField_VectorArray(t *testing.T) {
|
||
dim := int64(2)
|
||
v1 := &schemapb.VectorField{Dim: dim, Data: &schemapb.VectorField_FloatVector{FloatVector: &schemapb.FloatArray{Data: []float32{1.0, 2.0}}}}
|
||
v2 := &schemapb.VectorField{Dim: dim, Data: &schemapb.VectorField_FloatVector{FloatVector: &schemapb.FloatArray{Data: []float32{3.0, 4.0}}}}
|
||
v3 := &schemapb.VectorField{Dim: dim, Data: &schemapb.VectorField_FloatVector{FloatVector: &schemapb.FloatArray{Data: []float32{5.0, 6.0}}}}
|
||
|
||
r1 := &internalpb.RetrieveResults{
|
||
Ids: &schemapb.IDs{IdField: &schemapb.IDs_IntId{IntId: &schemapb.LongArray{Data: []int64{1, 2}}}},
|
||
FieldsData: []*schemapb.FieldData{
|
||
{
|
||
Type: schemapb.DataType_FloatVector, FieldId: 700,
|
||
Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Dim: dim,
|
||
Data: &schemapb.VectorField_VectorArray{VectorArray: &schemapb.VectorArray{
|
||
Dim: dim,
|
||
Data: []*schemapb.VectorField{v1, v2},
|
||
ElementType: schemapb.DataType_FloatVector,
|
||
}},
|
||
}},
|
||
},
|
||
},
|
||
}
|
||
r2 := &internalpb.RetrieveResults{
|
||
Ids: &schemapb.IDs{IdField: &schemapb.IDs_IntId{IntId: &schemapb.LongArray{Data: []int64{3}}}},
|
||
FieldsData: []*schemapb.FieldData{
|
||
{
|
||
Type: schemapb.DataType_FloatVector, FieldId: 700,
|
||
Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Dim: dim,
|
||
Data: &schemapb.VectorField_VectorArray{VectorArray: &schemapb.VectorArray{
|
||
Dim: dim,
|
||
Data: []*schemapb.VectorField{v3},
|
||
ElementType: schemapb.DataType_FloatVector,
|
||
}},
|
||
}},
|
||
},
|
||
},
|
||
}
|
||
|
||
results := []*internalpb.RetrieveResults{r1, r2}
|
||
selectedRows := []rowRef{{resultIdx: 1, rowIdx: 0}, {resultIdx: 0, rowIdx: 0}}
|
||
|
||
merged, err := buildMergedRetrieveResults(results, selectedRows, nil)
|
||
require.NoError(t, err)
|
||
|
||
va := merged.FieldsData[0].GetVectors().GetVectorArray()
|
||
require.Len(t, va.GetData(), 2)
|
||
assert.Equal(t, []float32{5.0, 6.0}, va.GetData()[0].GetFloatVector().GetData())
|
||
assert.Equal(t, []float32{1.0, 2.0}, va.GetData()[1].GetFloatVector().GetData())
|
||
}
|
||
|
||
func TestBuildMergedVectorField_NullableStructVectorArray_AllNull(t *testing.T) {
|
||
const fieldID = int64(700)
|
||
dim := int64(2)
|
||
result := &internalpb.RetrieveResults{
|
||
Ids: &schemapb.IDs{IdField: &schemapb.IDs_IntId{IntId: &schemapb.LongArray{Data: []int64{1}}}},
|
||
FieldsData: []*schemapb.FieldData{
|
||
{
|
||
Type: schemapb.DataType_ArrayOfVector,
|
||
FieldId: fieldID,
|
||
Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Dim: dim,
|
||
}},
|
||
ValidData: []bool{false},
|
||
},
|
||
},
|
||
}
|
||
schema := &schemapb.CollectionSchema{
|
||
StructArrayFields: []*schemapb.StructArrayFieldSchema{
|
||
{
|
||
Name: "struct_field",
|
||
Nullable: true,
|
||
Fields: []*schemapb.FieldSchema{
|
||
{
|
||
FieldID: fieldID,
|
||
Name: "struct_field[vec_array]",
|
||
DataType: schemapb.DataType_ArrayOfVector,
|
||
ElementType: schemapb.DataType_FloatVector,
|
||
Nullable: true,
|
||
TypeParams: []*commonpb.KeyValuePair{
|
||
{Key: "dim", Value: "2"},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
}
|
||
|
||
merged, err := buildMergedRetrieveResults([]*internalpb.RetrieveResults{result}, []rowRef{{resultIdx: 0, rowIdx: 0}}, schema)
|
||
require.NoError(t, err)
|
||
assert.Equal(t, []bool{false}, merged.FieldsData[0].GetValidData())
|
||
va := merged.FieldsData[0].GetVectors().GetVectorArray()
|
||
require.NotNil(t, va)
|
||
assert.Equal(t, dim, va.GetDim())
|
||
assert.Equal(t, schemapb.DataType_FloatVector, va.GetElementType())
|
||
require.Len(t, va.GetData(), 1)
|
||
assert.Empty(t, va.GetData()[0].GetFloatVector().GetData())
|
||
}
|
||
|
||
func TestBuildMergedVectorField_NullableStructVectorArray_AllNullWithEmptyVectorArray(t *testing.T) {
|
||
const fieldID = int64(700)
|
||
dim := int64(2)
|
||
result := &internalpb.RetrieveResults{
|
||
Ids: &schemapb.IDs{IdField: &schemapb.IDs_IntId{IntId: &schemapb.LongArray{Data: []int64{1, 2}}}},
|
||
FieldsData: []*schemapb.FieldData{
|
||
{
|
||
Type: schemapb.DataType_ArrayOfVector,
|
||
FieldId: fieldID,
|
||
Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Dim: dim,
|
||
Data: &schemapb.VectorField_VectorArray{VectorArray: &schemapb.VectorArray{
|
||
Dim: dim,
|
||
ElementType: schemapb.DataType_FloatVector,
|
||
}},
|
||
}},
|
||
ValidData: []bool{false, false},
|
||
},
|
||
},
|
||
}
|
||
schema := makeNullableSchema(fieldID, schemapb.DataType_ArrayOfVector, dim)
|
||
schema.GetFields()[0].ElementType = schemapb.DataType_FloatVector
|
||
|
||
merged, err := buildMergedRetrieveResults(
|
||
[]*internalpb.RetrieveResults{result},
|
||
[]rowRef{{resultIdx: 0, rowIdx: 1}},
|
||
schema,
|
||
)
|
||
require.NoError(t, err)
|
||
assert.Equal(t, []bool{false}, merged.FieldsData[0].GetValidData())
|
||
va := merged.FieldsData[0].GetVectors().GetVectorArray()
|
||
require.NotNil(t, va)
|
||
assert.Equal(t, dim, va.GetDim())
|
||
assert.Equal(t, schemapb.DataType_FloatVector, va.GetElementType())
|
||
require.Len(t, va.GetData(), 1)
|
||
assert.Empty(t, va.GetData()[0].GetFloatVector().GetData())
|
||
}
|
||
|
||
func TestBuildMergedVectorField_NullableArrayOfVector_RejectsEmptyValidData(t *testing.T) {
|
||
const fieldID = int64(700)
|
||
dim := int64(2)
|
||
result := &internalpb.RetrieveResults{
|
||
Ids: &schemapb.IDs{IdField: &schemapb.IDs_IntId{IntId: &schemapb.LongArray{Data: []int64{1}}}},
|
||
FieldsData: []*schemapb.FieldData{
|
||
{
|
||
Type: schemapb.DataType_ArrayOfVector,
|
||
FieldId: fieldID,
|
||
Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Dim: dim,
|
||
Data: &schemapb.VectorField_VectorArray{VectorArray: &schemapb.VectorArray{
|
||
Dim: dim,
|
||
Data: []*schemapb.VectorField{{Dim: dim, Data: &schemapb.VectorField_FloatVector{FloatVector: &schemapb.FloatArray{Data: []float32{1.0, 2.0}}}}},
|
||
ElementType: schemapb.DataType_FloatVector,
|
||
}},
|
||
}},
|
||
},
|
||
},
|
||
}
|
||
schema := makeNullableSchema(fieldID, schemapb.DataType_ArrayOfVector, dim)
|
||
schema.GetFields()[0].ElementType = schemapb.DataType_FloatVector
|
||
|
||
_, err := buildMergedRetrieveResults([]*internalpb.RetrieveResults{result}, []rowRef{{resultIdx: 0, rowIdx: 0}}, schema)
|
||
require.Error(t, err)
|
||
assert.Contains(t, err.Error(), "empty ValidData")
|
||
}
|
||
|
||
func TestBuildMergedVectorField_NullableArrayOfVector_RejectsValidDataRowOutOfRange(t *testing.T) {
|
||
const fieldID = int64(700)
|
||
dim := int64(2)
|
||
result := &internalpb.RetrieveResults{
|
||
Ids: &schemapb.IDs{IdField: &schemapb.IDs_IntId{IntId: &schemapb.LongArray{Data: []int64{1, 2}}}},
|
||
FieldsData: []*schemapb.FieldData{
|
||
{
|
||
Type: schemapb.DataType_ArrayOfVector,
|
||
FieldId: fieldID,
|
||
Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Dim: dim,
|
||
Data: &schemapb.VectorField_VectorArray{VectorArray: &schemapb.VectorArray{
|
||
Dim: dim,
|
||
Data: []*schemapb.VectorField{{Dim: dim, Data: &schemapb.VectorField_FloatVector{FloatVector: &schemapb.FloatArray{Data: []float32{1.0, 2.0}}}}, {Dim: dim, Data: &schemapb.VectorField_FloatVector{FloatVector: &schemapb.FloatArray{Data: []float32{3.0, 4.0}}}}},
|
||
ElementType: schemapb.DataType_FloatVector,
|
||
}},
|
||
}},
|
||
ValidData: []bool{true},
|
||
},
|
||
},
|
||
}
|
||
schema := makeNullableSchema(fieldID, schemapb.DataType_ArrayOfVector, dim)
|
||
schema.GetFields()[0].ElementType = schemapb.DataType_FloatVector
|
||
|
||
_, err := buildMergedRetrieveResults([]*internalpb.RetrieveResults{result}, []rowRef{{resultIdx: 0, rowIdx: 1}}, schema)
|
||
require.Error(t, err)
|
||
assert.Contains(t, err.Error(), "rowIdx=1")
|
||
assert.Contains(t, err.Error(), "len(ValidData)=1")
|
||
}
|
||
|
||
func TestBuildMergedVectorField_NullableArrayOfVectorMixedAllNullSourceKeepsRowDense(t *testing.T) {
|
||
const fieldID = int64(700)
|
||
dim := int64(2)
|
||
v1 := &schemapb.VectorField{Dim: dim, Data: &schemapb.VectorField_FloatVector{FloatVector: &schemapb.FloatArray{Data: []float32{1.0, 2.0}}}}
|
||
|
||
allNullResult := &internalpb.RetrieveResults{
|
||
Ids: &schemapb.IDs{IdField: &schemapb.IDs_IntId{IntId: &schemapb.LongArray{Data: []int64{1}}}},
|
||
FieldsData: []*schemapb.FieldData{
|
||
{
|
||
Type: schemapb.DataType_ArrayOfVector,
|
||
FieldId: fieldID,
|
||
Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Dim: dim,
|
||
}},
|
||
ValidData: []bool{false},
|
||
},
|
||
},
|
||
}
|
||
validResult := &internalpb.RetrieveResults{
|
||
Ids: &schemapb.IDs{IdField: &schemapb.IDs_IntId{IntId: &schemapb.LongArray{Data: []int64{2}}}},
|
||
FieldsData: []*schemapb.FieldData{
|
||
{
|
||
Type: schemapb.DataType_ArrayOfVector,
|
||
FieldId: fieldID,
|
||
Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Dim: dim,
|
||
Data: &schemapb.VectorField_VectorArray{VectorArray: &schemapb.VectorArray{
|
||
Dim: dim,
|
||
Data: []*schemapb.VectorField{v1},
|
||
ElementType: schemapb.DataType_FloatVector,
|
||
}},
|
||
}},
|
||
ValidData: []bool{true},
|
||
},
|
||
},
|
||
}
|
||
schema := makeNullableSchema(fieldID, schemapb.DataType_ArrayOfVector, dim)
|
||
schema.GetFields()[0].ElementType = schemapb.DataType_FloatVector
|
||
|
||
merged, err := buildMergedRetrieveResults(
|
||
[]*internalpb.RetrieveResults{allNullResult, validResult},
|
||
[]rowRef{{resultIdx: 0, rowIdx: 0}, {resultIdx: 1, rowIdx: 0}},
|
||
schema,
|
||
)
|
||
require.NoError(t, err)
|
||
assert.Equal(t, []bool{false, true}, merged.FieldsData[0].GetValidData())
|
||
va := merged.FieldsData[0].GetVectors().GetVectorArray()
|
||
require.Len(t, va.GetData(), 2)
|
||
assert.Empty(t, va.GetData()[0].GetFloatVector().GetData())
|
||
assert.Equal(t, []float32{1.0, 2.0}, va.GetData()[1].GetFloatVector().GetData())
|
||
}
|
||
|
||
func TestBuildMergedVectorField_NullableArrayOfVectorUsesLogicalRowIndex(t *testing.T) {
|
||
const fieldID = int64(700)
|
||
dim := int64(2)
|
||
v1 := &schemapb.VectorField{Dim: dim, Data: &schemapb.VectorField_FloatVector{FloatVector: &schemapb.FloatArray{Data: []float32{1.0, 2.0}}}}
|
||
empty := &schemapb.VectorField{Dim: dim, Data: &schemapb.VectorField_FloatVector{FloatVector: &schemapb.FloatArray{}}}
|
||
v3 := &schemapb.VectorField{Dim: dim, Data: &schemapb.VectorField_FloatVector{FloatVector: &schemapb.FloatArray{Data: []float32{3.0, 4.0}}}}
|
||
|
||
result := &internalpb.RetrieveResults{
|
||
Ids: &schemapb.IDs{IdField: &schemapb.IDs_IntId{IntId: &schemapb.LongArray{Data: []int64{1, 2, 3, 4}}}},
|
||
FieldsData: []*schemapb.FieldData{
|
||
{
|
||
Type: schemapb.DataType_ArrayOfVector,
|
||
FieldId: fieldID,
|
||
Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Dim: dim,
|
||
Data: &schemapb.VectorField_VectorArray{VectorArray: &schemapb.VectorArray{
|
||
Dim: dim,
|
||
Data: []*schemapb.VectorField{v1, empty, v3, empty},
|
||
ElementType: schemapb.DataType_FloatVector,
|
||
}},
|
||
}},
|
||
ValidData: []bool{true, false, true, false},
|
||
},
|
||
},
|
||
}
|
||
schema := makeNullableSchema(fieldID, schemapb.DataType_ArrayOfVector, dim)
|
||
schema.GetFields()[0].ElementType = schemapb.DataType_FloatVector
|
||
|
||
merged, err := buildMergedRetrieveResults(
|
||
[]*internalpb.RetrieveResults{result},
|
||
[]rowRef{{resultIdx: 0, rowIdx: 2}, {resultIdx: 0, rowIdx: 1}, {resultIdx: 0, rowIdx: 0}},
|
||
schema,
|
||
)
|
||
require.NoError(t, err)
|
||
assert.Equal(t, []bool{true, false, true}, merged.FieldsData[0].GetValidData())
|
||
|
||
va := merged.FieldsData[0].GetVectors().GetVectorArray()
|
||
require.Len(t, va.GetData(), 3)
|
||
assert.Equal(t, []float32{3.0, 4.0}, va.GetData()[0].GetFloatVector().GetData())
|
||
assert.Empty(t, va.GetData()[1].GetFloatVector().GetData())
|
||
assert.Equal(t, []float32{1.0, 2.0}, va.GetData()[2].GetFloatVector().GetData())
|
||
}
|
||
|
||
// =========================================================================
|
||
// sliceVectorField & rangeSliceVectorField: VectorArray branch
|
||
// =========================================================================
|
||
|
||
func TestSliceVectorField_VectorArray(t *testing.T) {
|
||
dim := int64(2)
|
||
v1 := &schemapb.VectorField{Dim: dim, Data: &schemapb.VectorField_FloatVector{FloatVector: &schemapb.FloatArray{Data: []float32{1.0, 2.0}}}}
|
||
v2 := &schemapb.VectorField{Dim: dim, Data: &schemapb.VectorField_FloatVector{FloatVector: &schemapb.FloatArray{Data: []float32{3.0, 4.0}}}}
|
||
v3 := &schemapb.VectorField{Dim: dim, Data: &schemapb.VectorField_FloatVector{FloatVector: &schemapb.FloatArray{Data: []float32{5.0, 6.0}}}}
|
||
|
||
vf := &schemapb.VectorField{
|
||
Dim: dim,
|
||
Data: &schemapb.VectorField_VectorArray{VectorArray: &schemapb.VectorArray{
|
||
Dim: dim,
|
||
Data: []*schemapb.VectorField{v1, v2, v3},
|
||
ElementType: schemapb.DataType_FloatVector,
|
||
}},
|
||
}
|
||
|
||
sliced := requireSliceVectorField(t, vf, []int{2, 0}, nil)
|
||
va := sliced.GetVectorArray()
|
||
require.Len(t, va.GetData(), 2)
|
||
assert.Equal(t, []float32{5.0, 6.0}, va.GetData()[0].GetFloatVector().GetData())
|
||
assert.Equal(t, []float32{1.0, 2.0}, va.GetData()[1].GetFloatVector().GetData())
|
||
assert.Equal(t, schemapb.DataType_FloatVector, va.GetElementType())
|
||
}
|
||
|
||
func TestRangeSliceVectorField_VectorArray(t *testing.T) {
|
||
dim := int64(2)
|
||
v1 := &schemapb.VectorField{Dim: dim, Data: &schemapb.VectorField_FloatVector{FloatVector: &schemapb.FloatArray{Data: []float32{1.0, 2.0}}}}
|
||
v2 := &schemapb.VectorField{Dim: dim, Data: &schemapb.VectorField_FloatVector{FloatVector: &schemapb.FloatArray{Data: []float32{3.0, 4.0}}}}
|
||
v3 := &schemapb.VectorField{Dim: dim, Data: &schemapb.VectorField_FloatVector{FloatVector: &schemapb.FloatArray{Data: []float32{5.0, 6.0}}}}
|
||
|
||
vf := &schemapb.VectorField{
|
||
Dim: dim,
|
||
Data: &schemapb.VectorField_VectorArray{VectorArray: &schemapb.VectorArray{
|
||
Dim: dim,
|
||
Data: []*schemapb.VectorField{v1, v2, v3},
|
||
ElementType: schemapb.DataType_FloatVector,
|
||
}},
|
||
}
|
||
|
||
sliced := requireRangeSliceVectorField(t, vf, 1, 3, nil)
|
||
va := sliced.GetVectorArray()
|
||
require.Len(t, va.GetData(), 2)
|
||
assert.Equal(t, []float32{3.0, 4.0}, va.GetData()[0].GetFloatVector().GetData())
|
||
assert.Equal(t, []float32{5.0, 6.0}, va.GetData()[1].GetFloatVector().GetData())
|
||
}
|
||
|
||
// =========================================================================
|
||
// comparePK: string PK
|
||
// =========================================================================
|
||
|
||
func TestComparePK_StringPK(t *testing.T) {
|
||
assert.Equal(t, -1, comparePK("apple", "banana"))
|
||
assert.Equal(t, 0, comparePK("same", "same"))
|
||
assert.Equal(t, 1, comparePK("zebra", "alpha"))
|
||
}
|
||
|
||
func TestComparePK_Int64PK(t *testing.T) {
|
||
assert.Equal(t, -1, comparePK(int64(1), int64(2)))
|
||
assert.Equal(t, 0, comparePK(int64(5), int64(5)))
|
||
assert.Equal(t, 1, comparePK(int64(10), int64(3)))
|
||
}
|
||
|
||
// =========================================================================
|
||
// buildMergedScalarField: additional type branches
|
||
// =========================================================================
|
||
|
||
func TestBuildMergedScalarField_BoolData(t *testing.T) {
|
||
r1 := makeInternalResultIntPK([]int64{1, 2}, &schemapb.FieldData{
|
||
Type: schemapb.DataType_Bool, FieldId: 100,
|
||
Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_BoolData{BoolData: &schemapb.BoolArray{Data: []bool{true, false}}},
|
||
}},
|
||
})
|
||
selectedRows := []rowRef{{resultIdx: 0, rowIdx: 1}, {resultIdx: 0, rowIdx: 0}}
|
||
merged, err := buildMergedRetrieveResults([]*internalpb.RetrieveResults{r1}, selectedRows, nil)
|
||
require.NoError(t, err)
|
||
assert.Equal(t, []bool{false, true}, merged.FieldsData[0].GetScalars().GetBoolData().GetData())
|
||
}
|
||
|
||
func TestBuildMergedScalarField_IntData(t *testing.T) {
|
||
r1 := makeInternalResultIntPK([]int64{1, 2}, &schemapb.FieldData{
|
||
Type: schemapb.DataType_Int32, FieldId: 100,
|
||
Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_IntData{IntData: &schemapb.IntArray{Data: []int32{10, 20}}},
|
||
}},
|
||
})
|
||
selectedRows := []rowRef{{resultIdx: 0, rowIdx: 1}}
|
||
merged, err := buildMergedRetrieveResults([]*internalpb.RetrieveResults{r1}, selectedRows, nil)
|
||
require.NoError(t, err)
|
||
assert.Equal(t, []int32{20}, merged.FieldsData[0].GetScalars().GetIntData().GetData())
|
||
}
|
||
|
||
func TestBuildMergedScalarField_FloatData(t *testing.T) {
|
||
r1 := makeInternalResultIntPK([]int64{1}, &schemapb.FieldData{
|
||
Type: schemapb.DataType_Float, FieldId: 100,
|
||
Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_FloatData{FloatData: &schemapb.FloatArray{Data: []float32{1.5, 2.5}}},
|
||
}},
|
||
})
|
||
selectedRows := []rowRef{{resultIdx: 0, rowIdx: 0}}
|
||
merged, err := buildMergedRetrieveResults([]*internalpb.RetrieveResults{r1}, selectedRows, nil)
|
||
require.NoError(t, err)
|
||
assert.Equal(t, []float32{1.5}, merged.FieldsData[0].GetScalars().GetFloatData().GetData())
|
||
}
|
||
|
||
func TestBuildMergedScalarField_DoubleData(t *testing.T) {
|
||
r1 := makeInternalResultIntPK([]int64{1, 2}, &schemapb.FieldData{
|
||
Type: schemapb.DataType_Double, FieldId: 100,
|
||
Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_DoubleData{DoubleData: &schemapb.DoubleArray{Data: []float64{3.14, 2.71}}},
|
||
}},
|
||
})
|
||
selectedRows := []rowRef{{resultIdx: 0, rowIdx: 1}}
|
||
merged, err := buildMergedRetrieveResults([]*internalpb.RetrieveResults{r1}, selectedRows, nil)
|
||
require.NoError(t, err)
|
||
assert.Equal(t, []float64{2.71}, merged.FieldsData[0].GetScalars().GetDoubleData().GetData())
|
||
}
|
||
|
||
func TestBuildMergedScalarField_BytesData(t *testing.T) {
|
||
r1 := makeInternalResultIntPK([]int64{1, 2}, &schemapb.FieldData{
|
||
Type: schemapb.DataType_BinaryVector, FieldId: 100,
|
||
Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_BytesData{BytesData: &schemapb.BytesArray{Data: [][]byte{{0x01}, {0x02}}}},
|
||
}},
|
||
})
|
||
selectedRows := []rowRef{{resultIdx: 0, rowIdx: 0}, {resultIdx: 0, rowIdx: 1}}
|
||
merged, err := buildMergedRetrieveResults([]*internalpb.RetrieveResults{r1}, selectedRows, nil)
|
||
require.NoError(t, err)
|
||
assert.Equal(t, [][]byte{{0x01}, {0x02}}, merged.FieldsData[0].GetScalars().GetBytesData().GetData())
|
||
}
|
||
|
||
func TestBuildMergedScalarField_JsonData(t *testing.T) {
|
||
r1 := makeInternalResultIntPK([]int64{1, 2}, &schemapb.FieldData{
|
||
Type: schemapb.DataType_JSON, FieldId: 100,
|
||
Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_JsonData{JsonData: &schemapb.JSONArray{Data: [][]byte{[]byte(`{"a":1}`), []byte(`{"b":2}`)}}},
|
||
}},
|
||
})
|
||
selectedRows := []rowRef{{resultIdx: 0, rowIdx: 1}}
|
||
merged, err := buildMergedRetrieveResults([]*internalpb.RetrieveResults{r1}, selectedRows, nil)
|
||
require.NoError(t, err)
|
||
assert.Equal(t, [][]byte{[]byte(`{"b":2}`)}, merged.FieldsData[0].GetScalars().GetJsonData().GetData())
|
||
}
|
||
|
||
// =========================================================================
|
||
// buildMergedIDs: string PK
|
||
// =========================================================================
|
||
|
||
func TestBuildMergedIDs_StringPK(t *testing.T) {
|
||
r1 := &internalpb.RetrieveResults{
|
||
Ids: &schemapb.IDs{IdField: &schemapb.IDs_StrId{StrId: &schemapb.StringArray{Data: []string{"a", "b"}}}},
|
||
FieldsData: []*schemapb.FieldData{
|
||
makeStringField(100, "pk", []string{"a", "b"}),
|
||
},
|
||
}
|
||
selectedRows := []rowRef{{resultIdx: 0, rowIdx: 1}, {resultIdx: 0, rowIdx: 0}}
|
||
merged, err := buildMergedRetrieveResults([]*internalpb.RetrieveResults{r1}, selectedRows, nil)
|
||
require.NoError(t, err)
|
||
assert.Equal(t, []string{"b", "a"}, merged.GetIds().GetStrId().GetData())
|
||
}
|
||
|
||
// =========================================================================
|
||
// buildMergedVectorField: additional vector type branches
|
||
// =========================================================================
|
||
|
||
func TestBuildMergedVectorField_BinaryVector(t *testing.T) {
|
||
dim := 16 // 2 bytes per row
|
||
r1 := makeInternalResultIntPK([]int64{1, 2}, &schemapb.FieldData{
|
||
Type: schemapb.DataType_BinaryVector, FieldId: 100,
|
||
Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Dim: int64(dim),
|
||
Data: &schemapb.VectorField_BinaryVector{BinaryVector: []byte{0xAA, 0xBB, 0xCC, 0xDD}},
|
||
}},
|
||
})
|
||
selectedRows := []rowRef{{resultIdx: 0, rowIdx: 1}}
|
||
merged, err := buildMergedRetrieveResults([]*internalpb.RetrieveResults{r1}, selectedRows, nil)
|
||
require.NoError(t, err)
|
||
assert.Equal(t, []byte{0xCC, 0xDD}, merged.FieldsData[0].GetVectors().GetBinaryVector())
|
||
}
|
||
|
||
func TestBuildMergedVectorField_Float16Vector(t *testing.T) {
|
||
dim := 2 // 4 bytes per row (dim*2)
|
||
r1 := makeInternalResultIntPK([]int64{1, 2}, &schemapb.FieldData{
|
||
Type: schemapb.DataType_Float16Vector, FieldId: 100,
|
||
Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Dim: int64(dim),
|
||
Data: &schemapb.VectorField_Float16Vector{Float16Vector: []byte{1, 2, 3, 4, 5, 6, 7, 8}},
|
||
}},
|
||
})
|
||
selectedRows := []rowRef{{resultIdx: 0, rowIdx: 1}}
|
||
merged, err := buildMergedRetrieveResults([]*internalpb.RetrieveResults{r1}, selectedRows, nil)
|
||
require.NoError(t, err)
|
||
assert.Equal(t, []byte{5, 6, 7, 8}, merged.FieldsData[0].GetVectors().GetFloat16Vector())
|
||
}
|
||
|
||
func TestBuildMergedVectorField_BFloat16Vector(t *testing.T) {
|
||
dim := 2
|
||
r1 := makeInternalResultIntPK([]int64{1, 2}, &schemapb.FieldData{
|
||
Type: schemapb.DataType_BFloat16Vector, FieldId: 100,
|
||
Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Dim: int64(dim),
|
||
Data: &schemapb.VectorField_Bfloat16Vector{Bfloat16Vector: []byte{10, 20, 30, 40, 50, 60, 70, 80}},
|
||
}},
|
||
})
|
||
selectedRows := []rowRef{{resultIdx: 0, rowIdx: 0}}
|
||
merged, err := buildMergedRetrieveResults([]*internalpb.RetrieveResults{r1}, selectedRows, nil)
|
||
require.NoError(t, err)
|
||
assert.Equal(t, []byte{10, 20, 30, 40}, merged.FieldsData[0].GetVectors().GetBfloat16Vector())
|
||
}
|
||
|
||
func TestBuildMergedVectorField_SparseFloatVector(t *testing.T) {
|
||
r1 := makeInternalResultIntPK([]int64{1, 2}, &schemapb.FieldData{
|
||
Type: schemapb.DataType_SparseFloatVector, FieldId: 100,
|
||
Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Dim: 100,
|
||
Data: &schemapb.VectorField_SparseFloatVector{SparseFloatVector: &schemapb.SparseFloatArray{
|
||
Contents: [][]byte{{0x01, 0x02}, {0x03, 0x04}},
|
||
Dim: 100,
|
||
}},
|
||
}},
|
||
})
|
||
selectedRows := []rowRef{{resultIdx: 0, rowIdx: 1}}
|
||
merged, err := buildMergedRetrieveResults([]*internalpb.RetrieveResults{r1}, selectedRows, nil)
|
||
require.NoError(t, err)
|
||
assert.Equal(t, [][]byte{{0x03, 0x04}}, merged.FieldsData[0].GetVectors().GetSparseFloatVector().GetContents())
|
||
}
|
||
|
||
// =========================================================================
|
||
// buildCompactIndices / getVecDataIdx: nullable vector
|
||
// =========================================================================
|
||
|
||
func TestBuildCompactIndices_NullableVector(t *testing.T) {
|
||
r1 := &internalpb.RetrieveResults{
|
||
Ids: &schemapb.IDs{IdField: &schemapb.IDs_IntId{IntId: &schemapb.LongArray{Data: []int64{1, 2, 3}}}},
|
||
FieldsData: []*schemapb.FieldData{
|
||
{
|
||
Type: schemapb.DataType_FloatVector, FieldId: 100,
|
||
Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Dim: 2,
|
||
Data: &schemapb.VectorField_FloatVector{FloatVector: &schemapb.FloatArray{Data: []float32{1, 2, 3, 4}}},
|
||
}},
|
||
ValidData: []bool{true, false, true}, // row0=valid(idx0), row1=null, row2=valid(idx1)
|
||
},
|
||
},
|
||
}
|
||
indices, err := buildCompactIndices([]*internalpb.RetrieveResults{r1}, 0, true)
|
||
require.NoError(t, err)
|
||
require.NotNil(t, indices)
|
||
assert.Equal(t, []int{0, -1, 1}, indices[0])
|
||
}
|
||
|
||
func TestBuildCompactIndices_NonNullable(t *testing.T) {
|
||
r1 := &internalpb.RetrieveResults{
|
||
FieldsData: []*schemapb.FieldData{
|
||
{
|
||
Type: schemapb.DataType_FloatVector, FieldId: 100,
|
||
Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Dim: 2,
|
||
Data: &schemapb.VectorField_FloatVector{FloatVector: &schemapb.FloatArray{Data: []float32{1, 2, 3, 4}}},
|
||
}},
|
||
// No ValidData
|
||
},
|
||
},
|
||
}
|
||
indices, err := buildCompactIndices([]*internalpb.RetrieveResults{r1}, 0, false)
|
||
require.NoError(t, err)
|
||
assert.Nil(t, indices) // non-nullable → nil
|
||
}
|
||
|
||
func TestBuildCompactIndices_FailFast_EmptyValidData(t *testing.T) {
|
||
// nullable field with numRows > 0 but empty ValidData → must error
|
||
r1 := &internalpb.RetrieveResults{
|
||
Ids: &schemapb.IDs{IdField: &schemapb.IDs_IntId{IntId: &schemapb.LongArray{Data: []int64{1, 2}}}},
|
||
FieldsData: []*schemapb.FieldData{
|
||
{
|
||
Type: schemapb.DataType_SparseFloatVector, FieldId: 100,
|
||
// ValidData intentionally empty — segcore contract violation
|
||
},
|
||
},
|
||
}
|
||
_, err := buildCompactIndices([]*internalpb.RetrieveResults{r1}, 0, true)
|
||
require.Error(t, err)
|
||
assert.Contains(t, err.Error(), "empty ValidData")
|
||
}
|
||
|
||
func TestBuildCompactIndices_FailFast_LengthMismatch(t *testing.T) {
|
||
// nullable field with len(ValidData) != numRows → must error
|
||
r1 := &internalpb.RetrieveResults{
|
||
Ids: &schemapb.IDs{IdField: &schemapb.IDs_IntId{IntId: &schemapb.LongArray{Data: []int64{1, 2, 3}}}},
|
||
FieldsData: []*schemapb.FieldData{
|
||
{
|
||
Type: schemapb.DataType_FloatVector, FieldId: 100,
|
||
ValidData: []bool{true, false}, // 2 entries but 3 rows → mismatch
|
||
},
|
||
},
|
||
}
|
||
_, err := buildCompactIndices([]*internalpb.RetrieveResults{r1}, 0, true)
|
||
require.Error(t, err)
|
||
assert.Contains(t, err.Error(), "len(ValidData)")
|
||
}
|
||
|
||
func TestGetVecDataIdx(t *testing.T) {
|
||
// nil compactIndices → returns rowIdx directly
|
||
assert.Equal(t, 3, getVecDataIdx(nil, rowRef{resultIdx: 0, rowIdx: 3}))
|
||
|
||
// With compact indices
|
||
compactIdx := [][]int{
|
||
{0, -1, 1}, // result 0: row0=0, row1=null, row2=1
|
||
nil, // result 1: non-nullable
|
||
}
|
||
assert.Equal(t, 0, getVecDataIdx(compactIdx, rowRef{resultIdx: 0, rowIdx: 0}))
|
||
assert.Equal(t, -1, getVecDataIdx(compactIdx, rowRef{resultIdx: 0, rowIdx: 1}))
|
||
assert.Equal(t, 1, getVecDataIdx(compactIdx, rowRef{resultIdx: 0, rowIdx: 2}))
|
||
assert.Equal(t, 5, getVecDataIdx(compactIdx, rowRef{resultIdx: 1, rowIdx: 5})) // nil slice → rowIdx
|
||
|
||
// Out of range → panics (validated upstream by buildCompactIndices)
|
||
assert.Panics(t, func() { getVecDataIdx(compactIdx, rowRef{resultIdx: 0, rowIdx: 10}) })
|
||
}
|
||
|
||
// =========================================================================
|
||
// buildMergedVectorField: truncated data error paths
|
||
// =========================================================================
|
||
|
||
func TestBuildMergedVectorField_FloatVector_TruncatedData(t *testing.T) {
|
||
// FloatVector with dim=2 but only 2 floats (enough for 1 row, not 2)
|
||
r1 := &internalpb.RetrieveResults{
|
||
Ids: &schemapb.IDs{IdField: &schemapb.IDs_IntId{IntId: &schemapb.LongArray{Data: []int64{1, 2}}}},
|
||
FieldsData: []*schemapb.FieldData{{
|
||
FieldId: 100, Type: schemapb.DataType_FloatVector,
|
||
Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Dim: 2,
|
||
Data: &schemapb.VectorField_FloatVector{FloatVector: &schemapb.FloatArray{Data: []float32{1, 2}}}, // only 1 row
|
||
}},
|
||
}},
|
||
}
|
||
selectedRows := []rowRef{{resultIdx: 0, rowIdx: 0}, {resultIdx: 0, rowIdx: 1}}
|
||
schema := &schemapb.CollectionSchema{Fields: []*schemapb.FieldSchema{
|
||
{FieldID: 100, DataType: schemapb.DataType_FloatVector, TypeParams: []*commonpb.KeyValuePair{{Key: "dim", Value: "2"}}},
|
||
}}
|
||
|
||
_, err := buildMergedRetrieveResults([]*internalpb.RetrieveResults{r1}, selectedRows, schema)
|
||
require.Error(t, err)
|
||
assert.Contains(t, err.Error(), "truncated data")
|
||
}
|
||
|
||
func TestBuildMergedVectorField_BinaryVector_TruncatedData(t *testing.T) {
|
||
// BinaryVector with dim=16 (2 bytes/row) but only 2 bytes (1 row, not 2)
|
||
r1 := &internalpb.RetrieveResults{
|
||
Ids: &schemapb.IDs{IdField: &schemapb.IDs_IntId{IntId: &schemapb.LongArray{Data: []int64{1, 2}}}},
|
||
FieldsData: []*schemapb.FieldData{{
|
||
FieldId: 100, Type: schemapb.DataType_BinaryVector,
|
||
Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Dim: 16,
|
||
Data: &schemapb.VectorField_BinaryVector{BinaryVector: []byte{0xFF, 0x00}}, // 1 row
|
||
}},
|
||
}},
|
||
}
|
||
selectedRows := []rowRef{{resultIdx: 0, rowIdx: 0}, {resultIdx: 0, rowIdx: 1}}
|
||
schema := &schemapb.CollectionSchema{Fields: []*schemapb.FieldSchema{
|
||
{FieldID: 100, DataType: schemapb.DataType_BinaryVector, TypeParams: []*commonpb.KeyValuePair{{Key: "dim", Value: "16"}}},
|
||
}}
|
||
|
||
_, err := buildMergedRetrieveResults([]*internalpb.RetrieveResults{r1}, selectedRows, schema)
|
||
require.Error(t, err)
|
||
assert.Contains(t, err.Error(), "truncated data")
|
||
}
|
||
|
||
func TestBuildMergedVectorField_SparseVector_TruncatedContents(t *testing.T) {
|
||
// Sparse with ValidData=[true, true] but Contents has only 1 entry
|
||
r1 := &internalpb.RetrieveResults{
|
||
Ids: &schemapb.IDs{IdField: &schemapb.IDs_IntId{IntId: &schemapb.LongArray{Data: []int64{1, 2}}}},
|
||
FieldsData: []*schemapb.FieldData{{
|
||
FieldId: 100, Type: schemapb.DataType_SparseFloatVector,
|
||
Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Data: &schemapb.VectorField_SparseFloatVector{SparseFloatVector: &schemapb.SparseFloatArray{
|
||
Contents: [][]byte{makeTestSparseVec(1, 0.5)}, // only 1 content
|
||
}},
|
||
}},
|
||
ValidData: []bool{true, true}, // claims 2 valid rows
|
||
}},
|
||
}
|
||
selectedRows := []rowRef{{resultIdx: 0, rowIdx: 0}, {resultIdx: 0, rowIdx: 1}}
|
||
|
||
_, err := buildMergedRetrieveResults([]*internalpb.RetrieveResults{r1}, selectedRows,
|
||
makeNullableSchema(100, schemapb.DataType_SparseFloatVector, 0))
|
||
require.Error(t, err)
|
||
assert.Contains(t, err.Error(), "truncated data")
|
||
}
|
||
|
||
func TestBuildMergedVectorField_NullableCompact_BinaryVector(t *testing.T) {
|
||
// BinaryVector dim=8 (1 byte/row), nullable: row0=valid, row1=null, row2=valid
|
||
// Compact data: 2 bytes (only valid rows)
|
||
r1 := &internalpb.RetrieveResults{
|
||
Ids: &schemapb.IDs{IdField: &schemapb.IDs_IntId{IntId: &schemapb.LongArray{Data: []int64{1, 2, 3}}}},
|
||
FieldsData: []*schemapb.FieldData{{
|
||
FieldId: 100, Type: schemapb.DataType_BinaryVector,
|
||
Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Dim: 8,
|
||
Data: &schemapb.VectorField_BinaryVector{BinaryVector: []byte{0xAA, 0xBB}}, // 2 compact rows
|
||
}},
|
||
ValidData: []bool{true, false, true},
|
||
}},
|
||
}
|
||
selectedRows := []rowRef{{resultIdx: 0, rowIdx: 2}, {resultIdx: 0, rowIdx: 0}, {resultIdx: 0, rowIdx: 1}}
|
||
|
||
merged, err := buildMergedRetrieveResults([]*internalpb.RetrieveResults{r1}, selectedRows,
|
||
makeNullableSchema(100, schemapb.DataType_BinaryVector, 8))
|
||
require.NoError(t, err)
|
||
|
||
// row2 → compact idx 1 → 0xBB, row0 → compact idx 0 → 0xAA, row1 → null (skipped)
|
||
assert.Equal(t, []byte{0xBB, 0xAA}, merged.FieldsData[0].GetVectors().GetBinaryVector())
|
||
assert.Equal(t, []bool{true, true, false}, merged.FieldsData[0].ValidData)
|
||
}
|
||
|
||
func TestBuildMergedVectorField_NullableCompact_Float16Vector(t *testing.T) {
|
||
// Float16 dim=1 (2 bytes/row), nullable: row0=null, row1=valid
|
||
// Compact data: 2 bytes (1 valid row)
|
||
r1 := &internalpb.RetrieveResults{
|
||
Ids: &schemapb.IDs{IdField: &schemapb.IDs_IntId{IntId: &schemapb.LongArray{Data: []int64{1, 2}}}},
|
||
FieldsData: []*schemapb.FieldData{{
|
||
FieldId: 100, Type: schemapb.DataType_Float16Vector,
|
||
Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Dim: 1,
|
||
Data: &schemapb.VectorField_Float16Vector{Float16Vector: []byte{0x01, 0x02}}, // 1 compact row
|
||
}},
|
||
ValidData: []bool{false, true},
|
||
}},
|
||
}
|
||
selectedRows := []rowRef{{resultIdx: 0, rowIdx: 1}, {resultIdx: 0, rowIdx: 0}}
|
||
|
||
merged, err := buildMergedRetrieveResults([]*internalpb.RetrieveResults{r1}, selectedRows,
|
||
makeNullableSchema(100, schemapb.DataType_Float16Vector, 1))
|
||
require.NoError(t, err)
|
||
|
||
// row1 → compact idx 0 → [0x01, 0x02], row0 → null (skipped)
|
||
assert.Equal(t, []byte{0x01, 0x02}, merged.FieldsData[0].GetVectors().GetFloat16Vector())
|
||
assert.Equal(t, []bool{true, false}, merged.FieldsData[0].ValidData)
|
||
}
|
||
|
||
func TestBuildMergedVectorField_Float16Vector_TruncatedData(t *testing.T) {
|
||
// Float16 dim=2 (4 bytes/row) but only 4 bytes provided (1 row, not 2)
|
||
r1 := &internalpb.RetrieveResults{
|
||
Ids: &schemapb.IDs{IdField: &schemapb.IDs_IntId{IntId: &schemapb.LongArray{Data: []int64{1, 2}}}},
|
||
FieldsData: []*schemapb.FieldData{{
|
||
FieldId: 100, Type: schemapb.DataType_Float16Vector,
|
||
Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Dim: 2,
|
||
Data: &schemapb.VectorField_Float16Vector{Float16Vector: []byte{0x01, 0x02, 0x03, 0x04}}, // 1 row
|
||
}},
|
||
}},
|
||
}
|
||
selectedRows := []rowRef{{resultIdx: 0, rowIdx: 0}, {resultIdx: 0, rowIdx: 1}}
|
||
schema := &schemapb.CollectionSchema{Fields: []*schemapb.FieldSchema{
|
||
{FieldID: 100, DataType: schemapb.DataType_Float16Vector, TypeParams: []*commonpb.KeyValuePair{{Key: "dim", Value: "2"}}},
|
||
}}
|
||
|
||
_, err := buildMergedRetrieveResults([]*internalpb.RetrieveResults{r1}, selectedRows, schema)
|
||
require.Error(t, err)
|
||
assert.Contains(t, err.Error(), "truncated data")
|
||
}
|
||
|
||
func TestBuildMergedVectorField_NullableCompact_BFloat16Vector(t *testing.T) {
|
||
// BFloat16 dim=1 (2 bytes/row), nullable: row0=valid, row1=null, row2=valid
|
||
// Compact data: 4 bytes (2 valid rows)
|
||
r1 := &internalpb.RetrieveResults{
|
||
Ids: &schemapb.IDs{IdField: &schemapb.IDs_IntId{IntId: &schemapb.LongArray{Data: []int64{1, 2, 3}}}},
|
||
FieldsData: []*schemapb.FieldData{{
|
||
FieldId: 100, Type: schemapb.DataType_BFloat16Vector,
|
||
Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Dim: 1,
|
||
Data: &schemapb.VectorField_Bfloat16Vector{Bfloat16Vector: []byte{0xAA, 0xBB, 0xCC, 0xDD}},
|
||
}},
|
||
ValidData: []bool{true, false, true},
|
||
}},
|
||
}
|
||
// Select row1(null), row2(valid), row0(valid)
|
||
selectedRows := []rowRef{{resultIdx: 0, rowIdx: 1}, {resultIdx: 0, rowIdx: 2}, {resultIdx: 0, rowIdx: 0}}
|
||
|
||
merged, err := buildMergedRetrieveResults([]*internalpb.RetrieveResults{r1}, selectedRows,
|
||
makeNullableSchema(100, schemapb.DataType_BFloat16Vector, 1))
|
||
require.NoError(t, err)
|
||
|
||
// row1 → null (skipped), row2 → compact idx 1 → [0xCC,0xDD], row0 → compact idx 0 → [0xAA,0xBB]
|
||
assert.Equal(t, []byte{0xCC, 0xDD, 0xAA, 0xBB}, merged.FieldsData[0].GetVectors().GetBfloat16Vector())
|
||
assert.Equal(t, []bool{false, true, true}, merged.FieldsData[0].ValidData)
|
||
}
|
||
|
||
func TestBuildMergedVectorField_BFloat16Vector_TruncatedData(t *testing.T) {
|
||
// BFloat16 dim=2 (4 bytes/row) but only 4 bytes (1 row, not 2)
|
||
r1 := &internalpb.RetrieveResults{
|
||
Ids: &schemapb.IDs{IdField: &schemapb.IDs_IntId{IntId: &schemapb.LongArray{Data: []int64{1, 2}}}},
|
||
FieldsData: []*schemapb.FieldData{{
|
||
FieldId: 100, Type: schemapb.DataType_BFloat16Vector,
|
||
Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Dim: 2,
|
||
Data: &schemapb.VectorField_Bfloat16Vector{Bfloat16Vector: []byte{0x01, 0x02, 0x03, 0x04}},
|
||
}},
|
||
}},
|
||
}
|
||
selectedRows := []rowRef{{resultIdx: 0, rowIdx: 0}, {resultIdx: 0, rowIdx: 1}}
|
||
schema := &schemapb.CollectionSchema{Fields: []*schemapb.FieldSchema{
|
||
{FieldID: 100, DataType: schemapb.DataType_BFloat16Vector, TypeParams: []*commonpb.KeyValuePair{{Key: "dim", Value: "2"}}},
|
||
}}
|
||
|
||
_, err := buildMergedRetrieveResults([]*internalpb.RetrieveResults{r1}, selectedRows, schema)
|
||
require.Error(t, err)
|
||
assert.Contains(t, err.Error(), "truncated data")
|
||
}
|
||
|
||
func TestBuildMergedVectorField_NullableCompact_Int8Vector(t *testing.T) {
|
||
// Int8Vector dim=2 (2 bytes/row), nullable: row0=null, row1=valid
|
||
// Compact data: 2 bytes (1 valid row)
|
||
r1 := &internalpb.RetrieveResults{
|
||
Ids: &schemapb.IDs{IdField: &schemapb.IDs_IntId{IntId: &schemapb.LongArray{Data: []int64{1, 2}}}},
|
||
FieldsData: []*schemapb.FieldData{{
|
||
FieldId: 100, Type: schemapb.DataType_Int8Vector,
|
||
Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Dim: 2,
|
||
Data: &schemapb.VectorField_Int8Vector{Int8Vector: []byte{0x11, 0x22}},
|
||
}},
|
||
ValidData: []bool{false, true},
|
||
}},
|
||
}
|
||
selectedRows := []rowRef{{resultIdx: 0, rowIdx: 0}, {resultIdx: 0, rowIdx: 1}}
|
||
|
||
merged, err := buildMergedRetrieveResults([]*internalpb.RetrieveResults{r1}, selectedRows,
|
||
makeNullableSchema(100, schemapb.DataType_Int8Vector, 2))
|
||
require.NoError(t, err)
|
||
|
||
// row0 → null (skipped), row1 → compact idx 0 → [0x11,0x22]
|
||
assert.Equal(t, []byte{0x11, 0x22}, merged.FieldsData[0].GetVectors().GetInt8Vector())
|
||
assert.Equal(t, []bool{false, true}, merged.FieldsData[0].ValidData)
|
||
}
|
||
|
||
func TestBuildMergedVectorField_Int8Vector_TruncatedData(t *testing.T) {
|
||
// Int8Vector dim=3 (3 bytes/row) but only 3 bytes (1 row, not 2)
|
||
r1 := &internalpb.RetrieveResults{
|
||
Ids: &schemapb.IDs{IdField: &schemapb.IDs_IntId{IntId: &schemapb.LongArray{Data: []int64{1, 2}}}},
|
||
FieldsData: []*schemapb.FieldData{{
|
||
FieldId: 100, Type: schemapb.DataType_Int8Vector,
|
||
Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Dim: 3,
|
||
Data: &schemapb.VectorField_Int8Vector{Int8Vector: []byte{0x01, 0x02, 0x03}},
|
||
}},
|
||
}},
|
||
}
|
||
selectedRows := []rowRef{{resultIdx: 0, rowIdx: 0}, {resultIdx: 0, rowIdx: 1}}
|
||
schema := &schemapb.CollectionSchema{Fields: []*schemapb.FieldSchema{
|
||
{FieldID: 100, DataType: schemapb.DataType_Int8Vector, TypeParams: []*commonpb.KeyValuePair{{Key: "dim", Value: "3"}}},
|
||
}}
|
||
|
||
_, err := buildMergedRetrieveResults([]*internalpb.RetrieveResults{r1}, selectedRows, schema)
|
||
require.Error(t, err)
|
||
assert.Contains(t, err.Error(), "truncated data")
|
||
}
|
||
|
||
func TestBuildMergedVectorField_VectorArray_TruncatedData(t *testing.T) {
|
||
dim := int64(2)
|
||
v1 := &schemapb.VectorField{Dim: dim, Data: &schemapb.VectorField_FloatVector{
|
||
FloatVector: &schemapb.FloatArray{Data: []float32{1.0, 2.0}},
|
||
}}
|
||
|
||
// Result has 2 IDs but VectorArray has only 1 entry
|
||
r1 := &internalpb.RetrieveResults{
|
||
Ids: &schemapb.IDs{IdField: &schemapb.IDs_IntId{IntId: &schemapb.LongArray{Data: []int64{1, 2}}}},
|
||
FieldsData: []*schemapb.FieldData{{
|
||
FieldId: 100, Type: schemapb.DataType_FloatVector,
|
||
Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Dim: dim,
|
||
Data: &schemapb.VectorField_VectorArray{VectorArray: &schemapb.VectorArray{
|
||
Dim: dim,
|
||
Data: []*schemapb.VectorField{v1}, // only 1 entry
|
||
ElementType: schemapb.DataType_FloatVector,
|
||
}},
|
||
}},
|
||
}},
|
||
}
|
||
selectedRows := []rowRef{{resultIdx: 0, rowIdx: 0}, {resultIdx: 0, rowIdx: 1}}
|
||
|
||
// nil schema → fallback infers VectorArray from data
|
||
_, err := buildMergedRetrieveResults([]*internalpb.RetrieveResults{r1}, selectedRows, nil)
|
||
require.Error(t, err)
|
||
assert.Contains(t, err.Error(), "truncated data")
|
||
}
|
||
|
||
// =========================================================================
|
||
// rangeSlice / slice: string IDs
|
||
// =========================================================================
|
||
|
||
func TestRangeSliceIDs_StringID(t *testing.T) {
|
||
ids := &schemapb.IDs{IdField: &schemapb.IDs_StrId{StrId: &schemapb.StringArray{Data: []string{"a", "b", "c", "d"}}}}
|
||
sliced := rangeSliceIDs(ids, 1, 3)
|
||
assert.Equal(t, []string{"b", "c"}, sliced.GetStrId().GetData())
|
||
}
|
||
|
||
func TestSliceIDs_StringID(t *testing.T) {
|
||
ids := &schemapb.IDs{IdField: &schemapb.IDs_StrId{StrId: &schemapb.StringArray{Data: []string{"x", "y", "z"}}}}
|
||
sliced := sliceIDs(ids, []int{2, 0})
|
||
assert.Equal(t, []string{"z", "x"}, sliced.GetStrId().GetData())
|
||
}
|
||
|
||
// =========================================================================
|
||
// rangeSliceVectorField: additional vector types
|
||
// =========================================================================
|
||
|
||
func TestRangeSliceVectorField_FloatVector(t *testing.T) {
|
||
vf := &schemapb.VectorField{
|
||
Dim: 2,
|
||
Data: &schemapb.VectorField_FloatVector{FloatVector: &schemapb.FloatArray{Data: []float32{1, 2, 3, 4, 5, 6}}},
|
||
}
|
||
sliced := requireRangeSliceVectorField(t, vf, 1, 3, nil)
|
||
assert.Equal(t, []float32{3, 4, 5, 6}, sliced.GetFloatVector().GetData())
|
||
}
|
||
|
||
func TestRangeSliceVectorField_BinaryVector(t *testing.T) {
|
||
vf := &schemapb.VectorField{
|
||
Dim: 16, // 2 bytes per row
|
||
Data: &schemapb.VectorField_BinaryVector{BinaryVector: []byte{0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}},
|
||
}
|
||
sliced := requireRangeSliceVectorField(t, vf, 0, 2, nil)
|
||
assert.Equal(t, []byte{0xAA, 0xBB, 0xCC, 0xDD}, sliced.GetBinaryVector())
|
||
}
|
||
|
||
func TestRangeSliceVectorField_Float16Vector(t *testing.T) {
|
||
vf := &schemapb.VectorField{
|
||
Dim: 2, // 4 bytes per row
|
||
Data: &schemapb.VectorField_Float16Vector{Float16Vector: []byte{1, 2, 3, 4, 5, 6, 7, 8}},
|
||
}
|
||
sliced := requireRangeSliceVectorField(t, vf, 1, 2, nil)
|
||
assert.Equal(t, []byte{5, 6, 7, 8}, sliced.GetFloat16Vector())
|
||
}
|
||
|
||
func TestRangeSliceVectorField_SparseFloatVector(t *testing.T) {
|
||
vf := &schemapb.VectorField{
|
||
Dim: 100,
|
||
Data: &schemapb.VectorField_SparseFloatVector{SparseFloatVector: &schemapb.SparseFloatArray{
|
||
Contents: [][]byte{{0x01}, {0x02}, {0x03}},
|
||
Dim: 100,
|
||
}},
|
||
}
|
||
sliced := requireRangeSliceVectorField(t, vf, 1, 3, nil)
|
||
assert.Equal(t, [][]byte{{0x02}, {0x03}}, sliced.GetSparseFloatVector().GetContents())
|
||
}
|
||
|
||
// =========================================================================
|
||
// sliceVectorField: additional vector types
|
||
// =========================================================================
|
||
|
||
func TestSliceVectorField_FloatVector(t *testing.T) {
|
||
vf := &schemapb.VectorField{
|
||
Dim: 2,
|
||
Data: &schemapb.VectorField_FloatVector{FloatVector: &schemapb.FloatArray{Data: []float32{1, 2, 3, 4, 5, 6}}},
|
||
}
|
||
sliced := requireSliceVectorField(t, vf, []int{2, 0}, nil)
|
||
assert.Equal(t, []float32{5, 6, 1, 2}, sliced.GetFloatVector().GetData())
|
||
}
|
||
|
||
func TestSliceVectorField_BinaryVector(t *testing.T) {
|
||
vf := &schemapb.VectorField{
|
||
Dim: 16, // 2 bytes per row
|
||
Data: &schemapb.VectorField_BinaryVector{BinaryVector: []byte{0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}},
|
||
}
|
||
sliced := requireSliceVectorField(t, vf, []int{2, 0}, nil)
|
||
assert.Equal(t, []byte{0xEE, 0xFF, 0xAA, 0xBB}, sliced.GetBinaryVector())
|
||
}
|
||
|
||
func TestSliceVectorField_Float16Vector(t *testing.T) {
|
||
vf := &schemapb.VectorField{
|
||
Dim: 2, // 4 bytes per row
|
||
Data: &schemapb.VectorField_Float16Vector{Float16Vector: []byte{1, 2, 3, 4, 5, 6, 7, 8}},
|
||
}
|
||
sliced := requireSliceVectorField(t, vf, []int{1}, nil)
|
||
assert.Equal(t, []byte{5, 6, 7, 8}, sliced.GetFloat16Vector())
|
||
}
|
||
|
||
func TestSliceVectorField_BFloat16Vector(t *testing.T) {
|
||
vf := &schemapb.VectorField{
|
||
Dim: 2, // 4 bytes per row
|
||
Data: &schemapb.VectorField_Bfloat16Vector{Bfloat16Vector: []byte{10, 20, 30, 40, 50, 60, 70, 80}},
|
||
}
|
||
sliced := requireSliceVectorField(t, vf, []int{1, 0}, nil)
|
||
assert.Equal(t, []byte{50, 60, 70, 80, 10, 20, 30, 40}, sliced.GetBfloat16Vector())
|
||
}
|
||
|
||
func TestSliceVectorField_Int8Vector(t *testing.T) {
|
||
vf := &schemapb.VectorField{
|
||
Dim: 3,
|
||
Data: &schemapb.VectorField_Int8Vector{Int8Vector: []byte{1, 2, 3, 4, 5, 6}},
|
||
}
|
||
sliced := requireSliceVectorField(t, vf, []int{1}, nil)
|
||
assert.Equal(t, []byte{4, 5, 6}, sliced.GetInt8Vector())
|
||
}
|
||
|
||
func TestSliceVectorField_SparseFloatVector(t *testing.T) {
|
||
vf := &schemapb.VectorField{
|
||
Dim: 100,
|
||
Data: &schemapb.VectorField_SparseFloatVector{SparseFloatVector: &schemapb.SparseFloatArray{
|
||
Contents: [][]byte{{0x01}, {0x02}, {0x03}},
|
||
Dim: 100,
|
||
}},
|
||
}
|
||
sliced := requireSliceVectorField(t, vf, []int{2, 0}, nil)
|
||
assert.Equal(t, [][]byte{{0x03}, {0x01}}, sliced.GetSparseFloatVector().GetContents())
|
||
}
|
||
|
||
// =========================================================================
|
||
// rangeSliceFieldData: vector branch
|
||
// =========================================================================
|
||
|
||
func TestRangeSliceFieldData_Vector(t *testing.T) {
|
||
fd := &schemapb.FieldData{
|
||
Type: schemapb.DataType_FloatVector, FieldId: 100, FieldName: "vec",
|
||
Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Dim: 2,
|
||
Data: &schemapb.VectorField_FloatVector{FloatVector: &schemapb.FloatArray{Data: []float32{1, 2, 3, 4, 5, 6}}},
|
||
}},
|
||
}
|
||
sliced := requireRangeSliceFieldData(t, fd, 1, 3)
|
||
assert.Equal(t, []float32{3, 4, 5, 6}, sliced.GetVectors().GetFloatVector().GetData())
|
||
}
|
||
|
||
func TestRangeSliceFieldData_Nil(t *testing.T) {
|
||
sliced := requireRangeSliceFieldData(t, nil, 0, 1)
|
||
assert.Nil(t, sliced)
|
||
}
|
||
|
||
// =========================================================================
|
||
// calcFieldElementSize: various types
|
||
// =========================================================================
|
||
|
||
func TestCalcFieldElementSize(t *testing.T) {
|
||
tests := []struct {
|
||
name string
|
||
fd *schemapb.FieldData
|
||
rowIdx int
|
||
expected int64
|
||
}{
|
||
{
|
||
name: "bool",
|
||
fd: &schemapb.FieldData{Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_BoolData{BoolData: &schemapb.BoolArray{Data: []bool{true}}},
|
||
}}},
|
||
expected: 1,
|
||
},
|
||
{
|
||
name: "int32",
|
||
fd: &schemapb.FieldData{Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_IntData{IntData: &schemapb.IntArray{Data: []int32{42}}},
|
||
}}},
|
||
expected: 4,
|
||
},
|
||
{
|
||
name: "int64",
|
||
fd: &schemapb.FieldData{Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_LongData{LongData: &schemapb.LongArray{Data: []int64{42}}},
|
||
}}},
|
||
expected: 8,
|
||
},
|
||
{
|
||
name: "float32",
|
||
fd: &schemapb.FieldData{Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_FloatData{FloatData: &schemapb.FloatArray{Data: []float32{1.0}}},
|
||
}}},
|
||
expected: 4,
|
||
},
|
||
{
|
||
name: "float64",
|
||
fd: &schemapb.FieldData{Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_DoubleData{DoubleData: &schemapb.DoubleArray{Data: []float64{1.0}}},
|
||
}}},
|
||
expected: 8,
|
||
},
|
||
{
|
||
name: "string",
|
||
fd: &schemapb.FieldData{Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_StringData{StringData: &schemapb.StringArray{Data: []string{"hello"}}},
|
||
}}},
|
||
expected: 5,
|
||
},
|
||
{
|
||
name: "json",
|
||
fd: &schemapb.FieldData{Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_JsonData{JsonData: &schemapb.JSONArray{Data: [][]byte{[]byte(`{"a":1}`)}}},
|
||
}}},
|
||
expected: 7,
|
||
},
|
||
{
|
||
name: "bytes",
|
||
fd: &schemapb.FieldData{Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_BytesData{BytesData: &schemapb.BytesArray{Data: [][]byte{{0x01, 0x02, 0x03}}}},
|
||
}}},
|
||
expected: 3,
|
||
},
|
||
{
|
||
name: "timestamptz",
|
||
fd: &schemapb.FieldData{Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_TimestamptzData{TimestamptzData: &schemapb.TimestamptzArray{Data: []int64{1000}}},
|
||
}}},
|
||
expected: 8,
|
||
},
|
||
{
|
||
name: "float_vector",
|
||
fd: &schemapb.FieldData{Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Dim: 4,
|
||
Data: &schemapb.VectorField_FloatVector{FloatVector: &schemapb.FloatArray{Data: []float32{1, 2, 3, 4}}},
|
||
}}},
|
||
expected: 16, // 4 * 4
|
||
},
|
||
{
|
||
name: "binary_vector",
|
||
fd: &schemapb.FieldData{Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Dim: 16,
|
||
Data: &schemapb.VectorField_BinaryVector{BinaryVector: []byte{0, 0}},
|
||
}}},
|
||
expected: 2, // 16/8
|
||
},
|
||
{
|
||
name: "float16_vector",
|
||
fd: &schemapb.FieldData{Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Dim: 4,
|
||
Data: &schemapb.VectorField_Float16Vector{Float16Vector: []byte{0, 0, 0, 0, 0, 0, 0, 0}},
|
||
}}},
|
||
expected: 8, // 4*2
|
||
},
|
||
{
|
||
name: "int8_vector",
|
||
fd: &schemapb.FieldData{Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Dim: 3,
|
||
Data: &schemapb.VectorField_Int8Vector{Int8Vector: []byte{1, 2, 3}},
|
||
}}},
|
||
expected: 3,
|
||
},
|
||
{
|
||
name: "sparse_float_vector",
|
||
fd: &schemapb.FieldData{Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Dim: 100,
|
||
Data: &schemapb.VectorField_SparseFloatVector{SparseFloatVector: &schemapb.SparseFloatArray{
|
||
Contents: [][]byte{{0x01, 0x02, 0x03, 0x04}},
|
||
Dim: 100,
|
||
}},
|
||
}}},
|
||
expected: 4,
|
||
},
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run(tt.name, func(t *testing.T) {
|
||
assert.Equal(t, tt.expected, calcFieldElementSize(tt.fd, tt.rowIdx))
|
||
})
|
||
}
|
||
}
|
||
|
||
// =========================================================================
|
||
// getFieldValue: various types
|
||
// =========================================================================
|
||
|
||
func TestGetFieldValue(t *testing.T) {
|
||
tests := []struct {
|
||
name string
|
||
fd *schemapb.FieldData
|
||
rowIdx int
|
||
expected any
|
||
isNull bool
|
||
}{
|
||
{
|
||
name: "bool",
|
||
fd: &schemapb.FieldData{Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_BoolData{BoolData: &schemapb.BoolArray{Data: []bool{true, false}}},
|
||
}}},
|
||
rowIdx: 1, expected: false,
|
||
},
|
||
{
|
||
name: "int32",
|
||
fd: &schemapb.FieldData{Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_IntData{IntData: &schemapb.IntArray{Data: []int32{10, 20}}},
|
||
}}},
|
||
rowIdx: 0, expected: int32(10),
|
||
},
|
||
{
|
||
name: "float32",
|
||
fd: &schemapb.FieldData{Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_FloatData{FloatData: &schemapb.FloatArray{Data: []float32{1.5}}},
|
||
}}},
|
||
rowIdx: 0, expected: float32(1.5),
|
||
},
|
||
{
|
||
name: "float64",
|
||
fd: &schemapb.FieldData{Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_DoubleData{DoubleData: &schemapb.DoubleArray{Data: []float64{3.14}}},
|
||
}}},
|
||
rowIdx: 0, expected: float64(3.14),
|
||
},
|
||
{
|
||
name: "nullable_null",
|
||
fd: &schemapb.FieldData{
|
||
Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_LongData{LongData: &schemapb.LongArray{Data: []int64{99}}},
|
||
}},
|
||
ValidData: []bool{false},
|
||
},
|
||
rowIdx: 0, expected: nil, isNull: true,
|
||
},
|
||
{
|
||
name: "nil_scalars",
|
||
fd: &schemapb.FieldData{},
|
||
rowIdx: 0, expected: nil, isNull: true,
|
||
},
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run(tt.name, func(t *testing.T) {
|
||
val, isNull := getFieldValue(tt.fd, tt.rowIdx)
|
||
assert.Equal(t, tt.isNull, isNull)
|
||
if !tt.isNull {
|
||
assert.Equal(t, tt.expected, val)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// =========================================================================
|
||
// getRowCount: various types
|
||
// =========================================================================
|
||
|
||
func TestGetRowCount(t *testing.T) {
|
||
tests := []struct {
|
||
name string
|
||
result *internalpb.RetrieveResults
|
||
expected int
|
||
}{
|
||
{
|
||
name: "from IDs",
|
||
result: makeInternalResultIntPK([]int64{1, 2, 3}),
|
||
expected: 3,
|
||
},
|
||
{
|
||
name: "from bool field",
|
||
result: &internalpb.RetrieveResults{FieldsData: []*schemapb.FieldData{
|
||
{Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_BoolData{BoolData: &schemapb.BoolArray{Data: []bool{true, false}}},
|
||
}}},
|
||
}},
|
||
expected: 2,
|
||
},
|
||
{
|
||
name: "from int32 field",
|
||
result: &internalpb.RetrieveResults{FieldsData: []*schemapb.FieldData{
|
||
{Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_IntData{IntData: &schemapb.IntArray{Data: []int32{1, 2, 3}}},
|
||
}}},
|
||
}},
|
||
expected: 3,
|
||
},
|
||
{
|
||
name: "from float field",
|
||
result: &internalpb.RetrieveResults{FieldsData: []*schemapb.FieldData{
|
||
{Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_FloatData{FloatData: &schemapb.FloatArray{Data: []float32{1.0}}},
|
||
}}},
|
||
}},
|
||
expected: 1,
|
||
},
|
||
{
|
||
name: "from double field",
|
||
result: &internalpb.RetrieveResults{FieldsData: []*schemapb.FieldData{
|
||
{Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_DoubleData{DoubleData: &schemapb.DoubleArray{Data: []float64{1.0, 2.0}}},
|
||
}}},
|
||
}},
|
||
expected: 2,
|
||
},
|
||
{
|
||
name: "from json field",
|
||
result: &internalpb.RetrieveResults{FieldsData: []*schemapb.FieldData{
|
||
{Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_JsonData{JsonData: &schemapb.JSONArray{Data: [][]byte{[]byte(`{}`)}}},
|
||
}}},
|
||
}},
|
||
expected: 1,
|
||
},
|
||
{
|
||
name: "from float vector",
|
||
result: &internalpb.RetrieveResults{FieldsData: []*schemapb.FieldData{
|
||
{Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Dim: 2,
|
||
Data: &schemapb.VectorField_FloatVector{FloatVector: &schemapb.FloatArray{Data: []float32{1, 2, 3, 4, 5, 6}}},
|
||
}}},
|
||
}},
|
||
expected: 3,
|
||
},
|
||
{
|
||
name: "from binary vector",
|
||
result: &internalpb.RetrieveResults{FieldsData: []*schemapb.FieldData{
|
||
{Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Dim: 16,
|
||
Data: &schemapb.VectorField_BinaryVector{BinaryVector: []byte{0, 0, 0, 0}},
|
||
}}},
|
||
}},
|
||
expected: 2,
|
||
},
|
||
{
|
||
name: "from sparse float vector",
|
||
result: &internalpb.RetrieveResults{FieldsData: []*schemapb.FieldData{
|
||
{Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Dim: 100,
|
||
Data: &schemapb.VectorField_SparseFloatVector{SparseFloatVector: &schemapb.SparseFloatArray{
|
||
Contents: [][]byte{{0x01}, {0x02}},
|
||
}},
|
||
}}},
|
||
}},
|
||
expected: 2,
|
||
},
|
||
{
|
||
name: "from struct array",
|
||
result: &internalpb.RetrieveResults{FieldsData: []*schemapb.FieldData{
|
||
makeStructArrayFieldData(100, "info", []*schemapb.FieldData{
|
||
makeInt64Field(101, "age", []int64{10, 20, 30}),
|
||
}),
|
||
}},
|
||
expected: 3,
|
||
},
|
||
{
|
||
name: "empty",
|
||
result: &internalpb.RetrieveResults{},
|
||
expected: 0,
|
||
},
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run(tt.name, func(t *testing.T) {
|
||
assert.Equal(t, tt.expected, getRowCount(tt.result))
|
||
})
|
||
}
|
||
}
|
||
|
||
// =========================================================================
|
||
// compareValues: various types
|
||
// =========================================================================
|
||
|
||
func TestCompareValues(t *testing.T) {
|
||
tests := []struct {
|
||
name string
|
||
a, b any
|
||
dataType schemapb.DataType
|
||
expected int
|
||
}{
|
||
{"bool_lt", false, true, schemapb.DataType_Bool, -1},
|
||
{"bool_gt", true, false, schemapb.DataType_Bool, 1},
|
||
{"bool_eq", true, true, schemapb.DataType_Bool, 0},
|
||
{"int32_lt", int32(1), int32(2), schemapb.DataType_Int32, -1},
|
||
{"int32_gt", int32(5), int32(3), schemapb.DataType_Int32, 1},
|
||
{"int32_eq", int32(7), int32(7), schemapb.DataType_Int32, 0},
|
||
{"int64_lt", int64(10), int64(20), schemapb.DataType_Int64, -1},
|
||
{"int64_eq", int64(5), int64(5), schemapb.DataType_Int64, 0},
|
||
{"float32_lt", float32(1.0), float32(2.0), schemapb.DataType_Float, -1},
|
||
{"float32_gt", float32(3.0), float32(1.0), schemapb.DataType_Float, 1},
|
||
{"float32_eq", float32(1.5), float32(1.5), schemapb.DataType_Float, 0},
|
||
{"float64_lt", float64(1.0), float64(2.0), schemapb.DataType_Double, -1},
|
||
{"float64_gt", float64(9.0), float64(1.0), schemapb.DataType_Double, 1},
|
||
{"float64_eq", float64(3.14), float64(3.14), schemapb.DataType_Double, 0},
|
||
{"string_lt", "apple", "banana", schemapb.DataType_VarChar, -1},
|
||
{"string_gt", "zebra", "alpha", schemapb.DataType_VarChar, 1},
|
||
{"string_eq", "same", "same", schemapb.DataType_VarChar, 0},
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run(tt.name, func(t *testing.T) {
|
||
assert.Equal(t, tt.expected, compareValues(tt.a, tt.b, tt.dataType))
|
||
})
|
||
}
|
||
}
|
||
|
||
// =========================================================================
|
||
// rangeSliceScalarField / sliceScalarField: more type branches
|
||
// =========================================================================
|
||
|
||
func TestRangeSliceScalarField_AllTypes(t *testing.T) {
|
||
tests := []struct {
|
||
name string
|
||
sf *schemapb.ScalarField
|
||
check func(t *testing.T, sf *schemapb.ScalarField)
|
||
}{
|
||
{
|
||
name: "bool",
|
||
sf: &schemapb.ScalarField{Data: &schemapb.ScalarField_BoolData{BoolData: &schemapb.BoolArray{Data: []bool{true, false, true}}}},
|
||
check: func(t *testing.T, sf *schemapb.ScalarField) {
|
||
assert.Equal(t, []bool{false}, sf.GetBoolData().GetData())
|
||
},
|
||
},
|
||
{
|
||
name: "int32",
|
||
sf: &schemapb.ScalarField{Data: &schemapb.ScalarField_IntData{IntData: &schemapb.IntArray{Data: []int32{10, 20, 30}}}},
|
||
check: func(t *testing.T, sf *schemapb.ScalarField) {
|
||
assert.Equal(t, []int32{20}, sf.GetIntData().GetData())
|
||
},
|
||
},
|
||
{
|
||
name: "float",
|
||
sf: &schemapb.ScalarField{Data: &schemapb.ScalarField_FloatData{FloatData: &schemapb.FloatArray{Data: []float32{1.0, 2.0, 3.0}}}},
|
||
check: func(t *testing.T, sf *schemapb.ScalarField) {
|
||
assert.Equal(t, []float32{2.0}, sf.GetFloatData().GetData())
|
||
},
|
||
},
|
||
{
|
||
name: "double",
|
||
sf: &schemapb.ScalarField{Data: &schemapb.ScalarField_DoubleData{DoubleData: &schemapb.DoubleArray{Data: []float64{1.0, 2.0, 3.0}}}},
|
||
check: func(t *testing.T, sf *schemapb.ScalarField) {
|
||
assert.Equal(t, []float64{2.0}, sf.GetDoubleData().GetData())
|
||
},
|
||
},
|
||
{
|
||
name: "bytes",
|
||
sf: &schemapb.ScalarField{Data: &schemapb.ScalarField_BytesData{BytesData: &schemapb.BytesArray{Data: [][]byte{{1}, {2}, {3}}}}},
|
||
check: func(t *testing.T, sf *schemapb.ScalarField) {
|
||
assert.Equal(t, [][]byte{{2}}, sf.GetBytesData().GetData())
|
||
},
|
||
},
|
||
{
|
||
name: "json",
|
||
sf: &schemapb.ScalarField{Data: &schemapb.ScalarField_JsonData{JsonData: &schemapb.JSONArray{Data: [][]byte{[]byte(`{}`), []byte(`[]`), []byte(`""`)}}}},
|
||
check: func(t *testing.T, sf *schemapb.ScalarField) {
|
||
assert.Equal(t, [][]byte{[]byte(`[]`)}, sf.GetJsonData().GetData())
|
||
},
|
||
},
|
||
{
|
||
name: "timestamptz",
|
||
sf: &schemapb.ScalarField{Data: &schemapb.ScalarField_TimestamptzData{TimestamptzData: &schemapb.TimestamptzArray{Data: []int64{100, 200, 300}}}},
|
||
check: func(t *testing.T, sf *schemapb.ScalarField) {
|
||
assert.Equal(t, []int64{200}, sf.GetTimestamptzData().GetData())
|
||
},
|
||
},
|
||
{
|
||
name: "mol",
|
||
sf: &schemapb.ScalarField{Data: &schemapb.ScalarField_MolData{MolData: &schemapb.MolArray{Data: [][]byte{{0xAA}, {0xBB}, {0xCC}}}}},
|
||
check: func(t *testing.T, sf *schemapb.ScalarField) {
|
||
assert.Equal(t, [][]byte{{0xBB}}, sf.GetMolData().GetData())
|
||
},
|
||
},
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run("rangeSlice_"+tt.name, func(t *testing.T) {
|
||
sliced := rangeSliceScalarField(tt.sf, 1, 2)
|
||
tt.check(t, sliced)
|
||
})
|
||
t.Run("slice_"+tt.name, func(t *testing.T) {
|
||
sliced := sliceScalarField(tt.sf, []int{1})
|
||
tt.check(t, sliced)
|
||
})
|
||
}
|
||
}
|
||
|
||
// =========================================================================
|
||
// rangeSliceVectorField: more vector types
|
||
// =========================================================================
|
||
|
||
func TestRangeSliceVectorField_BFloat16(t *testing.T) {
|
||
vf := &schemapb.VectorField{
|
||
Dim: 2,
|
||
Data: &schemapb.VectorField_Bfloat16Vector{Bfloat16Vector: []byte{10, 20, 30, 40, 50, 60, 70, 80}},
|
||
}
|
||
sliced := requireRangeSliceVectorField(t, vf, 1, 2, nil)
|
||
assert.Equal(t, []byte{50, 60, 70, 80}, sliced.GetBfloat16Vector())
|
||
}
|
||
|
||
func TestRangeSliceVectorField_Int8(t *testing.T) {
|
||
vf := &schemapb.VectorField{
|
||
Dim: 3,
|
||
Data: &schemapb.VectorField_Int8Vector{Int8Vector: []byte{1, 2, 3, 4, 5, 6}},
|
||
}
|
||
sliced := requireRangeSliceVectorField(t, vf, 0, 1, nil)
|
||
assert.Equal(t, []byte{1, 2, 3}, sliced.GetInt8Vector())
|
||
}
|
||
|
||
// =========================================================================
|
||
// getRowCount: additional vector + scalar types
|
||
// =========================================================================
|
||
|
||
func TestGetRowCount_AdditionalTypes(t *testing.T) {
|
||
tests := []struct {
|
||
name string
|
||
result *internalpb.RetrieveResults
|
||
expected int
|
||
}{
|
||
{
|
||
name: "string field",
|
||
result: &internalpb.RetrieveResults{FieldsData: []*schemapb.FieldData{
|
||
{Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_StringData{StringData: &schemapb.StringArray{Data: []string{"a", "b"}}},
|
||
}}},
|
||
}},
|
||
expected: 2,
|
||
},
|
||
{
|
||
name: "array field",
|
||
result: &internalpb.RetrieveResults{FieldsData: []*schemapb.FieldData{
|
||
{Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_ArrayData{ArrayData: &schemapb.ArrayArray{Data: []*schemapb.ScalarField{nil, nil, nil}}},
|
||
}}},
|
||
}},
|
||
expected: 3,
|
||
},
|
||
{
|
||
name: "geometry wkt field",
|
||
result: &internalpb.RetrieveResults{FieldsData: []*schemapb.FieldData{
|
||
{Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_GeometryWktData{GeometryWktData: &schemapb.GeometryWktArray{Data: []string{"POINT(0 0)"}}},
|
||
}}},
|
||
}},
|
||
expected: 1,
|
||
},
|
||
{
|
||
name: "timestamptz field",
|
||
result: &internalpb.RetrieveResults{FieldsData: []*schemapb.FieldData{
|
||
{Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_TimestamptzData{TimestamptzData: &schemapb.TimestamptzArray{Data: []int64{1, 2}}},
|
||
}}},
|
||
}},
|
||
expected: 2,
|
||
},
|
||
{
|
||
name: "mol field",
|
||
result: &internalpb.RetrieveResults{FieldsData: []*schemapb.FieldData{
|
||
{Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_MolData{MolData: &schemapb.MolArray{Data: [][]byte{{1}}}},
|
||
}}},
|
||
}},
|
||
expected: 1,
|
||
},
|
||
{
|
||
name: "float16 vector",
|
||
result: &internalpb.RetrieveResults{FieldsData: []*schemapb.FieldData{
|
||
{Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Dim: 2,
|
||
Data: &schemapb.VectorField_Float16Vector{Float16Vector: []byte{1, 2, 3, 4, 5, 6, 7, 8}},
|
||
}}},
|
||
}},
|
||
expected: 2,
|
||
},
|
||
{
|
||
name: "bfloat16 vector",
|
||
result: &internalpb.RetrieveResults{FieldsData: []*schemapb.FieldData{
|
||
{Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Dim: 2,
|
||
Data: &schemapb.VectorField_Bfloat16Vector{Bfloat16Vector: []byte{1, 2, 3, 4}},
|
||
}}},
|
||
}},
|
||
expected: 1,
|
||
},
|
||
{
|
||
name: "int8 vector",
|
||
result: &internalpb.RetrieveResults{FieldsData: []*schemapb.FieldData{
|
||
{Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Dim: 3,
|
||
Data: &schemapb.VectorField_Int8Vector{Int8Vector: []byte{1, 2, 3, 4, 5, 6}},
|
||
}}},
|
||
}},
|
||
expected: 2,
|
||
},
|
||
{
|
||
name: "vector array",
|
||
result: &internalpb.RetrieveResults{FieldsData: []*schemapb.FieldData{
|
||
{Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Dim: 2,
|
||
Data: &schemapb.VectorField_VectorArray{VectorArray: &schemapb.VectorArray{
|
||
Data: []*schemapb.VectorField{nil, nil},
|
||
}},
|
||
}}},
|
||
}},
|
||
expected: 2,
|
||
},
|
||
{
|
||
name: "geometry field",
|
||
result: &internalpb.RetrieveResults{FieldsData: []*schemapb.FieldData{
|
||
{Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_GeometryData{GeometryData: &schemapb.GeometryArray{Data: [][]byte{{1}, {2}}}},
|
||
}}},
|
||
}},
|
||
expected: 2,
|
||
},
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run(tt.name, func(t *testing.T) {
|
||
assert.Equal(t, tt.expected, getRowCount(tt.result))
|
||
})
|
||
}
|
||
}
|
||
|
||
// =========================================================================
|
||
// calcFieldElementSize: remaining branches (geometry, array, mol, vectorArray)
|
||
// =========================================================================
|
||
|
||
func TestCalcFieldElementSize_RemainingBranches(t *testing.T) {
|
||
tests := []struct {
|
||
name string
|
||
fd *schemapb.FieldData
|
||
rowIdx int
|
||
expected int64
|
||
}{
|
||
{
|
||
name: "geometry_data",
|
||
fd: &schemapb.FieldData{Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_GeometryData{GeometryData: &schemapb.GeometryArray{Data: [][]byte{{1, 2, 3}}}},
|
||
}}},
|
||
expected: 3,
|
||
},
|
||
{
|
||
name: "geometry_wkt_data",
|
||
fd: &schemapb.FieldData{Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_GeometryWktData{GeometryWktData: &schemapb.GeometryWktArray{Data: []string{"POINT(0 0)"}}},
|
||
}}},
|
||
expected: 10,
|
||
},
|
||
{
|
||
name: "mol_data",
|
||
fd: &schemapb.FieldData{Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_MolData{MolData: &schemapb.MolArray{Data: [][]byte{{0xAA, 0xBB}}}},
|
||
}}},
|
||
expected: 2,
|
||
},
|
||
{
|
||
name: "array_data",
|
||
fd: &schemapb.FieldData{Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_ArrayData{ArrayData: &schemapb.ArrayArray{
|
||
Data: []*schemapb.ScalarField{
|
||
{Data: &schemapb.ScalarField_IntData{IntData: &schemapb.IntArray{Data: []int32{1, 2}}}},
|
||
},
|
||
ElementType: schemapb.DataType_Int32,
|
||
}},
|
||
}}},
|
||
expected: 10, // proto.Size of the ScalarField
|
||
},
|
||
{
|
||
name: "bfloat16_vector",
|
||
fd: &schemapb.FieldData{Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Dim: 4,
|
||
Data: &schemapb.VectorField_Bfloat16Vector{Bfloat16Vector: []byte{0, 0, 0, 0, 0, 0, 0, 0}},
|
||
}}},
|
||
expected: 8, // 4*2
|
||
},
|
||
{
|
||
name: "vector_array",
|
||
fd: &schemapb.FieldData{Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Dim: 2,
|
||
Data: &schemapb.VectorField_VectorArray{VectorArray: &schemapb.VectorArray{
|
||
Data: []*schemapb.VectorField{
|
||
{Dim: 2, Data: &schemapb.VectorField_FloatVector{FloatVector: &schemapb.FloatArray{Data: []float32{1, 2}}}},
|
||
},
|
||
}},
|
||
}}},
|
||
},
|
||
{
|
||
name: "nil_field",
|
||
fd: &schemapb.FieldData{},
|
||
expected: 0,
|
||
},
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run(tt.name, func(t *testing.T) {
|
||
result := calcFieldElementSize(tt.fd, tt.rowIdx)
|
||
switch tt.name {
|
||
case "vector_array":
|
||
assert.True(t, result > 0, "vector_array size should be > 0")
|
||
case "array_data":
|
||
assert.True(t, result > 0, "array_data size should be > 0")
|
||
default:
|
||
assert.Equal(t, tt.expected, result)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// =========================================================================
|
||
// sliceFieldData / rangeSliceFieldData: vector + structArray branches
|
||
// =========================================================================
|
||
|
||
func TestSliceFieldData_Vector(t *testing.T) {
|
||
fd := &schemapb.FieldData{
|
||
Type: schemapb.DataType_FloatVector, FieldId: 100,
|
||
Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Dim: 2,
|
||
Data: &schemapb.VectorField_FloatVector{FloatVector: &schemapb.FloatArray{Data: []float32{1, 2, 3, 4, 5, 6}}},
|
||
}},
|
||
}
|
||
sliced := requireSliceFieldData(t, fd, []int{2, 0})
|
||
assert.Equal(t, []float32{5, 6, 1, 2}, sliced.GetVectors().GetFloatVector().GetData())
|
||
}
|
||
|
||
func TestSliceFieldData_NilOrEmpty(t *testing.T) {
|
||
sliced := requireSliceFieldData(t, nil, []int{0})
|
||
assert.Nil(t, sliced)
|
||
sliced = requireSliceFieldData(t, &schemapb.FieldData{}, nil)
|
||
assert.Nil(t, sliced)
|
||
}
|
||
|
||
func TestSliceFieldData_ValidData(t *testing.T) {
|
||
fd := &schemapb.FieldData{
|
||
Type: schemapb.DataType_Int64, FieldId: 100,
|
||
Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_LongData{LongData: &schemapb.LongArray{Data: []int64{10, 20, 30}}},
|
||
}},
|
||
ValidData: []bool{true, false, true},
|
||
}
|
||
sliced := requireSliceFieldData(t, fd, []int{2, 0})
|
||
assert.Equal(t, []bool{true, true}, sliced.GetValidData())
|
||
}
|
||
|
||
func TestRangeSliceFieldData_ValidData(t *testing.T) {
|
||
fd := &schemapb.FieldData{
|
||
Type: schemapb.DataType_Int64, FieldId: 100,
|
||
Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_LongData{LongData: &schemapb.LongArray{Data: []int64{10, 20, 30}}},
|
||
}},
|
||
ValidData: []bool{true, false, true},
|
||
}
|
||
sliced := requireRangeSliceFieldData(t, fd, 0, 2)
|
||
assert.Equal(t, []bool{true, false}, sliced.GetValidData())
|
||
}
|
||
|
||
// =========================================================================
|
||
// sliceScalarField: remaining branches (geometry, geometryWkt)
|
||
// =========================================================================
|
||
|
||
func TestSliceScalarField_GeometryData(t *testing.T) {
|
||
sf := &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_GeometryData{GeometryData: &schemapb.GeometryArray{Data: [][]byte{{1}, {2}, {3}}}},
|
||
}
|
||
sliced := sliceScalarField(sf, []int{2, 0})
|
||
assert.Equal(t, [][]byte{{3}, {1}}, sliced.GetGeometryData().GetData())
|
||
}
|
||
|
||
func TestSliceScalarField_GeometryWktData(t *testing.T) {
|
||
sf := &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_GeometryWktData{GeometryWktData: &schemapb.GeometryWktArray{Data: []string{"P1", "P2", "P3"}}},
|
||
}
|
||
sliced := sliceScalarField(sf, []int{1})
|
||
assert.Equal(t, []string{"P2"}, sliced.GetGeometryWktData().GetData())
|
||
}
|
||
|
||
// =========================================================================
|
||
// rangeSliceScalarField: remaining branches (geometry)
|
||
// =========================================================================
|
||
|
||
func TestRangeSliceScalarField_GeometryData(t *testing.T) {
|
||
sf := &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_GeometryData{GeometryData: &schemapb.GeometryArray{Data: [][]byte{{1}, {2}, {3}}}},
|
||
}
|
||
sliced := rangeSliceScalarField(sf, 0, 2)
|
||
assert.Equal(t, [][]byte{{1}, {2}}, sliced.GetGeometryData().GetData())
|
||
}
|
||
|
||
// =========================================================================
|
||
// sliceVectorField / rangeSliceVectorField: nullable compact mode branches
|
||
// =========================================================================
|
||
|
||
func TestSliceVectorField_NullableCompact(t *testing.T) {
|
||
// 3 logical rows, but only 2 valid rows in compact data
|
||
vf := &schemapb.VectorField{
|
||
Dim: 2,
|
||
Data: &schemapb.VectorField_FloatVector{FloatVector: &schemapb.FloatArray{Data: []float32{1, 2, 3, 4}}}, // 2 valid rows
|
||
}
|
||
validData := []bool{true, false, true} // row0=valid(data0), row1=null, row2=valid(data1)
|
||
|
||
sliced := requireSliceVectorField(t, vf, []int{2, 0}, validData)
|
||
assert.Equal(t, []float32{3, 4, 1, 2}, sliced.GetFloatVector().GetData())
|
||
}
|
||
|
||
func TestSliceVectorField_NullableCompact_BinaryVector(t *testing.T) {
|
||
vf := &schemapb.VectorField{
|
||
Dim: 16, // 2 bytes per row
|
||
Data: &schemapb.VectorField_BinaryVector{BinaryVector: []byte{0xAA, 0xBB, 0xCC, 0xDD}}, // 2 valid rows
|
||
}
|
||
validData := []bool{true, false, true}
|
||
|
||
sliced := requireSliceVectorField(t, vf, []int{2, 0}, validData)
|
||
assert.Equal(t, []byte{0xCC, 0xDD, 0xAA, 0xBB}, sliced.GetBinaryVector())
|
||
}
|
||
|
||
func TestSliceVectorField_NullableCompact_Float16(t *testing.T) {
|
||
vf := &schemapb.VectorField{
|
||
Dim: 2, // 4 bytes per row
|
||
Data: &schemapb.VectorField_Float16Vector{Float16Vector: []byte{1, 2, 3, 4, 5, 6, 7, 8}},
|
||
}
|
||
validData := []bool{true, false, true}
|
||
|
||
sliced := requireSliceVectorField(t, vf, []int{0}, validData)
|
||
assert.Equal(t, []byte{1, 2, 3, 4}, sliced.GetFloat16Vector())
|
||
}
|
||
|
||
func TestSliceVectorField_NullableCompact_BFloat16(t *testing.T) {
|
||
vf := &schemapb.VectorField{
|
||
Dim: 2,
|
||
Data: &schemapb.VectorField_Bfloat16Vector{Bfloat16Vector: []byte{10, 20, 30, 40, 50, 60, 70, 80}},
|
||
}
|
||
validData := []bool{true, false, true}
|
||
|
||
sliced := requireSliceVectorField(t, vf, []int{2}, validData)
|
||
assert.Equal(t, []byte{50, 60, 70, 80}, sliced.GetBfloat16Vector())
|
||
}
|
||
|
||
func TestSliceVectorField_NullableCompact_Int8(t *testing.T) {
|
||
vf := &schemapb.VectorField{
|
||
Dim: 3,
|
||
Data: &schemapb.VectorField_Int8Vector{Int8Vector: []byte{1, 2, 3, 4, 5, 6}},
|
||
}
|
||
validData := []bool{true, false, true}
|
||
|
||
sliced := requireSliceVectorField(t, vf, []int{2}, validData)
|
||
assert.Equal(t, []byte{4, 5, 6}, sliced.GetInt8Vector())
|
||
}
|
||
|
||
func TestSliceVectorField_NullableCompact_Sparse(t *testing.T) {
|
||
vf := &schemapb.VectorField{
|
||
Dim: 100,
|
||
Data: &schemapb.VectorField_SparseFloatVector{SparseFloatVector: &schemapb.SparseFloatArray{
|
||
Contents: [][]byte{{0x01}, {0x02}}, // 2 valid rows
|
||
Dim: 100,
|
||
}},
|
||
}
|
||
validData := []bool{true, false, true}
|
||
|
||
sliced := requireSliceVectorField(t, vf, []int{2}, validData)
|
||
assert.Equal(t, [][]byte{{0x02}}, sliced.GetSparseFloatVector().GetContents())
|
||
}
|
||
|
||
func TestSliceVectorField_NullableRowDense_VectorArray_SelectValidRow(t *testing.T) {
|
||
dim := int64(2)
|
||
v1 := &schemapb.VectorField{Dim: dim, Data: &schemapb.VectorField_FloatVector{FloatVector: &schemapb.FloatArray{Data: []float32{1, 2}}}}
|
||
empty := &schemapb.VectorField{Dim: dim, Data: &schemapb.VectorField_FloatVector{FloatVector: &schemapb.FloatArray{}}}
|
||
v2 := &schemapb.VectorField{Dim: dim, Data: &schemapb.VectorField_FloatVector{FloatVector: &schemapb.FloatArray{Data: []float32{3, 4}}}}
|
||
|
||
vf := &schemapb.VectorField{
|
||
Dim: dim,
|
||
Data: &schemapb.VectorField_VectorArray{VectorArray: &schemapb.VectorArray{
|
||
Dim: dim,
|
||
Data: []*schemapb.VectorField{v1, empty, v2},
|
||
ElementType: schemapb.DataType_FloatVector,
|
||
}},
|
||
}
|
||
validData := []bool{true, false, true}
|
||
|
||
sliced := requireSliceVectorField(t, vf, []int{2}, validData)
|
||
assert.Len(t, sliced.GetVectorArray().GetData(), 1)
|
||
assert.Equal(t, []float32{3, 4}, sliced.GetVectorArray().GetData()[0].GetFloatVector().GetData())
|
||
}
|
||
|
||
func TestSliceVectorField_NullableRowDense_VectorArray_PreservesNullPlaceholders(t *testing.T) {
|
||
dim := int64(2)
|
||
v1 := &schemapb.VectorField{Dim: dim, Data: &schemapb.VectorField_FloatVector{FloatVector: &schemapb.FloatArray{Data: []float32{1, 2}}}}
|
||
empty := &schemapb.VectorField{Dim: dim, Data: &schemapb.VectorField_FloatVector{FloatVector: &schemapb.FloatArray{}}}
|
||
v3 := &schemapb.VectorField{Dim: dim, Data: &schemapb.VectorField_FloatVector{FloatVector: &schemapb.FloatArray{Data: []float32{3, 4}}}}
|
||
|
||
vf := &schemapb.VectorField{
|
||
Dim: dim,
|
||
Data: &schemapb.VectorField_VectorArray{VectorArray: &schemapb.VectorArray{
|
||
Dim: dim,
|
||
Data: []*schemapb.VectorField{v1, empty, v3},
|
||
ElementType: schemapb.DataType_FloatVector,
|
||
}},
|
||
}
|
||
validData := []bool{true, false, true}
|
||
|
||
sliced := requireSliceVectorField(t, vf, []int{2, 1, 0}, validData)
|
||
require.Len(t, sliced.GetVectorArray().GetData(), 3)
|
||
assert.Equal(t, []float32{3, 4}, sliced.GetVectorArray().GetData()[0].GetFloatVector().GetData())
|
||
assert.Empty(t, sliced.GetVectorArray().GetData()[1].GetFloatVector().GetData())
|
||
assert.Equal(t, []float32{1, 2}, sliced.GetVectorArray().GetData()[2].GetFloatVector().GetData())
|
||
}
|
||
|
||
func TestSliceVectorField_NullableRowDense_VectorArray_AllNullNoData(t *testing.T) {
|
||
dim := int64(2)
|
||
vf := &schemapb.VectorField{
|
||
Dim: dim,
|
||
Data: &schemapb.VectorField_VectorArray{VectorArray: &schemapb.VectorArray{
|
||
Dim: dim,
|
||
ElementType: schemapb.DataType_FloatVector,
|
||
}},
|
||
}
|
||
validData := []bool{false}
|
||
|
||
sliced := requireSliceVectorField(t, vf, []int{0}, validData)
|
||
require.Len(t, sliced.GetVectorArray().GetData(), 1)
|
||
assert.Empty(t, sliced.GetVectorArray().GetData()[0].GetFloatVector().GetData())
|
||
}
|
||
|
||
func TestSliceVectorField_NullableRowDense_VectorArray_UnsupportedElementTypeReturnsError(t *testing.T) {
|
||
vf := &schemapb.VectorField{
|
||
Dim: 2,
|
||
Data: &schemapb.VectorField_VectorArray{VectorArray: &schemapb.VectorArray{
|
||
Dim: 2,
|
||
ElementType: schemapb.DataType_None,
|
||
}},
|
||
}
|
||
|
||
sliced, err := sliceVectorField(vf, []int{0}, []bool{false})
|
||
require.Error(t, err)
|
||
require.Nil(t, sliced)
|
||
assert.Contains(t, err.Error(), "unsupported ArrayOfVector element type")
|
||
}
|
||
|
||
func TestSliceVectorField_NullableCompact_SkipNull(t *testing.T) {
|
||
// Select a null row — it should be skipped
|
||
vf := &schemapb.VectorField{
|
||
Dim: 2,
|
||
Data: &schemapb.VectorField_FloatVector{FloatVector: &schemapb.FloatArray{Data: []float32{1, 2}}},
|
||
}
|
||
validData := []bool{true, false}
|
||
|
||
sliced := requireSliceVectorField(t, vf, []int{1}, validData) // row 1 is null
|
||
assert.Empty(t, sliced.GetFloatVector().GetData())
|
||
}
|
||
|
||
func TestRangeSliceVectorField_NullableCompact(t *testing.T) {
|
||
// 3 logical rows: valid, null, valid → 2 data entries in compact mode
|
||
vf := &schemapb.VectorField{
|
||
Dim: 2,
|
||
Data: &schemapb.VectorField_FloatVector{FloatVector: &schemapb.FloatArray{Data: []float32{1, 2, 3, 4}}},
|
||
}
|
||
validData := []bool{true, false, true}
|
||
|
||
// Range [0, 2) = logical row 0 and 1. Row 0 is valid(data0), row 1 is null → only data0
|
||
sliced := requireRangeSliceVectorField(t, vf, 0, 2, validData)
|
||
assert.Equal(t, []float32{1, 2}, sliced.GetFloatVector().GetData())
|
||
}
|
||
|
||
func TestRangeSliceVectorField_NullableCompact_BinaryVector(t *testing.T) {
|
||
vf := &schemapb.VectorField{
|
||
Dim: 16,
|
||
Data: &schemapb.VectorField_BinaryVector{BinaryVector: []byte{0xAA, 0xBB, 0xCC, 0xDD}},
|
||
}
|
||
validData := []bool{true, false, true}
|
||
|
||
sliced := requireRangeSliceVectorField(t, vf, 0, 2, validData)
|
||
assert.Equal(t, []byte{0xAA, 0xBB}, sliced.GetBinaryVector())
|
||
}
|
||
|
||
func TestRangeSliceVectorField_NullableCompact_Float16(t *testing.T) {
|
||
vf := &schemapb.VectorField{
|
||
Dim: 2,
|
||
Data: &schemapb.VectorField_Float16Vector{Float16Vector: []byte{1, 2, 3, 4, 5, 6, 7, 8}},
|
||
}
|
||
validData := []bool{true, false, true}
|
||
|
||
sliced := requireRangeSliceVectorField(t, vf, 2, 3, validData)
|
||
assert.Equal(t, []byte{5, 6, 7, 8}, sliced.GetFloat16Vector())
|
||
}
|
||
|
||
func TestRangeSliceVectorField_NullableCompact_BFloat16(t *testing.T) {
|
||
vf := &schemapb.VectorField{
|
||
Dim: 2,
|
||
Data: &schemapb.VectorField_Bfloat16Vector{Bfloat16Vector: []byte{10, 20, 30, 40, 50, 60, 70, 80}},
|
||
}
|
||
validData := []bool{true, false, true}
|
||
|
||
sliced := requireRangeSliceVectorField(t, vf, 0, 1, validData)
|
||
assert.Equal(t, []byte{10, 20, 30, 40}, sliced.GetBfloat16Vector())
|
||
}
|
||
|
||
func TestRangeSliceVectorField_NullableCompact_Int8(t *testing.T) {
|
||
vf := &schemapb.VectorField{
|
||
Dim: 3,
|
||
Data: &schemapb.VectorField_Int8Vector{Int8Vector: []byte{1, 2, 3, 4, 5, 6}},
|
||
}
|
||
validData := []bool{true, false, true}
|
||
|
||
sliced := requireRangeSliceVectorField(t, vf, 2, 3, validData)
|
||
assert.Equal(t, []byte{4, 5, 6}, sliced.GetInt8Vector())
|
||
}
|
||
|
||
func TestRangeSliceVectorField_NullableCompact_Sparse(t *testing.T) {
|
||
vf := &schemapb.VectorField{
|
||
Dim: 100,
|
||
Data: &schemapb.VectorField_SparseFloatVector{SparseFloatVector: &schemapb.SparseFloatArray{
|
||
Contents: [][]byte{{0x01}, {0x02}},
|
||
Dim: 100,
|
||
}},
|
||
}
|
||
validData := []bool{true, false, true}
|
||
|
||
sliced := requireRangeSliceVectorField(t, vf, 2, 3, validData)
|
||
assert.Equal(t, [][]byte{{0x02}}, sliced.GetSparseFloatVector().GetContents())
|
||
}
|
||
|
||
func TestRangeSliceVectorField_NullableRowDense_VectorArray_SelectPrefix(t *testing.T) {
|
||
dim := int64(2)
|
||
v1 := &schemapb.VectorField{Dim: dim, Data: &schemapb.VectorField_FloatVector{FloatVector: &schemapb.FloatArray{Data: []float32{1, 2}}}}
|
||
empty := &schemapb.VectorField{Dim: dim, Data: &schemapb.VectorField_FloatVector{FloatVector: &schemapb.FloatArray{}}}
|
||
v2 := &schemapb.VectorField{Dim: dim, Data: &schemapb.VectorField_FloatVector{FloatVector: &schemapb.FloatArray{Data: []float32{3, 4}}}}
|
||
|
||
vf := &schemapb.VectorField{
|
||
Dim: dim,
|
||
Data: &schemapb.VectorField_VectorArray{VectorArray: &schemapb.VectorArray{
|
||
Dim: dim,
|
||
Data: []*schemapb.VectorField{v1, empty, v2},
|
||
ElementType: schemapb.DataType_FloatVector,
|
||
}},
|
||
}
|
||
validData := []bool{true, false, true}
|
||
|
||
sliced := requireRangeSliceVectorField(t, vf, 0, 1, validData)
|
||
assert.Len(t, sliced.GetVectorArray().GetData(), 1)
|
||
assert.Equal(t, []float32{1, 2}, sliced.GetVectorArray().GetData()[0].GetFloatVector().GetData())
|
||
}
|
||
|
||
func TestRangeSliceVectorField_NullableRowDense_VectorArray_PreservesNullPlaceholders(t *testing.T) {
|
||
dim := int64(2)
|
||
v1 := &schemapb.VectorField{Dim: dim, Data: &schemapb.VectorField_FloatVector{FloatVector: &schemapb.FloatArray{Data: []float32{1, 2}}}}
|
||
empty := &schemapb.VectorField{Dim: dim, Data: &schemapb.VectorField_FloatVector{FloatVector: &schemapb.FloatArray{}}}
|
||
v3 := &schemapb.VectorField{Dim: dim, Data: &schemapb.VectorField_FloatVector{FloatVector: &schemapb.FloatArray{Data: []float32{3, 4}}}}
|
||
|
||
vf := &schemapb.VectorField{
|
||
Dim: dim,
|
||
Data: &schemapb.VectorField_VectorArray{VectorArray: &schemapb.VectorArray{
|
||
Dim: dim,
|
||
Data: []*schemapb.VectorField{v1, empty, v3},
|
||
ElementType: schemapb.DataType_FloatVector,
|
||
}},
|
||
}
|
||
validData := []bool{true, false, true}
|
||
|
||
sliced := requireRangeSliceVectorField(t, vf, 2, 3, validData)
|
||
require.Len(t, sliced.GetVectorArray().GetData(), 1)
|
||
assert.Equal(t, []float32{3, 4}, sliced.GetVectorArray().GetData()[0].GetFloatVector().GetData())
|
||
|
||
sliced = requireRangeSliceVectorField(t, vf, 0, 3, validData)
|
||
require.Len(t, sliced.GetVectorArray().GetData(), 3)
|
||
assert.Equal(t, []float32{1, 2}, sliced.GetVectorArray().GetData()[0].GetFloatVector().GetData())
|
||
assert.Empty(t, sliced.GetVectorArray().GetData()[1].GetFloatVector().GetData())
|
||
assert.Equal(t, []float32{3, 4}, sliced.GetVectorArray().GetData()[2].GetFloatVector().GetData())
|
||
}
|
||
|
||
func TestRangeSliceVectorField_NullableRowDense_VectorArray_AllNullNoData(t *testing.T) {
|
||
dim := int64(2)
|
||
vf := &schemapb.VectorField{
|
||
Dim: dim,
|
||
Data: &schemapb.VectorField_VectorArray{VectorArray: &schemapb.VectorArray{
|
||
Dim: dim,
|
||
ElementType: schemapb.DataType_FloatVector,
|
||
}},
|
||
}
|
||
validData := []bool{false, false}
|
||
|
||
sliced := requireRangeSliceVectorField(t, vf, 0, 2, validData)
|
||
require.Len(t, sliced.GetVectorArray().GetData(), 2)
|
||
assert.Empty(t, sliced.GetVectorArray().GetData()[0].GetFloatVector().GetData())
|
||
assert.Empty(t, sliced.GetVectorArray().GetData()[1].GetFloatVector().GetData())
|
||
}
|
||
|
||
func TestRangeSliceVectorField_NullableRowDense_VectorArray_UnsupportedElementTypeReturnsError(t *testing.T) {
|
||
vf := &schemapb.VectorField{
|
||
Dim: 2,
|
||
Data: &schemapb.VectorField_VectorArray{VectorArray: &schemapb.VectorArray{
|
||
Dim: 2,
|
||
ElementType: schemapb.DataType_None,
|
||
}},
|
||
}
|
||
|
||
sliced, err := rangeSliceVectorField(vf, 0, 1, []bool{false})
|
||
require.Error(t, err)
|
||
require.Nil(t, sliced)
|
||
assert.Contains(t, err.Error(), "unsupported ArrayOfVector element type")
|
||
}
|
||
|
||
// =========================================================================
|
||
// buildMergedVectorField: nullable compact mode — null row skip
|
||
// =========================================================================
|
||
|
||
func TestBuildMergedVectorField_NullableCompact_FloatVector(t *testing.T) {
|
||
// FloatVector dim=2, nullable: row0=valid(compact idx 0), row1=null, row2=valid(compact idx 1)
|
||
// Compact data: [1,2, 3,4] — 2 valid rows only
|
||
r1 := &internalpb.RetrieveResults{
|
||
Ids: &schemapb.IDs{IdField: &schemapb.IDs_IntId{IntId: &schemapb.LongArray{Data: []int64{1, 2, 3}}}},
|
||
FieldsData: []*schemapb.FieldData{
|
||
{
|
||
Type: schemapb.DataType_FloatVector, FieldId: 100,
|
||
Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Dim: 2,
|
||
Data: &schemapb.VectorField_FloatVector{FloatVector: &schemapb.FloatArray{Data: []float32{1, 2, 3, 4}}},
|
||
}},
|
||
ValidData: []bool{true, false, true},
|
||
},
|
||
},
|
||
}
|
||
// Include the null row (row1) in selectedRows to exercise the di < 0 skip path
|
||
selectedRows := []rowRef{{resultIdx: 0, rowIdx: 2}, {resultIdx: 0, rowIdx: 1}, {resultIdx: 0, rowIdx: 0}}
|
||
merged, err := buildMergedRetrieveResults([]*internalpb.RetrieveResults{r1}, selectedRows, makeNullableSchema(100, schemapb.DataType_FloatVector, 2))
|
||
require.NoError(t, err)
|
||
// row2 → compact idx 1 → [3,4], row1 → null (skipped), row0 → compact idx 0 → [1,2]
|
||
assert.Equal(t, []float32{3, 4, 1, 2}, merged.FieldsData[0].GetVectors().GetFloatVector().GetData())
|
||
assert.Equal(t, []bool{true, false, true}, merged.FieldsData[0].ValidData)
|
||
}
|
||
|
||
// =========================================================================
|
||
// getFieldValue: remaining branches (string, out-of-range)
|
||
// =========================================================================
|
||
|
||
func TestGetFieldValue_String(t *testing.T) {
|
||
fd := &schemapb.FieldData{Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_StringData{StringData: &schemapb.StringArray{Data: []string{"hello", "world"}}},
|
||
}}}
|
||
val, isNull := getFieldValue(fd, 1)
|
||
assert.False(t, isNull)
|
||
assert.Equal(t, "world", val)
|
||
}
|
||
|
||
func TestGetFieldValue_OutOfRange(t *testing.T) {
|
||
fd := &schemapb.FieldData{Field: &schemapb.FieldData_Scalars{Scalars: &schemapb.ScalarField{
|
||
Data: &schemapb.ScalarField_LongData{LongData: &schemapb.LongArray{Data: []int64{1}}},
|
||
}}}
|
||
_, isNull := getFieldValue(fd, 5) // out of range
|
||
assert.True(t, isNull)
|
||
}
|
||
|
||
// =========================================================================
|
||
// rangeSliceRetrieveResults: element-level metadata propagation
|
||
// =========================================================================
|
||
|
||
func TestRangeSliceRetrieveResults_EmptyRange(t *testing.T) {
|
||
result := &internalpb.RetrieveResults{
|
||
Ids: &schemapb.IDs{IdField: &schemapb.IDs_IntId{IntId: &schemapb.LongArray{Data: []int64{1, 2}}}},
|
||
}
|
||
sliced := requireRangeSliceRetrieveResults(t, result, 1, 1) // start == end
|
||
assert.Nil(t, sliced.GetIds())
|
||
}
|
||
|
||
func TestRangeSliceRetrieveResults_ElementLevel(t *testing.T) {
|
||
result := &internalpb.RetrieveResults{
|
||
Ids: &schemapb.IDs{IdField: &schemapb.IDs_IntId{IntId: &schemapb.LongArray{Data: []int64{1, 2, 3}}}},
|
||
ElementLevel: true,
|
||
ElementIndices: []*internalpb.ElementIndices{
|
||
{Indices: []int32{0}},
|
||
{Indices: []int32{1, 2}},
|
||
{Indices: []int32{3}},
|
||
},
|
||
FieldsData: []*schemapb.FieldData{
|
||
makeInt64Field(100, "val", []int64{10, 20, 30}),
|
||
},
|
||
}
|
||
sliced := requireRangeSliceRetrieveResults(t, result, 1, 3)
|
||
assert.True(t, sliced.GetElementLevel())
|
||
assert.Len(t, sliced.GetElementIndices(), 2)
|
||
assert.Equal(t, []int32{1, 2}, sliced.GetElementIndices()[0].GetIndices())
|
||
}
|
||
|
||
func TestValidateElementLevelConsistency_InconsistentFlag(t *testing.T) {
|
||
r1 := &internalpb.RetrieveResults{
|
||
ElementLevel: true,
|
||
Ids: &schemapb.IDs{IdField: &schemapb.IDs_IntId{IntId: &schemapb.LongArray{Data: []int64{1}}}},
|
||
ElementIndices: []*internalpb.ElementIndices{{Indices: []int32{0}}},
|
||
}
|
||
r2 := &internalpb.RetrieveResults{
|
||
ElementLevel: false,
|
||
Ids: &schemapb.IDs{IdField: &schemapb.IDs_IntId{IntId: &schemapb.LongArray{Data: []int64{2}}}},
|
||
}
|
||
err := validateElementLevelConsistency([]*internalpb.RetrieveResults{r1, r2}, nil)
|
||
assert.Error(t, err)
|
||
assert.Contains(t, err.Error(), "inconsistent element-level flag")
|
||
}
|
||
|
||
func TestValidateElementLevelConsistency_LengthMismatch(t *testing.T) {
|
||
r := &internalpb.RetrieveResults{
|
||
Ids: &schemapb.IDs{IdField: &schemapb.IDs_IntId{IntId: &schemapb.LongArray{Data: []int64{1, 2}}}},
|
||
ElementLevel: true,
|
||
ElementIndices: []*internalpb.ElementIndices{{Indices: []int32{0}}}, // 1 != 2 ids
|
||
}
|
||
err := validateElementLevelConsistency([]*internalpb.RetrieveResults{r}, nil)
|
||
assert.Error(t, err)
|
||
assert.Contains(t, err.Error(), "element_indices length")
|
||
}
|
||
|
||
// =========================================================================
|
||
// Nullable vector / scalar merge correctness tests
|
||
// =========================================================================
|
||
|
||
// makeTestIntIDs is a helper to build IDs for test results.
|
||
func makeTestIntIDs(ids ...int64) *schemapb.IDs {
|
||
return &schemapb.IDs{IdField: &schemapb.IDs_IntId{IntId: &schemapb.LongArray{Data: ids}}}
|
||
}
|
||
|
||
// makeTestSparseVec builds a knowhere sparse vector binary: [dim uint32][val float32], little-endian.
|
||
func makeTestSparseVec(dim uint32, val float32) []byte {
|
||
b := make([]byte, 8)
|
||
binary.LittleEndian.PutUint32(b[0:4], dim)
|
||
binary.LittleEndian.PutUint32(b[4:8], math.Float32bits(val))
|
||
return b
|
||
}
|
||
|
||
// TestBuildMergedVectorField_NullableSparseVector_AllNull_EmptyValidData validates Bug 1 fix:
|
||
// when a nullable sparse vector field has empty ValidData + empty Contents, merge must not panic,
|
||
// and the merged output must correctly mark those rows as null (ValidData=false).
|
||
func TestBuildMergedVectorField_NullableSparse_RejectsEmptyValidData(t *testing.T) {
|
||
sparseSchema := makeNullableSchema(100, schemapb.DataType_SparseFloatVector, 0)
|
||
|
||
// rA: 1 row, has valid data
|
||
rA := &internalpb.RetrieveResults{
|
||
Ids: makeTestIntIDs(1),
|
||
FieldsData: []*schemapb.FieldData{{
|
||
FieldId: 100,
|
||
Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Data: &schemapb.VectorField_SparseFloatVector{
|
||
SparseFloatVector: &schemapb.SparseFloatArray{
|
||
Contents: [][]byte{makeTestSparseVec(1, 0.5)},
|
||
},
|
||
},
|
||
}},
|
||
ValidData: []bool{true},
|
||
}},
|
||
}
|
||
|
||
// rB: 1 row, ValidData absent, Contents absent (segcore get_vector early return).
|
||
// This is now a contract violation: nullable field with numRows > 0 must have ValidData.
|
||
// The old code silently treated this as "all null"; the new code fails fast.
|
||
rB := &internalpb.RetrieveResults{
|
||
Ids: makeTestIntIDs(2),
|
||
FieldsData: []*schemapb.FieldData{{
|
||
FieldId: 100,
|
||
Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Data: &schemapb.VectorField_SparseFloatVector{
|
||
SparseFloatVector: &schemapb.SparseFloatArray{Contents: nil},
|
||
},
|
||
}},
|
||
ValidData: nil,
|
||
}},
|
||
}
|
||
|
||
results := []*internalpb.RetrieveResults{rA, rB}
|
||
selectedRows := []rowRef{{resultIdx: 0, rowIdx: 0}, {resultIdx: 1, rowIdx: 0}}
|
||
|
||
_, err := buildMergedRetrieveResults(results, selectedRows, sparseSchema)
|
||
require.Error(t, err)
|
||
assert.Contains(t, err.Error(), "empty ValidData")
|
||
}
|
||
|
||
// TestBuildMergedVectorField_NullableSparse_RejectsMultipleRowsWithEmptyValidData validates that
|
||
// multiple rows from a result with absent ValidData trigger fail-fast error.
|
||
func TestBuildMergedVectorField_NullableSparse_RejectsMultipleRowsWithEmptyValidData(t *testing.T) {
|
||
sparseSchema := makeNullableSchema(100, schemapb.DataType_SparseFloatVector, 0)
|
||
|
||
rA := &internalpb.RetrieveResults{
|
||
Ids: makeTestIntIDs(1),
|
||
FieldsData: []*schemapb.FieldData{{
|
||
FieldId: 100,
|
||
Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Data: &schemapb.VectorField_SparseFloatVector{
|
||
SparseFloatVector: &schemapb.SparseFloatArray{
|
||
Contents: [][]byte{makeTestSparseVec(1, 0.1)},
|
||
},
|
||
},
|
||
}},
|
||
ValidData: []bool{true},
|
||
}},
|
||
}
|
||
|
||
rB := &internalpb.RetrieveResults{
|
||
Ids: makeTestIntIDs(2, 3),
|
||
FieldsData: []*schemapb.FieldData{{
|
||
FieldId: 100,
|
||
Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Data: &schemapb.VectorField_SparseFloatVector{
|
||
SparseFloatVector: &schemapb.SparseFloatArray{Contents: nil},
|
||
},
|
||
}},
|
||
ValidData: nil,
|
||
}},
|
||
}
|
||
|
||
results := []*internalpb.RetrieveResults{rA, rB}
|
||
selectedRows := []rowRef{
|
||
{resultIdx: 0, rowIdx: 0},
|
||
{resultIdx: 1, rowIdx: 0},
|
||
{resultIdx: 1, rowIdx: 1},
|
||
}
|
||
|
||
// rB has 2 rows but no ValidData → contract violation, must error
|
||
_, err := buildMergedRetrieveResults(results, selectedRows, sparseSchema)
|
||
require.Error(t, err)
|
||
assert.Contains(t, err.Error(), "empty ValidData")
|
||
}
|
||
|
||
// TestBuildMergedVectorField_NullableSparseVector_AllNullWithValidData validates compact path
|
||
// when ValidData is present and all false (explicit all-null).
|
||
func TestBuildMergedVectorField_NullableSparseVector_AllNullWithValidData(t *testing.T) {
|
||
sparseSchema := makeNullableSchema(100, schemapb.DataType_SparseFloatVector, 0)
|
||
r := &internalpb.RetrieveResults{
|
||
Ids: makeTestIntIDs(1, 2),
|
||
FieldsData: []*schemapb.FieldData{{
|
||
FieldId: 100,
|
||
Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Data: &schemapb.VectorField_SparseFloatVector{
|
||
SparseFloatVector: &schemapb.SparseFloatArray{Contents: nil},
|
||
},
|
||
}},
|
||
ValidData: []bool{false, false},
|
||
}},
|
||
}
|
||
results := []*internalpb.RetrieveResults{r}
|
||
selectedRows := []rowRef{{resultIdx: 0, rowIdx: 0}, {resultIdx: 0, rowIdx: 1}}
|
||
|
||
merged, err := buildMergedRetrieveResults(results, selectedRows, sparseSchema)
|
||
require.NoError(t, err)
|
||
assert.Equal(t, []bool{false, false}, merged.FieldsData[0].ValidData)
|
||
assert.Empty(t, merged.FieldsData[0].GetVectors().GetSparseFloatVector().GetContents())
|
||
}
|
||
|
||
// TestBuildMergedVectorField_NullableSparseVector_Mixed validates compact index mapping when
|
||
// some rows are valid and some are null within the same result.
|
||
func TestBuildMergedVectorField_NullableSparseVector_Mixed(t *testing.T) {
|
||
sparseSchema := makeNullableSchema(100, schemapb.DataType_SparseFloatVector, 0)
|
||
// 3 rows: ValidData=[true,false,true], Contents=[vec0, vec2] (compact)
|
||
r := &internalpb.RetrieveResults{
|
||
Ids: makeTestIntIDs(1, 2, 3),
|
||
FieldsData: []*schemapb.FieldData{{
|
||
FieldId: 100,
|
||
Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Data: &schemapb.VectorField_SparseFloatVector{
|
||
SparseFloatVector: &schemapb.SparseFloatArray{
|
||
Contents: [][]byte{makeTestSparseVec(1, 0.1), makeTestSparseVec(3, 0.3)},
|
||
},
|
||
},
|
||
}},
|
||
ValidData: []bool{true, false, true},
|
||
}},
|
||
}
|
||
results := []*internalpb.RetrieveResults{r}
|
||
// Select row 0 (valid) and row 1 (null)
|
||
selectedRows := []rowRef{{resultIdx: 0, rowIdx: 0}, {resultIdx: 0, rowIdx: 1}}
|
||
|
||
merged, err := buildMergedRetrieveResults(results, selectedRows, sparseSchema)
|
||
require.NoError(t, err)
|
||
assert.Equal(t, []bool{true, false}, merged.FieldsData[0].ValidData)
|
||
// Only 1 non-null row in output
|
||
assert.Len(t, merged.FieldsData[0].GetVectors().GetSparseFloatVector().GetContents(), 1)
|
||
}
|
||
|
||
// TestBuildMergedFieldData_NullableScalar_EmptyValidData_FailFast validates that
|
||
// a nullable field with rows but absent ValidData triggers a fail-fast error,
|
||
// not a silent fallback to all-null. This catches segcore contract violations
|
||
// early instead of letting them propagate to downstream panics.
|
||
func TestBuildMergedVectorField_NullableVector_RejectsEmptyValidData(t *testing.T) {
|
||
// Note: buildCompactIndices only validates vector fields. For scalar fields,
|
||
// the ValidData check is still lenient (scalar fields are not compacted).
|
||
// This test uses a vector schema to trigger the vector-path validation.
|
||
sparseSchema := makeNullableSchema(100, schemapb.DataType_SparseFloatVector, 0)
|
||
|
||
rA := &internalpb.RetrieveResults{
|
||
Ids: makeTestIntIDs(1),
|
||
FieldsData: []*schemapb.FieldData{{
|
||
FieldId: 100,
|
||
Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Data: &schemapb.VectorField_SparseFloatVector{
|
||
SparseFloatVector: &schemapb.SparseFloatArray{
|
||
Contents: [][]byte{makeTestSparseVec(1, 0.5)},
|
||
},
|
||
},
|
||
}},
|
||
ValidData: []bool{true},
|
||
}},
|
||
}
|
||
// rB: ValidData absent → contract violation for nullable vector
|
||
rB := &internalpb.RetrieveResults{
|
||
Ids: makeTestIntIDs(2),
|
||
FieldsData: []*schemapb.FieldData{{
|
||
FieldId: 100,
|
||
Field: &schemapb.FieldData_Vectors{Vectors: &schemapb.VectorField{
|
||
Data: &schemapb.VectorField_SparseFloatVector{
|
||
SparseFloatVector: &schemapb.SparseFloatArray{Contents: nil},
|
||
},
|
||
}},
|
||
ValidData: nil,
|
||
}},
|
||
}
|
||
|
||
results := []*internalpb.RetrieveResults{rA, rB}
|
||
selectedRows := []rowRef{{resultIdx: 0, rowIdx: 0}, {resultIdx: 1, rowIdx: 0}}
|
||
|
||
_, err := buildMergedRetrieveResults(results, selectedRows, sparseSchema)
|
||
require.Error(t, err)
|
||
assert.Contains(t, err.Error(), "empty ValidData")
|
||
}
|