gopkg.in/rethinkdb/rethinkdb-go.v6@v6.2.2/query_control.go (about) 1 package rethinkdb 2 3 import ( 4 "encoding/base64" 5 "encoding/json" 6 7 "reflect" 8 9 p "gopkg.in/rethinkdb/rethinkdb-go.v6/ql2" 10 ) 11 12 // Expr converts any value to an expression and is also used by many other terms 13 // such as Insert and Update. This function can convert the following basic Go 14 // types (bool, int, uint, string, float) and even pointers, maps and structs. 15 // 16 // When evaluating structs they are encoded into a map before being sent to the 17 // server. Each exported field is added to the map unless 18 // 19 // - the field's tag is "-", or 20 // - the field is empty and its tag specifies the "omitempty" option. 21 // 22 // Each fields default name in the map is the field name but can be specified 23 // in the struct field's tag value. The "rethinkdb" key in the struct field's 24 // tag value is the key name, followed by an optional comma and options. Examples: 25 // 26 // // Field is ignored by this package. 27 // Field int `rethinkdb:"-"` 28 // // Field appears as key "myName". 29 // Field int `rethinkdb:"myName"` 30 // // Field appears as key "myName" and 31 // // the field is omitted from the object if its value is empty, 32 // // as defined above. 33 // Field int `rethinkdb:"myName,omitempty"` 34 // // Field appears as key "Field" (the default), but 35 // // the field is skipped if empty. 36 // // Note the leading comma. 37 // Field int `rethinkdb:",omitempty"` 38 func Expr(val interface{}) Term { 39 if val == nil { 40 return Term{ 41 termType: p.Term_DATUM, 42 data: nil, 43 } 44 } 45 46 switch val := val.(type) { 47 case Term: 48 return val 49 case []interface{}: 50 vals := make([]Term, len(val)) 51 for i, v := range val { 52 vals[i] = Expr(v) 53 } 54 55 return makeArray(vals) 56 case map[string]interface{}: 57 vals := make(map[string]Term, len(val)) 58 for k, v := range val { 59 vals[k] = Expr(v) 60 } 61 62 return makeObject(vals) 63 case 64 bool, 65 int, 66 int8, 67 int16, 68 int32, 69 int64, 70 uint, 71 uint8, 72 uint16, 73 uint32, 74 uint64, 75 float32, 76 float64, 77 uintptr, 78 string, 79 *bool, 80 *int, 81 *int8, 82 *int16, 83 *int32, 84 *int64, 85 *uint, 86 *uint8, 87 *uint16, 88 *uint32, 89 *uint64, 90 *float32, 91 *float64, 92 *uintptr, 93 *string: 94 return Term{ 95 termType: p.Term_DATUM, 96 data: val, 97 } 98 default: 99 // Use reflection to check for other types 100 valType := reflect.TypeOf(val) 101 valValue := reflect.ValueOf(val) 102 103 switch valType.Kind() { 104 case reflect.Func: 105 return makeFunc(val) 106 case reflect.Struct, reflect.Map, reflect.Ptr: 107 data, err := encode(val) 108 109 if err != nil || data == nil { 110 return Term{ 111 termType: p.Term_DATUM, 112 data: nil, 113 lastErr: err, 114 } 115 } 116 117 return Expr(data) 118 119 case reflect.Slice, reflect.Array: 120 // Check if slice is a byte slice 121 if valType.Elem().Kind() == reflect.Uint8 { 122 data, err := encode(val) 123 124 if err != nil || data == nil { 125 return Term{ 126 termType: p.Term_DATUM, 127 data: nil, 128 lastErr: err, 129 } 130 } 131 132 return Expr(data) 133 } 134 135 vals := make([]Term, valValue.Len()) 136 for i := 0; i < valValue.Len(); i++ { 137 vals[i] = Expr(valValue.Index(i).Interface()) 138 } 139 140 return makeArray(vals) 141 default: 142 data, err := encode(val) 143 144 if err != nil || data == nil { 145 return Term{ 146 termType: p.Term_DATUM, 147 data: nil, 148 lastErr: err, 149 } 150 } 151 152 return Term{ 153 termType: p.Term_DATUM, 154 data: data, 155 } 156 } 157 } 158 } 159 160 // JSOpts contains the optional arguments for the JS term 161 type JSOpts struct { 162 Timeout interface{} `rethinkdb:"timeout,omitempty"` 163 } 164 165 func (o JSOpts) toMap() map[string]interface{} { 166 return optArgsToMap(o) 167 } 168 169 // JS creates a JavaScript expression which is evaluated by the database when 170 // running the query. 171 func JS(jssrc interface{}, optArgs ...JSOpts) Term { 172 opts := map[string]interface{}{} 173 if len(optArgs) >= 1 { 174 opts = optArgs[0].toMap() 175 } 176 return constructRootTerm("Js", p.Term_JAVASCRIPT, []interface{}{jssrc}, opts) 177 } 178 179 // HTTPOpts contains the optional arguments for the HTTP term 180 type HTTPOpts struct { 181 // General Options 182 Timeout interface{} `rethinkdb:"timeout,omitempty"` 183 Reattempts interface{} `rethinkdb:"reattempts,omitempty"` 184 Redirects interface{} `rethinkdb:"redirect,omitempty"` 185 Verify interface{} `rethinkdb:"verify,omitempty"` 186 ResultFormat interface{} `rethinkdb:"result_format,omitempty"` 187 188 // Request Options 189 Method interface{} `rethinkdb:"method,omitempty"` 190 Auth interface{} `rethinkdb:"auth,omitempty"` 191 Params interface{} `rethinkdb:"params,omitempty"` 192 Header interface{} `rethinkdb:"header,omitempty"` 193 Data interface{} `rethinkdb:"data,omitempty"` 194 195 // Pagination 196 Page interface{} `rethinkdb:"page,omitempty"` 197 PageLimit interface{} `rethinkdb:"page_limit,omitempty"` 198 } 199 200 func (o HTTPOpts) toMap() map[string]interface{} { 201 return optArgsToMap(o) 202 } 203 204 // HTTP retrieves data from the specified URL over HTTP. The return type depends 205 // on the resultFormat option, which checks the Content-Type of the response by 206 // default. 207 func HTTP(url interface{}, optArgs ...HTTPOpts) Term { 208 opts := map[string]interface{}{} 209 if len(optArgs) >= 1 { 210 opts = optArgs[0].toMap() 211 } 212 return constructRootTerm("Http", p.Term_HTTP, []interface{}{url}, opts) 213 } 214 215 // JSON parses a JSON string on the server. 216 func JSON(args ...interface{}) Term { 217 return constructRootTerm("Json", p.Term_JSON, args, map[string]interface{}{}) 218 } 219 220 // Error throws a runtime error. If called with no arguments inside the second argument 221 // to `default`, re-throw the current error. 222 func Error(args ...interface{}) Term { 223 return constructRootTerm("Error", p.Term_ERROR, args, map[string]interface{}{}) 224 } 225 226 // Args is a special term used to splice an array of arguments into another term. 227 // This is useful when you want to call a variadic term such as GetAll with a set 228 // of arguments provided at runtime. 229 func Args(args ...interface{}) Term { 230 return constructRootTerm("Args", p.Term_ARGS, args, map[string]interface{}{}) 231 } 232 233 // Binary encapsulates binary data within a query. 234 // 235 // The type of data binary accepts depends on the client language. In Go, it 236 // expects either a byte array/slice or a bytes.Buffer. 237 // 238 // Only a limited subset of ReQL commands may be chained after binary: 239 // - coerceTo can coerce binary objects to string types 240 // - count will return the number of bytes in the object 241 // - slice will treat bytes like array indexes (i.e., slice(10,20) will return bytes 10–19) 242 // - typeOf returns PTYPE<BINARY> 243 // - info will return information on a binary object. 244 func Binary(data interface{}) Term { 245 var b []byte 246 247 switch data := data.(type) { 248 case Term: 249 return constructRootTerm("Binary", p.Term_BINARY, []interface{}{data}, map[string]interface{}{}) 250 case []byte: 251 b = data 252 default: 253 typ := reflect.TypeOf(data) 254 if typ.Kind() == reflect.Slice && typ.Elem().Kind() == reflect.Uint8 { 255 return Binary(reflect.ValueOf(data).Bytes()) 256 } else if typ.Kind() == reflect.Array && typ.Elem().Kind() == reflect.Uint8 { 257 v := reflect.ValueOf(data) 258 b = make([]byte, v.Len()) 259 for i := 0; i < v.Len(); i++ { 260 b[i] = v.Index(i).Interface().(byte) 261 } 262 return Binary(b) 263 } 264 panic("Unsupported binary type") 265 } 266 267 return binaryTerm(base64.StdEncoding.EncodeToString(b)) 268 } 269 270 func binaryTerm(data string) Term { 271 t := constructRootTerm("Binary", p.Term_BINARY, []interface{}{}, map[string]interface{}{}) 272 t.data = data 273 274 return t 275 } 276 277 // Do evaluates the expr in the context of one or more value bindings. The type of 278 // the result is the type of the value returned from expr. 279 func (t Term) Do(args ...interface{}) Term { 280 newArgs := []interface{}{} 281 newArgs = append(newArgs, funcWrap(args[len(args)-1])) 282 newArgs = append(newArgs, t) 283 newArgs = append(newArgs, args[:len(args)-1]...) 284 285 return constructRootTerm("Do", p.Term_FUNCALL, newArgs, map[string]interface{}{}) 286 } 287 288 // Do evaluates the expr in the context of one or more value bindings. The type of 289 // the result is the type of the value returned from expr. 290 func Do(args ...interface{}) Term { 291 newArgs := []interface{}{} 292 newArgs = append(newArgs, funcWrap(args[len(args)-1])) 293 newArgs = append(newArgs, args[:len(args)-1]...) 294 295 return constructRootTerm("Do", p.Term_FUNCALL, newArgs, map[string]interface{}{}) 296 } 297 298 // Branch evaluates one of two control paths based on the value of an expression. 299 // branch is effectively an if renamed due to language constraints. 300 // 301 // The type of the result is determined by the type of the branch that gets executed. 302 func Branch(args ...interface{}) Term { 303 return constructRootTerm("Branch", p.Term_BRANCH, args, map[string]interface{}{}) 304 } 305 306 // Branch evaluates one of two control paths based on the value of an expression. 307 // branch is effectively an if renamed due to language constraints. 308 // 309 // The type of the result is determined by the type of the branch that gets executed. 310 func (t Term) Branch(args ...interface{}) Term { 311 return constructMethodTerm(t, "Branch", p.Term_BRANCH, args, map[string]interface{}{}) 312 } 313 314 // ForEach loops over a sequence, evaluating the given write query for each element. 315 // 316 // It takes one argument of type `func (r.Term) interface{}`, for 317 // example clones a table: 318 // 319 // r.Table("table").ForEach(func (row r.Term) interface{} { 320 // return r.Table("new_table").Insert(row) 321 // }) 322 func (t Term) ForEach(args ...interface{}) Term { 323 return constructMethodTerm(t, "Foreach", p.Term_FOR_EACH, funcWrapArgs(args), map[string]interface{}{}) 324 } 325 326 // Range generates a stream of sequential integers in a specified range. It 327 // accepts 0, 1, or 2 arguments, all of which should be numbers. 328 func Range(args ...interface{}) Term { 329 return constructRootTerm("Range", p.Term_RANGE, args, map[string]interface{}{}) 330 } 331 332 // Default handles non-existence errors. Tries to evaluate and return its first argument. 333 // If an error related to the absence of a value is thrown in the process, or if 334 // its first argument returns null, returns its second argument. (Alternatively, 335 // the second argument may be a function which will be called with either the 336 // text of the non-existence error or null.) 337 func (t Term) Default(args ...interface{}) Term { 338 return constructMethodTerm(t, "Default", p.Term_DEFAULT, args, map[string]interface{}{}) 339 } 340 341 // CoerceTo converts a value of one type into another. 342 // 343 // You can convert: a selection, sequence, or object into an ARRAY, an array of 344 // pairs into an OBJECT, and any DATUM into a STRING. 345 func (t Term) CoerceTo(args ...interface{}) Term { 346 return constructMethodTerm(t, "CoerceTo", p.Term_COERCE_TO, args, map[string]interface{}{}) 347 } 348 349 // TypeOf gets the type of a value. 350 func TypeOf(args ...interface{}) Term { 351 return constructRootTerm("TypeOf", p.Term_TYPE_OF, args, map[string]interface{}{}) 352 } 353 354 // TypeOf gets the type of a value. 355 func (t Term) TypeOf(args ...interface{}) Term { 356 return constructMethodTerm(t, "TypeOf", p.Term_TYPE_OF, args, map[string]interface{}{}) 357 } 358 359 // ToJSON converts a ReQL value or object to a JSON string. 360 func (t Term) ToJSON() Term { 361 return constructMethodTerm(t, "ToJSON", p.Term_TO_JSON_STRING, []interface{}{}, map[string]interface{}{}) 362 } 363 364 // Info gets information about a RQL value. 365 func (t Term) Info(args ...interface{}) Term { 366 return constructMethodTerm(t, "Info", p.Term_INFO, args, map[string]interface{}{}) 367 } 368 369 // UUID returns a UUID (universally unique identifier), a string that can be used 370 // as a unique ID. If a string is passed to uuid as an argument, the UUID will be 371 // deterministic, derived from the string’s SHA-1 hash. 372 func UUID(args ...interface{}) Term { 373 return constructRootTerm("UUID", p.Term_UUID, args, map[string]interface{}{}) 374 } 375 376 // RawQuery creates a new query from a JSON string, this bypasses any encoding 377 // done by RethinkDB-go. The query should not contain the query type or any options 378 // as this should be handled using the normal driver API. 379 // 380 // THis query will only work if this is the only term in the query. 381 func RawQuery(q []byte) Term { 382 data := json.RawMessage(q) 383 return Term{ 384 name: "RawQuery", 385 rootTerm: true, 386 rawQuery: true, 387 data: &data, 388 args: []Term{ 389 Term{ 390 termType: p.Term_DATUM, 391 data: string(q), 392 }, 393 }, 394 } 395 }