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  }