github.com/weaviate/weaviate@v1.24.6/adapters/repos/db/ranked_fusion_test.go (about) 1 // _ _ 2 // __ _____ __ ___ ___ __ _| |_ ___ 3 // \ \ /\ / / _ \/ _` \ \ / / |/ _` | __/ _ \ 4 // \ V V / __/ (_| |\ V /| | (_| | || __/ 5 // \_/\_/ \___|\__,_| \_/ |_|\__,_|\__\___| 6 // 7 // Copyright © 2016 - 2024 Weaviate B.V. All rights reserved. 8 // 9 // CONTACT: hello@weaviate.io 10 // 11 12 //go:build integrationTest 13 14 package db 15 16 import ( 17 "context" 18 "encoding/json" 19 "fmt" 20 "os" 21 "testing" 22 23 "github.com/go-openapi/strfmt" 24 "github.com/google/uuid" 25 "github.com/sirupsen/logrus" 26 "github.com/sirupsen/logrus/hooks/test" 27 "github.com/stretchr/testify/mock" 28 "github.com/stretchr/testify/require" 29 "github.com/weaviate/weaviate/entities/additional" 30 "github.com/weaviate/weaviate/entities/dto" 31 "github.com/weaviate/weaviate/entities/filters" 32 "github.com/weaviate/weaviate/entities/models" 33 "github.com/weaviate/weaviate/entities/schema" 34 "github.com/weaviate/weaviate/entities/search" 35 "github.com/weaviate/weaviate/entities/searchparams" 36 "github.com/weaviate/weaviate/entities/storobj" 37 enthnsw "github.com/weaviate/weaviate/entities/vectorindex/hnsw" 38 "github.com/weaviate/weaviate/usecases/config" 39 "github.com/weaviate/weaviate/usecases/modules" 40 "github.com/weaviate/weaviate/usecases/traverser" 41 "github.com/weaviate/weaviate/usecases/traverser/hybrid" 42 ) 43 44 type TestDoc struct { 45 DocID string 46 Document string 47 } 48 49 type TestQuery struct { 50 QueryID string 51 Query string 52 MatchingDocIDs []string 53 } 54 55 var defaultConfig = config.Config{ 56 QueryDefaults: config.QueryDefaults{ 57 Limit: 100, 58 }, 59 QueryMaximumResults: 100, 60 } 61 62 func SetupStandardTestData(t require.TestingT, repo *DB, schemaGetter *fakeSchemaGetter, logger logrus.FieldLogger, k1, b float32) { 63 class := &models.Class{ 64 VectorIndexConfig: enthnsw.NewDefaultUserConfig(), 65 InvertedIndexConfig: BM25FinvertedConfig(k1, b, "none"), 66 Class: "StandardTest", 67 Properties: []*models.Property{ 68 { 69 Name: "document", 70 DataType: []string{string(schema.DataTypeText)}, 71 Tokenization: "word", 72 }, 73 }, 74 } 75 76 schema := schema.Schema{ 77 Objects: &models.Schema{ 78 Classes: []*models.Class{class}, 79 }, 80 } 81 82 schemaGetter.schema = schema 83 84 migrator := NewMigrator(repo, logger) 85 migrator.AddClass(context.Background(), class, schemaGetter.shardState) 86 87 // Load text from file standard_test_data.json 88 // This is a list of 1000 documents from the MEDLINE database 89 // Each document is a medical abstract 90 91 data, _ := os.ReadFile("NFCorpus-Corpus.json") 92 var docs []TestDoc 93 json.Unmarshal(data, &docs) 94 95 for i, doc := range docs { 96 id := strfmt.UUID(uuid.MustParse(fmt.Sprintf("%032d", i)).String()) 97 98 data := map[string]interface{}{"document": doc.Document, "code": doc.DocID} 99 obj := &models.Object{Class: "StandardTest", ID: id, Properties: data, CreationTimeUnix: 1565612833955, LastUpdateTimeUnix: 10000020} 100 err := repo.PutObject(context.Background(), obj, nil, nil, nil) 101 require.Nil(t, err) 102 } 103 } 104 105 func TestHybrid(t *testing.T) { 106 dirName := t.TempDir() 107 logger := logrus.New() 108 schemaGetter := &fakeSchemaGetter{ 109 schema: schema.Schema{Objects: &models.Schema{Classes: nil}}, 110 shardState: singleShardState(), 111 } 112 repo, err := New(logger, Config{ 113 RootPath: dirName, 114 QueryMaximumResults: 10000, 115 MaxImportGoroutinesFactor: 1, 116 }, &fakeRemoteClient{}, &fakeNodeResolver{}, &fakeRemoteNodeClient{}, nil, nil) 117 require.Nil(t, err) 118 repo.SetSchemaGetter(schemaGetter) 119 require.Nil(t, repo.WaitForStartup(context.TODO())) 120 defer repo.Shutdown(context.Background()) 121 122 SetupStandardTestData(t, repo, schemaGetter, logger, 1.2, 0.75) 123 124 idx := repo.GetIndex("StandardTest") 125 require.NotNil(t, idx) 126 127 // Load queries from file standard_test_queries.json 128 // This is a list of 100 queries from the MEDLINE database 129 130 data, _ := os.ReadFile("NFCorpus-Query.json") 131 var queries []TestQuery 132 json.Unmarshal(data, &queries) 133 for _, query := range queries { 134 kwr := &searchparams.KeywordRanking{Type: "bm25", Properties: []string{}, Query: query.Query} 135 addit := additional.Properties{} 136 res, _, _ := idx.objectSearch(context.TODO(), 1000, nil, kwr, nil, nil, addit, nil, "", 0) 137 138 fmt.Printf("query for %s returned %d results\n", query.Query, len(res)) 139 140 } 141 } 142 143 func TestBIER(t *testing.T) { 144 dirName := t.TempDir() 145 146 logger := logrus.New() 147 schemaGetter := &fakeSchemaGetter{ 148 schema: schema.Schema{Objects: &models.Schema{Classes: nil}}, 149 shardState: singleShardState(), 150 } 151 repo, err := New(logger, Config{ 152 RootPath: dirName, 153 QueryMaximumResults: 10000, 154 MaxImportGoroutinesFactor: 1, 155 }, &fakeRemoteClient{}, &fakeNodeResolver{}, &fakeRemoteNodeClient{}, nil, nil) 156 require.Nil(t, err) 157 repo.SetSchemaGetter(schemaGetter) 158 require.Nil(t, repo.WaitForStartup(context.TODO())) 159 defer repo.Shutdown(context.Background()) 160 161 SetupStandardTestData(t, repo, schemaGetter, logger, 1.2, 0.75) 162 163 idx := repo.GetIndex("StandardTest") 164 require.NotNil(t, idx) 165 166 // Load queries from file standard_test_queries.json 167 // This is a list of 100 queries from the MEDLINE database 168 169 data, _ := os.ReadFile("NFCorpus-Query.json") 170 var queries []TestQuery 171 json.Unmarshal(data, &queries) 172 for _, query := range queries { 173 kwr := &searchparams.KeywordRanking{Type: "bm25", Properties: []string{}, Query: query.Query} 174 addit := additional.Properties{} 175 res, _, _ := idx.objectSearch(context.TODO(), 1000, nil, kwr, nil, nil, addit, nil, "", 0) 176 177 fmt.Printf("query for %s returned %d results\n", query.Query, len(res)) 178 // fmt.Printf("Results: %v\n", res) 179 180 //for j, doc := range res { 181 // fmt.Printf("res %v, %v\n", j, doc.Object.GetAdditionalProperty("code")) 182 //} 183 184 //Check the docIDs are the same 185 //for j, doc := range res[0:10] { 186 // fmt.Printf("Result: rank %v, docID %v, score %v (%v)\n", j, doc.Object.GetAdditionalProperty("code"), doc.Score(), doc.Object.GetAdditionalProperty("document")) 187 // fmt.Printf("Expected: rank %v, docID %v\n", j, query.MatchingDocIDs[j].Object.GetAdditionalProperty("code")) 188 // require.Equal(t, query.MatchingDocIDs[j], doc.Object.GetAdditionalProperty("code").(string)) 189 //} 190 191 } 192 } 193 194 func addObj(repo *DB, i int, props map[string]interface{}, vec []float32) error { 195 id := strfmt.UUID(uuid.MustParse(fmt.Sprintf("%032d", i)).String()) 196 197 obj := &models.Object{Class: "MyClass", ID: id, Properties: props, CreationTimeUnix: 1565612833955, LastUpdateTimeUnix: 10000020} 198 vector := vec 199 err := repo.PutObject(context.Background(), obj, vector, nil, nil) 200 return err 201 } 202 203 func SetupFusionClass(t require.TestingT, repo *DB, schemaGetter *fakeSchemaGetter, logger logrus.FieldLogger, k1, b float32) *models.Class { 204 class := &models.Class{ 205 VectorIndexConfig: enthnsw.NewDefaultUserConfig(), 206 InvertedIndexConfig: BM25FinvertedConfig(k1, b, "none"), 207 Class: "MyClass", 208 Properties: []*models.Property{ 209 { 210 Name: "title", 211 DataType: schema.DataTypeText.PropString(), 212 Tokenization: models.PropertyTokenizationWord, 213 }, 214 { 215 Name: "description", 216 DataType: schema.DataTypeText.PropString(), 217 Tokenization: models.PropertyTokenizationWord, 218 }, 219 }, 220 } 221 222 schema := schema.Schema{ 223 Objects: &models.Schema{ 224 Classes: []*models.Class{class}, 225 }, 226 } 227 228 schemaGetter.schema = schema 229 230 migrator := NewMigrator(repo, logger) 231 migrator.AddClass(context.Background(), class, schemaGetter.shardState) 232 233 addObj(repo, 0, map[string]interface{}{"title": "Our journey to BM25F", "description": "This is how we get to BM25F"}, []float32{ 234 -0.04488207, -0.32971063, 0.23568298, 0.004971265, -0.12588727, 0.0036464946, -0.6209942, -0.23247992, -0.19338948, 0.3544481, 0.00050193403, 0.36604947, -0.13813384, -0.126298, 0.05640272, -0.36604303, -0.10976448, 0.196644, -0.02206351, -0.27649152, -0.0050981278, 0.020616557, 0.14605781, 0.093781, -0.25838777, -0.5038474, -0.46846977, 0.13360861, -0.15851927, 0.55075127, 0.34870508, 0.085248806, -0.02763206, -0.07068178, -0.26878145, 0.2814703, -0.33317414, 0.48958343, -0.39648432, 0.606744, 0.12882654, 0.07246548, 0.54059577, 0.19526751, 0.85892624, 0.1485534, 0.19790995, -0.34280643, 0.27512825, 0.105886005, 0.030610563, 0.3811836, 0.18384686, 0.29538825, -0.020791993, -0.31372088, -0.08811446, -0.12979206, 0.30209363, -0.14561261, 0.22077207, -0.40219122, -0.40567216, -0.08740993, -0.31625694, -0.18109407, 0.5411316, -0.09015073, 0.22272661, 0.13575949, -0.36186692, -0.02766613, -0.22463024, 0.15271285, 0.304631, -0.57313913, 0.31346974, -0.118818894, -0.36198893, 0.24609627, 0.47406524, -0.55662453, 0.37812573, -0.2959746, 0.6146945, -0.3934654, 0.30840993, -0.24944904, -0.2063059, -0.48078862, -0.08967737, -0.1273727, 0.1587198, -0.44776592, 0.0048942068, 0.18738478, -0.4544592, -0.4755225, 0.2156486, 0.39935833, 0.25160903, -0.35463294, 0.60699236, -0.12445828, -0.029569108, -0.4983043, 0.44752246, -0.2340386, -0.27559096, 0.67984164, 0.51470226, -0.5285723, 0.0024457057, -0.20425095, -0.4915065, -0.3221788, -0.15558766, 0.20102327, 0.23525643, 0.28365672, 0.58687097, 0.3190138, -0.31130886, 0.053733524, -0.10361888, -0.2598206, 1.5977107, 0.60224503, 0.0074084206, 0.17191416, 0.5619663, 0.41178456, 0.27006987, -0.5354418, -0.054428957, 0.6849038, -0.024342017, 0.43103293, -0.22892271, 0.036829337, -0.103084944, -0.2021301, -0.11352237, -0.17110321, 0.76075333, -0.1755375, 0.029183118, -0.34927735, 0.22040148, -0.18136469, 0.16048056, 0.34151044, -0.048658744, 0.03941434, 0.45190382, 0.103645615, 0.10437423, -0.086864054, 0.523172, -0.59672165, -0.1225319, -0.5800122, 0.2197229, 0.49325037, 0.30607533, 0.012414166, 0.1539727, -0.60095996, 0.05142522, 0.021675617, -0.54661363, -0.0050268047, -0.507448, -0.04522115, -0.77988946, 0.10536073, 0.099219516, 0.40711993, 0.27353838, 0.1728696, -0.4171313, -1.3076599, -0.19778727, -0.23201689, 0.40729725, -0.28640944, 0.06354561, -0.3877251, -0.7938625, 0.29908186, -0.24450836, -0.22622268, 0.32792783, 0.28376722, -0.3685573, 0.031423382, -0.012464195, 0.2254249, 0.26994115, -0.19821979, -0.24086252, 0.24454598, 0.30043048, -0.627896, -0.3355214, -0.14054148, 0.50488055, -0.073988594, -0.31053177, 0.36260405, -0.56093204, 0.12066587, -0.47301888, 0.88418764, 0.09010807, -0.10899238, 0.62317103, 0.27237964, -0.604178, 0.0067386073, 0.1370205, -0.094664395, 0.3479645, 0.25092986, 0.16948108, -0.20874223, -0.54980844, -0.100548536, 0.47177002, -0.4981452, 0.1815202, -0.80878633, -0.076736815, 0.43152434, -0.43210435, -0.28010413, 0.1249095, 0.385616, -0.2984289, -0.006841246, 0.3496464, -0.33298343, 0.06344994, 0.37393335, 0.18608452, -0.10631552, -0.40111285, -0.146849, -0.04161288, -0.31621853, -0.06889858, 0.13343252, -0.11599523, 0.5377954, 0.25938663, -0.43172404, -0.7476662, -0.54316807, 0.0029651164, 0.09958581, 0.0730254, 0.22785394, 0.3276773, 0.01816153, 0.094938636, 0.71604383, -0.09648144, -0.0035640472, -0.5383972, 0.28588042, 0.7625968, -0.22359839, 0.17167832, -0.06235203, -0.32480234, -0.18599075, 0.1570872, -0.06470149, -0.029198159, 0.23251827, 0.100047514, -0.06314679, 0.6390605, -0.06232509, 0.76272035, 0.2975126, 0.15871438, 0.18222457, -0.548036, 0.23633306, -0.17981203, 0.023965335, 0.24478278, -0.21601695, -0.108217336, 0.05834005, 0.3718355, 0.0970174, 0.04476983, -0.118143275, 235 }) 236 237 addObj(repo, 1, map[string]interface{}{"title": "Our peanuts to BM25F", "description": "This is how we get to BM25F"}, []float32{0.11676752, -0.4837953, -0.06559026, 0.3242706, 0.08680799, -0.30777612, -0.22926088, 0.01667141, 0.31844103, 0.4666344, 0.417305, 0.06108997, -0.0740552, 0.14234918, 0.06823654, 0.16182217, -0.012199775, -0.17269811, -0.16104576, -0.09208117, 0.063624315, 0.3113634, -0.3830663, 0.05831715, -0.14125349, -0.26962206, -0.0696671, -0.013111545, 0.20097807, 0.033809602, -0.048573885, 0.46815604, 0.32582077, 0.32308698, 0.20355524, -0.08757271, 0.17099291, 0.31500003, -0.05445185, 0.7712824, -0.2096038, 0.28787872, 0.10871067, -0.3266944, -0.1633618, 0.34630018, -0.15387866, -0.45506623, -0.21508889, -0.19249445, -0.28801772, -0.2694916, -0.18476918, -0.12890251, -0.29947013, 0.0008435306, -0.06490287, -0.006560939, 0.24637267, -0.111215346, 0.3775517, -0.82433224, -0.3179537, 0.022306278, 0.19248968, -0.1701471, 0.052865, -0.044782564, -0.10222186, 0.09571932, -0.19251339, 0.241193, -0.13216764, -0.19301765, 0.46628228, -0.29973802, 0.0030274116, 0.01664786, 0.1216316, 0.12837356, -0.048461247, -0.56439394, 0.06110007, 0.102808535, 0.63137263, -0.13134736, 0.41365498, -0.113528065, -0.06924132, 0.1076709, -0.06833764, 0.31522226, 0.13445137, -0.16227263, -0.15102008, 0.23768687, -0.41108298, -0.473573, -0.35702798, 0.21465969, -0.30590045, -0.26616427, 0.7287231, -0.036261655, -0.34903425, -0.1396425, -0.022058574, -0.33956096, -0.3359471, -0.035496157, -0.1786069, -0.0857123, -0.0845917, 0.13232024, -0.02890402, -0.45281035, -0.026353035, -0.39124215, -0.15753527, -0.075793914, 0.35795033, 0.35925874, -0.1423145, -0.0969307, 0.08920737, 0.15772092, 1.3536518, 0.29779792, -0.05407743, -0.048793554, 0.12263066, -0.06248072, -0.49598575, -0.46484944, -0.31050035, 0.6283043, 0.5242193, 0.25987545, -0.2584134, 0.32898954, 0.014580286, 0.14016634, -0.010093123, -0.22610027, -0.029830063, 0.18112054, 0.020298548, -0.025797658, -0.40394786, -0.17097965, 0.11640611, 0.29304397, -0.27026933, -0.14832975, -0.099585906, 0.4554175, -0.0018298444, 0.23190805, -0.65866566, -0.09366216, -0.7000203, 0.004698127, -0.17523476, -0.34830904, -0.16284281, 0.15495956, 0.5772887, 0.048939474, -0.12923703, -0.236143, -0.03874896, 0.2960667, 0.029154046, 0.42814374, -0.4332385, -0.31293675, -0.10682973, -0.12069777, 0.071893886, 0.06644212, -0.46342105, -0.8599067, 0.017380634, -0.38347453, 0.14165273, -0.08906643, -0.06801824, 0.19660597, -0.06807183, 0.33882818, 0.044932134, 0.27550527, 0.2308957, -0.101730466, -0.19064885, -0.015364495, 0.0149245, 0.24177131, -0.15636654, -0.002376896, -0.6399841, 0.14845476, 0.46339074, 0.036926877, -0.067630276, 0.289784, 0.15529989, -0.5235124, 0.50196457, -0.004536148, -0.3716798, 0.047304608, -0.027990041, 0.15901157, 0.021176483, 0.35387334, 0.4457043, 0.094738215, 0.08722517, 0.0450516, 0.1739127, -0.2606226, 0.035999063, -0.12919275, -0.11809982, -0.20865, -0.6917279, 0.093973815, -0.38069052, -0.114874505, -0.3051481, -0.357749, 0.48254266, 0.31795567, 0.37491056, 0.0047062743, -0.1265727, 0.51655954, 0.1622121, 0.39811996, -0.002116253, -0.375531, 0.6347343, 0.14833164, 0.032251768, 0.021101426, -0.34346518, 0.22451165, 0.028649824, -0.04794777, 0.056036226, 0.14179966, 0.32724753, 0.17185552, 0.2504634, -0.05013007, -0.31430584, -0.22200464, -0.508279, -0.10017326, 0.16302426, -0.09568865, 0.05985463, -0.22916546, -0.084666654, -0.15271503, -0.24385636, -0.028514259, -0.33194387, -0.17132543, -0.1474212, -0.18526097, 0.2198915, -0.1689729, -0.19907063, 0.19941927, -0.47478884, 0.0695081, 0.3741401, 0.19423902, 0.085894205, -0.53214043, 0.33309302, 0.18701339, 0.23461546, -0.14038202, 0.07201847, 0.3462437, 0.1640635, 0.07200127, -0.09130982, 0.3868172, -0.09754013, 0.040958565, -0.18743117, 0.14117524, -0.18739408, 0.13669269, -0.09902989, -0.16762646}) 238 239 addObj(repo, 2, map[string]interface{}{"title": "Elephant Parade", "description": "Elephants elephants elephant"}, []float32{-0.04488207, -0.32971063, 0.23568298, 0.004971265, -0.12588727, 0.0036464946, -0.6209942, -0.23247992, -0.19338948, 0.3544481, 0.00050193403, 0.36604947, -0.13813384, -0.126298, 0.05640272, -0.36604303, -0.10976448, 0.196644, -0.02206351, -0.27649152, -0.0050981278, 0.020616557, 0.14605781, 0.093781, -0.25838777, -0.5038474, -0.46846977, 0.13360861, -0.15851927, 0.55075127, 0.34870508, 0.085248806, -0.02763206, -0.07068178, -0.26878145, 0.2814703, -0.33317414, 0.48958343, -0.39648432, 0.606744, 0.12882654, 0.07246548, 0.54059577, 0.19526751, 0.85892624, 0.1485534, 0.19790995, -0.34280643, 0.27512825, 0.105886005, 0.030610563, 0.3811836, 0.18384686, 0.29538825, -0.020791993, -0.31372088, -0.08811446, -0.12979206, 0.30209363, -0.14561261, 0.22077207, -0.40219122, -0.40567216, -0.08740993, -0.31625694, -0.18109407, 0.5411316, -0.09015073, 0.22272661, 0.13575949, -0.36186692, -0.02766613, -0.22463024, 0.15271285, 0.304631, -0.57313913, 0.31346974, -0.118818894, -0.36198893, 0.24609627, 0.47406524, -0.55662453, 0.37812573, -0.2959746, 0.6146945, -0.3934654, 0.30840993, -0.24944904, -0.2063059, -0.48078862, -0.08967737, -0.1273727, 0.1587198, -0.44776592, 0.0048942068, 0.18738478, -0.4544592, -0.4755225, 0.2156486, 0.39935833, 0.25160903, -0.35463294, 0.60699236, -0.12445828, -0.029569108, -0.4983043, 0.44752246, -0.2340386, -0.27559096, 0.67984164, 0.51470226, -0.5285723, 0.0024457057, -0.20425095, -0.4915065, -0.3221788, -0.15558766, 0.20102327, 0.23525643, 0.28365672, 0.58687097, 0.3190138, -0.31130886, 0.053733524, -0.10361888, -0.2598206, 1.5977107, 0.60224503, 0.0074084206, 0.17191416, 0.5619663, 0.41178456, 0.27006987, -0.5354418, -0.054428957, 0.6849038, -0.024342017, 0.43103293, -0.22892271, 0.036829337, -0.103084944, -0.2021301, -0.11352237, -0.17110321, 0.76075333, -0.1755375, 0.029183118, -0.34927735, 0.22040148, -0.18136469, 0.16048056, 0.34151044, -0.048658744, 0.03941434, 0.45190382, 0.103645615, 0.10437423, -0.086864054, 0.523172, -0.59672165, -0.1225319, -0.5800122, 0.2197229, 0.49325037, 0.30607533, 0.012414166, 0.1539727, -0.60095996, 0.05142522, 0.021675617, -0.54661363, -0.0050268047, -0.507448, -0.04522115, -0.77988946, 0.10536073, 0.099219516, 0.40711993, 0.27353838, 0.1728696, -0.4171313, -1.3076599, -0.19778727, -0.23201689, 0.40729725, -0.28640944, 0.06354561, -0.3877251, -0.7938625, 0.29908186, -0.24450836, -0.22622268, 0.32792783, 0.28376722, -0.3685573, 0.031423382, -0.012464195, 0.2254249, 0.26994115, -0.19821979, -0.24086252, 0.24454598, 0.30043048, -0.627896, -0.3355214, -0.14054148, 0.50488055, -0.073988594, -0.31053177, 0.36260405, -0.56093204, 0.12066587, -0.47301888, 0.88418764, 0.09010807, -0.10899238, 0.62317103, 0.27237964, -0.604178, 0.0067386073, 0.1370205, -0.094664395, 0.3479645, 0.25092986, 0.16948108, -0.20874223, -0.54980844, -0.100548536, 0.47177002, -0.4981452, 0.1815202, -0.80878633, -0.076736815, 0.43152434, -0.43210435, -0.28010413, 0.1249095, 0.385616, -0.2984289, -0.006841246, 0.3496464, -0.33298343, 0.06344994, 0.37393335, 0.18608452, -0.10631552, -0.40111285, -0.146849, -0.04161288, -0.31621853, -0.06889858, 0.13343252, -0.11599523, 0.5377954, 0.25938663, -0.43172404, -0.7476662, -0.54316807, 0.0029651164, 0.09958581, 0.0730254, 0.22785394, 0.3276773, 0.01816153, 0.094938636, 0.71604383, -0.09648144, -0.0035640472, -0.5383972, 0.28588042, 0.7625968, -0.22359839, 0.17167832, -0.06235203, -0.32480234, -0.18599075, 0.1570872, -0.06470149, -0.029198159, 0.23251827, 0.100047514, -0.06314679, 0.6390605, -0.06232509, 0.76272035, 0.2975126, 0.15871438, 0.18222457, -0.548036, 0.23633306, -0.17981203, 0.023965335, 0.24478278, -0.21601695, -0.108217336, 0.05834005, 0.3718355, 0.0970174, 0.04476983, -0.118143275}) 240 241 return class 242 } 243 244 func TestRFJourney(t *testing.T) { 245 dirName := t.TempDir() 246 247 logger := logrus.New() 248 schemaGetter := &fakeSchemaGetter{ 249 schema: schema.Schema{Objects: &models.Schema{Classes: nil}}, 250 shardState: singleShardState(), 251 } 252 repo, err := New(logger, Config{ 253 RootPath: dirName, 254 QueryMaximumResults: 10000, 255 MaxImportGoroutinesFactor: 1, 256 QueryLimit: 20, 257 }, &fakeRemoteClient{}, &fakeNodeResolver{}, &fakeRemoteNodeClient{}, nil, nil) 258 require.Nil(t, err) 259 repo.SetSchemaGetter(schemaGetter) 260 require.Nil(t, repo.WaitForStartup(context.TODO())) 261 defer repo.Shutdown(context.Background()) 262 263 class := SetupFusionClass(t, repo, schemaGetter, logger, 1.2, 0.75) 264 idx := repo.GetIndex("MyClass") 265 require.NotNil(t, idx) 266 docId1 := uint64(1) 267 docId2 := uint64(2) 268 docId3 := uint64(3) 269 270 doc1 := &search.Result{ 271 ID: strfmt.UUID("e6f7e8b1-ac53-48eb-b6e4-cbe67396bcfa"), 272 DocID: &docId1, 273 Schema: map[string]interface{}{ 274 "title": "peanuts", 275 }, 276 Vector: []float32{0.1, 0.2, 0.3, 0.4, 0.5}, 277 Score: 0.1, 278 } 279 280 doc2 := &search.Result{ 281 ID: strfmt.UUID("2b7a8bc9-29d9-4cc8-b145-a0baf5fc231d"), 282 DocID: &docId2, 283 Schema: map[string]interface{}{ 284 "title": "journey", 285 }, 286 Vector: []float32{0.5, 0.4, 0.3, 0.3, 0.1}, 287 Score: 0.2, 288 } 289 290 doc3 := &search.Result{ 291 ID: strfmt.UUID("dddddddd-29d9-4cc8-b145-a0baf5fc231d"), 292 DocID: &docId3, 293 Schema: map[string]interface{}{ 294 "title": "alalala", 295 }, 296 Vector: []float32{0.5, 0.4, 0.3, 0.3, 0.1}, 297 Score: 0.2, 298 } 299 300 resultSet1 := []*search.Result{doc1, doc2, doc3} 301 resultSet2 := []*search.Result{doc2, doc1, doc3} 302 303 t.Run("Fusion Reciprocal", func(t *testing.T) { 304 results := hybrid.FusionRanked([]float64{0.4, 0.6}, 305 [][]*search.Result{resultSet1, resultSet2}, []string{"set1", "set2"}) 306 fmt.Println("--- Start results for Fusion Reciprocal ---") 307 for _, result := range results { 308 schema := result.Schema.(map[string]interface{}) 309 fmt.Println(schema["title"], result.ID, result.Score) 310 } 311 require.Equal(t, 3, len(results)) 312 require.Equal(t, resultSet2[0].ID, results[0].ID) 313 require.Equal(t, resultSet2[1].ID, results[1].ID) 314 require.Equal(t, resultSet2[2].ID, results[2].ID) 315 require.Equal(t, float32(0.016557377), results[0].Score) 316 require.Equal(t, float32(0.016502732), results[1].Score) 317 }) 318 319 t.Run("Fusion Reciprocal 2", func(t *testing.T) { 320 results := hybrid.FusionRanked([]float64{0.8, 0.2}, 321 [][]*search.Result{resultSet1, resultSet2}, []string{"set1", "set2"}) 322 fmt.Println("--- Start results for Fusion Reciprocal ---") 323 for _, result := range results { 324 schema := result.Schema.(map[string]interface{}) 325 fmt.Println(schema["title"], result.ID, result.Score) 326 } 327 require.Equal(t, 3, len(results)) 328 require.Equal(t, resultSet2[0].ID, results[1].ID) 329 require.Equal(t, resultSet2[1].ID, results[0].ID) 330 require.Equal(t, resultSet2[2].ID, results[2].ID) 331 require.Equal(t, float32(0.016612021), results[0].Score) 332 require.Equal(t, float32(0.016448088), results[1].Score) 333 }) 334 335 t.Run("Vector Only", func(t *testing.T) { 336 results := hybrid.FusionRanked([]float64{0.0, 1.0}, 337 [][]*search.Result{resultSet1, resultSet2}, []string{"set1", "set2"}) 338 fmt.Println("--- Start results for Fusion Reciprocal ---") 339 for _, result := range results { 340 schema := result.Schema.(map[string]interface{}) 341 fmt.Println(schema["title"], result.ID, result.Score) 342 } 343 require.Equal(t, 3, len(results)) 344 require.Equal(t, resultSet2[0].ID, results[0].ID) 345 require.Equal(t, resultSet2[1].ID, results[1].ID) 346 require.Equal(t, resultSet2[2].ID, results[2].ID) 347 require.Equal(t, float32(0.016666668), results[0].Score) 348 require.Equal(t, float32(0.016393442), results[1].Score) 349 }) 350 351 t.Run("BM25 only", func(t *testing.T) { 352 results := hybrid.FusionRanked([]float64{1.0, 0.0}, 353 [][]*search.Result{resultSet1, resultSet2}, []string{"set1", "set2"}) 354 fmt.Println("--- Start results for Fusion Reciprocal ---") 355 for _, result := range results { 356 schema := result.Schema.(map[string]interface{}) 357 fmt.Println(schema["title"], result.ID, result.Score) 358 } 359 require.Equal(t, 3, len(results)) 360 require.Equal(t, resultSet1[0].ID, results[0].ID) 361 require.Equal(t, resultSet1[1].ID, results[1].ID) 362 require.Equal(t, resultSet1[2].ID, results[2].ID) 363 require.Equal(t, float32(0.016666668), results[0].Score) 364 require.Equal(t, float32(0.016393442), results[1].Score) 365 }) 366 367 // Check basic search with one property 368 results_set_1, err := repo.VectorSearch(context.TODO(), dto.GetParams{ 369 ClassName: "MyClass", 370 SearchVector: peanutsVector(), 371 Pagination: &filters.Pagination{ 372 Offset: 0, 373 Limit: 6, 374 }, 375 }) 376 377 require.Nil(t, err) 378 results_set_2, err := repo.VectorSearch(context.TODO(), dto.GetParams{ 379 ClassName: "MyClass", 380 SearchVector: journeyVector(), 381 Pagination: &filters.Pagination{ 382 Offset: 0, 383 Limit: 6, 384 }, 385 }) 386 require.Nil(t, err) 387 388 // convert search.Result to hybrid.Result 389 var results_set_1_hybrid []*search.Result 390 for _, r := range results_set_1 { 391 // parse the last 12 digits of the id to get the uint64 392 393 results_set_1_hybrid = append(results_set_1_hybrid, &r) 394 } 395 396 var results_set_2_hybrid []*search.Result 397 for _, r := range results_set_2 { 398 results_set_2_hybrid = append(results_set_2_hybrid, &r) 399 } 400 401 res := hybrid.FusionRanked([]float64{0.2, 0.8}, [][]*search.Result{results_set_1_hybrid, results_set_2_hybrid}, []string{"set1", "set2"}) 402 fmt.Println("--- Start results for Fusion Reciprocal (", len(res), ")---") 403 for _, r := range res { 404 405 schema := r.Schema.(map[string]interface{}) 406 title := schema["title"].(string) 407 description := schema["description"].(string) 408 fmt.Printf("Result id: %v, score: %v, title: %v, description: %v, additional %+v\n", r.ID, r.Score, title, description, r.AdditionalProperties) 409 } 410 411 require.Equal(t, "00000000-0000-0000-0000-000000000000", string(res[0].ID)) 412 413 t.Run("Hybrid", func(t *testing.T) { 414 params := dto.GetParams{ 415 ClassName: "MyClass", 416 HybridSearch: &searchparams.HybridSearch{ 417 Query: "elephant", 418 Vector: elephantVector(), 419 Alpha: 0.5, 420 }, 421 Pagination: &filters.Pagination{ 422 Offset: 0, 423 Limit: 6, 424 }, 425 } 426 427 prov := modules.NewProvider() 428 prov.SetClassDefaults(class) 429 430 metrics := &fakeMetrics{} 431 log, _ := test.NewNullLogger() 432 explorer := traverser.NewExplorer(repo, log, prov, metrics, defaultConfig) 433 explorer.SetSchemaGetter(schemaGetter) 434 hybridResults, err := explorer.Hybrid(context.TODO(), params) 435 require.Nil(t, err) 436 437 fmt.Println("--- Start results for hybrid ---") 438 for _, r := range hybridResults { 439 schema := r.Schema.(map[string]interface{}) 440 title := schema["title"].(string) 441 description := schema["description"].(string) 442 fmt.Printf("Result id: %v, score: %v, title: %v, description: %v, additional %+v\n", r.ID, r.Score, title, description, r.AdditionalProperties) 443 } 444 }) 445 446 t.Run("Hybrid with negative limit", func(t *testing.T) { 447 params := dto.GetParams{ 448 ClassName: "MyClass", 449 HybridSearch: &searchparams.HybridSearch{ 450 Query: "Elephant Parade", 451 Vector: elephantVector(), 452 Alpha: 0.5, 453 }, 454 Pagination: &filters.Pagination{ 455 Offset: 0, 456 Limit: -1, 457 }, 458 } 459 460 prov := modules.NewProvider() 461 prov.SetClassDefaults(class) 462 463 metrics := &fakeMetrics{} 464 log, _ := test.NewNullLogger() 465 explorer := traverser.NewExplorer(repo, log, prov, metrics, defaultConfig) 466 explorer.SetSchemaGetter(schemaGetter) 467 hybridResults, err := explorer.Hybrid(context.TODO(), params) 468 469 fmt.Println("--- Start results for hybrid with negative limit ---") 470 for _, r := range hybridResults { 471 schema := r.Schema.(map[string]interface{}) 472 title := schema["title"].(string) 473 description := schema["description"].(string) 474 fmt.Printf("Result id: %v, score: %v, title: %v, description: %v, additional %+v\n", r.ID, r.Score, title, description, r.AdditionalProperties) 475 } 476 require.Nil(t, err) 477 require.True(t, len(hybridResults) > 0) 478 }) 479 480 t.Run("Hybrid with offset", func(t *testing.T) { 481 params := dto.GetParams{ 482 ClassName: "MyClass", 483 HybridSearch: &searchparams.HybridSearch{ 484 Query: "Elephant Parade", 485 Vector: elephantVector(), 486 Alpha: 0.5, 487 }, 488 Pagination: &filters.Pagination{ 489 Offset: 2, 490 Limit: 1, 491 }, 492 } 493 494 prov := modules.NewProvider() 495 prov.SetClassDefaults(class) 496 497 metrics := &fakeMetrics{} 498 log, _ := test.NewNullLogger() 499 explorer := traverser.NewExplorer(repo, log, prov, metrics, defaultConfig) 500 explorer.SetSchemaGetter(schemaGetter) 501 hybridResults, err := explorer.Hybrid(context.TODO(), params) 502 503 fmt.Println("--- Start results for hybrid with offset 2 ---") 504 for _, r := range hybridResults { 505 schema := r.Schema.(map[string]interface{}) 506 title := schema["title"].(string) 507 description := schema["description"].(string) 508 fmt.Printf("Result id: %v, score: %v, title: %v, description: %v, additional %+v\n", r.ID, r.Score, title, description, r.AdditionalProperties) 509 } 510 511 require.Nil(t, err) 512 require.True(t, len(hybridResults) == 1) 513 require.True(t, hybridResults[0].ID == "00000000-0000-0000-0000-000000000001") 514 }) 515 516 t.Run("Hybrid with offset", func(t *testing.T) { 517 params := dto.GetParams{ 518 ClassName: "MyClass", 519 HybridSearch: &searchparams.HybridSearch{ 520 Query: "Elephant Parade", 521 Vector: elephantVector(), 522 Alpha: 0.5, 523 }, 524 Pagination: &filters.Pagination{ 525 Offset: 4, 526 Limit: 1, 527 }, 528 } 529 530 prov := modules.NewProvider() 531 prov.SetClassDefaults(class) 532 533 metrics := &fakeMetrics{} 534 log, _ := test.NewNullLogger() 535 explorer := traverser.NewExplorer(repo, log, prov, metrics, defaultConfig) 536 explorer.SetSchemaGetter(schemaGetter) 537 hybridResults, err := explorer.Hybrid(context.TODO(), params) 538 539 fmt.Println("--- Start results for hybrid with offset 4 ---") 540 for _, r := range hybridResults { 541 schema := r.Schema.(map[string]interface{}) 542 title := schema["title"].(string) 543 description := schema["description"].(string) 544 fmt.Printf("Result id: %v, score: %v, title: %v, description: %v, additional %+v\n", r.ID, r.Score, title, description, r.AdditionalProperties) 545 } 546 547 require.Nil(t, err) 548 require.True(t, len(hybridResults) == 0) 549 }) 550 } 551 552 func TestRFJourneyWithFilters(t *testing.T) { 553 dirName := t.TempDir() 554 555 logger := logrus.New() 556 schemaGetter := &fakeSchemaGetter{ 557 schema: schema.Schema{Objects: &models.Schema{Classes: nil}}, 558 shardState: singleShardState(), 559 } 560 repo, err := New(logger, Config{ 561 RootPath: dirName, 562 QueryMaximumResults: 10000, 563 MaxImportGoroutinesFactor: 1, 564 QueryLimit: 20, 565 }, &fakeRemoteClient{}, &fakeNodeResolver{}, &fakeRemoteNodeClient{}, nil, nil) 566 require.Nil(t, err) 567 repo.SetSchemaGetter(schemaGetter) 568 require.Nil(t, repo.WaitForStartup(context.TODO())) 569 defer repo.Shutdown(context.Background()) 570 571 class := SetupFusionClass(t, repo, schemaGetter, logger, 1.2, 0.75) 572 idx := repo.GetIndex("MyClass") 573 require.NotNil(t, idx) 574 575 filter := &filters.LocalFilter{ 576 Root: &filters.Clause{ 577 Operator: filters.OperatorOr, 578 Operands: []filters.Clause{ 579 { 580 Operator: filters.OperatorEqual, 581 On: &filters.Path{ 582 Class: schema.ClassName("MyClass"), 583 Property: schema.PropertyName("title"), 584 }, 585 Value: &filters.Value{ 586 Value: "elephant", 587 Type: schema.DataTypeText, 588 }, 589 }, 590 { 591 Operator: filters.OperatorEqual, 592 On: &filters.Path{ 593 Class: schema.ClassName("MyClass"), 594 Property: schema.PropertyName("title"), 595 }, 596 Value: &filters.Value{ 597 Value: "elephant", 598 Type: schema.DataTypeText, 599 }, 600 }, 601 }, 602 }, 603 } 604 605 filter1 := &filters.LocalFilter{ 606 Root: &filters.Clause{ 607 Operator: filters.OperatorOr, 608 Operands: []filters.Clause{ 609 { 610 Operator: filters.OperatorEqual, 611 On: &filters.Path{ 612 Class: schema.ClassName("MyClass"), 613 Property: schema.PropertyName("title"), 614 }, 615 Value: &filters.Value{ 616 Value: "My", 617 Type: schema.DataTypeText, 618 }, 619 }, 620 { 621 Operator: filters.OperatorEqual, 622 On: &filters.Path{ 623 Class: schema.ClassName("MyClass"), 624 Property: schema.PropertyName("title"), 625 }, 626 Value: &filters.Value{ 627 Value: "journeys", 628 Type: schema.DataTypeText, 629 }, 630 }, 631 }, 632 }, 633 } 634 635 t.Run("Hybrid with filter - no results expected", func(t *testing.T) { 636 params := dto.GetParams{ 637 ClassName: "MyClass", 638 HybridSearch: &searchparams.HybridSearch{ 639 Query: "elephant", 640 Vector: elephantVector(), 641 Alpha: 0.5, 642 }, 643 Pagination: &filters.Pagination{ 644 Offset: 0, 645 Limit: 100, 646 }, 647 Filters: filter1, 648 } 649 650 prov := modules.NewProvider() 651 prov.SetClassDefaults(class) 652 653 metrics := &fakeMetrics{} 654 log, _ := test.NewNullLogger() 655 explorer := traverser.NewExplorer(repo, log, prov, metrics, defaultConfig) 656 explorer.SetSchemaGetter(schemaGetter) 657 hybridResults, err := explorer.Hybrid(context.TODO(), params) 658 require.Nil(t, err) 659 require.Equal(t, 0, len(hybridResults)) 660 }) 661 662 t.Run("Hybrid", func(t *testing.T) { 663 params := dto.GetParams{ 664 ClassName: "MyClass", 665 HybridSearch: &searchparams.HybridSearch{ 666 Query: "elephant", 667 Vector: elephantVector(), 668 Alpha: 0.5, 669 }, 670 Pagination: &filters.Pagination{ 671 Offset: 0, 672 Limit: -1, 673 }, 674 } 675 676 prov := modules.NewProvider() 677 prov.SetClassDefaults(class) 678 679 metrics := &fakeMetrics{} 680 log, _ := test.NewNullLogger() 681 explorer := traverser.NewExplorer(repo, log, prov, metrics, defaultConfig) 682 explorer.SetSchemaGetter(schemaGetter) 683 hybridResults, err := explorer.Hybrid(context.TODO(), params) 684 require.Nil(t, err) 685 require.Equal(t, 3, len(hybridResults)) 686 687 fmt.Println("--- Start results for hybrid vector ---") 688 for _, r := range hybridResults { 689 schema := r.Schema.(map[string]interface{}) 690 title := schema["title"].(string) 691 description := schema["description"].(string) 692 fmt.Printf("Result id: %v, score: %v, title: %v, description: %v, additional %+v\n", r.ID, r.Score, title, description, r.AdditionalProperties) 693 } 694 require.Equal(t, strfmt.UUID("00000000-0000-0000-0000-000000000002"), hybridResults[0].ID) 695 }) 696 697 t.Run("Hybrid with filter", func(t *testing.T) { 698 params := dto.GetParams{ 699 ClassName: "MyClass", 700 HybridSearch: &searchparams.HybridSearch{ 701 Query: "elephant", 702 Vector: elephantVector(), 703 Alpha: 0.5, 704 }, 705 Pagination: &filters.Pagination{ 706 Offset: 0, 707 Limit: -1, 708 }, 709 Filters: filter, 710 } 711 712 prov := modules.NewProvider() 713 prov.SetClassDefaults(class) 714 715 metrics := &fakeMetrics{} 716 log, _ := test.NewNullLogger() 717 explorer := traverser.NewExplorer(repo, log, prov, metrics, defaultConfig) 718 explorer.SetSchemaGetter(schemaGetter) 719 hybridResults, err := explorer.Hybrid(context.TODO(), params) 720 require.Nil(t, err) 721 require.Equal(t, 1, len(hybridResults)) 722 723 fmt.Println("--- Start results for hybrid with filter---") 724 for _, r := range hybridResults { 725 schema := r.Schema.(map[string]interface{}) 726 title := schema["title"].(string) 727 description := schema["description"].(string) 728 fmt.Printf("Result id: %v, score: %v, title: %v, description: %v, additional %+v\n", r.ID, r.Score, title, description, r.AdditionalProperties) 729 } 730 require.Equal(t, strfmt.UUID("00000000-0000-0000-0000-000000000002"), hybridResults[0].ID) 731 }) 732 } 733 734 func TestStability(t *testing.T) { 735 dirName := t.TempDir() 736 737 logger := logrus.New() 738 schemaGetter := &fakeSchemaGetter{ 739 schema: schema.Schema{Objects: &models.Schema{Classes: nil}}, 740 shardState: singleShardState(), 741 } 742 repo, err := New(logger, Config{ 743 RootPath: dirName, 744 QueryMaximumResults: 10000, 745 MaxImportGoroutinesFactor: 1, 746 QueryLimit: 20, 747 }, &fakeRemoteClient{}, &fakeNodeResolver{}, &fakeRemoteNodeClient{}, nil, nil) 748 require.Nil(t, err) 749 repo.SetSchemaGetter(schemaGetter) 750 require.Nil(t, repo.WaitForStartup(context.TODO())) 751 defer repo.Shutdown(context.Background()) 752 753 SetupFusionClass(t, repo, schemaGetter, logger, 1.2, 0.75) 754 idx := repo.GetIndex("MyClass") 755 require.NotNil(t, idx) 756 757 docId1 := uint64(1) 758 docId2 := uint64(2) 759 docId3 := uint64(3) 760 761 doc1 := &search.Result{ 762 ID: strfmt.UUID("e6f7e8b1-ac53-48eb-b6e4-cbe67396bcfa"), 763 DocID: &docId1, 764 Schema: map[string]interface{}{ 765 "title": "peanuts", 766 }, 767 Vector: []float32{0.1, 0.2, 0.3, 0.4, 0.5}, 768 Score: 0.1, 769 } 770 771 doc2 := &search.Result{ 772 ID: strfmt.UUID("e6f7e8b1-ac53-48eb-b6e4-cbe67396bcfb"), 773 DocID: &docId2, 774 Schema: map[string]interface{}{ 775 "title": "peanuts", 776 }, 777 Vector: []float32{0.1, 0.2, 0.3, 0.4, 0.5}, 778 Score: 0.1, 779 } 780 781 doc3 := &search.Result{ 782 ID: strfmt.UUID("e6f7e8b1-ac53-48eb-b6e4-cbe67396bcfc"), 783 DocID: &docId3, 784 Schema: map[string]interface{}{ 785 "title": "peanuts", 786 }, 787 Vector: []float32{0.1, 0.2, 0.3, 0.4, 0.5}, 788 Score: 0.1, 789 } 790 791 resultSet1 := []*search.Result{doc1, doc2, doc3} 792 resultSet2 := []*search.Result{doc2, doc1, doc3} 793 794 t.Run("Fusion Reciprocal", func(t *testing.T) { 795 results := hybrid.FusionRanked([]float64{0.4, 0.6}, 796 [][]*search.Result{resultSet1, resultSet2}, []string{"set1", "set2"}) 797 fmt.Println("--- Start results for Fusion Reciprocal ---") 798 for _, result := range results { 799 schema := result.Schema.(map[string]interface{}) 800 fmt.Println(schema["title"], result.ID, result.Score) 801 } 802 require.Equal(t, 3, len(results)) 803 require.Equal(t, resultSet2[0].ID, results[0].ID) 804 require.Equal(t, resultSet2[1].ID, results[1].ID) 805 require.Equal(t, resultSet2[2].ID, results[2].ID) 806 }) 807 } 808 809 func elephantVector() []float32 { 810 return []float32{ 811 -0.106136, -0.021716, 0.632442, 0.195315, -0.038854, -0.260533, -0.728847, -0.313725, -0.161967, 0.179243, -0.124185, 0.158839, 0.09563, -0.071267, 0.073928, -0.096735, 0.27266, -0.204127, -0.387028, -0.361406, -0.278027, 0.298766, 0.265405, 0.037477, -0.079904, -0.778953, -0.525643, -0.052346, -0.2174, 0.095746, 0.610937, 0.315672, -0.125526, 0.013475, -0.075578, -0.053183, -0.381475, 0.620278, -0.093857, 0.802608, -0.105773, -0.007902, 0.663528, 0.407708, 0.753832, 0.420718, 0.139289, -0.126864, 0.36345, -0.039222, 0.089002, 0.092151, 0.138025, 0.18881, 0.51416, -0.391045, -0.169528, -0.044023, 0.437196, -0.23917, 0.081247, -0.440846, -0.484764, 0.090495, 0.001852, -0.03441, 0.18548, -0.440182, 0.286827, -0.081451, 0.030155, -0.072746, -0.366531, 0.354118, 0.418432, -0.305682, 0.515893, -0.424999, -0.495273, 0.731375, 0.358407, -0.415989, 0.441337, -0.022167, 0.318837, -0.473018, 0.342046, -0.499794, -0.303161, -0.379234, -0.279082, -0.325648, 0.200613, -0.457396, 0.116745, 0.225836, -0.322175, -0.151425, 0.322014, 0.077097, 0.049998, -0.01005, 0.489028, -0.273297, 0.218896, -0.507729, 0.488891, -0.207774, -0.499136, 0.992803, 0.379556, -0.572352, -0.295821, -0.071392, -0.625823, -0.425159, 0.024593, 0.307965, 0.311686, 0.287844, 0.435028, 0.454474, -0.208158, -0.111947, -0.380334, -0.392014, 1.747561, 0.360315, 0.472088, 0.273835, 0.635424, 0.390057, -0.021349, -0.746944, 0.265353, 0.60709, -0.171053, 0.408823, -0.059646, 0.058306, -0.062817, -0.41064, -0.342016, -0.048077, 0.862758, -0.217101, -0.048961, -0.314094, 0.228395, -0.339353, 0.558551, 0.370054, -0.319855, 0.543137, 0.71334, 0.166296, 0.040412, -0.160482, 0.432088, -0.491292, 0.072819, -0.409627, 0.300197, 0.169077, 0.44379, 0.117131, 0.142459, -0.482226, -0.100245, 0.058273, -0.590567, -0.061971, -0.415718, -0.018105, -0.693528, -0.047609, -0.041873, 0.606186, 0.19767, -0.091001, -0.315381, -1.234111, 0.228805, -0.636861, 0.208757, -0.270024, -0.259684, -0.351592, -0.978549, 0.683986, -0.331669, -0.078729, 0.385676, 0.390955, -0.901898, -0.071451, -0.103991, 0.206379, 0.469656, 0.071528, -0.152589, 0.282268, 0.539651, -0.856463, -0.344053, -0.40572, 0.771483, -0.065611, -0.408832, 0.303948, -0.565157, 0.153293, -0.699892, 1.112725, 0.259508, 0.135771, 0.484552, 0.151274, -0.743235, 0.069811, 0.137583, 0.212661, 0.376839, 0.136164, 0.145626, -0.466645, -0.474334, -0.365033, 0.251158, -0.313904, 0.210487, -1.016155, 0.262768, 0.432895, -0.291339, -0.221825, 0.513278, 0.659038, -0.401398, -0.164522, 0.395279, -0.449811, 0.076142, 0.389243, 0.076184, 0.05539, -0.597094, -0.149824, 0.206724, -0.477001, -0.315719, 0.166689, -0.357187, 0.34429, 0.256624, -0.236781, -0.713059, -0.440255, 0.27353, -0.032257, 0.06925, 0.359134, -0.088975, 0.112507, -0.071103, 0.880417, 0.528587, 0.155656, -0.720531, 0.3068, 0.754715, 0.009366, 0.067487, -0.11898, -0.471064, -0.396507, 0.298669, 0.038283, 0.057218, -0.075818, -0.01513, -0.319236, 0.692123, -0.122985, 0.875938, 0.378184, 0.427029, 0.315545, -0.549573, 0.389602, -0.017071, 0.160122, 0.368208, 0.060474, -0.199651, 0.087829, 0.447339, 0.012265, -0.095388, -0.07034, 812 } 813 } 814 815 // "journey" 816 func journeyVector() []float32 { 817 return []float32{ 818 -0.523002, 0.14169, 0.016461, -0.069062, 0.487908, -0.024193, -0.282436, 0.004778, -0.378135, 0.396011, 0.094045, -0.06584, 0.061162, -0.600018, -0.110189, 0.244562, 0.433501, 0.303775, -0.451004, -0.453709, 0.350324, 0.2047, -0.091615, -0.282805, -0.232953, -0.215143, 0.333113, -0.126952, -0.639225, 0.101498, 0.232343, 0.58831, 0.971, 0.494446, -0.483305, -0.873438, -0.483694, 0.406465, 0.342816, 1.253387, -0.24718, -0.046063, -0.660406, 0.103386, -0.06063, 0.3422, 0.322542, 0.026074, -0.623612, 0.489793, -0.632363, 0.448922, -0.370049, 0.212377, -0.315855, 0.364525, 0.056798, 0.805679, 0.145633, 0.850648, 0.432728, -1.431841, -0.226569, -0.315194, 0.560742, 0.261859, -0.001653, -0.068738, -0.662729, -0.049259, -0.380322, -0.374194, 0.363328, 0.341796, -0.077566, 0.503337, 0.353664, -0.045754, -0.499081, 0.198603, 0.038837, -0.460198, 0.00735, -0.270993, 0.950923, -0.085815, -0.52167, -0.10439, 0.31398, -0.560229, 0.411738, -0.129033, -0.009998, 0.443882, -0.045643, -0.078445, -0.259311, -0.08337, 0.232652, -0.015912, -0.229458, -0.474973, 1.265934, -0.204483, -0.293586, -0.619023, 0.158895, -0.730671, -0.163626, 0.411716, -0.000132, 0.069014, -0.682714, 0.303234, 0.299097, -0.484469, 0.608172, -0.163785, -0.419754, -0.160745, 0.278904, 0.550542, -0.008052, 0.160397, -0.211354, -0.19755, 1.182627, 0.705073, -0.461941, -0.235292, 0.534275, -0.096419, -0.405812, -0.157745, -0.335469, 0.200545, 0.406497, -0.05341, -0.009234, -0.029925, -0.394101, -0.060133, 0.182601, 0.615583, 0.212157, 0.363921, 0.41868, -0.652791, 0.657173, -0.131662, 0.269305, 0.381748, -0.827964, -0.452596, 0.201918, 0.0673, -0.020293, 0.486942, -0.72454, -0.435051, -0.615452, -0.218852, 0.090703, -0.471036, 0.032373, 0.569953, 0.098359, -0.570767, -0.21015, -0.53019, -0.227117, 0.327978, 0.087079, -0.115037, 0.09193, -0.922884, -0.165566, -0.353596, 0.535904, -0.328579, 0.029465, -1.508702, -0.320394, -0.596324, 0.290277, -0.272515, 0.104348, 0.062855, -0.236447, 0.388958, -0.186552, -0.156253, 0.355678, 0.53834, -0.321627, 0.486004, 0.301326, 0.786779, 0.430292, -0.012458, -0.164964, -0.072951, 0.746564, 0.19136, 0.003213, 0.53479, 0.511118, -0.559153, -0.088731, -0.436206, 0.421004, 0.193043, -0.656222, 0.133223, 0.00107, 0.037087, 0.263503, 0.378593, 0.158718, -0.401664, -0.10563, -0.111221, 0.018598, -0.036396, 0.189584, -0.347721, -0.544111, -0.018158, 0.134147, -0.362431, -0.702383, -0.375221, 0.365745, 0.118082, -0.19102, -0.150732, 0.638995, 0.070662, -0.054605, 0.221755, 0.23726, -0.274418, 0.294639, 0.221177, -0.012947, 0.08444, -0.486605, -0.225034, 0.774728, 0.167609, 0.766647, 0.381622, 0.241907, -0.196452, 0.245138, -0.203225, -0.701671, 0.236662, -0.627221, 0.143006, 0.055671, 0.564561, -0.114897, -0.542244, 0.464601, 0.201577, -0.177196, -0.795015, -0.580793, -0.134996, -0.579672, -0.399042, 0.008118, -0.458077, -0.43296, 0.074138, 0.328092, 0.02934, 0.406294, 0.330677, -0.138583, -0.676608, -0.099983, -0.137182, 0.713108, 0.248643, 0.153462, 0.56039, -0.109877, 0.260655, -0.529779, -0.13416, 0.067448, -0.139468, -0.179535, 0.372629, 0.287185, 0.100582, 0.093573, -0.208796, 819 } 820 } 821 822 // "peanuts" 823 func peanutsVector() []float32 { 824 return []float32{0.563772, -0.779601, -0.18491, 0.509093, 0.080691, -0.621506, -0.127855, -0.165435, 0.57496, 0.006945, 0.452967, -0.285534, -0.129205, 0.193883, 0.092732, 0.083284, 0.714696, 0.107078, -0.398886, -0.117344, -0.387671, 0.026748, -0.562581, -0.007178, -0.354846, -0.431299, -0.788228, 0.175199, 0.914486, 0.441425, 0.089804, 0.284472, 0.106916, -0.133174, 0.399299, 0.002177, 0.551474, 0.389343, -0.016404, 0.770212, -0.219833, 0.303322, 0.127598, -0.378037, -0.172971, 0.394854, -0.424415, -0.71173, 0.080323, -0.406372, 0.398395, -0.594257, -0.418287, 0.055755, -0.352343, -0.393373, -0.732443, 0.333113, 0.420378, -0.50231, 0.261863, -0.061356, -0.180985, 0.311916, -0.180207, -0.154169, 0.371969, 0.454717, 0.320499, -0.182448, 0.087347, 0.585272, 0.136098, 0.288909, -0.229571, -0.140278, 0.229644, -0.557327, -0.110147, 0.034364, -0.021627, -0.598707, 0.221168, -0.059591, -0.203555, -0.434876, 0.209634, -0.460895, -0.345391, -0.18248, -0.24853, 0.730295, -0.295402, -0.562237, 0.255922, 0.076661, -0.713794, -0.354747, -1.109888, -0.066694, -0.195747, -0.282781, 0.459869, -0.309599, -0.002211, -0.274471, -0.003621, 0.008228, 0.011961, -0.258772, -0.210687, -0.664148, -0.257968, 0.231335, 0.530392, -0.205764, -0.621055, -0.440582, 0.080335, 0.017367, 0.880771, 0.656272, -0.713248, -0.208629, 0.095346, 0.336802, 0.888765, 0.251927, 0.066473, 0.182678, -0.220494, 0.288927, -0.602036, 0.057106, -0.594172, 0.848978, 0.751973, 0.090758, -0.732184, 0.683475, -0.075085, 0.381326, -0.076531, -0.253831, 0.10311, -0.02988, -0.043583, 0.005746, -0.460183, -0.189048, 0.25792, 0.477565, 0.391953, 0.08469, -0.10022, 0.454383, 0.170811, 0.196819, -0.760276, 0.045886, -0.743934, 0.190072, -0.216326, -0.624262, -0.22944, 0.066233, 1.024283, 0.044009, -0.373543, -0.243663, 0.204444, 0.402183, 0.043356, 0.31716, 0.302178, 0.369374, 0.36901, 0.02886, -0.26132, -0.234714, -0.791308, -0.433528, -0.098797, -0.447567, -0.124892, -0.119958, 0.31019, -0.096092, -0.259021, -0.078099, -0.178679, 0.14879, 0.106432, -0.450003, -0.294972, 0.044257, 0.402832, 0.263266, -0.309787, -0.17766, -0.399104, 0.577422, 0.30102, 0.05326, -0.271873, 0.204839, -0.019002, -0.743543, 0.739314, -0.115868, -0.504568, -0.115713, 0.042769, -0.123561, -0.057097, 0.407096, 0.770627, 0.372981, -0.321945, 0.349865, 0.437571, -0.77394, -0.090017, -0.011273, -0.468664, -0.735247, -0.745655, 0.018983, -0.248165, 0.215342, -0.136942, -0.458205, 0.4572, -0.032293, 0.654409, -0.024184, -0.392144, 0.634579, 0.222185, 0.471951, -0.063678, -0.473611, 0.796793, -0.295494, -0.157621, -0.103365, -0.564606, -0.092231, -0.517754, -0.369358, 0.137479, -0.214837, 0.11057, -0.095227, 0.726768, -0.079352, -0.065927, -0.846602, -0.317556, -0.344271, 0.201353, -0.367633, -0.004477, 0.157801, -0.249114, -0.549599, -0.147123, 0.308084, -0.175564, 0.306867, -0.071157, -0.588356, 0.450987, -0.184879, -0.096782, -0.006346, -0.017689, 0.005998, 0.200963, 0.225338, 0.189993, -1.105824, 0.520005, 0.129679, 0.198194, -0.254813, -0.127583, 0.326054, 0.009956, -0.016008, -0.483044, 0.801135, -0.517766, 0.067179, -0.372756, -0.511781, 0.058562, -0.082906, -0.28168, -0.285859} 825 } 826 827 type fakeMetrics struct { 828 mock.Mock 829 } 830 831 func (m *fakeMetrics) AddUsageDimensions(class, query, op string, dims int) { 832 m.Called(class, query, op, dims) 833 } 834 835 type fakeObjectSearcher struct{} 836 837 func (f *fakeObjectSearcher) Search(context.Context, dto.GetParams) ([]search.Result, error) { 838 return nil, nil 839 } 840 841 func (f *fakeObjectSearcher) VectorSearch(context.Context, dto.GetParams) ([]search.Result, error) { 842 return nil, nil 843 } 844 845 func (f *fakeObjectSearcher) CrossClassVectorSearch(context.Context, []float32, string, int, int, *filters.LocalFilter) ([]search.Result, error) { 846 return nil, nil 847 } 848 849 func (f *fakeObjectSearcher) Object(ctx context.Context, className string, id strfmt.UUID, props search.SelectProperties, additional additional.Properties, properties *additional.ReplicationProperties, tenant string) (*search.Result, error) { 850 return nil, nil 851 } 852 853 func (f *fakeObjectSearcher) ObjectsByID(ctx context.Context, id strfmt.UUID, props search.SelectProperties, additional additional.Properties, tenant string) (search.Results, error) { 854 return nil, nil 855 } 856 857 func (f *fakeObjectSearcher) SparseObjectSearch(ctx context.Context, params dto.GetParams) ([]*storobj.Object, []float32, error) { 858 out := []*storobj.Object{ 859 { 860 Object: models.Object{ 861 ID: "9889a225-3b28-477d-b8fc-5f6071bb4731", 862 }, 863 864 Vector: []float32{1, 2, 3}, 865 }, 866 { 867 Object: models.Object{ 868 ID: "0bcdef12-3314-442e-a4d1-e94d7c0afc3a", 869 }, 870 Vector: []float32{4, 5, 6}, 871 }, 872 } 873 lim := params.Pagination.Offset + params.Pagination.Limit 874 if lim > len(out) { 875 lim = len(out) 876 } 877 878 return out[:lim], []float32{0.008, 0.001}[:lim], nil 879 } 880 881 func (f *fakeObjectSearcher) DenseObjectSearch(ctx context.Context, class string, vector []float32, targetVector string, offset int, limit int, filters *filters.LocalFilter, additinal additional.Properties, tenant string) ([]*storobj.Object, []float32, error) { 882 out := []*storobj.Object{ 883 { 884 Object: models.Object{ 885 ID: "79a636c2-3314-442e-a4d1-e94d7c0afc3a", 886 }, 887 888 Vector: []float32{4, 5, 6}, 889 }, 890 { 891 Object: models.Object{ 892 ID: "9889a225-3b28-477d-b8fc-5f6071bb4731", 893 }, 894 895 Vector: []float32{1, 2, 3}, 896 }, 897 } 898 lim := offset + limit 899 if lim > len(out) { 900 lim = len(out) 901 } 902 903 return out[:lim], []float32{0.009, 0.008}[:lim], nil 904 } 905 906 func CopyElems[T any](list1, list2 []T, pos int) bool { 907 if len(list1) != len(list2) { 908 return false 909 } 910 if pos < 0 || pos >= len(list1) { 911 return true 912 } 913 list1[pos] = list2[pos] 914 return CopyElems(list1, list2, pos+1) 915 } 916 917 func (f *fakeObjectSearcher) ResolveReferences(ctx context.Context, objs search.Results, props search.SelectProperties, groupBy *searchparams.GroupBy, additional additional.Properties, tenant string) (search.Results, error) { 918 // Convert res1 to search.Results 919 out := make(search.Results, len(objs)) 920 CopyElems(out, objs, 0) 921 922 return out, nil 923 } 924 925 func TestHybridOverSearch(t *testing.T) { 926 dirName := t.TempDir() 927 928 logger := logrus.New() 929 schemaGetter := &fakeSchemaGetter{ 930 schema: schema.Schema{Objects: &models.Schema{Classes: nil}}, 931 shardState: singleShardState(), 932 } 933 repo, err := New(logger, Config{ 934 RootPath: dirName, 935 QueryMaximumResults: 10000, 936 MaxImportGoroutinesFactor: 1, 937 QueryLimit: 20, 938 }, &fakeRemoteClient{}, &fakeNodeResolver{}, &fakeRemoteNodeClient{}, nil, nil) 939 require.Nil(t, err) 940 repo.SetSchemaGetter(schemaGetter) 941 require.Nil(t, repo.WaitForStartup(context.TODO())) 942 defer repo.Shutdown(context.Background()) 943 944 fos := &fakeObjectSearcher{} 945 946 class := SetupFusionClass(t, repo, schemaGetter, logger, 1.2, 0.75) 947 idx := repo.GetIndex("MyClass") 948 require.NotNil(t, idx) 949 950 t.Run("Hybrid", func(t *testing.T) { 951 params := dto.GetParams{ 952 ClassName: "MyClass", 953 HybridSearch: &searchparams.HybridSearch{ 954 Query: "elephant", 955 Vector: elephantVector(), 956 Alpha: 0.5, 957 }, 958 Pagination: &filters.Pagination{ 959 Offset: 0, 960 Limit: 1, 961 }, 962 } 963 964 prov := modules.NewProvider() 965 prov.SetClassDefaults(class) 966 967 metrics := &fakeMetrics{} 968 log, _ := test.NewNullLogger() 969 explorer := traverser.NewExplorer(fos, log, prov, metrics, defaultConfig) 970 explorer.SetSchemaGetter(schemaGetter) 971 hybridResults, err := explorer.Hybrid(context.TODO(), params) 972 require.Nil(t, err) 973 require.Equal(t, 1, len(hybridResults)) 974 require.Equal(t, strfmt.UUID("9889a225-3b28-477d-b8fc-5f6071bb4731"), hybridResults[0].ID) 975 // require.Equal(t, "79a636c2-3314-442e-a4d1-e94d7c0afc3a", hybridResults[1].ID) 976 }) 977 }