github.com/altipla-consulting/ravendb-go-client@v0.1.3/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 }