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  }