github.com/weaviate/weaviate@v1.24.6/entities/filters/filters_validator.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 filters
    13  
    14  import (
    15  	"fmt"
    16  	"strings"
    17  
    18  	"github.com/pkg/errors"
    19  	"github.com/weaviate/weaviate/entities/schema"
    20  )
    21  
    22  // string and stringArray are deprecated as of v1.19
    23  // however they are allowed in filters and considered aliases
    24  // for text and textArray
    25  var deprecatedDataTypeAliases map[schema.DataType]schema.DataType = map[schema.DataType]schema.DataType{
    26  	schema.DataTypeString:      schema.DataTypeText,
    27  	schema.DataTypeStringArray: schema.DataTypeTextArray,
    28  }
    29  
    30  func ValidateFilters(sch schema.Schema, filters *LocalFilter) error {
    31  	if filters == nil {
    32  		return errors.New("empty where")
    33  	}
    34  	cw := newClauseWrapper(filters.Root)
    35  	if err := validateClause(sch, cw); err != nil {
    36  		return err
    37  	}
    38  	cw.updateClause()
    39  	return nil
    40  }
    41  
    42  func validateClause(sch schema.Schema, cw *clauseWrapper) error {
    43  	// check if nested
    44  	if cw.getOperands() != nil {
    45  		var errs []error
    46  
    47  		for i, child := range cw.getOperands() {
    48  			if err := validateClause(sch, child); err != nil {
    49  				errs = append(errs, errors.Wrapf(err, "child operand at position %d", i))
    50  			}
    51  		}
    52  
    53  		if len(errs) > 0 {
    54  			return mergeErrs(errs)
    55  		}
    56  		return nil
    57  	}
    58  
    59  	// validate current
    60  
    61  	className := cw.getClassName()
    62  	propName := cw.getPropertyName()
    63  
    64  	if IsInternalProperty(propName) {
    65  		return validateInternalPropertyClause(propName, cw)
    66  	}
    67  
    68  	class := sch.FindClassByName(className)
    69  	if class == nil {
    70  		return errors.Errorf("class %q does not exist in schema",
    71  			className)
    72  	}
    73  
    74  	propNameTyped := string(propName)
    75  	lengthPropName, isPropLengthFilter := schema.IsPropertyLength(propNameTyped, 0)
    76  	if isPropLengthFilter {
    77  		propName = schema.PropertyName(lengthPropName)
    78  	}
    79  
    80  	prop, err := sch.GetProperty(className, propName)
    81  	if err != nil {
    82  		return err
    83  	}
    84  
    85  	if cw.getOperator() == OperatorIsNull {
    86  		if !cw.isType(schema.DataTypeBoolean) {
    87  			return errors.Errorf("operator IsNull requires a booleanValue, got %q instead",
    88  				cw.getValueNameFromType())
    89  		}
    90  		return nil
    91  	}
    92  
    93  	if isPropLengthFilter {
    94  		if !cw.isType(schema.DataTypeInt) {
    95  			return errors.Errorf("Filtering for property length requires IntValue, got %q instead",
    96  				cw.getValueNameFromType())
    97  		}
    98  		switch op := cw.getOperator(); op {
    99  		case OperatorEqual, OperatorNotEqual, OperatorGreaterThan, OperatorGreaterThanEqual,
   100  			OperatorLessThan, OperatorLessThanEqual:
   101  			// ok
   102  		default:
   103  			return errors.Errorf("Filtering for property length supports operators (not) equal and greater/less than (equal), got %q instead",
   104  				op)
   105  		}
   106  		if val := cw.getValue(); val.(int) < 0 {
   107  			return errors.Errorf("Can only filter for positive property length got %v instead", val)
   108  		}
   109  		return nil
   110  	}
   111  
   112  	if isUUIDType(prop.DataType[0]) {
   113  		return validateUUIDType(propName, cw)
   114  	}
   115  
   116  	if schema.IsRefDataType(prop.DataType) {
   117  		// bit of an edge case, directly on refs (i.e. not on a primitive prop of a
   118  		// ref) we only allow valueInt which is what's used to count references
   119  		if cw.isType(schema.DataTypeInt) {
   120  			return nil
   121  		}
   122  		return errors.Errorf("Property %q is a ref prop to the class %q. Only "+
   123  			"\"valueInt\" can be used on a ref prop directly to count the number of refs. "+
   124  			"Or did you mean to filter on a primitive prop of the referenced class? "+
   125  			"In this case make sure your path contains 3 elements in the form of "+
   126  			"[<propName>, <ClassNameOfReferencedClass>, <primitvePropOnClass>]",
   127  			propName, prop.DataType[0])
   128  	} else if baseType, ok := schema.IsArrayType(schema.DataType(prop.DataType[0])); ok {
   129  		if !cw.isType(baseType) {
   130  			return errors.Errorf("data type filter cannot use %q on type %q, use %q instead",
   131  				cw.getValueNameFromType(),
   132  				schema.DataType(prop.DataType[0]),
   133  				valueNameFromDataType(baseType))
   134  		}
   135  	} else if !cw.isType(schema.DataType(prop.DataType[0])) {
   136  		return errors.Errorf("data type filter cannot use %q on type %q, use %q instead",
   137  			cw.getValueNameFromType(),
   138  			schema.DataType(prop.DataType[0]),
   139  			valueNameFromDataType(schema.DataType(prop.DataType[0])))
   140  	}
   141  
   142  	return nil
   143  }
   144  
   145  func valueNameFromDataType(dt schema.DataType) string {
   146  	return "value" + strings.ToUpper(string(dt[0])) + string(dt[1:])
   147  }
   148  
   149  func mergeErrs(errs []error) error {
   150  	msgs := make([]string, len(errs))
   151  	for i, err := range errs {
   152  		msgs[i] = err.Error()
   153  	}
   154  
   155  	return errors.Errorf("%s", strings.Join(msgs, ", "))
   156  }
   157  
   158  func IsInternalProperty(propName schema.PropertyName) bool {
   159  	switch propName {
   160  	case InternalPropBackwardsCompatID,
   161  		InternalPropID,
   162  		InternalPropCreationTimeUnix,
   163  		InternalPropLastUpdateTimeUnix:
   164  		return true
   165  	default:
   166  		return false
   167  	}
   168  }
   169  
   170  func validateInternalPropertyClause(propName schema.PropertyName, cw *clauseWrapper) error {
   171  	switch propName {
   172  	case InternalPropBackwardsCompatID, InternalPropID:
   173  		if cw.isType(schema.DataTypeText) {
   174  			return nil
   175  		}
   176  		return errors.Errorf(
   177  			`using ["_id"] to filter by uuid: must use "valueText" to specify the id`)
   178  	case InternalPropCreationTimeUnix, InternalPropLastUpdateTimeUnix:
   179  		if cw.isType(schema.DataTypeDate) || cw.isType(schema.DataTypeText) {
   180  			return nil
   181  		}
   182  		return errors.Errorf(
   183  			`using ["%s"] to filter by timestamp: must use "valueText" or "valueDate"`, propName)
   184  	default:
   185  		return errors.Errorf("unsupported internal property: %s", propName)
   186  	}
   187  }
   188  
   189  func isUUIDType(dtString string) bool {
   190  	dt := schema.DataType(dtString)
   191  	return dt == schema.DataTypeUUID || dt == schema.DataTypeUUIDArray
   192  }
   193  
   194  func validateUUIDType(propName schema.PropertyName, cw *clauseWrapper) error {
   195  	if cw.isType(schema.DataTypeText) {
   196  		return validateUUIDOperators(propName, cw)
   197  	}
   198  
   199  	return fmt.Errorf("property %q is of type \"uuid\" or \"uuid[]\": "+
   200  		"specify uuid as string using \"valueText\"", propName)
   201  }
   202  
   203  func validateUUIDOperators(propName schema.PropertyName, cw *clauseWrapper) error {
   204  	op := cw.getOperator()
   205  
   206  	switch op {
   207  	case OperatorEqual, OperatorNotEqual, OperatorLessThan, OperatorLessThanEqual,
   208  		OperatorGreaterThan, OperatorGreaterThanEqual, ContainsAll, ContainsAny:
   209  		return nil
   210  	default:
   211  		return fmt.Errorf("operator %q cannot be used on uuid/uuid[] props", op.Name())
   212  	}
   213  }
   214  
   215  type clauseWrapper struct {
   216  	clause    *Clause
   217  	origType  schema.DataType
   218  	aliasType schema.DataType
   219  	operands  []*clauseWrapper
   220  }
   221  
   222  func newClauseWrapper(clause *Clause) *clauseWrapper {
   223  	w := &clauseWrapper{clause: clause}
   224  	if clause.Operands != nil {
   225  		w.operands = make([]*clauseWrapper, len(clause.Operands))
   226  		for i := range clause.Operands {
   227  			w.operands[i] = newClauseWrapper(&clause.Operands[i])
   228  		}
   229  	} else {
   230  		w.origType = clause.Value.Type
   231  		w.aliasType = deprecatedDataTypeAliases[clause.Value.Type]
   232  	}
   233  	return w
   234  }
   235  
   236  func (w *clauseWrapper) isType(dt schema.DataType) bool {
   237  	if w.operands != nil {
   238  		return false
   239  	}
   240  	return dt == w.origType || (dt == w.aliasType && w.aliasType != "")
   241  }
   242  
   243  func (w *clauseWrapper) getValueNameFromType() string {
   244  	return valueNameFromDataType(w.origType)
   245  }
   246  
   247  func (w *clauseWrapper) getOperands() []*clauseWrapper {
   248  	return w.operands
   249  }
   250  
   251  func (w *clauseWrapper) getOperator() Operator {
   252  	return w.clause.Operator
   253  }
   254  
   255  func (w *clauseWrapper) getValue() interface{} {
   256  	return w.clause.Value.Value
   257  }
   258  
   259  func (w *clauseWrapper) getClassName() schema.ClassName {
   260  	if w.operands != nil {
   261  		return ""
   262  	}
   263  	return w.clause.On.GetInnerMost().Class
   264  }
   265  
   266  func (w *clauseWrapper) getPropertyName() schema.PropertyName {
   267  	if w.operands != nil {
   268  		return ""
   269  	}
   270  	return w.clause.On.GetInnerMost().Property
   271  }
   272  
   273  func (w *clauseWrapper) updateClause() {
   274  	if w.operands != nil {
   275  		for i := range w.operands {
   276  			w.operands[i].updateClause()
   277  		}
   278  	} else {
   279  		if w.aliasType != "" {
   280  			w.clause.Value.Type = w.aliasType
   281  		}
   282  	}
   283  }