github.com/weaviate/weaviate@v1.24.6/adapters/handlers/grpc/v1/filters.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 package v1 13 14 import ( 15 "fmt" 16 17 "github.com/weaviate/weaviate/entities/filters" 18 "github.com/weaviate/weaviate/entities/models" 19 "github.com/weaviate/weaviate/entities/schema" 20 pb "github.com/weaviate/weaviate/grpc/generated/protocol/v1" 21 ) 22 23 func extractFilters(filterIn *pb.Filters, scheme schema.Schema, className string) (filters.Clause, error) { 24 returnFilter := filters.Clause{} 25 if filterIn.Operator == pb.Filters_OPERATOR_AND || filterIn.Operator == pb.Filters_OPERATOR_OR { 26 if filterIn.Operator == pb.Filters_OPERATOR_AND { 27 returnFilter.Operator = filters.OperatorAnd 28 } else { 29 returnFilter.Operator = filters.OperatorOr 30 } 31 32 clauses := make([]filters.Clause, len(filterIn.Filters)) 33 for i, clause := range filterIn.Filters { 34 retClause, err := extractFilters(clause, scheme, className) 35 if err != nil { 36 return filters.Clause{}, err 37 } 38 clauses[i] = retClause 39 } 40 41 returnFilter.Operands = clauses 42 43 } else { 44 if filterIn.Target == nil && len(filterIn.On)%2 != 1 { 45 return filters.Clause{}, fmt.Errorf( 46 "paths needs to have a uneven number of components: property, class, property, ...., got %v", filterIn.On, 47 ) 48 } 49 50 switch filterIn.Operator { 51 case pb.Filters_OPERATOR_EQUAL: 52 returnFilter.Operator = filters.OperatorEqual 53 case pb.Filters_OPERATOR_NOT_EQUAL: 54 returnFilter.Operator = filters.OperatorNotEqual 55 case pb.Filters_OPERATOR_GREATER_THAN: 56 returnFilter.Operator = filters.OperatorGreaterThan 57 case pb.Filters_OPERATOR_GREATER_THAN_EQUAL: 58 returnFilter.Operator = filters.OperatorGreaterThanEqual 59 case pb.Filters_OPERATOR_LESS_THAN: 60 returnFilter.Operator = filters.OperatorLessThan 61 case pb.Filters_OPERATOR_LESS_THAN_EQUAL: 62 returnFilter.Operator = filters.OperatorLessThanEqual 63 case pb.Filters_OPERATOR_WITHIN_GEO_RANGE: 64 returnFilter.Operator = filters.OperatorWithinGeoRange 65 case pb.Filters_OPERATOR_LIKE: 66 returnFilter.Operator = filters.OperatorLike 67 case pb.Filters_OPERATOR_IS_NULL: 68 returnFilter.Operator = filters.OperatorIsNull 69 case pb.Filters_OPERATOR_CONTAINS_ANY: 70 returnFilter.Operator = filters.ContainsAny 71 case pb.Filters_OPERATOR_CONTAINS_ALL: 72 returnFilter.Operator = filters.ContainsAll 73 default: 74 return filters.Clause{}, fmt.Errorf("unknown filter operator %v", filterIn.Operator) 75 } 76 77 var dataType schema.DataType 78 if filterIn.Target == nil { 79 path, err := extractPath(scheme, className, filterIn.On) 80 if err != nil { 81 return filters.Clause{}, err 82 } 83 returnFilter.On = path 84 85 dataType, err = extractDataType(scheme, returnFilter.Operator, className, filterIn.On) 86 if err != nil { 87 return filters.Clause{}, err 88 } 89 } else { 90 path, dataType2, err := extractPathNew(scheme, className, filterIn.Target, returnFilter.Operator) 91 if err != nil { 92 return filters.Clause{}, err 93 } 94 dataType = dataType2 95 returnFilter.On = path 96 } 97 98 // datatype UUID is just a string 99 if dataType == schema.DataTypeUUID { 100 dataType = schema.DataTypeText 101 } 102 103 var val interface{} 104 switch filterIn.TestValue.(type) { 105 case *pb.Filters_ValueText: 106 val = filterIn.GetValueText() 107 case *pb.Filters_ValueInt: 108 val = int(filterIn.GetValueInt()) 109 case *pb.Filters_ValueBoolean: 110 val = filterIn.GetValueBoolean() 111 case *pb.Filters_ValueNumber: 112 val = filterIn.GetValueNumber() 113 case *pb.Filters_ValueIntArray: 114 // convert from int32 GRPC to go-int 115 valInt32 := filterIn.GetValueIntArray().Values 116 valInt := make([]int, len(valInt32)) 117 for i := 0; i < len(valInt32); i++ { 118 valInt[i] = int(valInt32[i]) 119 } 120 val = valInt 121 case *pb.Filters_ValueTextArray: 122 val = filterIn.GetValueTextArray().Values 123 case *pb.Filters_ValueNumberArray: 124 val = filterIn.GetValueNumberArray().Values 125 case *pb.Filters_ValueBooleanArray: 126 val = filterIn.GetValueBooleanArray().Values 127 case *pb.Filters_ValueGeo: 128 valueFilter := filterIn.GetValueGeo() 129 val = filters.GeoRange{ 130 GeoCoordinates: &models.GeoCoordinates{ 131 Latitude: &valueFilter.Latitude, 132 Longitude: &valueFilter.Longitude, 133 }, 134 Distance: valueFilter.Distance, 135 } 136 default: 137 return filters.Clause{}, fmt.Errorf("unknown value type %v", filterIn.TestValue) 138 } 139 140 // correct the type of value when filtering on a float/int property but sending an int/float. This is easy to 141 // get wrong 142 if number, ok := val.(int); ok && dataType == schema.DataTypeNumber { 143 val = float64(number) 144 } 145 if number, ok := val.(float64); ok && dataType == schema.DataTypeInt { 146 val = int(number) 147 if float64(int(number)) != number { 148 return filters.Clause{}, fmt.Errorf("filtering for integer, but received a floating point number %v", number) 149 } 150 } 151 152 // correct type for containsXXX in case users send int/float for a float/int array 153 if (returnFilter.Operator == filters.ContainsAll || returnFilter.Operator == filters.ContainsAny) && dataType == schema.DataTypeNumber { 154 valSlice, ok := val.([]int) 155 if ok { 156 val64 := make([]float64, len(valSlice)) 157 for i := 0; i < len(valSlice); i++ { 158 val64[i] = float64(valSlice[i]) 159 } 160 val = val64 161 } 162 } 163 164 if (returnFilter.Operator == filters.ContainsAll || returnFilter.Operator == filters.ContainsAny) && dataType == schema.DataTypeInt { 165 valSlice, ok := val.([]float64) 166 if ok { 167 valInt := make([]int, len(valSlice)) 168 for i := 0; i < len(valSlice); i++ { 169 if float64(int(valSlice[i])) != valSlice[i] { 170 return filters.Clause{}, fmt.Errorf("filtering for integer, but received a floating point number %v", valSlice[i]) 171 } 172 valInt[i] = int(valSlice[i]) 173 } 174 val = valInt 175 } 176 } 177 178 value := filters.Value{Value: val, Type: dataType} 179 returnFilter.Value = &value 180 181 } 182 return returnFilter, nil 183 } 184 185 func extractDataTypeProperty(scheme schema.Schema, operator filters.Operator, classname string, on []string) (schema.DataType, error) { 186 var dataType schema.DataType 187 if operator == filters.OperatorIsNull { 188 dataType = schema.DataTypeBoolean 189 } else if len(on) > 1 { 190 propToCheck := on[len(on)-1] 191 _, isPropLengthFilter := schema.IsPropertyLength(propToCheck, 0) 192 if isPropLengthFilter { 193 return schema.DataTypeInt, nil 194 } 195 196 classOfProp := on[len(on)-2] 197 prop, err := scheme.GetProperty(schema.ClassName(classOfProp), schema.PropertyName(propToCheck)) 198 if err != nil { 199 return dataType, err 200 } 201 dataType = schema.DataType(prop.DataType[0]) 202 } else { 203 propToCheck := on[0] 204 _, isPropLengthFilter := schema.IsPropertyLength(propToCheck, 0) 205 if isPropLengthFilter { 206 return schema.DataTypeInt, nil 207 } 208 209 prop, err := scheme.GetProperty(schema.ClassName(classname), schema.PropertyName(propToCheck)) 210 if err != nil { 211 return dataType, err 212 } 213 if schema.IsRefDataType(prop.DataType) { 214 // This is a filter on a reference property without a path so is counting 215 // the number of references. Needs schema.DataTypeInt: entities/filters/filters_validator.go#L116-L127 216 return schema.DataTypeInt, nil 217 } 218 dataType = schema.DataType(prop.DataType[0]) 219 } 220 221 // searches on array datatypes always need the base-type as value-type 222 if baseType, isArray := schema.IsArrayType(dataType); isArray { 223 return baseType, nil 224 } 225 return dataType, nil 226 } 227 228 func extractDataType(scheme schema.Schema, operator filters.Operator, classname string, on []string) (schema.DataType, error) { 229 propToFilterOn := on[len(on)-1] 230 if propToFilterOn == filters.InternalPropID { 231 return schema.DataTypeText, nil 232 } else if propToFilterOn == filters.InternalPropCreationTimeUnix || propToFilterOn == filters.InternalPropLastUpdateTimeUnix { 233 return schema.DataTypeDate, nil 234 } else { 235 return extractDataTypeProperty(scheme, operator, classname, on) 236 } 237 } 238 239 func extractPath(scheme schema.Schema, className string, on []string) (*filters.Path, error) { 240 if len(on) > 1 { 241 var err error 242 child, err := extractPath(scheme, on[1], on[2:]) 243 if err != nil { 244 return nil, err 245 } 246 return &filters.Path{Class: schema.ClassName(className), Property: schema.PropertyName(on[0]), Child: child}, nil 247 248 } 249 return &filters.Path{Class: schema.ClassName(className), Property: schema.PropertyName(on[0]), Child: nil}, nil 250 } 251 252 func extractPathNew(scheme schema.Schema, className string, target *pb.FilterTarget, operator filters.Operator) (*filters.Path, schema.DataType, error) { 253 class := scheme.GetClass(schema.ClassName(className)) 254 switch target.Target.(type) { 255 case *pb.FilterTarget_Property: 256 dt, err := extractDataType(scheme, operator, className, []string{target.GetProperty()}) 257 if err != nil { 258 return nil, "", err 259 } 260 return &filters.Path{Class: schema.ClassName(className), Property: schema.PropertyName(target.GetProperty()), Child: nil}, dt, nil 261 case *pb.FilterTarget_SingleTarget: 262 singleTarget := target.GetSingleTarget() 263 normalizedRefPropName := schema.LowercaseFirstLetter(singleTarget.On) 264 refProp, err := schema.GetPropertyByName(class, normalizedRefPropName) 265 if err != nil { 266 return nil, "", err 267 } 268 if len(refProp.DataType) != 1 { 269 return nil, "", fmt.Errorf("expected reference property with a single target, got %v for %v ", refProp.DataType, refProp.Name) 270 } 271 child, property, err := extractPathNew(scheme, refProp.DataType[0], singleTarget.Target, operator) 272 if err != nil { 273 return nil, "", err 274 } 275 return &filters.Path{Class: schema.ClassName(className), Property: schema.PropertyName(normalizedRefPropName), Child: child}, property, nil 276 case *pb.FilterTarget_MultiTarget: 277 multiTarget := target.GetMultiTarget() 278 child, property, err := extractPathNew(scheme, multiTarget.TargetCollection, multiTarget.Target, operator) 279 if err != nil { 280 return nil, "", err 281 } 282 return &filters.Path{Class: schema.ClassName(className), Property: schema.PropertyName(schema.LowercaseFirstLetter(multiTarget.On)), Child: child}, property, nil 283 case *pb.FilterTarget_Count: 284 count := target.GetCount() 285 return &filters.Path{Class: schema.ClassName(className), Property: schema.PropertyName(schema.LowercaseFirstLetter(count.On)), Child: nil}, schema.DataTypeInt, nil 286 default: 287 return nil, "", fmt.Errorf("unknown target type %v", target) 288 } 289 }