github.com/ravendb/ravendb-go-client@v0.0.0-20240229102137-4474ee7aa0fa/where_token.go (about)

     1  package ravendb
     2  
     3  import (
     4  	"math"
     5  	"strings"
     6  )
     7  
     8  var _ queryToken = &whereToken{}
     9  
    10  type MethodsType = string
    11  
    12  const (
    13  	MethodsTypeCmpXChg = "CmpXChg"
    14  )
    15  
    16  type whereMethodCall struct {
    17  	methodType MethodsType
    18  	parameters []string
    19  	property   string
    20  }
    21  
    22  func newWhereMethodCall() *whereMethodCall {
    23  	return &whereMethodCall{}
    24  }
    25  
    26  type whereOptions struct {
    27  	searchOperator    SearchOperator
    28  	fromParameterName string
    29  	toParameterName   string
    30  	boost             float64
    31  	fuzzy             float64
    32  	proximity         int
    33  	exact             bool
    34  	method            *whereMethodCall
    35  	whereShape        *shapeToken
    36  	distanceErrorPct  float64
    37  }
    38  
    39  func defaultWhereOptions() *whereOptions {
    40  	return newWhereOptions()
    41  }
    42  
    43  func newWhereOptions() *whereOptions {
    44  	return &whereOptions{}
    45  }
    46  
    47  func newWhereOptionsWithExact(exact bool) *whereOptions {
    48  	return &whereOptions{
    49  		exact: exact,
    50  	}
    51  }
    52  
    53  func newWhereOptionsWithOperator(search SearchOperator) *whereOptions {
    54  	return &whereOptions{
    55  		searchOperator: search,
    56  	}
    57  }
    58  
    59  func newWhereOptionsWithTokenAndDistance(shape *shapeToken, distance float64) *whereOptions {
    60  	return &whereOptions{
    61  		whereShape:       shape,
    62  		distanceErrorPct: distance,
    63  	}
    64  }
    65  
    66  func newWhereOptionsWithMethod(methodType MethodsType, parameters []string, property string, exact bool) *whereOptions {
    67  	method := newWhereMethodCall()
    68  	method.methodType = methodType
    69  	method.parameters = parameters
    70  	method.property = property
    71  
    72  	return &whereOptions{
    73  		method: method,
    74  		exact:  exact,
    75  	}
    76  }
    77  
    78  func newWhereOptionsWithFromTo(exact bool, from string, to string) *whereOptions {
    79  	return &whereOptions{
    80  		exact:             exact,
    81  		fromParameterName: from,
    82  		toParameterName:   to,
    83  	}
    84  }
    85  
    86  type whereToken struct {
    87  	fieldName     string
    88  	whereOperator whereOperator
    89  	parameterName string
    90  	options       *whereOptions
    91  }
    92  
    93  func createWhereToken(op whereOperator, fieldName string, parameterName string) *whereToken {
    94  	return createWhereTokenWithOptions(op, fieldName, parameterName, nil)
    95  }
    96  
    97  func createWhereTokenWithOptions(op whereOperator, fieldName string, parameterName string, options *whereOptions) *whereToken {
    98  	token := &whereToken{
    99  		fieldName:     fieldName,
   100  		parameterName: parameterName,
   101  		whereOperator: op,
   102  	}
   103  	if options != nil {
   104  		token.options = options
   105  	} else {
   106  		token.options = defaultWhereOptions()
   107  	}
   108  	return token
   109  }
   110  
   111  func (t *whereToken) addAlias(alias string) {
   112  	if t.fieldName == "id()" {
   113  		return
   114  	}
   115  	t.fieldName = alias + "." + t.fieldName
   116  }
   117  
   118  func (t *whereToken) writeMethod(writer *strings.Builder) (bool, error) {
   119  	if t.options.method != nil {
   120  		switch t.options.method.methodType {
   121  		case MethodsTypeCmpXChg:
   122  			writer.WriteString("cmpxchg(")
   123  		default:
   124  			return false, newIllegalArgumentError("Unsupported method: %s", t.options.method.methodType)
   125  		}
   126  
   127  		first := true
   128  		for _, parameter := range t.options.method.parameters {
   129  			if !first {
   130  				writer.WriteString(",")
   131  			}
   132  			first = false
   133  			writer.WriteString("$")
   134  			writer.WriteString(parameter)
   135  		}
   136  		writer.WriteString(")")
   137  
   138  		if t.options.method.property != "" {
   139  			writer.WriteString(".")
   140  			writer.WriteString(t.options.method.property)
   141  		}
   142  		return true, nil
   143  	}
   144  
   145  	return false, nil
   146  }
   147  
   148  func (t *whereToken) writeTo(writer *strings.Builder) error {
   149  	options := t.options
   150  	if options.boost != 0 {
   151  		writer.WriteString("boost(")
   152  	}
   153  
   154  	if options.fuzzy != 0 {
   155  		writer.WriteString("fuzzy(")
   156  	}
   157  
   158  	if options.proximity != 0 {
   159  		writer.WriteString("proximity(")
   160  	}
   161  
   162  	if options.exact {
   163  		writer.WriteString("exact(")
   164  	}
   165  
   166  	switch t.whereOperator {
   167  	case whereOperatorSearch:
   168  		writer.WriteString("search(")
   169  	case whereOperatorLucene:
   170  		writer.WriteString("lucene(")
   171  	case whereOperatorStartsWith:
   172  		writer.WriteString("startsWith(")
   173  	case whereOperatorEndsWith:
   174  		writer.WriteString("endsWith(")
   175  	case whereOperatorExists:
   176  		writer.WriteString("exists(")
   177  	case whereOperatorSpatialWithin:
   178  		writer.WriteString("spatial.within(")
   179  	case whereOperatorSpatialContains:
   180  		writer.WriteString("spatial.contains(")
   181  	case whereOperatorSpatialDisjoint:
   182  		writer.WriteString("spatial.disjoint(")
   183  	case whereOperatorSpatialIntersects:
   184  		writer.WriteString("spatial.intersects(")
   185  	case whereOperatorRegex:
   186  		writer.WriteString("regex(")
   187  	}
   188  
   189  	if err := t.writeInnerWhere(writer); err != nil {
   190  		return err
   191  	}
   192  
   193  	if options.exact {
   194  		writer.WriteString(")")
   195  	}
   196  
   197  	if options.proximity != 0 {
   198  		writer.WriteString(", ")
   199  		builderWriteInt(writer, options.proximity)
   200  		writer.WriteString(")")
   201  	}
   202  
   203  	if options.fuzzy != 0 {
   204  		writer.WriteString(", ")
   205  		builderWriteFloat64(writer, options.fuzzy)
   206  		writer.WriteString(")")
   207  	}
   208  
   209  	if options.boost != 0 {
   210  		writer.WriteString(", ")
   211  		builderWriteFloat64(writer, options.boost)
   212  		writer.WriteString(")")
   213  	}
   214  	return nil
   215  }
   216  
   217  func (t *whereToken) writeInnerWhere(writer *strings.Builder) error {
   218  
   219  	writeQueryTokenField(writer, t.fieldName)
   220  
   221  	switch t.whereOperator {
   222  	case whereOperatorEquals:
   223  		writer.WriteString(" = ")
   224  	case whereOperatorNotEquals:
   225  		writer.WriteString(" != ")
   226  	case whereOperatorGreaterThan:
   227  		writer.WriteString(" > ")
   228  	case whereOperatorGreaterThanOrEqual:
   229  		writer.WriteString(" >= ")
   230  	case whereOperatorLessThan:
   231  		writer.WriteString(" < ")
   232  	case whereOperatorLessThanOrEqual:
   233  		writer.WriteString(" <= ")
   234  	default:
   235  		return t.specialOperator(writer)
   236  	}
   237  
   238  	ok, err := t.writeMethod(writer)
   239  	if err != nil {
   240  		return err
   241  	}
   242  	if !ok {
   243  		writer.WriteString("$")
   244  		writer.WriteString(t.parameterName)
   245  	}
   246  	return nil
   247  }
   248  
   249  func (t *whereToken) specialOperator(writer *strings.Builder) error {
   250  	options := t.options
   251  	parameterName := t.parameterName
   252  	switch t.whereOperator {
   253  	case whereOperatorIn:
   254  		writer.WriteString(" in ($")
   255  		writer.WriteString(parameterName)
   256  		writer.WriteString(")")
   257  	case whereOperatorAllIn:
   258  		writer.WriteString(" all in ($")
   259  		writer.WriteString(parameterName)
   260  		writer.WriteString(")")
   261  	case whereOperatorBetween:
   262  		writer.WriteString(" between $")
   263  		writer.WriteString(options.fromParameterName)
   264  		writer.WriteString(" and $")
   265  		writer.WriteString(options.toParameterName)
   266  	case whereOperatorSearch:
   267  		writer.WriteString(", $")
   268  		writer.WriteString(parameterName)
   269  		if options.searchOperator == SearchOperatorAnd {
   270  			writer.WriteString(", and")
   271  		}
   272  		writer.WriteString(")")
   273  	case whereOperatorLucene, whereOperatorStartsWith, whereOperatorEndsWith, whereOperatorRegex:
   274  		writer.WriteString(", $")
   275  		writer.WriteString(parameterName)
   276  		writer.WriteString(")")
   277  	case whereOperatorExists:
   278  		writer.WriteString(")")
   279  	case whereOperatorSpatialWithin, whereOperatorSpatialContains, whereOperatorSpatialDisjoint, whereOperatorSpatialIntersects:
   280  		writer.WriteString(", ")
   281  		if err := options.whereShape.writeTo(writer); err != nil {
   282  			return err
   283  		}
   284  
   285  		if math.Abs(options.distanceErrorPct-IndexingSpatialDefaultDistnaceErrorPct) > 1e-40 {
   286  			writer.WriteString(", ")
   287  			builderWriteFloat64(writer, options.distanceErrorPct)
   288  		}
   289  		writer.WriteString(")")
   290  	default:
   291  		return newIllegalStateError("unsupported operator %d", t.whereOperator)
   292  	}
   293  	return nil
   294  }