├── .gitignore ├── DEVELOP ├── LICENSE ├── README.md ├── basic_test.go ├── conn.go ├── datum.go ├── doc.go ├── error.go ├── protobuf.go ├── ql2 ├── ql2.pb.go └── ql2.proto ├── query.go ├── responses.go ├── rows.go ├── session.go └── utils.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | -------------------------------------------------------------------------------- /DEVELOP: -------------------------------------------------------------------------------- 1 | how to compile ql2.go: 2 | brew install mercurial 3 | go get code.google.com/p/goprotobuf/{proto,protoc-gen-go} 4 | brew install protobuf 5 | protoc --go_out=. ql2.proto 6 | 7 | testing: 8 | go get launchpad.net/gocheck 9 | go test 10 | for specific test function: 11 | go test -gocheck.f TestGroups 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Christopher Hesse. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Christopher Hesse nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | rethinkgo 2 | ========= 3 | 4 | NOTE: No future work is planned on this driver. See @dancannon's [gorethink](https://github.com/dancannon/gorethink) instead. 5 | ========= 6 | 7 | [Go language](http://golang.org/) driver for [RethinkDB](http://www.rethinkdb.com/) made by [Christopher Hesse](http://www.christopherhesse.com/) 8 | 9 | Current supported RethinkDB version: 1.7.1 10 | 11 | Installation 12 | ============ 13 | 14 | go get -u github.com/christopherhesse/rethinkgo 15 | 16 | If you do not have the [goprotobuf](https://code.google.com/p/goprotobuf/) runtime installed, it is required: 17 | 18 | brew install mercurial # if you do not have mercurial installed 19 | go get code.google.com/p/goprotobuf/{proto,protoc-gen-go} 20 | 21 | 22 | Example 23 | =================== 24 | 25 | package main 26 | 27 | import ( 28 | "fmt" 29 | r "github.com/christopherhesse/rethinkgo" 30 | ) 31 | 32 | type Employee struct { 33 | FirstName string 34 | LastName string 35 | Job string 36 | Id string `json:"id,omitempty"` // (will appear in json as "id", and not be sent if empty) 37 | } 38 | 39 | func main() { 40 | // To access a RethinkDB database, you connect to it with the Connect function 41 | session, err := r.Connect("localhost:28015", "company_info") 42 | if err != nil { 43 | fmt.Println("error connecting:", err) 44 | return 45 | } 46 | 47 | var response []Employee 48 | // Using .All(), we can read the entire response into a slice, without iteration 49 | err = r.Table("employees").Run(session).All(&response) 50 | if err != nil { 51 | fmt.Println("err:", err) 52 | } else { 53 | fmt.Println("response:", response) 54 | } 55 | 56 | // If we want to iterate over each result individually, we can use the rows 57 | // object as an iterator 58 | rows := r.Table("employees").Run(session) 59 | for rows.Next() { 60 | var row Employee 61 | if err = rows.Scan(&row); err != nil { 62 | fmt.Println("err:", err) 63 | break 64 | } 65 | fmt.Println("row:", row) 66 | } 67 | if err = rows.Err(); err != nil { 68 | fmt.Println("err:", err) 69 | } 70 | } 71 | 72 | 73 | Overview 74 | ======== 75 | 76 | The Go driver is most similar to the [official Javascript driver](http://www.rethinkdb.com/api/#js). 77 | 78 | Most of the functions have the same names as in the Javascript driver, only with the first letter capitalized. See [Go Driver Documentation](http://godoc.org/github.com/christopherhesse/rethinkgo) for examples and documentation for each function. 79 | 80 | To use RethinkDB with this driver, you build a query using r.Table(), for example, and then call query.Run(session) to execute the query on the server and return an iterator for the results. 81 | 82 | There are 3 convenience functions on the iterator if you don't want to iterate over the results, .One(&result) for a query that returns a single result, .All(&results) for multiple results, and .Exec() for a query that returns an empty response (for instance, r.TableCreate(string)) or to ignore the response. 83 | 84 | The important types are r.Exp (for RethinkDB expressions), r.Query (interface for all queries, including expressions), r.List (used for Arrays, an alias for []interface{}), and r.Map (used for Objects, an alias for map[string]interface{}). 85 | 86 | The function r.Expr() can take arbitrary structs and uses the "json" module to serialize them. This means that structs can use the json.Marshaler interface (define a method MarshalJSON() on the struct). Also, struct fields can also be annotated to specify their JSON equivalents: 87 | 88 | type MyStruct struct { 89 | MyField int `json:"my_field"` // (will appear in json as my_field) 90 | OtherField int // (will appear in json as OtherField) 91 | } 92 | 93 | See the [json docs](http://golang.org/pkg/encoding/json/) for more information. 94 | 95 | Changelog 96 | ========= 97 | 98 | Changes for RethinkDB 1.7.1: 99 | * Added Json() and a couple of changes, see http://rethinkdb.com/blog/1.7-release/ 100 | 101 | Changes for RethinkDB 1.6.1: 102 | * Added a number of new functions, auth support etc, see http://rethinkdb.com/blog/1.6-release/ 103 | * Removed connection pooling (this didn't seem that helpful and may have caused problems for some users) you no longer need to use rows.Close(), and sessions should not be shared between goroutines. Either make a new connection inside each goroutine or create a pool (with some channels or maybe a sync.Mutex). 104 | 105 | Differences from official RethinkDB drivers 106 | =========================================== 107 | 108 | * When running queries, getting results is a little different from the more dynamic languages. .Run(*Session) returns a *Rows iterator object with the following methods that put the response into a variable `dest`, here's when you should use the different methods: 109 | * You want to iterate through the results of the query individually: rows.Next() and rows.Scan(&dest) 110 | * The query always returns a single response: .One(&dest) 111 | * The query returns a list of responses: .All(&dest) 112 | * The query returns an empty response (or you want to ignore the result): .Exec() 113 | * No errors are generated when creating queries, only when running them, so Table(string) returns only an Exp instance, but sess.Run(Query).Err() will tell you if your query could not be serialized for the server. To check just the serialization of the query before calling .Run(*Session), use .Check(*Session) 114 | * Go does not have optional args, most optional args are either require or separate methods. 115 | * .Atomic(bool), .Overwrite(bool), .UseOutdated(bool) are methods on any Table() or other Exp (will apply to all tables, inserts, etc that have already been specified) 116 | * .TableCreate(string) has a variant called TableCreateWithSpec(TableSpec) which takes a TableSpec instance specifying the parameters for the table 117 | * There's no r(attributeName) or row[attributeName] function call / item indexing to get attributes of the "current" row or a specific row respectively. Instead, there is a .Attr() method on the global "Row" object (r.Row) and any row Expressions that can be used to access attributes. Examples: 118 | 119 | r.Table("marvel").OuterJoin(r.Table("dc"), 120 | func(marvel, dc r.Exp) interface{} { 121 | return marvel.Attr("strength").Eq(dc.Attr("strength")) 122 | }) 123 | 124 | r.Table("marvel").Map(r.Row.Attr("strength").Mul(2)) 125 | -------------------------------------------------------------------------------- /basic_test.go: -------------------------------------------------------------------------------- 1 | package rethinkgo 2 | 3 | // Based off of RethinkDB's javascript test.js 4 | // https://github.com/rethinkdb/rethinkdb/blob/next/drivers/javascript/rethinkdb/test.js 5 | 6 | import ( 7 | "encoding/json" 8 | "fmt" 9 | test "launchpad.net/gocheck" 10 | "testing" 11 | ) 12 | 13 | // Global expressions used in tests 14 | var arr = Expr(List{1, 2, 3, 4, 5, 6}) 15 | var tobj = Expr(Map{"a": 1, "b": 2, "c": 3}) 16 | var tbl = Table("table1") 17 | var tbl2 = Table("table2") 18 | var tbl3 = Table("table3") 19 | var tbl4 = Table("table4") 20 | var gobj = Expr(List{ 21 | Map{"g1": 1, "g2": 1, "num": 0}, 22 | Map{"g1": 1, "g2": 2, "num": 5}, 23 | Map{"g1": 1, "g2": 2, "num": 10}, 24 | Map{"g1": 2, "g2": 3, "num": 0}, 25 | Map{"g1": 2, "g2": 3, "num": 100}, 26 | }) 27 | var j1 = Table("joins1") 28 | var j2 = Table("joins2") 29 | var j3 = Table("joins3") 30 | var docs = []Map{ 31 | Map{"id": 0}, 32 | Map{"id": 1}, 33 | Map{"id": 2}, 34 | Map{"id": 3}, 35 | Map{"id": 4}, 36 | Map{"id": 5}, 37 | Map{"id": 6}, 38 | Map{"id": 7}, 39 | Map{"id": 8}, 40 | Map{"id": 9}, 41 | } 42 | var session *Session 43 | 44 | // Hook up gocheck into the gotest runner. 45 | func Test(t *testing.T) { test.TestingT(t) } 46 | 47 | type RethinkSuite struct{} 48 | 49 | func (s *RethinkSuite) SetUpSuite(c *test.C) { 50 | SetDebug(true) 51 | var err error 52 | session, err = Connect("localhost:28015", "test") 53 | c.Assert(err, test.IsNil) 54 | 55 | resetDatabase(c) 56 | } 57 | 58 | func (s *RethinkSuite) TearDownSuite(c *test.C) { 59 | session.Close() 60 | } 61 | 62 | func resetDatabase(c *test.C) { 63 | // Drop the test database, then re-create it with some test data 64 | DbDrop("test").Run(session) 65 | err := DbCreate("test").Run(session).Err() 66 | c.Assert(err, test.IsNil) 67 | 68 | err = Db("test").TableCreate("table1").Run(session).Err() 69 | c.Assert(err, test.IsNil) 70 | 71 | pair := ExpectPair{tbl.Insert(Map{"id": 0, "num": 20}), MatchMap{"inserted": 1}} 72 | runQuery(c, pair) 73 | 74 | var others []Map 75 | for i := 1; i < 10; i++ { 76 | others = append(others, Map{"id": i, "num": 20 - i}) 77 | } 78 | pair = ExpectPair{tbl.Insert(others), MatchMap{"inserted": 9}} 79 | runQuery(c, pair) 80 | 81 | err = Db("test").TableCreate("table2").Run(session).Err() 82 | c.Assert(err, test.IsNil) 83 | 84 | pair = ExpectPair{tbl2.Insert(List{ 85 | Map{"id": 20, "name": "bob"}, 86 | Map{"id": 19, "name": "tom"}, 87 | Map{"id": 18, "name": "joe"}, 88 | }), MatchMap{"inserted": 3}} 89 | runQuery(c, pair) 90 | 91 | // det 92 | err = Db("test").TableCreate("table3").Run(session).Err() 93 | c.Assert(err, test.IsNil) 94 | 95 | err = tbl3.Insert(docs).Run(session).Err() 96 | c.Assert(err, test.IsNil) 97 | 98 | err = Db("test").TableCreate("table4").Run(session).Err() 99 | c.Assert(err, test.IsNil) 100 | 101 | // joins tables 102 | s1 := List{ 103 | Map{"id": 0, "name": "bob"}, 104 | Map{"id": 1, "name": "tom"}, 105 | Map{"id": 2, "name": "joe"}, 106 | } 107 | s2 := List{ 108 | Map{"id": 0, "title": "goof"}, 109 | Map{"id": 2, "title": "lmoe"}, 110 | } 111 | s3 := List{ 112 | Map{"it": 0, "title": "goof"}, 113 | Map{"it": 2, "title": "lmoe"}, 114 | } 115 | 116 | Db("test").TableCreate("joins1").Run(session) 117 | j1.Insert(s1).Run(session) 118 | Db("test").TableCreate("joins2").Run(session) 119 | j2.Insert(s2).Run(session) 120 | spec := TableSpec{Name: "joins3", PrimaryKey: "it"} 121 | Db("test").TableCreateWithSpec(spec).Run(session) 122 | j3.Insert(s3).Run(session) 123 | } 124 | 125 | var _ = test.Suite(&RethinkSuite{}) 126 | 127 | type jsonChecker struct { 128 | info *test.CheckerInfo 129 | } 130 | 131 | func (j jsonChecker) Info() *test.CheckerInfo { 132 | return j.info 133 | } 134 | 135 | func (j jsonChecker) Check(params []interface{}, names []string) (result bool, error string) { 136 | var jsonParams []interface{} 137 | for _, param := range params { 138 | jsonParam, err := json.Marshal(param) 139 | if err != nil { 140 | return false, err.Error() 141 | } 142 | jsonParams = append(jsonParams, jsonParam) 143 | } 144 | return test.DeepEquals.Check(jsonParams, names) 145 | } 146 | 147 | // JsonEquals compares two interface{} objects by converting them to JSON and 148 | // seeing if the strings match 149 | var JsonEquals = &jsonChecker{ 150 | &test.CheckerInfo{Name: "JsonEquals", Params: []string{"obtained", "expected"}}, 151 | } 152 | 153 | type ExpectPair struct { 154 | query Exp 155 | expected interface{} 156 | } 157 | 158 | type MatchMap map[string]interface{} 159 | 160 | // Used to indicate that we expect an error from the server 161 | type ErrorResponse struct{} 162 | 163 | func runQuery(c *test.C, pair ExpectPair) { 164 | fmt.Println("query:", pair.query) 165 | var result interface{} 166 | err := pair.query.Run(session).One(&result) 167 | fmt.Printf("result: %v %T %v\n", result, result, err) 168 | _, ok := pair.expected.(ErrorResponse) 169 | if ok { 170 | c.Assert(err, test.NotNil) 171 | return 172 | } else { 173 | c.Assert(err, test.IsNil) 174 | } 175 | 176 | // when reading in a number into an interface{}, the json library seems to 177 | // choose float64 as the type to use 178 | // since c.Assert() compares the types directly, we need to make sure to pass 179 | // it a float64 if we have a number 180 | switch v := pair.expected.(type) { 181 | case int: 182 | c.Assert(result, test.Equals, float64(v)) 183 | case Map, List: 184 | // Even if v is converted with toObject(), the maps don't seem to compare 185 | // correctly with gocheck, and the gocheck api docs don't mention maps, so 186 | // just convert to a []byte with json, then compare the bytes 187 | v1, _ := json.Marshal(result) 188 | v2, _ := json.Marshal(pair.expected) 189 | fmt.Println("out:", string(v1), string(v2)) 190 | c.Assert(result, JsonEquals, pair.expected) 191 | case MatchMap: 192 | // In some cases we want to match against a map, but only against those keys 193 | // that appear in the map, not against all keys in the result, the MatchMap 194 | // type does this. 195 | resultMap := result.(map[string]interface{}) 196 | filteredResult := map[string]interface{}{} 197 | for key, _ := range v { 198 | filteredResult[key] = resultMap[key] 199 | } 200 | c.Assert(filteredResult, JsonEquals, pair.expected) 201 | default: 202 | c.Assert(result, test.Equals, pair.expected) 203 | } 204 | } 205 | 206 | var testGroups = map[string][]ExpectPair{ 207 | "basic": { 208 | {Expr(1), 1}, 209 | {Expr(true), true}, 210 | {Expr("bob"), "bob"}, 211 | {Expr(nil), nil}, 212 | }, 213 | "arith": { 214 | {Expr(1).Add(2), 3}, 215 | {Expr(1).Sub(2), -1}, 216 | {Expr(5).Mul(8), 40}, 217 | {Expr(8).Div(2), 4}, 218 | {Expr(7).Mod(2), 1}, 219 | }, 220 | "compare": { 221 | {Expr(1).Eq(1), true}, 222 | {Expr(1).Eq(2), false}, 223 | {Expr(1).Lt(2), true}, 224 | {Expr(8).Lt(-4), false}, 225 | {Expr(8).Le(8), true}, 226 | {Expr(8).Gt(7), true}, 227 | {Expr(8).Gt(8), false}, 228 | {Expr(8).Ge(8), true}, 229 | }, 230 | "bool": { 231 | {Expr(true).Not(), false}, 232 | {Expr(true).And(true), true}, 233 | {Expr(true).And(false), false}, 234 | {Expr(true).Or(false), true}, 235 | // DeMorgan's 236 | {Expr(true).And(false).Eq(Expr(true).Not().Or(Expr(false).Not()).Not()), true}, 237 | }, 238 | "slices": { 239 | {arr.Nth(0), 1}, 240 | {arr.Count(), 6}, 241 | {arr.Limit(5).Count(), 5}, 242 | {arr.Skip(4).Count(), 2}, 243 | {arr.Skip(4).Nth(0), 5}, 244 | {arr.Slice(1, 4).Count(), 4}, 245 | {arr.Nth(2), 3}, 246 | }, 247 | "append": { 248 | {arr.Append(7).Nth(6), 7}, 249 | }, 250 | "merge": { 251 | {Expr(Map{"a": 1}).Merge(Map{"b": 2}), Map{"a": 1, "b": 2}}, 252 | }, 253 | "if": { 254 | {Branch(true, 1, 2), 1}, 255 | {Branch(false, 1, 2), 2}, 256 | {Branch(Expr(2).Mul(8).Ge(Expr(30).Div(2)), Expr(8).Div(2), Expr(9).Div(3)), 4}, 257 | }, 258 | "distinct": { 259 | {Expr(List{1, 1, 2, 3, 3, 3, 3}).Distinct(), List{1, 2, 3}}, 260 | }, 261 | "map": { 262 | {arr.Map(func(a Exp) Exp { 263 | return a.Add(1) 264 | }).Nth(2), 265 | 4, 266 | }, 267 | }, 268 | "reduce": { 269 | {arr.Reduce(func(a, b Exp) Exp { 270 | return a.Add(b) 271 | }, 0), 272 | 21, 273 | }, 274 | }, 275 | "filter": { 276 | {arr.Filter(func(val Exp) Exp { 277 | return val.Lt(3) 278 | }).Count(), 279 | 2, 280 | }, 281 | }, 282 | "has_fields": { 283 | {tobj.HasFields("a"), true}, 284 | {tobj.HasFields("d"), false}, 285 | {tobj.HasFields("a", "c"), true}, 286 | {tobj.HasFields("a", "d"), false}, 287 | }, 288 | "getfield": { 289 | {tobj.Attr("a"), 1}, 290 | {tobj.Attr("b"), 2}, 291 | {tobj.Attr("c"), 3}, 292 | }, 293 | "orderby": { 294 | {tbl.OrderBy("num").Nth(2), Map{"id": 7, "num": 13}}, 295 | {tbl.OrderBy("num").Nth(2).Pluck("num"), Map{"num": 13}}, 296 | {tbl.OrderBy(Asc("num")).Nth(2), Map{"id": 7, "num": 13}}, 297 | {tbl.OrderBy(Asc("num")).Nth(2).Pluck("num"), Map{"num": 13}}, 298 | {tbl.OrderBy(Desc("num")).Nth(2), Map{"id": 2, "num": 18}}, 299 | {tbl.OrderBy(Desc("num")).Nth(2).Pluck("num"), Map{"num": 18}}, 300 | }, 301 | "pluck": { 302 | {tobj.Pluck("a"), Map{"a": 1}}, 303 | {tobj.Pluck("a", "b"), Map{"a": 1, "b": 2}}, 304 | {tbl.OrderBy("num").Pluck("num").Nth(0), Map{"num": 11}}, 305 | }, 306 | "without": { 307 | {tobj.Without("a"), Map{"b": 2, "c": 3}}, 308 | {tobj.Without("a", "b"), Map{"c": 3}}, 309 | {tbl.OrderBy("num").Without("num").Nth(0), Map{"id": 9}}, 310 | }, 311 | "union": { 312 | {Expr(List{1, 2, 3}).Union(List{4, 5, 6}), List{1, 2, 3, 4, 5, 6}}, 313 | {tbl.Union(tbl).Count().Eq(tbl.Count().Mul(2)), true}, 314 | }, 315 | "tablefilter": { 316 | {tbl.Filter(func(row Exp) Exp { 317 | return row.Attr("num").Gt(16) 318 | }).Count(), 319 | 4, 320 | }, 321 | {tbl.Filter(Row.Attr("num").Gt(16)).Count(), 4}, 322 | {tbl.Filter(Map{"num": 16}).Nth(0), Map{"id": 4, "num": 16}}, 323 | {tbl.Filter(Map{"num": Expr(20).Sub(Row.Attr("id"))}).Count(), 10}, 324 | }, 325 | "tablemap": { 326 | {tbl.OrderBy("num").Map(Row.Attr("num")).Nth(2), 13}, 327 | }, 328 | "tablereduce": { 329 | {tbl.Map(Row.Attr("num")).Reduce(func(a, b Exp) Exp { return b.Add(a) }, 0), 155}, 330 | }, 331 | "tablechain": { 332 | {tbl.Filter(func(row Exp) Exp { 333 | return Row.Attr("num").Gt(16) 334 | }).Count(), 335 | 4, 336 | }, 337 | 338 | {tbl.Map(func(row Exp) Exp { 339 | return Row.Attr("num").Add(2) 340 | }).Filter(func(val Exp) Exp { 341 | return val.Gt(16) 342 | }).Count(), 343 | 6, 344 | }, 345 | 346 | {tbl.Filter(func(row Exp) Exp { 347 | return Row.Attr("num").Gt(16) 348 | }).Map(func(row Exp) Exp { 349 | return row.Attr("num").Mul(4) 350 | }).Reduce(func(acc, val Exp) Exp { 351 | return acc.Add(val) 352 | }, 0), 353 | 296, 354 | }, 355 | }, 356 | "between": { 357 | {tbl.Between("id", 2, 3).Count(), 2}, 358 | {tbl.Between("id", 2, 3).OrderBy("id").Nth(0), Map{"id": 2, "num": 18}}, 359 | }, 360 | "groupedmapreduce": { 361 | {tbl.GroupedMapReduce( 362 | func(row Exp) Exp { 363 | return Branch(row.Attr("id").Lt(5), 0, 1) 364 | }, 365 | func(row Exp) Exp { 366 | return row.Attr("num") 367 | }, 368 | func(acc, num Exp) Exp { 369 | return acc.Add(num) 370 | }, 371 | 0, 372 | ), 373 | List{ 374 | Map{"group": 0, "reduction": 90}, 375 | Map{"group": 1, "reduction": 65}, 376 | }, 377 | }, 378 | }, 379 | "groupby": { 380 | {gobj.GroupBy("g1", Avg("num")), 381 | List{ 382 | Map{"group": List{1}, "reduction": 5}, 383 | Map{"group": List{2}, "reduction": 50}, 384 | }, 385 | }, 386 | {gobj.GroupBy("g1", Count()), 387 | List{ 388 | Map{"group": List{1}, "reduction": 3}, 389 | Map{"group": List{2}, "reduction": 2}, 390 | }, 391 | }, 392 | {gobj.GroupBy("g1", Sum("num")), 393 | List{ 394 | Map{"group": List{1}, "reduction": 15}, 395 | Map{"group": List{2}, "reduction": 100}, 396 | }, 397 | }, 398 | {gobj.GroupBy([]string{"g1", "g2"}, Avg("num")), 399 | List{ 400 | Map{"group": List{1, 1}, "reduction": 0}, 401 | Map{"group": List{1, 2}, "reduction": 7.5}, 402 | Map{"group": List{2, 3}, "reduction": 50}, 403 | }, 404 | }, 405 | }, 406 | "concatmap": { 407 | {tbl.ConcatMap(func(row Exp) Exp {return Expr(List{1, 2})}).Count(), 20}, 408 | }, 409 | "update": { 410 | {tbl.Filter(func(row Exp) Exp { 411 | return row.Attr("id").Ge(5) 412 | }).Update(func(a Exp) Exp { 413 | return a.Merge(Map{"replaced": true}) 414 | }), 415 | MatchMap{"replaced": 5}, 416 | }, 417 | {tbl.Filter(func(row Exp) Exp { 418 | return row.Attr("id").Lt(5) 419 | }).Update(func(a Exp) Exp { 420 | return a.Merge(Map{"replaced": true}) 421 | }), 422 | MatchMap{"replaced": 5}, 423 | }, 424 | {tbl.Filter(func(row Exp) Exp { 425 | return row.Attr("replaced").Eq(true) 426 | }).Count(), 10}, 427 | }, 428 | "pointupdate": { 429 | {tbl.Get(0).Update(func(row Exp) Exp { 430 | return row.Merge(Map{"pointupdated": true}) 431 | }), 432 | MatchMap{"replaced": 1}, 433 | }, 434 | {tbl.Get(0).Attr("pointupdated"), true}, 435 | }, 436 | "replace": { 437 | {tbl.Replace(func(row Exp) Exp { 438 | return row.Pluck("id").Merge(Map{"mutated": true}) 439 | }), 440 | MatchMap{"replaced": 10}, 441 | }, 442 | {tbl.Filter(func(row Exp) Exp { 443 | return row.Attr("mutated").Eq(true) 444 | }).Count(), 445 | 10, 446 | }, 447 | }, 448 | "pointreplace": { 449 | {tbl.Get(0).Replace(func(row Exp) Exp { 450 | return row.Pluck("id").Merge(Map{"pointmutated": true}) 451 | }), 452 | MatchMap{"replaced": 1}, 453 | }, 454 | {tbl.Get(0).Attr("pointmutated"), true}, 455 | }, 456 | "det": { 457 | {tbl3.Update(func(row Exp) interface{} { 458 | return Map{"count": Js(`0`)} 459 | }).Atomic(false), 460 | MatchMap{"replaced": 10}, 461 | }, 462 | {tbl3.Update(func(row Exp) interface{} { 463 | return Map{"count": 0} 464 | }), 465 | MatchMap{"unchanged": 10}, 466 | }, 467 | {tbl3.Replace(func(row Exp) Exp { 468 | return tbl3.Get(row.Attr("id")) 469 | }).Atomic(false), 470 | MatchMap{"unchanged": 10}, 471 | }, 472 | {tbl3.Replace(func(row Exp) Exp { 473 | return row 474 | }), 475 | MatchMap{"unchanged": 10}, 476 | }, 477 | {tbl3.Update(Map{"count": tbl3.Map(func(x Exp) Exp { 478 | return x.Attr("count") 479 | }).Reduce(func(a, b Exp) Exp { 480 | return a.Add(b) 481 | }, 0)}).Atomic(false), 482 | MatchMap{"unchanged": 10}, 483 | }, 484 | {tbl3.Update(Map{"count": Expr(docs).Map(func(x Exp) Exp { 485 | return x.Attr("id") 486 | }).Reduce(func(a, b Exp) Exp { 487 | return a.Add(b) 488 | }, 0)}), 489 | MatchMap{"replaced": 10}, 490 | }, 491 | }, 492 | "nonatomic": { 493 | {tbl3.Update(func(row Exp) interface{} { 494 | return Map{"count": 0} 495 | }), 496 | MatchMap{"replaced": 10}, 497 | }, 498 | {tbl3.Update(func(row Exp) interface{} { 499 | return Map{"x": Js(`1`)} 500 | }).Atomic(false), 501 | MatchMap{"replaced": 10}, 502 | }, 503 | {tbl3.Map(func(row Exp) interface{} { 504 | return row.Attr("x") 505 | }).Reduce(func(a, b Exp) Exp { 506 | return a.Add(b) 507 | }, 0), 508 | 10, 509 | }, 510 | {tbl3.Get(0).Update(func(row Exp) interface{} { 511 | return Map{"x": Js(`1`)} 512 | }), 513 | ErrorResponse{}, 514 | }, 515 | {tbl3.Get(0).Update(func(row Exp) interface{} { 516 | return Map{"x": Js(`2`)} 517 | }).Atomic(false), 518 | MatchMap{"replaced": 1}, 519 | }, 520 | {tbl3.Map(func(a Exp) Exp { 521 | return a.Attr("x") 522 | }).Reduce(func(a, b Exp) Exp { 523 | return a.Add(b) 524 | }, 0), 525 | 11, 526 | }, 527 | // This currently crashes rethinkdb, so disable it for now 528 | // {tbl3.Update(func(row Exp) interface{} { 529 | // return Map{"x": Js(`x`)} 530 | // }).Atomic(false), 531 | // MatchMap{"errors": 10}, 532 | // }, 533 | {tbl3.Map(func(a Exp) Exp { 534 | return a.Attr("x") 535 | }).Reduce(func(a, b Exp) Exp { 536 | return a.Add(b) 537 | }, 0), 538 | 11, 539 | }, 540 | {tbl3.Get(0).Update(func(row Exp) interface{} { 541 | return Map{"x": Js(`x`)} 542 | }), 543 | ErrorResponse{}, 544 | }, 545 | {tbl3.Map(func(a Exp) Exp { 546 | return a.Attr("x") 547 | }).Reduce(func(a, b Exp) Exp { 548 | return a.Add(b) 549 | }, 0), 550 | 11, 551 | }, 552 | {tbl3.Update(func(row Exp) interface{} { 553 | return Branch(Js(`true`), nil, Map{"x": 0.1}) 554 | }).Atomic(false), 555 | MatchMap{"unchanged": 10}, 556 | }, 557 | {tbl3.Map(func(a Exp) Exp { 558 | return a.Attr("x") 559 | }).Reduce(func(a, b Exp) Exp { 560 | return a.Add(b) 561 | }, 0), 562 | 11, 563 | }, 564 | {tbl3.Get(0).Replace(func(row Exp) interface{} { 565 | return Branch(Js(`true`), row, nil) 566 | }), 567 | ErrorResponse{}, 568 | }, 569 | {tbl3.Get(0).Replace(func(row Exp) interface{} { 570 | return Branch(Js(`true`), row, nil) 571 | }).Atomic(false), 572 | MatchMap{"unchanged": 1}, 573 | }, 574 | {tbl3.Map(func(a Exp) Exp { 575 | return a.Attr("x") 576 | }).Reduce(func(a, b Exp) Exp { 577 | return a.Add(b) 578 | }, 0), 579 | 11, 580 | }, 581 | }, 582 | "delete": { 583 | {tbl.Get(0).Delete(), 584 | MatchMap{"deleted": 1}, 585 | }, 586 | {tbl.Count(), 587 | 9, 588 | }, 589 | {tbl.Delete(), 590 | MatchMap{"deleted": 9}, 591 | }, 592 | {tbl.Count(), 593 | 0, 594 | }, 595 | }, 596 | "foreach": { 597 | {Expr(List{1, 2, 3}).ForEach(func(a Exp) Exp { 598 | return tbl4.Insert(Map{"id": a, "fe": true}) 599 | }), 600 | MatchMap{"inserted": 3}, 601 | }, 602 | {tbl4.ForEach(func(a Exp) Exp { 603 | return tbl4.Get(a.Attr("id")).Update(Map{"fe": true}) 604 | }), 605 | MatchMap{"unchanged": 3}, 606 | }, 607 | {tbl4.ForEach(func(a Exp) Exp { 608 | return tbl4.Get(a.Attr("id")).Update(Map{"fe": false}) 609 | }), 610 | MatchMap{"replaced": 3}, 611 | }, 612 | {tbl4.Filter(Map{"fe": false}).Count(), 613 | 3, 614 | }, 615 | }, 616 | "join": { 617 | {j1.InnerJoin(j2, func(one, two Exp) Exp { 618 | return one.Attr("id").Eq(two.Attr("id")) 619 | }).Zip().OrderBy("id").CoerceTo("array"), 620 | List{ 621 | Map{"id": 0, "name": "bob", "title": "goof"}, 622 | Map{"id": 2, "name": "joe", "title": "lmoe"}, 623 | }, 624 | }, 625 | {j1.OuterJoin(j2, func(one, two Exp) Exp { 626 | return one.Attr("id").Eq(two.Attr("id")) 627 | }).Zip().OrderBy("id").CoerceTo("array"), 628 | List{ 629 | Map{"id": 0, "name": "bob", "title": "goof"}, 630 | Map{"id": 1, "name": "tom"}, 631 | Map{"id": 2, "name": "joe", "title": "lmoe"}, 632 | }, 633 | }, 634 | {j1.EqJoin("id", j2, "id").Zip().OrderBy("id").CoerceTo("array"), 635 | List{ 636 | Map{"id": 0, "name": "bob", "title": "goof"}, 637 | Map{"id": 2, "name": "joe", "title": "lmoe"}, 638 | }, 639 | }, 640 | {j1.EqJoin("id", j3, "it").Zip().OrderBy("id").CoerceTo("array"), 641 | List{ 642 | Map{"id": 0, "it": 0, "name": "bob", "title": "goof"}, 643 | Map{"id": 2, "it": 2, "name": "joe", "title": "lmoe"}, 644 | }, 645 | }, 646 | }, 647 | "typeof": { 648 | {Expr("foo").TypeOf(), 649 | "STRING", 650 | }, 651 | }, 652 | "coerceto": { 653 | {Expr(1).CoerceTo("string"), 654 | "1", 655 | }, 656 | }, 657 | } 658 | 659 | func (s *RethinkSuite) TestGroups(c *test.C) { 660 | for group, pairs := range testGroups { 661 | resetDatabase(c) 662 | for index, pair := range pairs { 663 | fmt.Println("group:", group, index) 664 | runQuery(c, pair) 665 | } 666 | } 667 | } 668 | 669 | func (s *RethinkSuite) TestGet(c *test.C) { 670 | for i := 0; i < 10; i++ { 671 | pair := ExpectPair{tbl.Get(i), Map{"id": i, "num": 20 - i}} 672 | runQuery(c, pair) 673 | } 674 | } 675 | 676 | func (s *RethinkSuite) TestOrderBy(c *test.C) { 677 | var results1 []Map 678 | var results2 []Map 679 | 680 | tbl.OrderBy("num").Run(session).All(&results1) 681 | tbl.OrderBy(Asc("num")).Run(session).All(&results2) 682 | 683 | c.Assert(results1, JsonEquals, results2) 684 | } 685 | 686 | func (s *RethinkSuite) TestDropTable(c *test.C) { 687 | err := Db("test").TableCreate("tablex").Run(session).Err() 688 | c.Assert(err, test.IsNil) 689 | err = Db("test").TableDrop("tablex").Run(session).Err() 690 | c.Assert(err, test.IsNil) 691 | } 692 | -------------------------------------------------------------------------------- /conn.go: -------------------------------------------------------------------------------- 1 | package rethinkgo 2 | 3 | import ( 4 | "code.google.com/p/goprotobuf/proto" 5 | "encoding/binary" 6 | "errors" 7 | "fmt" 8 | "net" 9 | "io" 10 | "bufio" 11 | p "github.com/christopherhesse/rethinkgo/ql2" 12 | "time" 13 | ) 14 | 15 | // connection is a connection to a rethinkdb database 16 | type connection struct { 17 | // embed the net.Conn type, so that we can effectively define new methods on 18 | // it (interfaces do not allow that) 19 | net.Conn 20 | } 21 | 22 | var debugMode bool = false 23 | 24 | func serverConnect(address string, authkey string) (*connection, error) { 25 | conn, err := net.Dial("tcp", address) 26 | if err != nil { 27 | return nil, err 28 | } 29 | 30 | if err := binary.Write(conn, binary.LittleEndian, p.VersionDummy_V0_2); err != nil { 31 | return nil, err 32 | } 33 | 34 | // authorization key 35 | if err := binary.Write(conn, binary.LittleEndian, uint32(len(authkey))); err != nil { 36 | return nil, err 37 | } 38 | 39 | if err := binary.Write(conn, binary.BigEndian, []byte(authkey)); err != nil { 40 | return nil, err 41 | } 42 | 43 | // read server response to authorization key (terminated by NUL) 44 | reader := bufio.NewReader(conn) 45 | line, err := reader.ReadBytes('\x00') 46 | if err != nil { 47 | return nil, err 48 | } 49 | // convert to string and remove trailing NUL byte 50 | response := string(line[:len(line)-1]) 51 | if response != "SUCCESS" { 52 | // we failed authorization or something else terrible happened 53 | return nil, fmt.Errorf("Failed to connect to server: %v", response) 54 | } 55 | 56 | return &connection{conn}, nil 57 | } 58 | 59 | // SetDebug causes all queries sent to the server and responses received to be 60 | // printed to stdout in raw form. 61 | // 62 | // Example usage: 63 | // 64 | // r.SetDebug(true) 65 | func SetDebug(debug bool) { 66 | debugMode = debug 67 | } 68 | 69 | // writeMessage writes a byte array to the stream preceeded by the length in 70 | // bytes. 71 | func (c *connection) writeMessage(data []byte) error { 72 | messageLength := uint32(len(data)) 73 | if err := binary.Write(c, binary.LittleEndian, messageLength); err != nil { 74 | return err 75 | } 76 | 77 | _, err := c.Write(data) 78 | return err 79 | } 80 | 81 | // writeQuery writes a protobuf message to the connection. 82 | func (c *connection) writeQuery(protobuf *p.Query) error { 83 | data, err := proto.Marshal(protobuf) 84 | if err != nil { 85 | return fmt.Errorf("rethinkdb: Could not marshal protocol buffer: %v, %v", protobuf, err) 86 | } 87 | 88 | return c.writeMessage(data) 89 | } 90 | 91 | // readMessage reads a single message from a connection. A message is a length 92 | // followed by a serialized protocol buffer. 93 | func (c *connection) readMessage() ([]byte, error) { 94 | var messageLength uint32 95 | if err := binary.Read(c, binary.LittleEndian, &messageLength); err != nil { 96 | return nil, err 97 | } 98 | 99 | buffer := make([]byte, messageLength) 100 | _, err := io.ReadFull(c, buffer) 101 | if err != nil { 102 | return nil, err 103 | } 104 | return buffer, nil 105 | } 106 | 107 | // readResponse reads a protobuf message from a connection and parses it. 108 | func (c *connection) readResponse() (*p.Response, error) { 109 | data, err := c.readMessage() 110 | if err != nil { 111 | return nil, err 112 | } 113 | response := &p.Response{} 114 | err = proto.Unmarshal(data, response) 115 | return response, err 116 | } 117 | 118 | // executeQueryProtobuf sends a single query to the server and retrieves the parsed 119 | // response, a lower level function used by .executeQuery() 120 | func (c *connection) executeQueryProtobuf(protobuf *p.Query) (responseProto *p.Response, err error) { 121 | if err = c.writeQuery(protobuf); err != nil { 122 | return 123 | } 124 | 125 | for { 126 | responseProto, err = c.readResponse() 127 | if err != nil { 128 | return 129 | } 130 | 131 | if responseProto.GetToken() == protobuf.GetToken() { 132 | break 133 | } else if responseProto.GetToken() > protobuf.GetToken() { 134 | return nil, errors.New("rethinkdb: The server returned a response for a protobuf that was not submitted by us") 135 | } 136 | } 137 | return 138 | } 139 | 140 | // executeQuery is an internal function, shared by Rows iterator and the normal 141 | // Run() call. Runs a protocol buffer formatted query, returns a list of strings 142 | // and a status code. 143 | func (c *connection) executeQuery(queryProto *p.Query, timeout time.Duration) (result []*p.Datum, responseType p.Response_ResponseType, err error) { 144 | if debugMode { 145 | fmt.Printf("rethinkdb: queryProto:\n%v", protobufToString(queryProto, 1)) 146 | } 147 | 148 | // if the user has set a timeout, make sure we set a deadline on the connection 149 | // so that we don't exceed the timeout. if not, use the zero time value to 150 | // indicate no deadline 151 | if timeout == 0 { 152 | c.SetDeadline(time.Time{}) 153 | } else { 154 | c.SetDeadline(time.Now().Add(timeout)) 155 | } 156 | 157 | r, err := c.executeQueryProtobuf(queryProto) 158 | 159 | // reset the deadline for the connection 160 | c.SetDeadline(time.Time{}) 161 | 162 | if err != nil { 163 | return 164 | } 165 | if debugMode { 166 | fmt.Printf("rethinkdb: responseProto:\n%v", protobufToString(r, 1)) 167 | } 168 | 169 | responseType = r.GetType() 170 | switch responseType { 171 | case p.Response_SUCCESS_ATOM, p.Response_SUCCESS_SEQUENCE, p.Response_SUCCESS_PARTIAL: 172 | result = r.Response 173 | default: 174 | // some sort of error 175 | switch responseType { 176 | case p.Response_CLIENT_ERROR: 177 | err = ErrBrokenClient{response: r} 178 | case p.Response_COMPILE_ERROR: 179 | err = ErrBadQuery{response: r} 180 | case p.Response_RUNTIME_ERROR: 181 | err = ErrRuntime{response: r} 182 | default: 183 | err = fmt.Errorf("rethinkdb: Unexpected response type from server: %v", responseType) 184 | } 185 | } 186 | return 187 | } 188 | -------------------------------------------------------------------------------- /datum.go: -------------------------------------------------------------------------------- 1 | package rethinkgo 2 | 3 | import ( 4 | "encoding/json" 5 | p "github.com/christopherhesse/rethinkgo/ql2" 6 | "strings" 7 | ) 8 | 9 | func datumMarshal(v interface{}) (*p.Term, error) { 10 | // convert arbitrary types to a datum tree using the json module 11 | data, err := json.Marshal(v) 12 | if err != nil { 13 | return nil, err 14 | } 15 | 16 | dataString := string(data) 17 | 18 | datumTerm := &p.Term{ 19 | Type: p.Term_DATUM.Enum(), 20 | Datum: &p.Datum{ 21 | Type: p.Datum_R_STR.Enum(), 22 | RStr: &dataString, 23 | }, 24 | } 25 | 26 | term := &p.Term{ 27 | Type: p.Term_JSON.Enum(), 28 | Args: []*p.Term{datumTerm}, 29 | } 30 | 31 | return term, nil 32 | } 33 | 34 | func datumUnmarshal(datum *p.Datum, v interface{}) error { 35 | // convert a datum tree into an arbitrary type using the json module 36 | data, err := datumToJson(datum) 37 | if err != nil { 38 | return err 39 | } 40 | return json.Unmarshal(data, v) 41 | } 42 | 43 | func datumToJson(datum *p.Datum) ([]byte, error) { 44 | switch datum.GetType() { 45 | case p.Datum_R_NULL: 46 | return json.Marshal(nil) 47 | case p.Datum_R_BOOL: 48 | return json.Marshal(datum.GetRBool()) 49 | case p.Datum_R_NUM: 50 | return json.Marshal(datum.GetRNum()) 51 | case p.Datum_R_STR: 52 | return json.Marshal(datum.GetRStr()) 53 | case p.Datum_R_ARRAY: 54 | items := []string{} 55 | for _, d := range datum.GetRArray() { 56 | item, err := datumToJson(d) 57 | if err != nil { 58 | return nil, err 59 | } 60 | items = append(items, string(item)) 61 | } 62 | return []byte("[" + strings.Join(items, ",") + "]"), nil 63 | case p.Datum_R_OBJECT: 64 | pairs := []string{} 65 | for _, assoc := range datum.GetRObject() { 66 | raw_key := assoc.GetKey() 67 | raw_val := assoc.GetVal() 68 | 69 | // convert to json form 70 | key, err := json.Marshal(raw_key) 71 | if err != nil { 72 | return nil, err 73 | } 74 | val, err := datumToJson(raw_val) 75 | if err != nil { 76 | return nil, err 77 | } 78 | 79 | pairs = append(pairs, string(key)+":"+string(val)) 80 | } 81 | return []byte("{" + strings.Join(pairs, ",") + "}"), nil 82 | } 83 | panic("unknown datum type") 84 | } 85 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | // Package rethinkgo implements the RethinkDB API for Go. RethinkDB 2 | // (http://www.rethinkdb.com/) is an open-source distributed database in the 3 | // style of MongoDB. 4 | // 5 | // If you haven't tried it out, it takes about a minute to setup and has a sweet 6 | // web console. Even runs on Mac OS X. 7 | // (http://www.rethinkdb.com/docs/guides/quickstart/) 8 | // 9 | // If you are familiar with the RethinkDB API, this package should be 10 | // straightforward to use. If not, the docs for this package contain plenty of 11 | // examples, but you may want to look through the RethinkDB tutorials 12 | // (http://www.rethinkdb.com/docs/). 13 | // 14 | // Example usage: 15 | // 16 | // package main 17 | // 18 | // import ( 19 | // "fmt" 20 | // r "github.com/christopherhesse/rethinkgo" 21 | // ) 22 | // 23 | // type Employee struct { 24 | // FirstName string 25 | // LastName string 26 | // Job string 27 | // Id string `json:"id,omitempty"` // (will appear in json as "id", and not be sent if empty) 28 | // } 29 | // 30 | // func main() { 31 | // // To access a RethinkDB database, you connect to it with the Connect function 32 | // session, err := r.Connect("localhost:28015", "company_info") 33 | // if err != nil { 34 | // fmt.Println("error connecting:", err) 35 | // return 36 | // } 37 | // 38 | // var response []Employee 39 | // // Using .All(), we can read the entire response into a slice, without iteration 40 | // err = r.Table("employees").Run(session).All(&response) 41 | // if err != nil { 42 | // fmt.Println("err:", err) 43 | // } else { 44 | // fmt.Println("response:", response) 45 | // } 46 | // 47 | // // If we want to iterate over each result individually, we can use the rows 48 | // // object as an iterator 49 | // rows := r.Table("employees").Run(session) 50 | // for rows.Next() { 51 | // var row Employee 52 | // if err = rows.Scan(&row); err != nil { 53 | // fmt.Println("err:", err) 54 | // break 55 | // } 56 | // fmt.Println("row:", row) 57 | // } 58 | // if err = rows.Err(); err != nil { 59 | // fmt.Println("err:", err) 60 | // } 61 | // } 62 | // 63 | // Besides this simple read query, you can run almost arbitrary expressions on 64 | // the server, even Javascript code. See the rest of these docs for more 65 | // details. 66 | package rethinkgo 67 | -------------------------------------------------------------------------------- /error.go: -------------------------------------------------------------------------------- 1 | package rethinkgo 2 | 3 | import ( 4 | "fmt" 5 | p "github.com/christopherhesse/rethinkgo/ql2" 6 | ) 7 | 8 | func formatError(message string, response *p.Response) string { 9 | datums := response.GetResponse() 10 | var responseString string 11 | if len(datums) == 1 { 12 | datumUnmarshal(datums[0], &responseString) 13 | } 14 | 15 | if responseString == "" { 16 | responseString = fmt.Sprintf("%v", datums) 17 | } 18 | return fmt.Sprintf("rethinkdb: %v: %v", message, responseString) 19 | } 20 | 21 | func getBacktraceFrames(response *p.Response) []string { 22 | bt := response.GetBacktrace() 23 | if bt == nil { 24 | return nil 25 | } 26 | frames := []string{} 27 | for _, frame := range bt.GetFrames() { 28 | frames = append(frames, frame.String()) 29 | } 30 | return frames 31 | } 32 | 33 | // ErrBadQuery indicates that the server has told us we have constructed an 34 | // invalid query. 35 | // 36 | // Example usage: 37 | // 38 | // err := r.Table("heroes").ArrayToStream().ArrayToStream().Run(session).Err() 39 | type ErrBadQuery struct { 40 | response *p.Response 41 | } 42 | 43 | func (e ErrBadQuery) Error() string { 44 | return formatError("Server could not make sense of our query", e.response) 45 | } 46 | 47 | // ErrRuntime indicates that the server has encountered an error while 48 | // trying to execute our query. 49 | // 50 | // Example usage: 51 | // 52 | // err := r.Table("table_that_doesnt_exist").Run(session).Err() 53 | // err := r.RuntimeError("error time!").Run(session).Err() 54 | type ErrRuntime struct { 55 | response *p.Response 56 | } 57 | 58 | func (e ErrRuntime) Error() string { 59 | return formatError("Server could not execute our query", e.response) 60 | } 61 | 62 | // ErrBrokenClient means the server believes there's a bug in the client 63 | // library, for instance a malformed protocol buffer. 64 | type ErrBrokenClient struct { 65 | response *p.Response 66 | } 67 | 68 | func (e ErrBrokenClient) Error() string { 69 | return formatError("Whoops, looks like there's a bug in this client library, please report it at https://github.com/christopherhesse/rethinkgo/issues/new", e.response) 70 | } 71 | 72 | // ErrWrongResponseType is returned when .Exec(), .One(). or .All() have 73 | // been used, but the expected response type does not match the type we got 74 | // from the server. 75 | // 76 | // Example usage: 77 | // 78 | // var row []interface{} 79 | // err := r.Table("heroes").Get("Archangel", "name").Run(session).All(&row) 80 | type ErrWrongResponseType struct { 81 | response *p.Response 82 | } 83 | 84 | func (e ErrWrongResponseType) Error() string { 85 | return "rethinkdb: Wrong response type, you may have used the wrong one of: .Exec(), .One(), .All()" 86 | } 87 | -------------------------------------------------------------------------------- /protobuf.go: -------------------------------------------------------------------------------- 1 | package rethinkgo 2 | 3 | // Convert Exp trees and queries into protocol buffer form. 4 | // Functions in this file will panic on failure, the caller is expected to 5 | // recover(). 6 | 7 | import ( 8 | "code.google.com/p/goprotobuf/proto" 9 | "fmt" 10 | p "github.com/christopherhesse/rethinkgo/ql2" 11 | "reflect" 12 | "runtime" 13 | "sync/atomic" 14 | ) 15 | 16 | // context stores some state that is required when converting Expressions to 17 | // protocol buffers, and has to be passed by value throughout. 18 | type context struct { 19 | databaseName string 20 | useOutdated bool 21 | durability string 22 | overwrite bool 23 | atomic bool 24 | returnValues bool 25 | } 26 | 27 | // toTerm converts an arbitrary object to a Term, within the context that toTerm 28 | // was called on. 29 | func (ctx context) toTerm(o interface{}) *p.Term { 30 | e := Expr(o) 31 | 32 | var termType p.Term_TermType 33 | arguments := e.args 34 | options := map[string]interface{}{} 35 | 36 | switch e.kind { 37 | case literalKind: 38 | return ctx.literalToTerm(e.args[0]) 39 | case javascriptKind: 40 | termType = p.Term_JAVASCRIPT 41 | if len(arguments) == 2 { 42 | options["timeout"] = arguments[1] 43 | arguments = arguments[:1] 44 | } 45 | 46 | case tableKind: 47 | termType = p.Term_TABLE 48 | // first arg to table must be the database 49 | if len(arguments) == 1 { 50 | dbExpr := naryOperator(databaseKind, ctx.databaseName) 51 | arguments = []interface{}{dbExpr, arguments[0]} 52 | } 53 | if ctx.useOutdated { 54 | options["use_outdated"] = ctx.useOutdated 55 | } 56 | 57 | case betweenKind: 58 | termType = p.Term_BETWEEN 59 | if len(arguments) == 4 { 60 | // last argument is an index 61 | options["index"] = arguments[3] 62 | arguments = arguments[:3] 63 | } 64 | case reduceKind: 65 | termType = p.Term_REDUCE 66 | options["base"] = arguments[2] 67 | arguments = arguments[:2] 68 | case groupedMapReduceKind: 69 | termType = p.Term_GROUPED_MAP_REDUCE 70 | options["base"] = arguments[4] 71 | arguments = arguments[:4] 72 | case eqJoinKind: 73 | termType = p.Term_EQ_JOIN 74 | options["index"] = arguments[3] 75 | arguments = arguments[:3] 76 | 77 | case updateKind, deleteKind, replaceKind, insertKind: 78 | if ctx.durability != "" { 79 | options["durability"] = ctx.durability 80 | } 81 | if ctx.returnValues { 82 | options["return_vals"] = true 83 | } 84 | switch e.kind { 85 | case updateKind: 86 | termType = p.Term_UPDATE 87 | options["non_atomic"] = !ctx.atomic 88 | case deleteKind: 89 | termType = p.Term_DELETE 90 | case replaceKind: 91 | termType = p.Term_REPLACE 92 | options["non_atomic"] = !ctx.atomic 93 | case insertKind: 94 | termType = p.Term_INSERT 95 | options["upsert"] = ctx.overwrite 96 | } 97 | 98 | case tableCreateKind: 99 | termType = p.Term_TABLE_CREATE 100 | // last argument is the table spec 101 | spec := arguments[len(arguments)-1].(TableSpec) 102 | arguments = arguments[:len(arguments)-1] 103 | 104 | if len(arguments) == 0 { 105 | // just spec, need to add database 106 | dbExpr := naryOperator(databaseKind, ctx.databaseName) 107 | arguments = append(arguments, dbExpr) 108 | } 109 | arguments = append(arguments, spec.Name) 110 | 111 | if spec.Datacenter != "" { 112 | options["datacenter"] = spec.Datacenter 113 | } 114 | if spec.PrimaryKey != "" { 115 | options["primary_key"] = spec.PrimaryKey 116 | } 117 | if spec.CacheSize != 0 { 118 | options["cache_size"] = spec.CacheSize 119 | } 120 | if spec.Durability != "" { 121 | options["durability"] = spec.Durability 122 | } 123 | case tableDropKind: 124 | termType = p.Term_TABLE_DROP 125 | if len(arguments) == 1 { 126 | // no database specified, use the session database 127 | dbExpr := naryOperator(databaseKind, ctx.databaseName) 128 | arguments = []interface{}{dbExpr, arguments[0]} 129 | } 130 | case tableListKind: 131 | termType = p.Term_TABLE_LIST 132 | if len(arguments) == 0 { 133 | // no database specified, use the session database 134 | dbExpr := naryOperator(databaseKind, ctx.databaseName) 135 | arguments = append(arguments, dbExpr) 136 | } 137 | case getAllKind: 138 | termType = p.Term_GET_ALL 139 | options["index"] = arguments[len(arguments)-1] 140 | arguments = arguments[:len(arguments)-1] 141 | 142 | case funcKind: 143 | return ctx.toFuncTerm(arguments[0], arguments[1].(int)) 144 | 145 | // special made-up kind to set options on the query 146 | case upsertKind: 147 | ctx.overwrite = e.args[1].(bool) 148 | return ctx.toTerm(e.args[0]) 149 | case atomicKind: 150 | ctx.atomic = e.args[1].(bool) 151 | return ctx.toTerm(e.args[0]) 152 | case useOutdatedKind: 153 | ctx.useOutdated = e.args[1].(bool) 154 | return ctx.toTerm(e.args[0]) 155 | case durabilityKind: 156 | ctx.durability = e.args[1].(string) 157 | return ctx.toTerm(e.args[0]) 158 | case returnValuesKind: 159 | ctx.returnValues = true 160 | return ctx.toTerm(e.args[0]) 161 | 162 | case jsonKind: 163 | termType = p.Term_JSON 164 | case mapKind: 165 | termType = p.Term_MAP 166 | case filterKind: 167 | termType = p.Term_FILTER 168 | case concatMapKind: 169 | termType = p.Term_CONCATMAP 170 | case orderByKind: 171 | termType = p.Term_ORDERBY 172 | case distinctKind: 173 | termType = p.Term_DISTINCT 174 | case countKind: 175 | termType = p.Term_COUNT 176 | case unionKind: 177 | termType = p.Term_UNION 178 | case nthKind: 179 | termType = p.Term_NTH 180 | case groupByKind: 181 | termType = p.Term_GROUPBY 182 | case innerJoinKind: 183 | termType = p.Term_INNER_JOIN 184 | case outerJoinKind: 185 | termType = p.Term_OUTER_JOIN 186 | case zipKind: 187 | termType = p.Term_ZIP 188 | case coerceToKind: 189 | termType = p.Term_COERCE_TO 190 | case typeOfKind: 191 | termType = p.Term_TYPEOF 192 | case infoKind: 193 | termType = p.Term_INFO 194 | case keysKind: 195 | termType = p.Term_KEYS 196 | case getKind: 197 | termType = p.Term_GET 198 | case equalityKind: 199 | termType = p.Term_EQ 200 | case inequalityKind: 201 | termType = p.Term_NE 202 | case lessThanKind: 203 | termType = p.Term_LT 204 | case lessThanOrEqualKind: 205 | termType = p.Term_LE 206 | case greaterThanKind: 207 | termType = p.Term_GT 208 | case greaterThanOrEqualKind: 209 | termType = p.Term_GE 210 | case logicalNotKind: 211 | termType = p.Term_NOT 212 | case addKind: 213 | termType = p.Term_ADD 214 | case subtractKind: 215 | termType = p.Term_SUB 216 | case multiplyKind: 217 | termType = p.Term_MUL 218 | case divideKind: 219 | termType = p.Term_DIV 220 | case moduloKind: 221 | termType = p.Term_MOD 222 | case appendKind: 223 | termType = p.Term_APPEND 224 | case prependKind: 225 | termType = p.Term_PREPEND 226 | case insertAtKind: 227 | termType = p.Term_INSERT_AT 228 | case spliceAtKind: 229 | termType = p.Term_SPLICE_AT 230 | case deleteAtKind: 231 | termType = p.Term_DELETE_AT 232 | case changeAtKind: 233 | termType = p.Term_CHANGE_AT 234 | case differenceKind: 235 | termType = p.Term_DIFFERENCE 236 | case indexesOfKind: 237 | termType = p.Term_INDEXES_OF 238 | case isEmptyKind: 239 | termType = p.Term_IS_EMPTY 240 | case setInsertKind: 241 | termType = p.Term_SET_INSERT 242 | case setUnionKind: 243 | termType = p.Term_SET_UNION 244 | case setDifferenceKind: 245 | termType = p.Term_SET_DIFFERENCE 246 | case setIntersectionKind: 247 | termType = p.Term_SET_INTERSECTION 248 | case containsKind: 249 | termType = p.Term_CONTAINS 250 | case sliceKind: 251 | termType = p.Term_SLICE 252 | case skipKind: 253 | termType = p.Term_SKIP 254 | case limitKind: 255 | termType = p.Term_LIMIT 256 | case sampleKind: 257 | termType = p.Term_SAMPLE 258 | case matchKind: 259 | termType = p.Term_MATCH 260 | case getFieldKind: 261 | termType = p.Term_GET_FIELD 262 | case hasFieldsKind: 263 | termType = p.Term_HAS_FIELDS 264 | case withFieldsKind: 265 | termType = p.Term_WITH_FIELDS 266 | case pluckKind: 267 | termType = p.Term_PLUCK 268 | case withoutKind: 269 | termType = p.Term_WITHOUT 270 | case mergeKind: 271 | termType = p.Term_MERGE 272 | case indexCreateKind: 273 | termType = p.Term_INDEX_CREATE 274 | case indexListKind: 275 | termType = p.Term_INDEX_LIST 276 | case indexDropKind: 277 | termType = p.Term_INDEX_DROP 278 | case funcallKind: 279 | termType = p.Term_FUNCALL 280 | case branchKind: 281 | termType = p.Term_BRANCH 282 | case anyKind: 283 | termType = p.Term_ANY 284 | case allKind: 285 | termType = p.Term_ALL 286 | case forEachKind: 287 | termType = p.Term_FOREACH 288 | case databaseCreateKind: 289 | termType = p.Term_DB_CREATE 290 | case databaseDropKind: 291 | termType = p.Term_DB_DROP 292 | case databaseListKind: 293 | termType = p.Term_DB_LIST 294 | case errorKind: 295 | termType = p.Term_ERROR 296 | case implicitVariableKind: 297 | termType = p.Term_IMPLICIT_VAR 298 | case databaseKind: 299 | termType = p.Term_DB 300 | case variableKind: 301 | termType = p.Term_VAR 302 | case ascendingKind: 303 | termType = p.Term_ASC 304 | case descendingKind: 305 | termType = p.Term_DESC 306 | case defaultKind: 307 | termType = p.Term_DEFAULT 308 | 309 | default: 310 | panic("invalid term kind") 311 | } 312 | 313 | args := []*p.Term{} 314 | for _, arg := range arguments { 315 | args = append(args, ctx.toTerm(arg)) 316 | } 317 | 318 | var optargs []*p.Term_AssocPair 319 | for key, value := range options { 320 | optarg := &p.Term_AssocPair{ 321 | Key: proto.String(key), 322 | Val: ctx.toTerm(value), 323 | } 324 | optargs = append(optargs, optarg) 325 | } 326 | 327 | return &p.Term{ 328 | Type: termType.Enum(), 329 | Args: args, 330 | Optargs: optargs, 331 | } 332 | } 333 | 334 | var variableCounter int64 = 0 335 | 336 | func nextVariableNumber() int64 { 337 | return atomic.AddInt64(&variableCounter, 1) 338 | } 339 | 340 | func containsImplicitVariable(term *p.Term) bool { 341 | if *term.Type == p.Term_IMPLICIT_VAR { 342 | return true 343 | } 344 | 345 | for _, arg := range term.Args { 346 | if containsImplicitVariable(arg) { 347 | return true 348 | } 349 | } 350 | 351 | for _, optarg := range term.Optargs { 352 | if containsImplicitVariable(optarg.Val) { 353 | return true 354 | } 355 | } 356 | 357 | return false 358 | } 359 | 360 | func (ctx context) toFuncTerm(f interface{}, requiredArgs int) *p.Term { 361 | if reflect.ValueOf(f).Kind() == reflect.Func { 362 | return ctx.compileGoFunc(f, requiredArgs) 363 | } 364 | e := Expr(f) 365 | // the user may pass in a Map with r.Row elements, such as: 366 | // r.Table("heroes").Filter(r.Map{"durability": r.Row.Attr("speed")}) 367 | // these have to be sent to the server as a function, but it looks a lot like a 368 | // literal or other expression, so in order to determine if we should send it 369 | // to the server as a function, we just check for the use of r.Row 370 | // if we just convert all literals to functions, something like: 371 | // r.Expr(r.List{"a", "b", "b", "a"}).IndexesOf("a") 372 | // won't work 373 | term := ctx.toTerm(e) 374 | if e.kind == javascriptKind || (e.kind == literalKind && !containsImplicitVariable(term)) { 375 | return term 376 | } 377 | return ctx.compileExpressionFunc(e, requiredArgs) 378 | } 379 | 380 | func (ctx context) compileExpressionFunc(e Exp, requiredArgs int) *p.Term { 381 | // an expression that takes no args, e.g. Row.Attr("name") 382 | params := []int64{} 383 | for requiredArgs > 0 { 384 | params = append(params, nextVariableNumber()) 385 | requiredArgs-- 386 | } 387 | 388 | paramsTerm := paramsToTerm(params) 389 | funcTerm := ctx.toTerm(e) 390 | 391 | return &p.Term{ 392 | Type: p.Term_FUNC.Enum(), 393 | Args: []*p.Term{paramsTerm, funcTerm}, 394 | } 395 | } 396 | 397 | func (ctx context) compileGoFunc(f interface{}, requiredArgs int) *p.Term { 398 | // presumably if we're here, the user has supplied a go func to be 399 | // converted to an expression 400 | value := reflect.ValueOf(f) 401 | valueType := value.Type() 402 | 403 | if requiredArgs != -1 && valueType.NumIn() != requiredArgs { 404 | panic("Function expression has incorrect number of arguments") 405 | } 406 | 407 | // check input types and generate the variables to pass to the function 408 | // the args have generated names because when the function is serialized, 409 | // the server can't figure out which variable is which in a closure 410 | var params []int64 411 | var args []reflect.Value 412 | for i := 0; i < valueType.NumIn(); i++ { 413 | number := nextVariableNumber() 414 | e := naryOperator(variableKind, number) 415 | args = append(args, reflect.ValueOf(e)) 416 | params = append(params, number) 417 | 418 | // make sure all input arguments are of type Exp 419 | if !valueType.In(i).AssignableTo(reflect.TypeOf(Exp{})) { 420 | panic("Function argument is not of type Exp") 421 | } 422 | } 423 | 424 | if valueType.NumOut() != 1 { 425 | panic("Function does not have a single return value") 426 | } 427 | 428 | outValue := value.Call(args)[0] 429 | paramsTerm := paramsToTerm(params) 430 | funcTerm := ctx.toTerm(outValue.Interface()) 431 | 432 | return &p.Term{ 433 | Type: p.Term_FUNC.Enum(), 434 | Args: []*p.Term{paramsTerm, funcTerm}, 435 | } 436 | } 437 | 438 | func paramsToTerm(params []int64) *p.Term { 439 | terms := []*p.Term{} 440 | for _, param := range params { 441 | num := float64(param) 442 | term := &p.Term{ 443 | Type: p.Term_DATUM.Enum(), 444 | Datum: &p.Datum{ 445 | Type: p.Datum_R_NUM.Enum(), 446 | RNum: &num, 447 | }, 448 | } 449 | terms = append(terms, term) 450 | } 451 | 452 | return &p.Term{ 453 | Type: p.Term_MAKE_ARRAY.Enum(), 454 | Args: terms, 455 | } 456 | } 457 | 458 | func (ctx context) literalToTerm(literal interface{}) *p.Term { 459 | value := reflect.ValueOf(literal) 460 | 461 | if value.Kind() == reflect.Map { 462 | return &p.Term{ 463 | Type: p.Term_MAKE_OBJ.Enum(), 464 | Optargs: ctx.mapToAssocPairs(literal), 465 | } 466 | } 467 | 468 | term, err := datumMarshal(literal) 469 | if err != nil { 470 | panic(err) 471 | } 472 | 473 | return term 474 | } 475 | 476 | // toArray and toObject seem overly complicated, like maybe some sort 477 | // of assignment assertion would be enough 478 | func toArray(a interface{}) []interface{} { 479 | array := []interface{}{} 480 | 481 | arrayValue := reflect.ValueOf(a) 482 | for i := 0; i < arrayValue.Len(); i++ { 483 | value := arrayValue.Index(i).Interface() 484 | array = append(array, value) 485 | } 486 | return array 487 | } 488 | 489 | func toObject(m interface{}) map[string]interface{} { 490 | object := map[string]interface{}{} 491 | 492 | mapValue := reflect.ValueOf(m) 493 | mapType := mapValue.Type() 494 | keyType := mapType.Key() 495 | 496 | if keyType.Kind() != reflect.String { 497 | panic("string keys only in maps") 498 | } 499 | 500 | for _, keyValue := range mapValue.MapKeys() { 501 | key := keyValue.String() 502 | valueValue := mapValue.MapIndex(keyValue) 503 | value := valueValue.Interface() 504 | object[key] = value 505 | } 506 | return object 507 | } 508 | 509 | func (ctx context) mapToAssocPairs(m interface{}) (pairs []*p.Term_AssocPair) { 510 | for key, value := range toObject(m) { 511 | pair := &p.Term_AssocPair{ 512 | Key: proto.String(key), 513 | Val: ctx.toTerm(value), 514 | } 515 | pairs = append(pairs, pair) 516 | } 517 | return pairs 518 | } 519 | 520 | func (e Exp) toProtobuf(ctx context) *p.Query { 521 | return &p.Query{ 522 | Type: p.Query_START.Enum(), 523 | Query: ctx.toTerm(e), 524 | } 525 | } 526 | 527 | // buildProtobuf converts a query to a protobuf and catches any panics raised 528 | // by the toProtobuf() functions. 529 | func (ctx context) buildProtobuf(query Exp) (queryProto *p.Query, err error) { 530 | defer func() { 531 | if r := recover(); r != nil { 532 | if _, ok := r.(runtime.Error); ok { 533 | panic(r) 534 | } 535 | err = fmt.Errorf("rethinkdb: %v", r) 536 | } 537 | }() 538 | 539 | queryProto = query.toProtobuf(ctx) 540 | return 541 | } 542 | 543 | // Check compiles a query for sending to the server, but does not send it. 544 | // There is one .Check() method for each query type. 545 | func (e Exp) Check(s *Session) error { 546 | _, err := s.getContext().buildProtobuf(e) 547 | return err 548 | } 549 | -------------------------------------------------------------------------------- /ql2/ql2.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. 2 | // source: ql2.proto 3 | // DO NOT EDIT! 4 | 5 | package ql2 6 | 7 | import proto "code.google.com/p/goprotobuf/proto" 8 | import json "encoding/json" 9 | import math "math" 10 | 11 | // Reference proto, json, and math imports to suppress error if they are not otherwise used. 12 | var _ = proto.Marshal 13 | var _ = &json.SyntaxError{} 14 | var _ = math.Inf 15 | 16 | type VersionDummy_Version int32 17 | 18 | const ( 19 | VersionDummy_V0_1 VersionDummy_Version = 1063369270 20 | VersionDummy_V0_2 VersionDummy_Version = 1915781601 21 | ) 22 | 23 | var VersionDummy_Version_name = map[int32]string{ 24 | 1063369270: "V0_1", 25 | 1915781601: "V0_2", 26 | } 27 | var VersionDummy_Version_value = map[string]int32{ 28 | "V0_1": 1063369270, 29 | "V0_2": 1915781601, 30 | } 31 | 32 | func (x VersionDummy_Version) Enum() *VersionDummy_Version { 33 | p := new(VersionDummy_Version) 34 | *p = x 35 | return p 36 | } 37 | func (x VersionDummy_Version) String() string { 38 | return proto.EnumName(VersionDummy_Version_name, int32(x)) 39 | } 40 | func (x VersionDummy_Version) MarshalJSON() ([]byte, error) { 41 | return json.Marshal(x.String()) 42 | } 43 | func (x *VersionDummy_Version) UnmarshalJSON(data []byte) error { 44 | value, err := proto.UnmarshalJSONEnum(VersionDummy_Version_value, data, "VersionDummy_Version") 45 | if err != nil { 46 | return err 47 | } 48 | *x = VersionDummy_Version(value) 49 | return nil 50 | } 51 | 52 | type Query_QueryType int32 53 | 54 | const ( 55 | Query_START Query_QueryType = 1 56 | Query_CONTINUE Query_QueryType = 2 57 | Query_STOP Query_QueryType = 3 58 | ) 59 | 60 | var Query_QueryType_name = map[int32]string{ 61 | 1: "START", 62 | 2: "CONTINUE", 63 | 3: "STOP", 64 | } 65 | var Query_QueryType_value = map[string]int32{ 66 | "START": 1, 67 | "CONTINUE": 2, 68 | "STOP": 3, 69 | } 70 | 71 | func (x Query_QueryType) Enum() *Query_QueryType { 72 | p := new(Query_QueryType) 73 | *p = x 74 | return p 75 | } 76 | func (x Query_QueryType) String() string { 77 | return proto.EnumName(Query_QueryType_name, int32(x)) 78 | } 79 | func (x Query_QueryType) MarshalJSON() ([]byte, error) { 80 | return json.Marshal(x.String()) 81 | } 82 | func (x *Query_QueryType) UnmarshalJSON(data []byte) error { 83 | value, err := proto.UnmarshalJSONEnum(Query_QueryType_value, data, "Query_QueryType") 84 | if err != nil { 85 | return err 86 | } 87 | *x = Query_QueryType(value) 88 | return nil 89 | } 90 | 91 | type Frame_FrameType int32 92 | 93 | const ( 94 | Frame_POS Frame_FrameType = 1 95 | Frame_OPT Frame_FrameType = 2 96 | ) 97 | 98 | var Frame_FrameType_name = map[int32]string{ 99 | 1: "POS", 100 | 2: "OPT", 101 | } 102 | var Frame_FrameType_value = map[string]int32{ 103 | "POS": 1, 104 | "OPT": 2, 105 | } 106 | 107 | func (x Frame_FrameType) Enum() *Frame_FrameType { 108 | p := new(Frame_FrameType) 109 | *p = x 110 | return p 111 | } 112 | func (x Frame_FrameType) String() string { 113 | return proto.EnumName(Frame_FrameType_name, int32(x)) 114 | } 115 | func (x Frame_FrameType) MarshalJSON() ([]byte, error) { 116 | return json.Marshal(x.String()) 117 | } 118 | func (x *Frame_FrameType) UnmarshalJSON(data []byte) error { 119 | value, err := proto.UnmarshalJSONEnum(Frame_FrameType_value, data, "Frame_FrameType") 120 | if err != nil { 121 | return err 122 | } 123 | *x = Frame_FrameType(value) 124 | return nil 125 | } 126 | 127 | type Response_ResponseType int32 128 | 129 | const ( 130 | Response_SUCCESS_ATOM Response_ResponseType = 1 131 | Response_SUCCESS_SEQUENCE Response_ResponseType = 2 132 | Response_SUCCESS_PARTIAL Response_ResponseType = 3 133 | Response_CLIENT_ERROR Response_ResponseType = 16 134 | Response_COMPILE_ERROR Response_ResponseType = 17 135 | Response_RUNTIME_ERROR Response_ResponseType = 18 136 | ) 137 | 138 | var Response_ResponseType_name = map[int32]string{ 139 | 1: "SUCCESS_ATOM", 140 | 2: "SUCCESS_SEQUENCE", 141 | 3: "SUCCESS_PARTIAL", 142 | 16: "CLIENT_ERROR", 143 | 17: "COMPILE_ERROR", 144 | 18: "RUNTIME_ERROR", 145 | } 146 | var Response_ResponseType_value = map[string]int32{ 147 | "SUCCESS_ATOM": 1, 148 | "SUCCESS_SEQUENCE": 2, 149 | "SUCCESS_PARTIAL": 3, 150 | "CLIENT_ERROR": 16, 151 | "COMPILE_ERROR": 17, 152 | "RUNTIME_ERROR": 18, 153 | } 154 | 155 | func (x Response_ResponseType) Enum() *Response_ResponseType { 156 | p := new(Response_ResponseType) 157 | *p = x 158 | return p 159 | } 160 | func (x Response_ResponseType) String() string { 161 | return proto.EnumName(Response_ResponseType_name, int32(x)) 162 | } 163 | func (x Response_ResponseType) MarshalJSON() ([]byte, error) { 164 | return json.Marshal(x.String()) 165 | } 166 | func (x *Response_ResponseType) UnmarshalJSON(data []byte) error { 167 | value, err := proto.UnmarshalJSONEnum(Response_ResponseType_value, data, "Response_ResponseType") 168 | if err != nil { 169 | return err 170 | } 171 | *x = Response_ResponseType(value) 172 | return nil 173 | } 174 | 175 | type Datum_DatumType int32 176 | 177 | const ( 178 | Datum_R_NULL Datum_DatumType = 1 179 | Datum_R_BOOL Datum_DatumType = 2 180 | Datum_R_NUM Datum_DatumType = 3 181 | Datum_R_STR Datum_DatumType = 4 182 | Datum_R_ARRAY Datum_DatumType = 5 183 | Datum_R_OBJECT Datum_DatumType = 6 184 | ) 185 | 186 | var Datum_DatumType_name = map[int32]string{ 187 | 1: "R_NULL", 188 | 2: "R_BOOL", 189 | 3: "R_NUM", 190 | 4: "R_STR", 191 | 5: "R_ARRAY", 192 | 6: "R_OBJECT", 193 | } 194 | var Datum_DatumType_value = map[string]int32{ 195 | "R_NULL": 1, 196 | "R_BOOL": 2, 197 | "R_NUM": 3, 198 | "R_STR": 4, 199 | "R_ARRAY": 5, 200 | "R_OBJECT": 6, 201 | } 202 | 203 | func (x Datum_DatumType) Enum() *Datum_DatumType { 204 | p := new(Datum_DatumType) 205 | *p = x 206 | return p 207 | } 208 | func (x Datum_DatumType) String() string { 209 | return proto.EnumName(Datum_DatumType_name, int32(x)) 210 | } 211 | func (x Datum_DatumType) MarshalJSON() ([]byte, error) { 212 | return json.Marshal(x.String()) 213 | } 214 | func (x *Datum_DatumType) UnmarshalJSON(data []byte) error { 215 | value, err := proto.UnmarshalJSONEnum(Datum_DatumType_value, data, "Datum_DatumType") 216 | if err != nil { 217 | return err 218 | } 219 | *x = Datum_DatumType(value) 220 | return nil 221 | } 222 | 223 | type Term_TermType int32 224 | 225 | const ( 226 | Term_DATUM Term_TermType = 1 227 | Term_MAKE_ARRAY Term_TermType = 2 228 | Term_MAKE_OBJ Term_TermType = 3 229 | Term_VAR Term_TermType = 10 230 | Term_JAVASCRIPT Term_TermType = 11 231 | Term_ERROR Term_TermType = 12 232 | Term_IMPLICIT_VAR Term_TermType = 13 233 | Term_DB Term_TermType = 14 234 | Term_TABLE Term_TermType = 15 235 | Term_GET Term_TermType = 16 236 | Term_GET_ALL Term_TermType = 78 237 | Term_EQ Term_TermType = 17 238 | Term_NE Term_TermType = 18 239 | Term_LT Term_TermType = 19 240 | Term_LE Term_TermType = 20 241 | Term_GT Term_TermType = 21 242 | Term_GE Term_TermType = 22 243 | Term_NOT Term_TermType = 23 244 | Term_ADD Term_TermType = 24 245 | Term_SUB Term_TermType = 25 246 | Term_MUL Term_TermType = 26 247 | Term_DIV Term_TermType = 27 248 | Term_MOD Term_TermType = 28 249 | Term_APPEND Term_TermType = 29 250 | Term_PREPEND Term_TermType = 80 251 | Term_DIFFERENCE Term_TermType = 95 252 | Term_SET_INSERT Term_TermType = 88 253 | Term_SET_INTERSECTION Term_TermType = 89 254 | Term_SET_UNION Term_TermType = 90 255 | Term_SET_DIFFERENCE Term_TermType = 91 256 | Term_SLICE Term_TermType = 30 257 | Term_SKIP Term_TermType = 70 258 | Term_LIMIT Term_TermType = 71 259 | Term_INDEXES_OF Term_TermType = 87 260 | Term_CONTAINS Term_TermType = 93 261 | Term_GET_FIELD Term_TermType = 31 262 | Term_KEYS Term_TermType = 94 263 | Term_HAS_FIELDS Term_TermType = 32 264 | Term_WITH_FIELDS Term_TermType = 96 265 | Term_PLUCK Term_TermType = 33 266 | Term_WITHOUT Term_TermType = 34 267 | Term_MERGE Term_TermType = 35 268 | Term_BETWEEN Term_TermType = 36 269 | Term_REDUCE Term_TermType = 37 270 | Term_MAP Term_TermType = 38 271 | Term_FILTER Term_TermType = 39 272 | Term_CONCATMAP Term_TermType = 40 273 | Term_ORDERBY Term_TermType = 41 274 | Term_DISTINCT Term_TermType = 42 275 | Term_COUNT Term_TermType = 43 276 | Term_IS_EMPTY Term_TermType = 86 277 | Term_UNION Term_TermType = 44 278 | Term_NTH Term_TermType = 45 279 | Term_GROUPED_MAP_REDUCE Term_TermType = 46 280 | Term_GROUPBY Term_TermType = 47 281 | Term_INNER_JOIN Term_TermType = 48 282 | Term_OUTER_JOIN Term_TermType = 49 283 | Term_EQ_JOIN Term_TermType = 50 284 | Term_ZIP Term_TermType = 72 285 | Term_INSERT_AT Term_TermType = 82 286 | Term_DELETE_AT Term_TermType = 83 287 | Term_CHANGE_AT Term_TermType = 84 288 | Term_SPLICE_AT Term_TermType = 85 289 | Term_COERCE_TO Term_TermType = 51 290 | Term_TYPEOF Term_TermType = 52 291 | Term_UPDATE Term_TermType = 53 292 | Term_DELETE Term_TermType = 54 293 | Term_REPLACE Term_TermType = 55 294 | Term_INSERT Term_TermType = 56 295 | Term_DB_CREATE Term_TermType = 57 296 | Term_DB_DROP Term_TermType = 58 297 | Term_DB_LIST Term_TermType = 59 298 | Term_TABLE_CREATE Term_TermType = 60 299 | Term_TABLE_DROP Term_TermType = 61 300 | Term_TABLE_LIST Term_TermType = 62 301 | Term_INDEX_CREATE Term_TermType = 75 302 | Term_INDEX_DROP Term_TermType = 76 303 | Term_INDEX_LIST Term_TermType = 77 304 | Term_FUNCALL Term_TermType = 64 305 | Term_BRANCH Term_TermType = 65 306 | Term_ANY Term_TermType = 66 307 | Term_ALL Term_TermType = 67 308 | Term_FOREACH Term_TermType = 68 309 | Term_FUNC Term_TermType = 69 310 | Term_ASC Term_TermType = 73 311 | Term_DESC Term_TermType = 74 312 | Term_INFO Term_TermType = 79 313 | Term_MATCH Term_TermType = 97 314 | Term_SAMPLE Term_TermType = 81 315 | Term_DEFAULT Term_TermType = 92 316 | Term_JSON Term_TermType = 98 317 | ) 318 | 319 | var Term_TermType_name = map[int32]string{ 320 | 1: "DATUM", 321 | 2: "MAKE_ARRAY", 322 | 3: "MAKE_OBJ", 323 | 10: "VAR", 324 | 11: "JAVASCRIPT", 325 | 12: "ERROR", 326 | 13: "IMPLICIT_VAR", 327 | 14: "DB", 328 | 15: "TABLE", 329 | 16: "GET", 330 | 78: "GET_ALL", 331 | 17: "EQ", 332 | 18: "NE", 333 | 19: "LT", 334 | 20: "LE", 335 | 21: "GT", 336 | 22: "GE", 337 | 23: "NOT", 338 | 24: "ADD", 339 | 25: "SUB", 340 | 26: "MUL", 341 | 27: "DIV", 342 | 28: "MOD", 343 | 29: "APPEND", 344 | 80: "PREPEND", 345 | 95: "DIFFERENCE", 346 | 88: "SET_INSERT", 347 | 89: "SET_INTERSECTION", 348 | 90: "SET_UNION", 349 | 91: "SET_DIFFERENCE", 350 | 30: "SLICE", 351 | 70: "SKIP", 352 | 71: "LIMIT", 353 | 87: "INDEXES_OF", 354 | 93: "CONTAINS", 355 | 31: "GET_FIELD", 356 | 94: "KEYS", 357 | 32: "HAS_FIELDS", 358 | 96: "WITH_FIELDS", 359 | 33: "PLUCK", 360 | 34: "WITHOUT", 361 | 35: "MERGE", 362 | 36: "BETWEEN", 363 | 37: "REDUCE", 364 | 38: "MAP", 365 | 39: "FILTER", 366 | 40: "CONCATMAP", 367 | 41: "ORDERBY", 368 | 42: "DISTINCT", 369 | 43: "COUNT", 370 | 86: "IS_EMPTY", 371 | 44: "UNION", 372 | 45: "NTH", 373 | 46: "GROUPED_MAP_REDUCE", 374 | 47: "GROUPBY", 375 | 48: "INNER_JOIN", 376 | 49: "OUTER_JOIN", 377 | 50: "EQ_JOIN", 378 | 72: "ZIP", 379 | 82: "INSERT_AT", 380 | 83: "DELETE_AT", 381 | 84: "CHANGE_AT", 382 | 85: "SPLICE_AT", 383 | 51: "COERCE_TO", 384 | 52: "TYPEOF", 385 | 53: "UPDATE", 386 | 54: "DELETE", 387 | 55: "REPLACE", 388 | 56: "INSERT", 389 | 57: "DB_CREATE", 390 | 58: "DB_DROP", 391 | 59: "DB_LIST", 392 | 60: "TABLE_CREATE", 393 | 61: "TABLE_DROP", 394 | 62: "TABLE_LIST", 395 | 75: "INDEX_CREATE", 396 | 76: "INDEX_DROP", 397 | 77: "INDEX_LIST", 398 | 64: "FUNCALL", 399 | 65: "BRANCH", 400 | 66: "ANY", 401 | 67: "ALL", 402 | 68: "FOREACH", 403 | 69: "FUNC", 404 | 73: "ASC", 405 | 74: "DESC", 406 | 79: "INFO", 407 | 97: "MATCH", 408 | 81: "SAMPLE", 409 | 92: "DEFAULT", 410 | 98: "JSON", 411 | } 412 | var Term_TermType_value = map[string]int32{ 413 | "DATUM": 1, 414 | "MAKE_ARRAY": 2, 415 | "MAKE_OBJ": 3, 416 | "VAR": 10, 417 | "JAVASCRIPT": 11, 418 | "ERROR": 12, 419 | "IMPLICIT_VAR": 13, 420 | "DB": 14, 421 | "TABLE": 15, 422 | "GET": 16, 423 | "GET_ALL": 78, 424 | "EQ": 17, 425 | "NE": 18, 426 | "LT": 19, 427 | "LE": 20, 428 | "GT": 21, 429 | "GE": 22, 430 | "NOT": 23, 431 | "ADD": 24, 432 | "SUB": 25, 433 | "MUL": 26, 434 | "DIV": 27, 435 | "MOD": 28, 436 | "APPEND": 29, 437 | "PREPEND": 80, 438 | "DIFFERENCE": 95, 439 | "SET_INSERT": 88, 440 | "SET_INTERSECTION": 89, 441 | "SET_UNION": 90, 442 | "SET_DIFFERENCE": 91, 443 | "SLICE": 30, 444 | "SKIP": 70, 445 | "LIMIT": 71, 446 | "INDEXES_OF": 87, 447 | "CONTAINS": 93, 448 | "GET_FIELD": 31, 449 | "KEYS": 94, 450 | "HAS_FIELDS": 32, 451 | "WITH_FIELDS": 96, 452 | "PLUCK": 33, 453 | "WITHOUT": 34, 454 | "MERGE": 35, 455 | "BETWEEN": 36, 456 | "REDUCE": 37, 457 | "MAP": 38, 458 | "FILTER": 39, 459 | "CONCATMAP": 40, 460 | "ORDERBY": 41, 461 | "DISTINCT": 42, 462 | "COUNT": 43, 463 | "IS_EMPTY": 86, 464 | "UNION": 44, 465 | "NTH": 45, 466 | "GROUPED_MAP_REDUCE": 46, 467 | "GROUPBY": 47, 468 | "INNER_JOIN": 48, 469 | "OUTER_JOIN": 49, 470 | "EQ_JOIN": 50, 471 | "ZIP": 72, 472 | "INSERT_AT": 82, 473 | "DELETE_AT": 83, 474 | "CHANGE_AT": 84, 475 | "SPLICE_AT": 85, 476 | "COERCE_TO": 51, 477 | "TYPEOF": 52, 478 | "UPDATE": 53, 479 | "DELETE": 54, 480 | "REPLACE": 55, 481 | "INSERT": 56, 482 | "DB_CREATE": 57, 483 | "DB_DROP": 58, 484 | "DB_LIST": 59, 485 | "TABLE_CREATE": 60, 486 | "TABLE_DROP": 61, 487 | "TABLE_LIST": 62, 488 | "INDEX_CREATE": 75, 489 | "INDEX_DROP": 76, 490 | "INDEX_LIST": 77, 491 | "FUNCALL": 64, 492 | "BRANCH": 65, 493 | "ANY": 66, 494 | "ALL": 67, 495 | "FOREACH": 68, 496 | "FUNC": 69, 497 | "ASC": 73, 498 | "DESC": 74, 499 | "INFO": 79, 500 | "MATCH": 97, 501 | "SAMPLE": 81, 502 | "DEFAULT": 92, 503 | "JSON": 98, 504 | } 505 | 506 | func (x Term_TermType) Enum() *Term_TermType { 507 | p := new(Term_TermType) 508 | *p = x 509 | return p 510 | } 511 | func (x Term_TermType) String() string { 512 | return proto.EnumName(Term_TermType_name, int32(x)) 513 | } 514 | func (x Term_TermType) MarshalJSON() ([]byte, error) { 515 | return json.Marshal(x.String()) 516 | } 517 | func (x *Term_TermType) UnmarshalJSON(data []byte) error { 518 | value, err := proto.UnmarshalJSONEnum(Term_TermType_value, data, "Term_TermType") 519 | if err != nil { 520 | return err 521 | } 522 | *x = Term_TermType(value) 523 | return nil 524 | } 525 | 526 | type VersionDummy struct { 527 | XXX_unrecognized []byte `json:"-"` 528 | } 529 | 530 | func (m *VersionDummy) Reset() { *m = VersionDummy{} } 531 | func (m *VersionDummy) String() string { return proto.CompactTextString(m) } 532 | func (*VersionDummy) ProtoMessage() {} 533 | 534 | type Query struct { 535 | Type *Query_QueryType `protobuf:"varint,1,opt,name=type,enum=Query_QueryType" json:"type,omitempty"` 536 | Query *Term `protobuf:"bytes,2,opt,name=query" json:"query,omitempty"` 537 | Token *int64 `protobuf:"varint,3,opt,name=token" json:"token,omitempty"` 538 | OBSOLETENoreply *bool `protobuf:"varint,4,opt,name=OBSOLETE_noreply,def=0" json:"OBSOLETE_noreply,omitempty"` 539 | GlobalOptargs []*Query_AssocPair `protobuf:"bytes,6,rep,name=global_optargs" json:"global_optargs,omitempty"` 540 | XXX_unrecognized []byte `json:"-"` 541 | } 542 | 543 | func (m *Query) Reset() { *m = Query{} } 544 | func (m *Query) String() string { return proto.CompactTextString(m) } 545 | func (*Query) ProtoMessage() {} 546 | 547 | const Default_Query_OBSOLETENoreply bool = false 548 | 549 | func (m *Query) GetType() Query_QueryType { 550 | if m != nil && m.Type != nil { 551 | return *m.Type 552 | } 553 | return 0 554 | } 555 | 556 | func (m *Query) GetQuery() *Term { 557 | if m != nil { 558 | return m.Query 559 | } 560 | return nil 561 | } 562 | 563 | func (m *Query) GetToken() int64 { 564 | if m != nil && m.Token != nil { 565 | return *m.Token 566 | } 567 | return 0 568 | } 569 | 570 | func (m *Query) GetOBSOLETENoreply() bool { 571 | if m != nil && m.OBSOLETENoreply != nil { 572 | return *m.OBSOLETENoreply 573 | } 574 | return Default_Query_OBSOLETENoreply 575 | } 576 | 577 | func (m *Query) GetGlobalOptargs() []*Query_AssocPair { 578 | if m != nil { 579 | return m.GlobalOptargs 580 | } 581 | return nil 582 | } 583 | 584 | type Query_AssocPair struct { 585 | Key *string `protobuf:"bytes,1,opt,name=key" json:"key,omitempty"` 586 | Val *Term `protobuf:"bytes,2,opt,name=val" json:"val,omitempty"` 587 | XXX_unrecognized []byte `json:"-"` 588 | } 589 | 590 | func (m *Query_AssocPair) Reset() { *m = Query_AssocPair{} } 591 | func (m *Query_AssocPair) String() string { return proto.CompactTextString(m) } 592 | func (*Query_AssocPair) ProtoMessage() {} 593 | 594 | func (m *Query_AssocPair) GetKey() string { 595 | if m != nil && m.Key != nil { 596 | return *m.Key 597 | } 598 | return "" 599 | } 600 | 601 | func (m *Query_AssocPair) GetVal() *Term { 602 | if m != nil { 603 | return m.Val 604 | } 605 | return nil 606 | } 607 | 608 | type Frame struct { 609 | Type *Frame_FrameType `protobuf:"varint,1,opt,name=type,enum=Frame_FrameType" json:"type,omitempty"` 610 | Pos *int64 `protobuf:"varint,2,opt,name=pos" json:"pos,omitempty"` 611 | Opt *string `protobuf:"bytes,3,opt,name=opt" json:"opt,omitempty"` 612 | XXX_unrecognized []byte `json:"-"` 613 | } 614 | 615 | func (m *Frame) Reset() { *m = Frame{} } 616 | func (m *Frame) String() string { return proto.CompactTextString(m) } 617 | func (*Frame) ProtoMessage() {} 618 | 619 | func (m *Frame) GetType() Frame_FrameType { 620 | if m != nil && m.Type != nil { 621 | return *m.Type 622 | } 623 | return 0 624 | } 625 | 626 | func (m *Frame) GetPos() int64 { 627 | if m != nil && m.Pos != nil { 628 | return *m.Pos 629 | } 630 | return 0 631 | } 632 | 633 | func (m *Frame) GetOpt() string { 634 | if m != nil && m.Opt != nil { 635 | return *m.Opt 636 | } 637 | return "" 638 | } 639 | 640 | type Backtrace struct { 641 | Frames []*Frame `protobuf:"bytes,1,rep,name=frames" json:"frames,omitempty"` 642 | XXX_unrecognized []byte `json:"-"` 643 | } 644 | 645 | func (m *Backtrace) Reset() { *m = Backtrace{} } 646 | func (m *Backtrace) String() string { return proto.CompactTextString(m) } 647 | func (*Backtrace) ProtoMessage() {} 648 | 649 | func (m *Backtrace) GetFrames() []*Frame { 650 | if m != nil { 651 | return m.Frames 652 | } 653 | return nil 654 | } 655 | 656 | type Response struct { 657 | Type *Response_ResponseType `protobuf:"varint,1,opt,name=type,enum=Response_ResponseType" json:"type,omitempty"` 658 | Token *int64 `protobuf:"varint,2,opt,name=token" json:"token,omitempty"` 659 | Response []*Datum `protobuf:"bytes,3,rep,name=response" json:"response,omitempty"` 660 | Backtrace *Backtrace `protobuf:"bytes,4,opt,name=backtrace" json:"backtrace,omitempty"` 661 | XXX_unrecognized []byte `json:"-"` 662 | } 663 | 664 | func (m *Response) Reset() { *m = Response{} } 665 | func (m *Response) String() string { return proto.CompactTextString(m) } 666 | func (*Response) ProtoMessage() {} 667 | 668 | func (m *Response) GetType() Response_ResponseType { 669 | if m != nil && m.Type != nil { 670 | return *m.Type 671 | } 672 | return 0 673 | } 674 | 675 | func (m *Response) GetToken() int64 { 676 | if m != nil && m.Token != nil { 677 | return *m.Token 678 | } 679 | return 0 680 | } 681 | 682 | func (m *Response) GetResponse() []*Datum { 683 | if m != nil { 684 | return m.Response 685 | } 686 | return nil 687 | } 688 | 689 | func (m *Response) GetBacktrace() *Backtrace { 690 | if m != nil { 691 | return m.Backtrace 692 | } 693 | return nil 694 | } 695 | 696 | type Datum struct { 697 | Type *Datum_DatumType `protobuf:"varint,1,opt,name=type,enum=Datum_DatumType" json:"type,omitempty"` 698 | RBool *bool `protobuf:"varint,2,opt,name=r_bool" json:"r_bool,omitempty"` 699 | RNum *float64 `protobuf:"fixed64,3,opt,name=r_num" json:"r_num,omitempty"` 700 | RStr *string `protobuf:"bytes,4,opt,name=r_str" json:"r_str,omitempty"` 701 | RArray []*Datum `protobuf:"bytes,5,rep,name=r_array" json:"r_array,omitempty"` 702 | RObject []*Datum_AssocPair `protobuf:"bytes,6,rep,name=r_object" json:"r_object,omitempty"` 703 | XXX_extensions map[int32]proto.Extension `json:"-"` 704 | XXX_unrecognized []byte `json:"-"` 705 | } 706 | 707 | func (m *Datum) Reset() { *m = Datum{} } 708 | func (m *Datum) String() string { return proto.CompactTextString(m) } 709 | func (*Datum) ProtoMessage() {} 710 | 711 | var extRange_Datum = []proto.ExtensionRange{ 712 | {10000, 20000}, 713 | } 714 | 715 | func (*Datum) ExtensionRangeArray() []proto.ExtensionRange { 716 | return extRange_Datum 717 | } 718 | func (m *Datum) ExtensionMap() map[int32]proto.Extension { 719 | if m.XXX_extensions == nil { 720 | m.XXX_extensions = make(map[int32]proto.Extension) 721 | } 722 | return m.XXX_extensions 723 | } 724 | 725 | func (m *Datum) GetType() Datum_DatumType { 726 | if m != nil && m.Type != nil { 727 | return *m.Type 728 | } 729 | return 0 730 | } 731 | 732 | func (m *Datum) GetRBool() bool { 733 | if m != nil && m.RBool != nil { 734 | return *m.RBool 735 | } 736 | return false 737 | } 738 | 739 | func (m *Datum) GetRNum() float64 { 740 | if m != nil && m.RNum != nil { 741 | return *m.RNum 742 | } 743 | return 0 744 | } 745 | 746 | func (m *Datum) GetRStr() string { 747 | if m != nil && m.RStr != nil { 748 | return *m.RStr 749 | } 750 | return "" 751 | } 752 | 753 | func (m *Datum) GetRArray() []*Datum { 754 | if m != nil { 755 | return m.RArray 756 | } 757 | return nil 758 | } 759 | 760 | func (m *Datum) GetRObject() []*Datum_AssocPair { 761 | if m != nil { 762 | return m.RObject 763 | } 764 | return nil 765 | } 766 | 767 | type Datum_AssocPair struct { 768 | Key *string `protobuf:"bytes,1,opt,name=key" json:"key,omitempty"` 769 | Val *Datum `protobuf:"bytes,2,opt,name=val" json:"val,omitempty"` 770 | XXX_unrecognized []byte `json:"-"` 771 | } 772 | 773 | func (m *Datum_AssocPair) Reset() { *m = Datum_AssocPair{} } 774 | func (m *Datum_AssocPair) String() string { return proto.CompactTextString(m) } 775 | func (*Datum_AssocPair) ProtoMessage() {} 776 | 777 | func (m *Datum_AssocPair) GetKey() string { 778 | if m != nil && m.Key != nil { 779 | return *m.Key 780 | } 781 | return "" 782 | } 783 | 784 | func (m *Datum_AssocPair) GetVal() *Datum { 785 | if m != nil { 786 | return m.Val 787 | } 788 | return nil 789 | } 790 | 791 | type Term struct { 792 | Type *Term_TermType `protobuf:"varint,1,opt,name=type,enum=Term_TermType" json:"type,omitempty"` 793 | Datum *Datum `protobuf:"bytes,2,opt,name=datum" json:"datum,omitempty"` 794 | Args []*Term `protobuf:"bytes,3,rep,name=args" json:"args,omitempty"` 795 | Optargs []*Term_AssocPair `protobuf:"bytes,4,rep,name=optargs" json:"optargs,omitempty"` 796 | XXX_extensions map[int32]proto.Extension `json:"-"` 797 | XXX_unrecognized []byte `json:"-"` 798 | } 799 | 800 | func (m *Term) Reset() { *m = Term{} } 801 | func (m *Term) String() string { return proto.CompactTextString(m) } 802 | func (*Term) ProtoMessage() {} 803 | 804 | var extRange_Term = []proto.ExtensionRange{ 805 | {10000, 20000}, 806 | } 807 | 808 | func (*Term) ExtensionRangeArray() []proto.ExtensionRange { 809 | return extRange_Term 810 | } 811 | func (m *Term) ExtensionMap() map[int32]proto.Extension { 812 | if m.XXX_extensions == nil { 813 | m.XXX_extensions = make(map[int32]proto.Extension) 814 | } 815 | return m.XXX_extensions 816 | } 817 | 818 | func (m *Term) GetType() Term_TermType { 819 | if m != nil && m.Type != nil { 820 | return *m.Type 821 | } 822 | return 0 823 | } 824 | 825 | func (m *Term) GetDatum() *Datum { 826 | if m != nil { 827 | return m.Datum 828 | } 829 | return nil 830 | } 831 | 832 | func (m *Term) GetArgs() []*Term { 833 | if m != nil { 834 | return m.Args 835 | } 836 | return nil 837 | } 838 | 839 | func (m *Term) GetOptargs() []*Term_AssocPair { 840 | if m != nil { 841 | return m.Optargs 842 | } 843 | return nil 844 | } 845 | 846 | type Term_AssocPair struct { 847 | Key *string `protobuf:"bytes,1,opt,name=key" json:"key,omitempty"` 848 | Val *Term `protobuf:"bytes,2,opt,name=val" json:"val,omitempty"` 849 | XXX_unrecognized []byte `json:"-"` 850 | } 851 | 852 | func (m *Term_AssocPair) Reset() { *m = Term_AssocPair{} } 853 | func (m *Term_AssocPair) String() string { return proto.CompactTextString(m) } 854 | func (*Term_AssocPair) ProtoMessage() {} 855 | 856 | func (m *Term_AssocPair) GetKey() string { 857 | if m != nil && m.Key != nil { 858 | return *m.Key 859 | } 860 | return "" 861 | } 862 | 863 | func (m *Term_AssocPair) GetVal() *Term { 864 | if m != nil { 865 | return m.Val 866 | } 867 | return nil 868 | } 869 | 870 | func init() { 871 | proto.RegisterEnum("VersionDummy_Version", VersionDummy_Version_name, VersionDummy_Version_value) 872 | proto.RegisterEnum("Query_QueryType", Query_QueryType_name, Query_QueryType_value) 873 | proto.RegisterEnum("Frame_FrameType", Frame_FrameType_name, Frame_FrameType_value) 874 | proto.RegisterEnum("Response_ResponseType", Response_ResponseType_name, Response_ResponseType_value) 875 | proto.RegisterEnum("Datum_DatumType", Datum_DatumType_name, Datum_DatumType_value) 876 | proto.RegisterEnum("Term_TermType", Term_TermType_name, Term_TermType_value) 877 | } 878 | -------------------------------------------------------------------------------- /ql2/ql2.proto: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // THE HIGH-LEVEL VIEW // 3 | //////////////////////////////////////////////////////////////////////////////// 4 | 5 | // Process: When you first open a connection, send the magic number 6 | // for the version of the protobuf you're targetting (in the [Version] 7 | // enum). This should **NOT** be sent as a protobuf; just send the 8 | // little-endian 32-bit integer over the wire raw. This number should 9 | // only be sent once per connection. 10 | 11 | // The magic number shall be followed by an authorization key. The 12 | // first 4 bytes are the length of the key to be sent as a little-endian 13 | // 32-bit integer, followed by the key string. Even if there is no key, 14 | // an empty string should be sent (length 0 and no data). The server will 15 | // then respond with a NULL-terminated string response. "SUCCESS" indicates 16 | // that the connection has been accepted. Any other response indicates an 17 | // error, and the response string should describe the error. 18 | 19 | // Next, for each query you want to send, construct a [Query] protobuf 20 | // and serialize it to a binary blob. Send the blob's size to the 21 | // server encoded as a little-endian 32-bit integer, followed by the 22 | // blob itself. You will recieve a [Response] protobuf back preceded 23 | // by its own size, once again encoded as a little-endian 32-bit 24 | // integer. You can see an example exchange below in **EXAMPLE**. 25 | 26 | // A query consists of a [Term] to evaluate and a unique-per-connection 27 | // [token]. 28 | 29 | // Tokens are used for two things: 30 | // * Keeping track of which responses correspond to which queries. 31 | // * Batched queries. Some queries return lots of results, so we send back 32 | // batches of <1000, and you need to send a [CONTINUE] query with the same 33 | // token to get more results from the original query. 34 | //////////////////////////////////////////////////////////////////////////////// 35 | 36 | // This enum contains the magic numbers for your version. See **THE HIGH-LEVEL 37 | // VIEW** for what to do with it. 38 | message VersionDummy { // We need to wrap it like this for some 39 | // non-conforming protobuf libraries 40 | enum Version { 41 | V0_1 = 0x3f61ba36; 42 | V0_2 = 0x723081e1; 43 | } 44 | } 45 | 46 | // You send one of: 47 | // * A [START] query with a [Term] to evaluate and a unique-per-connection token. 48 | // * A [CONTINUE] query with the same token as a [START] query that returned 49 | // [SUCCESS_PARTIAL] in its [Response]. 50 | // * A [STOP] query with the same token as a [START] query that you want to stop. 51 | message Query { 52 | enum QueryType { 53 | START = 1; // Start a new query. 54 | CONTINUE = 2; // Continue a query that returned [SUCCESS_PARTIAL] 55 | // (see [Response]). 56 | STOP = 3; // Stop a query partway through executing. 57 | } 58 | optional QueryType type = 1; 59 | // A [Term] is how we represent the operations we want a query to perform. 60 | optional Term query = 2; // only present when [type] = [START] 61 | optional int64 token = 3; 62 | // This flag is ignored on the server. `noreply` should be added 63 | // to `global_optargs` instead (the key "noreply" should map to 64 | // either true or false). 65 | optional bool OBSOLETE_noreply = 4 [default = false]; 66 | 67 | message AssocPair { 68 | optional string key = 1; 69 | optional Term val = 2; 70 | } 71 | repeated AssocPair global_optargs = 6; 72 | } 73 | 74 | // A backtrace frame (see `backtrace` in Response below) 75 | message Frame { 76 | enum FrameType { 77 | POS = 1; // Error occured in a positional argument. 78 | OPT = 2; // Error occured in an optional argument. 79 | } 80 | optional FrameType type = 1; 81 | optional int64 pos = 2; // The index of the positional argument. 82 | optional string opt = 3; // The name of the optional argument. 83 | } 84 | message Backtrace { 85 | repeated Frame frames = 1; 86 | } 87 | 88 | // You get back a response with the same [token] as your query. 89 | message Response { 90 | enum ResponseType { 91 | // These response types indicate success. 92 | SUCCESS_ATOM = 1; // Query returned a single RQL datatype. 93 | SUCCESS_SEQUENCE = 2; // Query returned a sequence of RQL datatypes. 94 | SUCCESS_PARTIAL = 3; // Query returned a partial sequence of RQL 95 | // datatypes. If you send a [CONTINUE] query with 96 | // the same token as this response, you will get 97 | // more of the sequence. Keep sending [CONTINUE] 98 | // queries until you get back [SUCCESS_SEQUENCE]. 99 | 100 | // These response types indicate failure. 101 | CLIENT_ERROR = 16; // Means the client is buggy. An example is if the 102 | // client sends a malformed protobuf, or tries to 103 | // send [CONTINUE] for an unknown token. 104 | COMPILE_ERROR = 17; // Means the query failed during parsing or type 105 | // checking. For example, if you pass too many 106 | // arguments to a function. 107 | RUNTIME_ERROR = 18; // Means the query failed at runtime. An example is 108 | // if you add together two values from a table, but 109 | // they turn out at runtime to be booleans rather 110 | // than numbers. 111 | } 112 | optional ResponseType type = 1; 113 | optional int64 token = 2; // Indicates what [Query] this response corresponds to. 114 | 115 | // [response] contains 1 RQL datum if [type] is [SUCCESS_ATOM], or many RQL 116 | // data if [type] is [SUCCESS_SEQUENCE] or [SUCCESS_PARTIAL]. It contains 1 117 | // error message (of type [R_STR]) in all other cases. 118 | repeated Datum response = 3; 119 | 120 | // If [type] is [CLIENT_ERROR], [TYPE_ERROR], or [RUNTIME_ERROR], then a 121 | // backtrace will be provided. The backtrace says where in the query the 122 | // error occured. Ideally this information will be presented to the user as 123 | // a pretty-printed version of their query with the erroneous section 124 | // underlined. A backtrace is a series of 0 or more [Frame]s, each of which 125 | // specifies either the index of a positional argument or the name of an 126 | // optional argument. (Those words will make more sense if you look at the 127 | // [Term] message below.) 128 | 129 | optional Backtrace backtrace = 4; // Contains n [Frame]s when you get back an error. 130 | } 131 | 132 | // A [Datum] is a chunk of data that can be serialized to disk or returned to 133 | // the user in a Response. Currently we only support JSON types, but we may 134 | // support other types in the future (e.g., a date type or an integer type). 135 | message Datum { 136 | enum DatumType { 137 | R_NULL = 1; 138 | R_BOOL = 2; 139 | R_NUM = 3; // a double 140 | R_STR = 4; 141 | R_ARRAY = 5; 142 | R_OBJECT = 6; 143 | } 144 | optional DatumType type = 1; 145 | optional bool r_bool = 2; 146 | optional double r_num = 3; 147 | optional string r_str = 4; 148 | 149 | repeated Datum r_array = 5; 150 | message AssocPair { 151 | optional string key = 1; 152 | optional Datum val = 2; 153 | } 154 | repeated AssocPair r_object = 6; 155 | 156 | extensions 10000 to 20000; 157 | } 158 | 159 | // A [Term] is either a piece of data (see **Datum** above), or an operator and 160 | // its operands. If you have a [Datum], it's stored in the member [datum]. If 161 | // you have an operator, its positional arguments are stored in [args] and its 162 | // optional arguments are stored in [optargs]. 163 | // 164 | // A note about type signatures: 165 | // We use the following notation to denote types: 166 | // arg1_type, arg2_type, argrest_type... -> result_type 167 | // So, for example, if we have a function `avg` that takes any number of 168 | // arguments and averages them, we might write: 169 | // NUMBER... -> NUMBER 170 | // Or if we had a function that took one number modulo another: 171 | // NUMBER, NUMBER -> NUMBER 172 | // Or a function that takes a table and a primary key of any Datum type, then 173 | // retrieves the entry with that primary key: 174 | // Table, DATUM -> OBJECT 175 | // Some arguments must be provided as literal values (and not the results of sub 176 | // terms). These are marked with a `!`. 177 | // Optional arguments are specified within curly braces as argname `:` value 178 | // type (e.x `{use_outdated:BOOL}`) 179 | // Many RQL operations are polymorphic. For these, alterantive type signatures 180 | // are separated by `|`. 181 | // 182 | // The RQL type hierarchy is as follows: 183 | // Top 184 | // DATUM 185 | // NULL 186 | // BOOL 187 | // NUMBER 188 | // STRING 189 | // OBJECT 190 | // SingleSelection 191 | // ARRAY 192 | // Sequence 193 | // ARRAY 194 | // Stream 195 | // StreamSelection 196 | // Table 197 | // Database 198 | // Function 199 | // Ordering - used only by ORDER_BY 200 | // Error 201 | message Term { 202 | enum TermType { 203 | // A RQL datum, stored in `datum` below. 204 | DATUM = 1; 205 | 206 | MAKE_ARRAY = 2; // DATUM... -> ARRAY 207 | // Evaluate the terms in [optargs] and make an object 208 | MAKE_OBJ = 3; // {...} -> OBJECT 209 | 210 | // * Compound types 211 | // Takes an integer representing a variable and returns the value stored 212 | // in that variable. It's the responsibility of the client to translate 213 | // from their local representation of a variable to a unique integer for 214 | // that variable. (We do it this way instead of letting clients provide 215 | // variable names as strings to discourage variable-capturing client 216 | // libraries, and because it's more efficient on the wire.) 217 | VAR = 10; // !NUMBER -> DATUM 218 | // Takes some javascript code and executes it. 219 | JAVASCRIPT = 11; // STRING {timeout: !NUMBER} -> DATUM | 220 | // STRING {timeout: !NUMBER} -> Function(*) 221 | 222 | // Takes a string and throws an error with that message. 223 | // Inside of a `default` block, you can omit the first 224 | // argument to rethrow whatever error you catch (this is most 225 | // useful as an argument to the `default` filter optarg). 226 | ERROR = 12; // STRING -> Error | -> Error 227 | // Takes nothing and returns a reference to the implicit variable. 228 | IMPLICIT_VAR = 13; // -> DATUM 229 | 230 | // * Data Operators 231 | // Returns a reference to a database. 232 | DB = 14; // STRING -> Database 233 | // Returns a reference to a table. 234 | TABLE = 15; // Database, STRING, {use_outdated:BOOL} -> Table | STRING, {use_outdated:BOOL} -> Table 235 | // Gets a single element from a table by its primary or a secondary key. 236 | GET = 16; // Table, STRING -> SingleSelection | Table, NUMBER -> SingleSelection | 237 | // Table, STRING -> NULL | Table, NUMBER -> NULL | 238 | GET_ALL = 78; // Table, DATUM..., {index:!STRING} => ARRAY 239 | 240 | // Simple DATUM Ops 241 | EQ = 17; // DATUM... -> BOOL 242 | NE = 18; // DATUM... -> BOOL 243 | LT = 19; // DATUM... -> BOOL 244 | LE = 20; // DATUM... -> BOOL 245 | GT = 21; // DATUM... -> BOOL 246 | GE = 22; // DATUM... -> BOOL 247 | NOT = 23; // BOOL -> BOOL 248 | // ADD can either add two numbers or concatenate two arrays. 249 | ADD = 24; // NUMBER... -> NUMBER | STRING... -> STRING 250 | SUB = 25; // NUMBER... -> NUMBER 251 | MUL = 26; // NUMBER... -> NUMBER 252 | DIV = 27; // NUMBER... -> NUMBER 253 | MOD = 28; // NUMBER, NUMBER -> NUMBER 254 | 255 | // DATUM Array Ops 256 | // Append a single element to the end of an array (like `snoc`). 257 | APPEND = 29; // ARRAY, DATUM -> ARRAY 258 | // Prepend a single element to the end of an array (like `cons`). 259 | PREPEND = 80; // ARRAY, DATUM -> ARRAY 260 | //Remove the elements of one array from another array. 261 | DIFFERENCE = 95; // ARRAY, ARRAY -> ARRAY 262 | 263 | // DATUM Set Ops 264 | // Set ops work on arrays. They don't use actual sets and thus have 265 | // performance characteristics you would expect from arrays rather than 266 | // from sets. All set operations have the post condition that they 267 | // array they return contains no duplicate values. 268 | SET_INSERT = 88; // ARRAY, DATUM -> ARRAY 269 | SET_INTERSECTION = 89; // ARRAY, ARRAY -> ARRAY 270 | SET_UNION = 90; // ARRAY, ARRAY -> ARRAY 271 | SET_DIFFERENCE = 91; // ARRAY, ARRAY -> ARRAY 272 | 273 | SLICE = 30; // Sequence, NUMBER, NUMBER -> Sequence 274 | SKIP = 70; // Sequence, NUMBER -> Sequence 275 | LIMIT = 71; // Sequence, NUMBER -> Sequence 276 | INDEXES_OF = 87; // Sequence, DATUM -> Sequence | Sequence, Function(1) -> Sequence 277 | CONTAINS = 93; // Sequence, DATUM -> BOOL 278 | 279 | // Stream/Object Ops 280 | // Get a particular field from an object, or map that over a 281 | // sequence. 282 | GET_FIELD = 31; // OBJECT, STRING -> DATUM 283 | // | Sequence, STRING -> Sequence 284 | // Return an array containing the keys of the object. 285 | KEYS = 94; // OBJECT -> ARRAY 286 | // Check whether an object contains all the specified fields, 287 | // or filters a sequence so that all objects inside of it 288 | // contain all the specified fields. 289 | HAS_FIELDS = 32; // OBJECT, STRING... -> BOOL 290 | // x.with_fields(...) <=> x.has_fields(...).pluck(...) 291 | WITH_FIELDS = 96; // Sequence, STRING... -> Sequence 292 | // Get a subset of an object by selecting some attributes to preserve, 293 | // or map that over a sequence. (Both pick and pluck, polymorphic.) 294 | PLUCK = 33; // Sequence, STRING... -> Sequence | OBJECT, STRING... -> OBJECT 295 | // Get a subset of an object by selecting some attributes to discard, or 296 | // map that over a sequence. (Both unpick and without, polymorphic.) 297 | WITHOUT = 34; // Sequence, STRING... -> Sequence | OBJECT, STRING... -> OBJECT 298 | // Merge objects (right-preferential) 299 | MERGE = 35; // OBJECT... -> OBJECT | Sequence -> Sequence 300 | 301 | // Sequence Ops 302 | // Get all elements of a sequence between two values. 303 | BETWEEN = 36; // StreamSelection, DATUM, DATUM, {:index:!STRING} -> StreamSelection 304 | REDUCE = 37; // Sequence, Function(2), {base:DATUM} -> DATUM 305 | MAP = 38; // Sequence, Function(1) -> Sequence 306 | 307 | // Filter a sequence with either a function or a shortcut 308 | // object (see API docs for details). The body of FILTER is 309 | // wrapped in an implicit `.default(false)`, and you can 310 | // change the default value by specifying the `default` 311 | // optarg. If you make the default `r.error`, all errors 312 | // caught by `default` will be rethrown as if the `default` 313 | // did not exist. 314 | FILTER = 39; // Sequence, Function(1), {default:DATUM} -> Sequence | 315 | // Sequence, OBJECT, {default:DATUM} -> Sequence 316 | // Map a function over a sequence and then concatenate the results together. 317 | CONCATMAP = 40; // Sequence, Function(1) -> Sequence 318 | // Order a sequence based on one or more attributes. 319 | ORDERBY = 41; // Sequence, (!STRING | Ordering)... -> Sequence 320 | // Get all distinct elements of a sequence (like `uniq`). 321 | DISTINCT = 42; // Sequence -> Sequence 322 | // Count the number of elements in a sequence, or only the elements that match 323 | // a given filter. 324 | COUNT = 43; // Sequence -> NUMBER | Sequence, DATUM -> NUMBER | Sequence, Function(1) -> NUMBER 325 | IS_EMPTY = 86; // Sequence -> BOOL 326 | // Take the union of multiple sequences (preserves duplicate elements! (use distinct)). 327 | UNION = 44; // Sequence... -> Sequence 328 | // Get the Nth element of a sequence. 329 | NTH = 45; // Sequence, NUMBER -> DATUM 330 | // Takes a sequence, and three functions: 331 | // - A function to group the sequence by. 332 | // - A function to map over the groups. 333 | // - A reduction to apply to each of the groups. 334 | GROUPED_MAP_REDUCE = 46; // Sequence, Function(1), Function(1), Function(2), {base:DATUM} -> ARRAY 335 | // Groups a sequence by one or more attributes, and then applies a reduction. 336 | // The third argument is a special object literal giving the kind of operation to be 337 | // performed and any necessary arguments. 338 | // At present, GROUPBY suports the following operations 339 | // * {'COUNT': } - count the size of the group 340 | // * {'SUM': attr} - sum the values of the given attribute across the group 341 | // * {'AVG': attr} - average the values of the given attribute across the group 342 | GROUPBY = 47; // Sequence, ARRAY, !GROUP_BY_OBJECT -> Sequence 343 | INNER_JOIN = 48; // Sequence, Sequence, Function(2) -> Sequence 344 | OUTER_JOIN = 49; // Sequence, Sequence, Function(2) -> Sequence 345 | // An inner-join that does an equality comparison on two attributes. 346 | EQ_JOIN = 50; // Sequence, !STRING, Sequence, {index:!STRING} -> Sequence 347 | ZIP = 72; // Sequence -> Sequence 348 | 349 | // Array Ops 350 | // Insert an element in to an array at a given index. 351 | INSERT_AT = 82; // ARRAY, NUMBER, DATUM -> ARRAY 352 | // Remove an element at a given index from an array. 353 | DELETE_AT = 83; // ARRAY, NUMBER -> ARRAY | 354 | // ARRAY, NUMBER, NUMBER -> ARRAY 355 | // Change the element at a given index of an array. 356 | CHANGE_AT = 84; // ARRAY, NUMBER, DATUM -> ARRAY 357 | // Splice one array in to another array. 358 | SPLICE_AT = 85; // ARRAY, NUMBER, ARRAY -> ARRAY 359 | 360 | // * Type Ops 361 | // Coerces a datum to a named type (e.g. "bool"). 362 | // If you previously used `stream_to_array`, you should use this instead 363 | // with the type "array". 364 | COERCE_TO = 51; // Top, STRING -> Top 365 | // Returns the named type of a datum (e.g. TYPEOF(true) = "BOOL") 366 | TYPEOF = 52; // Top -> STRING 367 | 368 | // * Write Ops (the OBJECTs contain data about number of errors etc.) 369 | // Updates all the rows in a selection. Calls its Function with the row 370 | // to be updated, and then merges the result of that call. 371 | UPDATE = 53; // StreamSelection, Function(1), {non_atomic:BOOL, durability:STRING} -> OBJECT | 372 | // SingleSelection, Function(1), {non_atomic:BOOL, durability:STRING} -> OBJECT | 373 | // StreamSelection, OBJECT, {non_atomic:BOOL, durability:STRING} -> OBJECT | 374 | // SingleSelection, OBJECT, {non_atomic:BOOL, durability:STRING} -> OBJECT 375 | // Deletes all the rows in a selection. 376 | DELETE = 54; // StreamSelection, {durability:STRING} -> OBJECT | SingleSelection -> OBJECT 377 | // Replaces all the rows in a selection. Calls its Function with the row 378 | // to be replaced, and then discards it and stores the result of that 379 | // call. 380 | REPLACE = 55; // StreamSelection, Function(1), {non_atomic:BOOL} -> OBJECT | SingleSelection, Function(1), {non_atomic:BOOL, durability:STRING} -> OBJECT 381 | // Inserts into a table. If `upsert` is true, overwrites entries with 382 | // the same primary key (otherwise errors). 383 | INSERT = 56; // Table, OBJECT, {upsert:BOOL} -> OBJECT | Table, Sequence, {upsert:BOOL, durability:STRING} -> OBJECT 384 | 385 | // * Administrative OPs 386 | // Creates a database with a particular name. 387 | DB_CREATE = 57; // STRING -> OBJECT 388 | // Drops a database with a particular name. 389 | DB_DROP = 58; // STRING -> OBJECT 390 | // Lists all the databases by name. (Takes no arguments) 391 | DB_LIST = 59; // -> ARRAY 392 | // Creates a table with a particular name in a particular 393 | // database. (You may omit the first argument to use the 394 | // default database.) 395 | TABLE_CREATE = 60; // Database, STRING, {datacenter:STRING, primary_key:STRING, cache_size:NUMBER, durability:STRING} -> OBJECT 396 | // STRING, {datacenter:STRING, primary_key:STRING, cache_size:NUMBER, durability:STRING} -> OBJECT 397 | // Drops a table with a particular name from a particular 398 | // database. (You may omit the first argument to use the 399 | // default database.) 400 | TABLE_DROP = 61; // Database, STRING -> OBJECT 401 | // STRING -> OBJECT 402 | // Lists all the tables in a particular database. (You may 403 | // omit the first argument to use the default database.) 404 | TABLE_LIST = 62; // Database -> ARRAY 405 | // -> ARRAY 406 | 407 | // * Secondary indexes OPs 408 | // Creates a new secondary index with a particular name and definition. 409 | INDEX_CREATE = 75; // Table, STRING, Function(1) -> OBJECT 410 | // Drops a secondary index with a particular name from the specified table. 411 | INDEX_DROP = 76; // Table, STRING -> OBJECT 412 | // Lists all secondary indexes on a particular table. 413 | INDEX_LIST = 77; // Table -> ARRAY 414 | 415 | // * Control Operators 416 | // Calls a function on data 417 | FUNCALL = 64; // Function(*), DATUM... -> DATUM 418 | // Executes its first argument, and returns its second argument if it 419 | // got [true] or its third argument if it got [false] (like an `if` 420 | // statement). 421 | BRANCH = 65; // BOOL, Top, Top -> Top 422 | // Returns true if any of its arguments returns true (short-circuits). 423 | // (Like `or` in most languages.) 424 | ANY = 66; // BOOL... -> BOOL 425 | // Returns true if all of its arguments return true (short-circuits). 426 | // (Like `and` in most languages.) 427 | ALL = 67; // BOOL... -> BOOL 428 | // Calls its Function with each entry in the sequence 429 | // and executes the array of terms that Function returns. 430 | FOREACH = 68; // Sequence, Function(1) -> OBJECT 431 | 432 | //////////////////////////////////////////////////////////////////////////////// 433 | ////////// Special Terms 434 | //////////////////////////////////////////////////////////////////////////////// 435 | 436 | // An anonymous function. Takes an array of numbers representing 437 | // variables (see [VAR] above), and a [Term] to execute with those in 438 | // scope. Returns a function that may be passed an array of arguments, 439 | // then executes the Term with those bound to the variable names. The 440 | // user will never construct this directly. We use it internally for 441 | // things like `map` which take a function. The "arity" of a [Function] is 442 | // the number of arguments it takes. 443 | // For example, here's what `_X_.map{|x| x+2}` turns into: 444 | // Term { 445 | // type = MAP; 446 | // args = [_X_, 447 | // Term { 448 | // type = Function; 449 | // args = [Term { 450 | // type = DATUM; 451 | // datum = Datum { 452 | // type = R_ARRAY; 453 | // r_array = [Datum { type = R_NUM; r_num = 1; }]; 454 | // }; 455 | // }, 456 | // Term { 457 | // type = ADD; 458 | // args = [Term { 459 | // type = VAR; 460 | // args = [Term { 461 | // type = DATUM; 462 | // datum = Datum { type = R_NUM; 463 | // r_num = 1}; 464 | // }]; 465 | // }, 466 | // Term { 467 | // type = DATUM; 468 | // datum = Datum { type = R_NUM; r_num = 2; }; 469 | // }]; 470 | // }]; 471 | // }]; 472 | FUNC = 69; // ARRAY, Top -> ARRAY -> Top 473 | 474 | // Indicates to ORDER_BY that this attribute is to be sorted in ascending order. 475 | ASC = 73; // !STRING -> Ordering 476 | // Indicates to ORDER_BY that this attribute is to be sorted in descending order. 477 | DESC = 74; // !STRING -> Ordering 478 | 479 | // Gets info about anything. INFO is most commonly called on tables. 480 | INFO = 79; // Top -> OBJECT 481 | 482 | // `a.match(b)` returns a match object if the string `a` 483 | // matches the regular expression `b`. 484 | MATCH = 97; // STRING, STRING -> DATUM 485 | 486 | // Select a number of elements from sequence with uniform distribution. 487 | SAMPLE = 81; // Sequence, NUMBER -> Sequence 488 | 489 | // Evaluates its first argument. If that argument returns 490 | // NULL or throws an error related to the absence of an 491 | // expected value (for instance, accessing a non-existent 492 | // field or adding NULL to an integer), DEFAULT will either 493 | // return its second argument or execute it if it's a 494 | // function. If the second argument is a function, it will be 495 | // passed either the text of the error or NULL as its 496 | // argument. 497 | DEFAULT = 92; // Top, Top -> Top 498 | 499 | // Parses its first argument as a json string and returns it as a 500 | // datum. 501 | JSON = 98; // STRING -> DATUM 502 | } 503 | optional TermType type = 1; 504 | 505 | // This is only used when type is DATUM. 506 | optional Datum datum = 2; 507 | 508 | repeated Term args = 3; // Holds the positional arguments of the query. 509 | message AssocPair { 510 | optional string key = 1; 511 | optional Term val = 2; 512 | } 513 | repeated AssocPair optargs = 4; // Holds the optional arguments of the query. 514 | // (Note that the order of the optional arguments doesn't matter; think of a 515 | // Hash.) 516 | 517 | extensions 10000 to 20000; 518 | } 519 | 520 | //////////////////////////////////////////////////////////////////////////////// 521 | // EXAMPLE // 522 | //////////////////////////////////////////////////////////////////////////////// 523 | // ```ruby 524 | // r.table('tbl', {:use_outdated => true}).insert([{:id => 0}, {:id => 1}]) 525 | // ``` 526 | // Would turn into: 527 | // Term { 528 | // type = INSERT; 529 | // args = [Term { 530 | // type = TABLE; 531 | // args = [Term { 532 | // type = DATUM; 533 | // datum = Datum { type = R_STR; r_str = "tbl"; }; 534 | // }]; 535 | // optargs = [["use_outdated", 536 | // Term { 537 | // type = DATUM; 538 | // datum = Datum { type = R_BOOL; r_bool = true; }; 539 | // }]]; 540 | // }, 541 | // Term { 542 | // type = MAKE_ARRAY; 543 | // args = [Term { 544 | // type = DATUM; 545 | // datum = Datum { type = R_OBJECT; r_object = [["id", 0]]; }; 546 | // }, 547 | // Term { 548 | // type = DATUM; 549 | // datum = Datum { type = R_OBJECT; r_object = [["id", 1]]; }; 550 | // }]; 551 | // }] 552 | // } 553 | // And the server would reply: 554 | // Response { 555 | // type = SUCCESS_ATOM; 556 | // token = 1; 557 | // response = [Datum { type = R_OBJECT; r_object = [["inserted", 2]]; }]; 558 | // } 559 | // Or, if there were an error: 560 | // Response { 561 | // type = RUNTIME_ERROR; 562 | // token = 1; 563 | // response = [Datum { type = R_STR; r_str = "The table `tbl` doesn't exist!"; }]; 564 | // backtrace = [Frame { type = POS; pos = 0; }, Frame { type = POS; pos = 0; }]; 565 | // } 566 | -------------------------------------------------------------------------------- /query.go: -------------------------------------------------------------------------------- 1 | package rethinkgo 2 | 3 | // Let user create queries as RQL Exp trees, any errors are deferred 4 | // until the query is run, so most all functions take interface{} types. 5 | // interface{} is effectively a void* type that we look at later to determine 6 | // the underlying type and perform any conversions. 7 | 8 | // Map is a shorter name for a mapping from strings to arbitrary objects 9 | type Map map[string]interface{} 10 | 11 | // List is a shorter name for an array of arbitrary objects 12 | type List []interface{} 13 | 14 | type expressionKind int 15 | 16 | const ( 17 | addKind expressionKind = iota 18 | allKind 19 | anyKind 20 | appendKind 21 | ascendingKind 22 | betweenKind 23 | branchKind 24 | changeAtKind 25 | coerceToKind 26 | concatMapKind 27 | containsKind 28 | countKind 29 | databaseCreateKind 30 | databaseDropKind 31 | databaseKind 32 | databaseListKind 33 | deleteAtKind 34 | deleteKind 35 | descendingKind 36 | differenceKind 37 | distinctKind 38 | divideKind 39 | defaultKind 40 | eqJoinKind 41 | equalityKind 42 | errorKind 43 | filterKind 44 | forEachKind 45 | funcallKind 46 | funcKind 47 | getAllKind 48 | getFieldKind 49 | getKind 50 | greaterThanKind 51 | greaterThanOrEqualKind 52 | groupByKind 53 | groupedMapReduceKind 54 | hasFieldsKind 55 | implicitVariableKind 56 | indexCreateKind 57 | indexDropKind 58 | indexesOfKind 59 | indexListKind 60 | inequalityKind 61 | infoKind 62 | innerJoinKind 63 | insertAtKind 64 | insertKind 65 | isEmptyKind 66 | javascriptKind 67 | jsonKind 68 | keysKind 69 | lessThanKind 70 | lessThanOrEqualKind 71 | limitKind 72 | logicalNotKind 73 | mapKind 74 | matchKind 75 | mergeKind 76 | moduloKind 77 | multiplyKind 78 | nthKind 79 | orderByKind 80 | outerJoinKind 81 | pluckKind 82 | prependKind 83 | reduceKind 84 | returnValuesKind 85 | replaceKind 86 | sampleKind 87 | setDifferenceKind 88 | setInsertKind 89 | setIntersectionKind 90 | setUnionKind 91 | skipKind 92 | sliceKind 93 | spliceAtKind 94 | subtractKind 95 | tableCreateKind 96 | tableDropKind 97 | tableKind 98 | tableListKind 99 | typeOfKind 100 | unionKind 101 | updateKind 102 | variableKind 103 | withFieldsKind 104 | withoutKind 105 | zipKind 106 | 107 | // custom rethinkgo ones 108 | upsertKind 109 | atomicKind 110 | useOutdatedKind 111 | durabilityKind 112 | literalKind 113 | ) 114 | 115 | func nullaryOperator(kind expressionKind) Exp { 116 | return Exp{kind: kind} 117 | } 118 | 119 | func naryOperator(kind expressionKind, operand interface{}, operands ...interface{}) Exp { 120 | args := []interface{}{operand} 121 | args = append(args, operands...) 122 | return Exp{kind: kind, args: args} 123 | } 124 | 125 | func stringsToInterfaces(strings []string) []interface{} { 126 | interfaces := make([]interface{}, len(strings)) 127 | for i, v := range strings { 128 | interfaces[i] = interface{}(v) 129 | } 130 | return interfaces 131 | } 132 | 133 | func funcWrapper(f interface{}, arity int) Exp { 134 | return naryOperator(funcKind, f, arity) 135 | } 136 | 137 | // Exp represents an RQL expression, such as the return value of 138 | // r.Expr(). Exp has all the RQL methods on it, such as .Add(), .Attr(), 139 | // .Filter() etc. 140 | // 141 | // To create an Exp from a native or user-defined type, or function, use 142 | // r.Expr(). 143 | // 144 | // Example usage: 145 | // 146 | // r.Expr(2).Mul(2) => 4 147 | // 148 | // Exp is the type used for the arguments to any functions that are used 149 | // in RQL. 150 | // 151 | // Example usage: 152 | // 153 | // var response []interface{} 154 | // // Get the intelligence rating for each of our heroes 155 | // getIntelligence := func(row r.Exp) r.Exp { 156 | // return row.Attr("intelligence") 157 | // } 158 | // err := r.Table("heroes").Map(getIntelligence).Run(session).All(&response) 159 | // 160 | // Example response: 161 | // 162 | // [7, 5, 4, 6, 2, 2, 6, 4, ...] 163 | // 164 | // Literal Expressions can be used directly for queries. 165 | // 166 | // Example usage: 167 | // 168 | // var squares []int 169 | // // Square a series of numbers 170 | // square := func(row r.Exp) r.Exp { return row.Mul(row) } 171 | // err := r.Expr(1,2,3).Map(square).Run(session).One(&squares) 172 | // 173 | // Example response: 174 | // 175 | // [1, 2, 3] 176 | type Exp struct { // this would be Expr, but then it would conflict with the function that creates Exp instances 177 | args []interface{} 178 | kind expressionKind 179 | } 180 | 181 | // Row supplies access to the current row in any query, even if there's no go 182 | // func with a reference to it. 183 | // 184 | // Example without Row: 185 | // 186 | // var response []interface{} 187 | // // Get the real names of all the villains 188 | // err := r.Table("villains").Map(func(row r.Exp) r.Exp { 189 | // return row.Attr("real_name") 190 | // }).Run(session).All(&response) 191 | // 192 | // Example with Row: 193 | // 194 | // var response []interface{} 195 | // // Get the real names of all the villains 196 | // err := r.Table("employees").Map(Row.Attr("real_name")).Run(session).All(&response) 197 | // 198 | // Example response: 199 | // 200 | // ["En Sabah Nur", "Victor von Doom", ...] 201 | var Row = Exp{kind: implicitVariableKind} 202 | 203 | // Expr converts any value to an expression. Internally it uses the `json` 204 | // module to convert any literals, so any type annotations or methods understood 205 | // by that module can be used. If the value cannot be converted, an error is 206 | // returned at query .Run(session) time. 207 | // 208 | // If you want to call expression methods on an object that is not yet an 209 | // expression, this is the function you want. 210 | // 211 | // Example usage: 212 | // 213 | // var response interface{} 214 | // rows := r.Expr(r.Map{"go": "awesome", "rethinkdb": "awesomer"}).Run(session).One(&response) 215 | // 216 | // Example response: 217 | // 218 | // {"go": "awesome", "rethinkdb": "awesomer"} 219 | func Expr(value interface{}) Exp { 220 | v, ok := value.(Exp) // check if it's already an Exp 221 | if ok { 222 | return v 223 | } 224 | return naryOperator(literalKind, value) 225 | } 226 | 227 | // Json creates an object using a literal json string. 228 | // 229 | // Example usage: 230 | // 231 | // var response interface{} 232 | // rows := r.Json(`"go": "awesome", "rethinkdb": "awesomer"}`).Run(session).One(&response) 233 | // 234 | // Example response: 235 | // 236 | // {"go": "awesome", "rethinkdb": "awesomer"} 237 | func Json(value string) Exp { 238 | return naryOperator(jsonKind, value) 239 | } 240 | 241 | /////////// 242 | // Terms // 243 | /////////// 244 | 245 | // Js creates an expression using Javascript code. The code is executed 246 | // on the server (using eval() https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/eval) 247 | // and can be used in a couple of roles, as value or as a function. When used 248 | // as a function, it receives two named arguments, 'row' and/or 'acc' (used for 249 | // reductions). 250 | // 251 | // JsWithTimeout lets you specify a timeout for the javascript expression. 252 | // 253 | // The value of the 'this' object inside Javascript code is the current row. 254 | // 255 | // Example usages: 256 | // 257 | // // (same effect as r.Expr(1,2,3)) 258 | // r.Js(`[1,2,3]`).Run(session) 259 | // // Parens are required here, otherwise eval() thinks it's a block. 260 | // r.Js(`({name: 2})`).Run(session) 261 | // // String concatenation is possible using r.Js 262 | // r.Table("employees").Map(r.Js(`this.first_name[0] + ' Fucking ' + this.last_name[0]`)).Run(session) 263 | // 264 | // Example without Js: 265 | // 266 | // var response []interface{} 267 | // // Combine each hero's strength and durability 268 | // err := r.Table("heroes").Map(func(row r.Exp) r.Exp { 269 | // return row.Attr("strength").Add(row.Attr("durability")) 270 | // }).Run(session).All(&response) 271 | // 272 | // Example with Js: 273 | // 274 | // var response []interface{} 275 | // // Combine each hero's strength and durability 276 | // err := r.Table("heroes").Map( 277 | // r.Js(`(function (row) { return row.strength + row.durability; })`), 278 | // ).Run(session).All(&response) 279 | // 280 | // Example response: 281 | // 282 | // [11, 6, 9, 11, ...] 283 | func Js(body string) Exp { 284 | return naryOperator(javascriptKind, body) 285 | } 286 | 287 | // JsWithTimeout lets you specify the timeout for a javascript expression to run 288 | // (in seconds). The default value is 5 seconds. 289 | func JsWithTimeout(body string, timeout float64) Exp { 290 | return naryOperator(javascriptKind, body, timeout) 291 | } 292 | 293 | // RuntimeError tells the server to respond with a ErrRuntime, useful for 294 | // testing. 295 | // 296 | // Example usage: 297 | // 298 | // err := r.RuntimeError("hi there").Run(session).Err() 299 | func RuntimeError(message string) Exp { 300 | return Exp{kind: errorKind, args: List{message}} 301 | } 302 | 303 | // Branch checks a test expression, evaluating the trueBranch expression if it's 304 | // true and falseBranch otherwise. 305 | // 306 | // Example usage: 307 | // 308 | // // Roughly equivalent RQL expression 309 | // r.Branch(r.Row.Attr("first_name").Eq("Marc"), "is probably marc", "who cares") 310 | func Branch(test, trueBranch, falseBranch interface{}) Exp { 311 | return naryOperator(branchKind, test, trueBranch, falseBranch) 312 | } 313 | 314 | // Get retrieves a single row by primary key. 315 | // 316 | // Example usage: 317 | // 318 | // var response map[string]interface{} 319 | // err := r.Table("heroes").Get("Doctor Strange").Run(session).One(&response) 320 | // 321 | // Example response: 322 | // 323 | // { 324 | // "strength": 3, 325 | // "name": "Doctor Strange", 326 | // "durability": 6, 327 | // "intelligence": 4, 328 | // "energy": 7, 329 | // "fighting": 7, 330 | // "real_name": "Stephen Vincent Strange", 331 | // "speed": 5, 332 | // "id": "edc3a46b-95a0-4f64-9d1c-0dd7d83c4bcd" 333 | // } 334 | func (e Exp) Get(key interface{}) Exp { 335 | return naryOperator(getKind, e, key) 336 | } 337 | 338 | // GetAll retrieves all documents where the given value matches the requested 339 | // index. 340 | // 341 | // Example usage (awesomeness is a secondary index defined as speed * strength): 342 | // 343 | // var response []interface{} 344 | // err := r.Table("heroes").GetAll("awesomeness", 10).Run(session).All(&response) 345 | // 346 | // Example response: 347 | // 348 | // { 349 | // "strength": 2, 350 | // "name": "Storm", 351 | // "durability": 3, 352 | // "intelligence": 5, 353 | // "energy": 6, 354 | // "fighting": 5, 355 | // "real_name": "Ororo Munroe", 356 | // "speed": 5, 357 | // "id": "59d1ad55-a61e-49d9-a375-0fb014b0e6ea" 358 | // } 359 | func (e Exp) GetAll(index string, values ...interface{}) Exp { 360 | return naryOperator(getAllKind, e, append(values, index)...) 361 | } 362 | 363 | // GroupBy does a sort of grouped map reduce. First the server groups all rows 364 | // that have the same value for `attribute`, then it applys the map reduce to 365 | // each group. It takes one of the following reductions: r.Count(), 366 | // r.Sum(string), r.Avg(string) 367 | // 368 | // `attribute` must be a single attribute (string) or a list of attributes 369 | // ([]string) 370 | // 371 | // Example usage: 372 | // 373 | // var response []interface{} 374 | // // Find all heroes with the same durability, calculate their average speed 375 | // // to see if more durable heroes are slower. 376 | // err := r.Table("heroes").GroupBy("durability", r.Avg("speed")).Run(session).One(&response) 377 | // 378 | // Example response: 379 | // 380 | // [ 381 | // { 382 | // "group": 1, // this is the strength attribute for every member of this group 383 | // "reduction": 1.5 // this is the sum of the intelligence attribute of all members of the group 384 | // }, 385 | // { 386 | // "group": 2, 387 | // "reduction": 3.5 388 | // }, 389 | // ... 390 | // ] 391 | // 392 | // Example with multiple attributes: 393 | // 394 | // // Find all heroes with the same strength and speed, sum their intelligence 395 | // rows := r.Table("heroes").GroupBy([]string{"strength", "speed"}, r.Count()).Run(session) 396 | func (e Exp) GroupBy(attribute, groupedMapReduce interface{}) Exp { 397 | _, ok := attribute.(string) 398 | if ok { 399 | attribute = List{attribute} 400 | } 401 | return naryOperator(groupByKind, e, attribute, groupedMapReduce) 402 | } 403 | 404 | // UseOutdated tells the server to use potentially out-of-date data from all 405 | // tables already specified in this query. The advantage is that read queries 406 | // may be faster if this is set. 407 | // 408 | // Example with single table: 409 | // 410 | // rows := r.Table("heroes").UseOutdated(true).Run(session) 411 | // 412 | // Example with multiple tables (all tables would be allowed to use outdated data): 413 | // 414 | // villain_strength := r.Table("villains").Get("Doctor Doom", "name").Attr("strength") 415 | // compareFunc := r.Row.Attr("strength").Eq(villain_strength) 416 | // rows := r.Table("heroes").Filter(compareFunc).UseOutdated(true).Run(session) 417 | func (e Exp) UseOutdated(useOutdated bool) Exp { 418 | return naryOperator(useOutdatedKind, e, useOutdated) 419 | } 420 | 421 | // Durability sets the durability for the expression, this can be set to either 422 | // "soft" or "hard". 423 | // 424 | // Example usage: 425 | // 426 | // var response r.WriteResponse 427 | // r.Table("heroes").Insert(r.Map{"superhero": "Iron Man"}).Durability("soft").Run(session).One(&response) 428 | // 429 | // Example response: 430 | func (e Exp) Durability(durability string) Exp { 431 | return naryOperator(durabilityKind, e, durability) 432 | } 433 | 434 | // Attr gets an attribute's value from the row. 435 | // 436 | // Example usage: 437 | // 438 | // r.Expr(r.Map{"key": "value"}).Attr("key") => "value" 439 | func (e Exp) Attr(name string) Exp { 440 | return naryOperator(getFieldKind, e, name) 441 | } 442 | 443 | // Add sums two numbers or concatenates two arrays. 444 | // 445 | // Example usage: 446 | // 447 | // r.Expr(1,2,3).Add(r.Expr(4,5,6)) => [1,2,3,4,5,6] 448 | // r.Expr(2).Add(2) => 4 449 | func (e Exp) Add(operand interface{}) Exp { 450 | return naryOperator(addKind, e, operand) 451 | } 452 | 453 | // Sub subtracts two numbers. 454 | // 455 | // Example usage: 456 | // 457 | // r.Expr(2).Sub(2) => 0 458 | func (e Exp) Sub(operand interface{}) Exp { 459 | return naryOperator(subtractKind, e, operand) 460 | } 461 | 462 | // Mul multiplies two numbers. 463 | // 464 | // Example usage: 465 | // 466 | // r.Expr(2).Mul(3) => 6 467 | func (e Exp) Mul(operand interface{}) Exp { 468 | return naryOperator(multiplyKind, e, operand) 469 | } 470 | 471 | // Div divides two numbers. 472 | // 473 | // Example usage: 474 | // 475 | // r.Expr(3).Div(2) => 1.5 476 | func (e Exp) Div(operand interface{}) Exp { 477 | return naryOperator(divideKind, e, operand) 478 | } 479 | 480 | // Mod divides two numbers and returns the remainder. 481 | // 482 | // Example usage: 483 | // 484 | // r.Expr(23).Mod(10) => 3 485 | func (e Exp) Mod(operand interface{}) Exp { 486 | return naryOperator(moduloKind, e, operand) 487 | } 488 | 489 | // And performs a logical and on two values. 490 | // 491 | // Example usage: 492 | // 493 | // r.Expr(true).And(true) => true 494 | func (e Exp) And(operand interface{}) Exp { 495 | return naryOperator(allKind, e, operand) 496 | } 497 | 498 | // Or performs a logical or on two values. 499 | // 500 | // Example usage: 501 | // 502 | // r.Expr(true).Or(false) => true 503 | func (e Exp) Or(operand interface{}) Exp { 504 | return naryOperator(anyKind, e, operand) 505 | } 506 | 507 | // Eq returns true if two values are equal. 508 | // 509 | // Example usage: 510 | // 511 | // r.Expr(1).Eq(1) => true 512 | func (e Exp) Eq(operand interface{}) Exp { 513 | return naryOperator(equalityKind, e, operand) 514 | } 515 | 516 | // Ne returns true if two values are not equal. 517 | // 518 | // Example usage: 519 | // 520 | // r.Expr(1).Ne(-1) => true 521 | func (e Exp) Ne(operand interface{}) Exp { 522 | return naryOperator(inequalityKind, e, operand) 523 | } 524 | 525 | // Gt returns true if the first value is greater than the second. 526 | // 527 | // Example usage: 528 | // 529 | // r.Expr(2).Gt(1) => true 530 | func (e Exp) Gt(operand interface{}) Exp { 531 | return naryOperator(greaterThanKind, e, operand) 532 | } 533 | 534 | // Gt returns true if the first value is greater than or equal to the second. 535 | // 536 | // Example usage: 537 | // 538 | // r.Expr(2).Gt(2) => true 539 | func (e Exp) Ge(operand interface{}) Exp { 540 | return naryOperator(greaterThanOrEqualKind, e, operand) 541 | } 542 | 543 | // Lt returns true if the first value is less than the second. 544 | // 545 | // Example usage: 546 | // 547 | // r.Expr(1).Lt(2) => true 548 | func (e Exp) Lt(operand interface{}) Exp { 549 | return naryOperator(lessThanKind, e, operand) 550 | } 551 | 552 | // Le returns true if the first value is less than or equal to the second. 553 | // 554 | // Example usage: 555 | // 556 | // r.Expr(2).Lt(2) => true 557 | func (e Exp) Le(operand interface{}) Exp { 558 | return naryOperator(lessThanOrEqualKind, e, operand) 559 | } 560 | 561 | // Not performs a logical not on a value. 562 | // 563 | // Example usage: 564 | // 565 | // r.Expr(true).Not() => false 566 | func (e Exp) Not() Exp { 567 | return naryOperator(logicalNotKind, e) 568 | } 569 | 570 | // Distinct removes duplicate elements from a sequence. 571 | // 572 | // Example usage: 573 | // 574 | // var response []interface{} 575 | // // Get a list of all possible strength values for our heroes 576 | // err := r.Table("heroes").Map(r.Row.Attr("strength")).Distinct().Run(session).All(&response) 577 | // 578 | // Example response: 579 | // 580 | // [7, 1, 6, 4, 2, 5, 3] 581 | func (e Exp) Distinct() Exp { 582 | return naryOperator(distinctKind, e) 583 | } 584 | 585 | // Count returns the number of elements in the response. 586 | // 587 | // Example usage: 588 | // 589 | // var response int 590 | // err := r.Table("heroes").Count().Run(session).One(&response) 591 | // 592 | // Example response: 593 | // 594 | // 42 595 | func (e Exp) Count() Exp { 596 | return naryOperator(countKind, e) 597 | } 598 | 599 | // Merge combines an object with another object, overwriting properties from 600 | // the first with properties from the second. 601 | // 602 | // Example usage: 603 | // 604 | // var response interface{} 605 | // firstMap := r.Map{"name": "HAL9000", "role": "Support System"} 606 | // secondMap := r.Map{"color": "Red", "role": "Betrayal System"} 607 | // err := r.Expr(firstMap).Merge(secondMap).Run(session).One(&response) 608 | // 609 | // Example response: 610 | // 611 | // { 612 | // "color": "Red", 613 | // "name": "HAL9000", 614 | // "role": "Betrayal System" 615 | // } 616 | func (e Exp) Merge(operand interface{}) Exp { 617 | return naryOperator(mergeKind, e, operand) 618 | } 619 | 620 | // Append appends a value to an array. 621 | // 622 | // Example usage: 623 | // 624 | // var response []interface{} 625 | // err := r.Expr(r.List{1, 2, 3, 4}).Append(5).Run(session).One(&response) 626 | // 627 | // Example response: 628 | // 629 | // [1, 2, 3, 4, 5] 630 | func (e Exp) Append(value interface{}) Exp { 631 | return naryOperator(appendKind, e, value) 632 | } 633 | 634 | // Union concatenates two sequences. 635 | // 636 | // Example usage: 637 | // 638 | // var response []interface{} 639 | // // Retrieve all heroes and villains 640 | // r.Table("heroes").Union(r.Table("villains")).Run(session).All(&response) 641 | // 642 | // Example response: 643 | // 644 | // [ 645 | // { 646 | // "durability": 6, 647 | // "energy": 6, 648 | // "fighting": 3, 649 | // "id": "1a760d0b-57ef-42a8-9fec-c3a1f34930aa", 650 | // "intelligence": 6, 651 | // "name": "Iron Man", 652 | // "real_name": "Anthony Edward \"Tony\" Stark", 653 | // "speed": 5, 654 | // "strength": 6 655 | // }, 656 | // ... 657 | // ] 658 | func (e Exp) Union(operands ...interface{}) Exp { 659 | return naryOperator(unionKind, e, operands...) 660 | } 661 | 662 | // Nth returns the nth element in sequence, zero-indexed. 663 | // 664 | // Example usage: 665 | // 666 | // var response int 667 | // // Get the second element of an array 668 | // err := r.Expr(4,3,2,1).Nth(1).Run(session).One(&response) 669 | // 670 | // Example response: 671 | // 672 | // 3 673 | func (e Exp) Nth(operand interface{}) Exp { 674 | return naryOperator(nthKind, e, operand) 675 | } 676 | 677 | // Slice returns a section of a sequence, with bounds [lower, upper), where 678 | // lower bound is inclusive and upper bound is exclusive. 679 | // 680 | // Example usage: 681 | // 682 | // var response []int 683 | // err := r.Expr(1,2,3,4,5).Slice(2,4).Run(session).One(&response) 684 | // 685 | // Example response: 686 | // 687 | // [3, 4] 688 | func (e Exp) Slice(lower, upper interface{}) Exp { 689 | return naryOperator(sliceKind, e, lower, upper) 690 | } 691 | 692 | // Limit returns only the first `limit` results from the query. 693 | // 694 | // Example usage: 695 | // 696 | // var response []int 697 | // err := r.Expr(1,2,3,4,5).Limit(3).Run(session).One(&response) 698 | // 699 | // Example response: 700 | // 701 | // [1, 2, 3] 702 | func (e Exp) Limit(limit interface{}) Exp { 703 | return naryOperator(limitKind, e, limit) 704 | } 705 | 706 | // Skip returns all results after the first `start` results. Basically it's the 707 | // opposite of .Limit(). 708 | // 709 | // Example usage: 710 | // 711 | // var response []int 712 | // err := r.Expr(1,2,3,4,5).Skip(3).Run(session).One(&response) 713 | // 714 | // Example response: 715 | // 716 | // [4, 5] 717 | func (e Exp) Skip(start interface{}) Exp { 718 | return naryOperator(skipKind, e, start) 719 | } 720 | 721 | // Map transforms a sequence by applying the given function to each row. 722 | // 723 | // Example usage: 724 | // 725 | // var squares []int 726 | // // Square a series of numbers 727 | // square := func(row r.Exp) r.Exp { return row.Mul(row) } 728 | // err := r.Expr(1,2,3).Map(square).Run(session).One(&squares) 729 | // 730 | // Example response: 731 | // 732 | // [1, 2, 3] 733 | // 734 | // Example usage: 735 | // 736 | // var heroes []interface{} 737 | // // Fetch multiple rows by primary key 738 | // heroNames := []string{"Iron Man", "Colossus"} 739 | // getHero := func (name r.Exp) r.Exp { return r.Table("heroes").Get(name, "name") } 740 | // err := r.Expr(heroNames).Map(getHero).Run(session).One(&heroes) 741 | // 742 | // Example response: 743 | // 744 | // [ 745 | // { 746 | // "durability": 6, 747 | // "energy": 6, 748 | // "fighting": 3, 749 | // "intelligence": 6, 750 | // "name": "Iron Man", 751 | // "real_name": "Anthony Edward \"Tony\" Stark", 752 | // "speed": 5, 753 | // "strength": 6 754 | // }, 755 | // ... 756 | // ] 757 | func (e Exp) Map(operand interface{}) Exp { 758 | return naryOperator(mapKind, e, funcWrapper(operand, 1)) 759 | } 760 | 761 | // ConcatMap constructs a sequence by running the provided function on each row, 762 | // then concatenating all the results. 763 | // 764 | // Example usage: 765 | // 766 | // var flattened []int 767 | // // Flatten some nested lists 768 | // flatten := func(row r.Exp) r.Exp { return row } 769 | // err := r.Expr(r.List{1,2}, r.List{3,4}).ConcatMap(flatten).Run(session).One(&flattened) 770 | // 771 | // Example response: 772 | // 773 | // [1, 2, 3, 4] 774 | // 775 | // Example usage: 776 | // 777 | // var names []string 778 | // // Get all hero real names and aliases in a list 779 | // getNames := func(row r.Exp) interface{} { 780 | // return r.List{row.Attr("name"), row.Attr("real_name")} 781 | // } 782 | // err := r.Table("heroes").ConcatMap(getNames).Run(session).All(&names) 783 | // 784 | // Example response: 785 | // 786 | // ["Captain Britain", "Brian Braddock", "Iceman", "Robert \"Bobby\" Louis Drake", ...] 787 | func (e Exp) ConcatMap(operand interface{}) Exp { 788 | return naryOperator(concatMapKind, e, funcWrapper(operand, 1)) 789 | } 790 | 791 | // Filter removes all objects from a sequence that do not match the given 792 | // condition. The condition can be an RQL expression, an r.Map, or a function 793 | // that returns true or false. 794 | // 795 | // Example with an RQL expression: 796 | // 797 | // var response []interface{} 798 | // // Get all heroes with durability 6 799 | // err := r.Table("heroes").Filter(r.Row.Attr("durability").Eq(6)).Run(session).All(&response) 800 | // 801 | // Example with r.Map: 802 | // 803 | // err := r.Table("heroes").Filter(r.Map{"durability": 6}).Run(session).All(&response) 804 | // 805 | // Example with function: 806 | // 807 | // filterFunc := func (row r.Exp) r.Exp { return row.Attr("durability").Eq(6) } 808 | // err := r.Table("heroes").Filter(filterFunc).Run(session).All(&response) 809 | // 810 | // Example response: 811 | // 812 | // [ 813 | // { 814 | // "durability": 6, 815 | // "energy": 6, 816 | // "fighting": 3, 817 | // "id": "1a760d0b-57ef-42a8-9fec-c3a1f34930aa", 818 | // "intelligence": 6, 819 | // "name": "Iron Man", 820 | // "real_name": "Anthony Edward \"Tony\" Stark", 821 | // "speed": 5, 822 | // "strength": 6 823 | // } 824 | // ... 825 | // ] 826 | func (e Exp) Filter(operand interface{}) Exp { 827 | return naryOperator(filterKind, e, funcWrapper(operand, 1)) 828 | } 829 | 830 | // HasFields returns true if an object has all the given attributes. 831 | // 832 | // Example usage: 833 | // 834 | // hero := r.Map{"name": "Iron Man", "energy": 6, "speed": 5} 835 | // r.Expr(hero).HasFields("energy", "speed") => true 836 | // r.Expr(hero).HasFields("energy", "guns") => false 837 | func (e Exp) HasFields(keys ...string) Exp { 838 | return naryOperator(hasFieldsKind, e, stringsToInterfaces(keys)...) 839 | } 840 | 841 | // Between gets all rows where the key attribute's value falls between the 842 | // lowerbound and upperbound (inclusive). Use nil to represent no upper or 843 | // lower bound. Requires an index on the key (primary keys already have an 844 | // index with the name of the primary key). 845 | // 846 | // Example usage: 847 | // 848 | // var response []interface{} 849 | // // Retrieve all heroes with names between "E" and "F" 850 | // err := r.Table("heroes").Between("name", "E", "F").Run(session).All(&response) 851 | // 852 | // Example response: 853 | // 854 | // { 855 | // "strength": 4, 856 | // "name": "Elektra", 857 | // "durability": 2, 858 | // "intelligence": 4, 859 | // "energy": 3, 860 | // "fighting": 7, 861 | // "real_name": "Elektra Natchios", 862 | // "speed": 6, 863 | // } 864 | func (e Exp) Between(index string, lowerbound, upperbound interface{}) Exp { 865 | return naryOperator(betweenKind, e, lowerbound, upperbound, index) 866 | } 867 | 868 | // OrderBy sort the sequence by the values of the given key(s) in each row. The 869 | // default sort is increasing. 870 | // 871 | // Example usage: 872 | // 873 | // var response []interface{} 874 | // // Retrieve villains in order of increasing strength 875 | // err := r.Table("villains").OrderBy("strength").Run(session).All(&response) 876 | // 877 | // // Retrieve villains in order of decreasing strength, then increasing intelligence 878 | // query := r.Table("villains").OrderBy(r.Desc("strength"), "intelligence") 879 | // err := query.Run(session).All(&response) 880 | func (e Exp) OrderBy(orderings ...interface{}) Exp { 881 | // These are not required to be strings because they could also be 882 | // orderByAttr structs which specify the direction of sorting 883 | return naryOperator(orderByKind, e, orderings...) 884 | } 885 | 886 | // Asc tells OrderBy to sort a particular attribute in ascending order. This is 887 | // the default sort. 888 | // 889 | // Example usage: 890 | // 891 | // var response []interface{} 892 | // // Retrieve villains in order of increasing fighting ability (worst fighters first) 893 | // err := r.Table("villains").OrderBy(r.Asc("fighting")).Run(session).All(&response) 894 | func Asc(attr string) Exp { 895 | return naryOperator(ascendingKind, attr) 896 | } 897 | 898 | // Desc tells OrderBy to sort a particular attribute in descending order. 899 | // 900 | // Example usage: 901 | // 902 | // var response []interface{} 903 | // // Retrieve villains in order of decreasing speed (fastest villains first) 904 | // err := r.Table("villains").OrderBy(r.Desc("speed")).Run(session).All(&response) 905 | func Desc(attr string) Exp { 906 | return naryOperator(descendingKind, attr) 907 | } 908 | 909 | // Reduce iterates over a sequence, starting with a base value and applying a 910 | // reduction function to the value so far and the next row of the sequence. 911 | // 912 | // Example usage: 913 | // 914 | // var sum int 915 | // // Add the numbers 1-4 together 916 | // reduction := func(acc, val r.Exp) r.Exp { return acc.Add(val) } 917 | // err := r.Expr(1,2,3,4).Reduce(reduction, 0).Run(session).One(&sum) 918 | // 919 | // Example response: 920 | // 921 | // 10 922 | // 923 | // Example usage: 924 | // 925 | // var totalSpeed int 926 | // // Compute the total speed for all heroes, the types of acc and val should 927 | // // be the same, so we extract the speed first with a .Map() 928 | // mapping := func(row r.Exp) r.Exp { return row.Attr("speed") } 929 | // reduction := func(acc, val r.Exp) r.Exp { return acc.Add(val) } 930 | // err := r.Table("heroes").Map(mapping).Reduce(reduction, 0).Run(session).One(&totalSpeed) 931 | // 932 | // Example response: 933 | // 934 | // 232 935 | func (e Exp) Reduce(reduction, base interface{}) Exp { 936 | return naryOperator(reduceKind, e, funcWrapper(reduction, 2), base) 937 | } 938 | 939 | // GroupedMapReduce partitions a sequence into groups, then performs a map and a 940 | // reduction on each group. See also .Map() and .GroupBy(). 941 | // 942 | // Example usage: 943 | // 944 | // // Find the sum of the even and odd numbers separately 945 | // grouping := func(row r.Exp) r.Exp { return r.Branch(row.Mod(2).Eq(0), "even", "odd") } 946 | // mapping := func(row r.Exp) r.Exp { return row } 947 | // base := 0 948 | // reduction := func(acc, row r.Exp) r.Exp { 949 | // return acc.Add(row) 950 | // } 951 | // 952 | // var response []interface{} 953 | // query := r.Expr(1,2,3,4,5).GroupedMapReduce(grouping, mapping, reduction, base) 954 | // err := query.Run(session).One(&response) 955 | // 956 | // Example response: 957 | // 958 | // [ 959 | // { 960 | // "group": "even", 961 | // "reduction": 6 962 | // }, 963 | // { 964 | // "group": "odd", 965 | // "reduction": 9 966 | // } 967 | // ] 968 | // 969 | // Example usage: 970 | // 971 | // // Group all heroes by intelligence, then find the fastest one in each group 972 | // grouping := func(row r.Exp) r.Exp { return row.Attr("intelligence") } 973 | // mapping := func(row r.Exp) r.Exp { return row.Pluck("name", "speed") } 974 | // base := r.Map{"name": nil, "speed": 0} 975 | // reduction := func(acc, row r.Exp) r.Exp { 976 | // return r.Branch(acc.Attr("speed").Lt(row.Attr("speed")), row, acc) 977 | // } 978 | // 979 | // var response []interface{} 980 | // query := r.Table("heroes").GroupedMapReduce(grouping, mapping, reduction, base) 981 | // err := query.Run(session).One(&response) 982 | // 983 | // Example response: 984 | // 985 | // [ 986 | // { 987 | // "group": 1, 988 | // "reduction": { 989 | // "name": "Northstar", 990 | // "speed": 2 991 | // } 992 | // }, 993 | // { 994 | // "group": 2, 995 | // "reduction": { 996 | // "name": "Thor", 997 | // "speed": 6 998 | // } 999 | // }, 1000 | // ... 1001 | // ] 1002 | func (e Exp) GroupedMapReduce(grouping, mapping, reduction, base interface{}) Exp { 1003 | return naryOperator(groupedMapReduceKind, e, funcWrapper(grouping, 1), funcWrapper(mapping, 1), funcWrapper(reduction, 2), base) 1004 | } 1005 | 1006 | ///////////////////// 1007 | // Derived Methods // 1008 | ///////////////////// 1009 | 1010 | // Pluck takes only the given attributes from an object, discarding all others. 1011 | // See also .Without(). 1012 | // 1013 | // Example usage: 1014 | // 1015 | // var heroes []interface{} 1016 | // err := r.Table("heroes").Pluck("real_name", "id").Run(session).All(&heroes) 1017 | // 1018 | // Example response: 1019 | // 1020 | // [ 1021 | // { 1022 | // "real_name": "Peter Benjamin Parker", 1023 | // "id": "1227f639-38f0-4cbb-a7b1-9c49f13fe89d", 1024 | // }, 1025 | // ... 1026 | // ] 1027 | func (e Exp) Pluck(attributes ...interface{}) Exp { 1028 | return naryOperator(pluckKind, e, attributes...) 1029 | } 1030 | 1031 | // Without removes the given attributes from an object. See also .Pluck(). 1032 | // 1033 | // Example usage: 1034 | // 1035 | // var heroes []interface{} 1036 | // err := r.Table("heroes").Without("real_name", "id").Run(session).All(&heroes) 1037 | // 1038 | // Example response: 1039 | // 1040 | // [ 1041 | // { 1042 | // "durability": 4, 1043 | // "energy": 7, 1044 | // "fighting": 4, 1045 | // "intelligence": 7, 1046 | // "name": "Professor X", 1047 | // "speed": 2, 1048 | // "strength": 4 1049 | // }, 1050 | // ... 1051 | // ] 1052 | func (e Exp) Without(attributes ...string) Exp { 1053 | return naryOperator(withoutKind, e, stringsToInterfaces(attributes)...) 1054 | } 1055 | 1056 | // InnerJoin performs an inner join on two sequences, using the provided 1057 | // function to compare the rows from each sequence. See also .EqJoin() and 1058 | // .OuterJoin(). 1059 | // 1060 | // Each row from the left sequence is compared to every row from the right 1061 | // sequence using the provided predicate function. If the function returns 1062 | // true for a pair of rows, that pair will appear in the resulting sequence. 1063 | // 1064 | // Example usage: 1065 | // 1066 | // var response []interface{} 1067 | // // Get each hero and their associated lair, in this case, "villain_id" is 1068 | // // the primary key for the "lairs" table 1069 | // compareRows := func (left, right r.Exp) r.Exp { 1070 | // return left.Attr("id").Eq(right.Attr("villain_id")) 1071 | // } 1072 | // err := r.Table("villains").InnerJoin(r.Table("lairs"), compareRows).Run(session).All(&response) 1073 | // 1074 | // Example response: 1075 | // 1076 | // [ 1077 | // { 1078 | // "left": { 1079 | // "durability": 6, 1080 | // "energy": 6, 1081 | // "fighting": 3, 1082 | // "id": "c0d1b94f-b07e-40c3-a1db-448e645daedc", 1083 | // "intelligence": 6, 1084 | // "name": "Magneto", 1085 | // "real_name": "Max Eisenhardt", 1086 | // "speed": 4, 1087 | // "strength": 2 1088 | // }, 1089 | // "right": { 1090 | // "lair": "Asteroid M", 1091 | // "villain_id": "c0d1b94f-b07e-40c3-a1db-448e645daedc" 1092 | // } 1093 | // } 1094 | // ] 1095 | func (leftExpr Exp) InnerJoin(rightExpr Exp, predicate interface{}) Exp { 1096 | return naryOperator(innerJoinKind, leftExpr, rightExpr, funcWrapper(predicate, 2)) 1097 | } 1098 | 1099 | // OuterJoin performs a left outer join on two sequences, using the provided 1100 | // function to compare the rows from each sequence. See also .EqJoin() and 1101 | // .InnerJoin(). 1102 | // 1103 | // Each row from the left sequence is compared to every row from the right 1104 | // sequence using the provided predicate function. If the function returns 1105 | // true for a pair of rows, that pair will appear in the resulting sequence. 1106 | // 1107 | // If the predicate is false for every pairing for a specific left row, the left 1108 | // row will appear in the sequence with no right row present. 1109 | // 1110 | // Example usage: 1111 | // 1112 | // var response []interface{} 1113 | // // Get each hero and their associated lair, in this case, "villain_id" is 1114 | // // the primary key for the "lairs" table 1115 | // compareRows := func (left, right r.Exp) r.Exp { 1116 | // return left.Attr("id").Eq(right.Attr("villain_id")) 1117 | // } 1118 | // err := r.Table("villains").OuterJoin(r.Table("lairs"), compareRows).Run(session).All(&response) 1119 | // 1120 | // Example response: 1121 | // 1122 | // [ 1123 | // { 1124 | // "left": { 1125 | // "durability": 6, 1126 | // "energy": 6, 1127 | // "fighting": 3, 1128 | // "id": "c0d1b94f-b07e-40c3-a1db-448e645daedc", 1129 | // "intelligence": 6, 1130 | // "name": "Magneto", 1131 | // "real_name": "Max Eisenhardt", 1132 | // "speed": 4, 1133 | // "strength": 2 1134 | // }, 1135 | // "right": { 1136 | // "lair": "Asteroid M", 1137 | // "villain_id": "c0d1b94f-b07e-40c3-a1db-448e645daedc" 1138 | // } 1139 | // }, 1140 | // { 1141 | // "left": { 1142 | // "durability": 4, 1143 | // "energy": 1, 1144 | // "fighting": 7, 1145 | // "id": "ab140a9c-63d1-455e-862e-045ad7f57ae3", 1146 | // "intelligence": 2, 1147 | // "name": "Sabretooth", 1148 | // "real_name": "Victor Creed", 1149 | // "speed": 2, 1150 | // "strength": 4 1151 | // } 1152 | // } 1153 | // ... 1154 | // ] 1155 | func (leftExpr Exp) OuterJoin(rightExpr Exp, predicate interface{}) Exp { 1156 | return naryOperator(outerJoinKind, leftExpr, rightExpr, funcWrapper(predicate, 2)) 1157 | } 1158 | 1159 | // EqJoin performs a join on two expressions, it is more efficient than 1160 | // .InnerJoin() and .OuterJoin() because it looks up elements in the right table 1161 | // by primary key. See also .InnerJoin() and .OuterJoin(). 1162 | // 1163 | // Example usage: 1164 | // 1165 | // var response []interface{} 1166 | // // Get each hero and their associated lair 1167 | // query := r.Table("villains").EqJoin("id", r.Table("lairs"), "villain_id") 1168 | // err := query.Run(session).All(&response) 1169 | // 1170 | // Example response: 1171 | // 1172 | // [ 1173 | // { 1174 | // "left": { 1175 | // "durability": 6, 1176 | // "energy": 6, 1177 | // "fighting": 3, 1178 | // "id": "c0d1b94f-b07e-40c3-a1db-448e645daedc", 1179 | // "intelligence": 6, 1180 | // "name": "Magneto", 1181 | // "real_name": "Max Eisenhardt", 1182 | // "speed": 4, 1183 | // "strength": 2 1184 | // }, 1185 | // "right": { 1186 | // "lair": "Asteroid M", 1187 | // "villain_id": "c0d1b94f-b07e-40c3-a1db-448e645daedc" 1188 | // } 1189 | // }, 1190 | // ... 1191 | // ] 1192 | func (leftExpr Exp) EqJoin(leftAttribute string, rightExpr Exp, index string) Exp { 1193 | return naryOperator(eqJoinKind, leftExpr, leftAttribute, rightExpr, index) 1194 | } 1195 | 1196 | // Zip flattens the results of a join by merging the "left" and "right" fields 1197 | // of each row together. If any keys conflict, the "right" field takes 1198 | // precedence. 1199 | // 1200 | // Example without .Zip(): 1201 | // 1202 | // var response []interface{} 1203 | // // Find each hero-villain pair with the same strength 1204 | // equalStrength := func(hero, villain r.Exp) r.Exp { 1205 | // return hero.Attr("strength").Eq(villain.Attr("strength")) 1206 | // } 1207 | // query := r.Table("heroes").InnerJoin(r.Table("villains"), equalStrength) 1208 | // err := query.Run(session).All(&response) 1209 | // 1210 | // Example response: 1211 | // 1212 | // [ 1213 | // { 1214 | // "left": 1215 | // { 1216 | // "durability": 5, 1217 | // "energy": 7, 1218 | // "fighting": 7, 1219 | // "id": "f915d9a7-6cfa-4151-b5f6-6aded7da595f", 1220 | // "intelligence": 5, 1221 | // "name": "Nightcrawler", 1222 | // "real_name": "Kurt Wagner", 1223 | // "speed": 7, 1224 | // "strength": 4 1225 | // }, 1226 | // "right": 1227 | // { 1228 | // "durability": 4, 1229 | // "energy": 1, 1230 | // "fighting": 7, 1231 | // "id": "12e58b11-93b3-4e89-987d-efb345001dfe", 1232 | // "intelligence": 2, 1233 | // "name": "Sabretooth", 1234 | // "real_name": "Victor Creed", 1235 | // "speed": 2, 1236 | // "strength": 4 1237 | // } 1238 | // }, 1239 | // ... 1240 | // ] 1241 | // 1242 | // Example with .Zip(): 1243 | // 1244 | // var response []interface{} 1245 | // // Find each hero-villain pair with the same strength 1246 | // equalStrength := func(hero, villain r.Exp) r.Exp { 1247 | // return hero.Attr("strength").Eq(villain.Attr("strength")) 1248 | // } 1249 | // query := r.Table("heroes").InnerJoin(r.Table("villains"), equalStrength).Zip() 1250 | // err := query.Run(session).All(&response) 1251 | // 1252 | // Example response: 1253 | // 1254 | // [ 1255 | // { 1256 | // "durability": 4, 1257 | // "energy": 1, 1258 | // "fighting": 7, 1259 | // "id": "12e58b11-93b3-4e89-987d-efb345001dfe", 1260 | // "intelligence": 2, 1261 | // "name": "Sabretooth", 1262 | // "real_name": "Victor Creed", 1263 | // "speed": 2, 1264 | // "strength": 4 1265 | // }, 1266 | // ... 1267 | // ] 1268 | func (e Exp) Zip() Exp { 1269 | return naryOperator(zipKind, e) 1270 | } 1271 | 1272 | // Count counts the number of rows in a group, for use with the .GroupBy() 1273 | // method. 1274 | // 1275 | // Example usage: 1276 | // 1277 | // var response []interface{} 1278 | // // Count all heroes in each superhero group 1279 | // err := r.Table("heroes").GroupBy("affiliation", r.Count()).Run(session).One(&response) 1280 | // 1281 | // Example response: 1282 | // 1283 | // [ 1284 | // { 1285 | // "group": "Avengers", // this is the affiliation attribute for every member of this group 1286 | // "reduction": 9 // this is the number of members in this group 1287 | // }, 1288 | // { 1289 | // "group": "X-Men", 1290 | // "reduction": 12 1291 | // }, 1292 | // ... 1293 | // ] 1294 | func Count() Exp { 1295 | return Expr(Map{"COUNT": true}) 1296 | } 1297 | 1298 | // Sum computes the sum of an attribute for a group, for use with the .GroupBy() 1299 | // method. 1300 | // 1301 | // Example usage: 1302 | // 1303 | // var response []interface{} 1304 | // // Get the total intelligence of all heroes who have the same strength 1305 | // err := r.Table("heroes").GroupBy("strength", r.Sum("intelligence")).Run(session).One(&response) 1306 | // 1307 | // Example response: 1308 | // 1309 | // [ 1310 | // { 1311 | // // this is the strength attribute for every member of this group 1312 | // "group": 1, 1313 | // // this is the sum of the intelligence attribute of all members of the group 1314 | // "reduction": 2 1315 | // }, 1316 | // { 1317 | // "group": 2, 1318 | // "reduction": 15 1319 | // }, 1320 | // ... 1321 | // ] 1322 | func Sum(attribute string) Exp { 1323 | return Expr(Map{"SUM": attribute}) 1324 | } 1325 | 1326 | // Avg computes the average value of an attribute for a group, for use with the 1327 | // .GroupBy() method. 1328 | // 1329 | // Example usage: 1330 | // 1331 | // var response []interface{} 1332 | // err := r.Table("heroes").GroupBy("strength", r.Avg("intelligence")).Run(session).One(&response) 1333 | // 1334 | // Example response: 1335 | // 1336 | // [ 1337 | // { 1338 | // // this is the strength attribute for every member of this group 1339 | // "group": 1, 1340 | // // this is the average value of the intelligence attribute of all members of the group 1341 | // "reduction": 1 1342 | // }, 1343 | // { 1344 | // "group": 2, 1345 | // "reduction": 3 1346 | // }, 1347 | // ... 1348 | // ] 1349 | func Avg(attribute string) Exp { 1350 | return Expr(Map{"AVG": attribute}) 1351 | } 1352 | 1353 | // DbCreate creates a database with the supplied name. 1354 | // 1355 | // Example usage: 1356 | // 1357 | // err := r.DbCreate("marvel").Run(session).Exec() 1358 | func DbCreate(name string) Exp { 1359 | return naryOperator(databaseCreateKind, name) 1360 | } 1361 | 1362 | // DbDrop deletes the specified database 1363 | // 1364 | // Example usage: 1365 | // 1366 | // err := r.DbDrop("marvel").Run(session).Exec() 1367 | func DbDrop(name string) Exp { 1368 | return naryOperator(databaseDropKind, name) 1369 | } 1370 | 1371 | // DbList lists all databases on the server 1372 | // 1373 | // Example usage: 1374 | // 1375 | // var databases []string 1376 | // err := r.DbList().Run(session).All(&databases) 1377 | // 1378 | // Example response: 1379 | // 1380 | // ["test", "marvel"] 1381 | func DbList() Exp { 1382 | return Exp{kind: databaseListKind} 1383 | } 1384 | 1385 | // Db lets you perform operations within a specific database (this will override 1386 | // the database specified on the session). This can be used to access or 1387 | // create/list/delete tables within any database available on the server. 1388 | // 1389 | // Example usage: 1390 | // 1391 | // var response []interface{} 1392 | // // this query will use the default database of the last created session 1393 | // r.Table("test").Run(session).All(&response) 1394 | // // this query will use database "marvel" regardless of what database the session has set 1395 | // r.Db("marvel").Table("heroes").Run(session).All(&response) 1396 | func Db(name string) Exp { 1397 | return naryOperator(databaseKind, name) 1398 | } 1399 | 1400 | // TableSpec lets you specify the various parameters for a table, then create it 1401 | // with TableCreateWithSpec(). See that function for documentation. 1402 | type TableSpec struct { 1403 | Name string 1404 | PrimaryKey string 1405 | Datacenter string 1406 | CacheSize int64 1407 | Durability string // either "soft" or "hard" 1408 | } 1409 | 1410 | // TableCreate creates a table with the specified name. 1411 | // 1412 | // Example usage: 1413 | // 1414 | // err := r.TableCreate("heroes").Run(session).Exec() 1415 | func TableCreate(name string) Exp { 1416 | spec := TableSpec{Name: name} 1417 | return naryOperator(tableCreateKind, spec) 1418 | } 1419 | 1420 | func (e Exp) TableCreate(name string) Exp { 1421 | spec := TableSpec{Name: name} 1422 | return naryOperator(tableCreateKind, e, spec) 1423 | } 1424 | 1425 | // TableCreateWithSpec creates a table with the specified attributes. 1426 | // 1427 | // Example usage: 1428 | // 1429 | // spec := TableSpec{Name: "heroes", PrimaryKey: "name"} 1430 | // err := r.TableCreateWithSpec(spec).Run(session).Exec() 1431 | func TableCreateWithSpec(spec TableSpec) Exp { 1432 | return naryOperator(tableCreateKind, spec) 1433 | } 1434 | 1435 | func (e Exp) TableCreateWithSpec(spec TableSpec) Exp { 1436 | return naryOperator(tableCreateKind, e, spec) 1437 | } 1438 | 1439 | // TableList lists all tables in the database. 1440 | // 1441 | // Example usage: 1442 | // 1443 | // var tables []string 1444 | // err := r.TableList().Run(session).All(&tables) 1445 | // 1446 | // Example response: 1447 | // 1448 | // ["heroes", "villains"] 1449 | func TableList() Exp { 1450 | return nullaryOperator(tableListKind) 1451 | } 1452 | 1453 | func (e Exp) TableList() Exp { 1454 | return naryOperator(tableListKind, e) 1455 | } 1456 | 1457 | // TableDrop removes a table from the database. 1458 | // 1459 | // Example usage: 1460 | // 1461 | // err := r.Db("marvel").TableDrop("heroes").Run(session).Exec() 1462 | func TableDrop(name string) Exp { 1463 | return naryOperator(tableDropKind, name) 1464 | } 1465 | 1466 | func (e Exp) TableDrop(name string) Exp { 1467 | return naryOperator(tableDropKind, e, name) 1468 | } 1469 | 1470 | // Table references all rows in a specific table, using the database that this 1471 | // method was called on. 1472 | // 1473 | // Example usage: 1474 | // 1475 | // var response []map[string]interface{} 1476 | // err := r.Db("marvel").Table("heroes").Run(session).All(&response) 1477 | // 1478 | // Example response: 1479 | // 1480 | // [ 1481 | // { 1482 | // "strength": 3, 1483 | // "name": "Doctor Strange", 1484 | // "durability": 6, 1485 | // "intelligence": 4, 1486 | // "energy": 7, 1487 | // "fighting": 7, 1488 | // "real_name": "Stephen Vincent Strange", 1489 | // "speed": 5, 1490 | // "id": "edc3a46b-95a0-4f64-9d1c-0dd7d83c4bcd" 1491 | // }, 1492 | // ... 1493 | // ] 1494 | func Table(name string) Exp { 1495 | return naryOperator(tableKind, name) 1496 | } 1497 | 1498 | func (e Exp) Table(name string) Exp { 1499 | return naryOperator(tableKind, e, name) 1500 | } 1501 | 1502 | // IndexCreate creates a secondary index on the specified table with the given 1503 | // name. If the function for the index is nil, the index is created for an attribute 1504 | // with the same name as the index. 1505 | // 1506 | // Example usage: 1507 | // 1508 | // var response map[string]int 1509 | // err := r.Table("heroes").IndexCreate("name", nil).Run(session).All(&response) 1510 | // 1511 | // Example response: 1512 | // 1513 | // { 1514 | // "created": 1, 1515 | // } 1516 | // 1517 | // Example usage with function: 1518 | // 1519 | // var response map[string]int 1520 | // awesomeness_f := func(hero r.Exp) r.Exp { 1521 | // return hero.Attr("speed").Mul(hero.Attr("strength")) 1522 | // } 1523 | // err := r.Table("heroes").IndexCreate("name", awesomeness_f).Run(session).All(&response) 1524 | // 1525 | // Example response: 1526 | // 1527 | // { 1528 | // "created": 1, 1529 | // } 1530 | func (e Exp) IndexCreate(name string, function interface{}) Exp { 1531 | if function == nil { 1532 | return naryOperator(indexCreateKind, e, name) 1533 | } 1534 | return naryOperator(indexCreateKind, e, name, funcWrapper(function, 1)) 1535 | } 1536 | 1537 | // IndexList lists all secondary indexes on a specified table. 1538 | // 1539 | // Example usage: 1540 | // 1541 | // var response []string 1542 | // err := r.Table("heroes").IndexList().Run(session).One(&response) 1543 | // 1544 | // Example response: 1545 | // 1546 | // ["name", "speed"] 1547 | func (e Exp) IndexList() Exp { 1548 | return naryOperator(indexListKind, e) 1549 | } 1550 | 1551 | // IndexDrop deletes a secondary index from a table. 1552 | // 1553 | // Example usage: 1554 | // 1555 | // var response map[string]int 1556 | // err := r.Table("heroes").IndexDrop("name").Run(session).One(&response) 1557 | // 1558 | // Example response: 1559 | // 1560 | // { 1561 | // "dropped": 1, 1562 | // } 1563 | func (e Exp) IndexDrop(name string) Exp { 1564 | return naryOperator(indexDropKind, e, name) 1565 | } 1566 | 1567 | // Insert inserts rows into the database. If no value is specified for the 1568 | // primary key (by default "id"), a value will be generated by the server, e.g. 1569 | // "05679c96-9a05-4f42-a2f6-a9e47c45a5ae". 1570 | // 1571 | // Example usage: 1572 | // 1573 | // var response r.WriteResponse 1574 | // row := r.Map{"name": "Thing"} 1575 | // err := r.Table("heroes").Insert(row).Run(session).One(&response) 1576 | func (e Exp) Insert(rows ...interface{}) Exp { 1577 | return naryOperator(insertKind, e, rows...) 1578 | } 1579 | 1580 | // Overwrite tells an Insert query to overwrite existing rows instead of 1581 | // returning an error. 1582 | // 1583 | // Example usage: 1584 | // 1585 | // var response r.WriteResponse 1586 | // row := r.Map{"name": "Thing"} 1587 | // err := r.Table("heroes").Insert(row).Overwrite(true).Run(session).One(&response) 1588 | func (e Exp) Overwrite(overwrite bool) Exp { 1589 | return naryOperator(upsertKind, e, overwrite) 1590 | } 1591 | 1592 | // Atomic changes the required atomic-ness of a query. By default queries will 1593 | // only be run if they can be executed atomically, that is, all at once. If a 1594 | // query may not be executed atomically, the server will return an error. To 1595 | // disable the atomic requirement, use .Atomic(false). 1596 | // 1597 | // Example usage: 1598 | // 1599 | // var response r.WriteResponse 1600 | // id := "05679c96-9a05-4f42-a2f6-a9e47c45a5ae" 1601 | // replacement := r.Map{"name": r.Js("Thing")} 1602 | // // The following will return an error, because of the use of r.Js 1603 | // err := r.Table("heroes").GetById(id).Update(replacement).Run(session).One(&response) 1604 | // // This will work 1605 | // err := r.Table("heroes").GetById(id).Update(replacement).Atomic(false).Run(session).One(&response) 1606 | func (e Exp) Atomic(atomic bool) Exp { 1607 | return naryOperator(atomicKind, e, atomic) 1608 | } 1609 | 1610 | // Update updates rows in the database. Accepts a JSON document, a RQL 1611 | // expression, or a combination of the two. 1612 | // 1613 | // Example usage: 1614 | // 1615 | // var response r.WriteResponse 1616 | // replacement := r.Map{"name": "Thing"} 1617 | // // Update a single row by id 1618 | // id := "05679c96-9a05-4f42-a2f6-a9e47c45a5ae" 1619 | // err := r.Table("heroes").GetById(id).Update(replacement).Run(session).One(&response) 1620 | // // Update all rows in the database 1621 | // err := r.Table("heroes").Update(replacement).Run(session).One(&response) 1622 | func (e Exp) Update(mapping interface{}) Exp { 1623 | return naryOperator(updateKind, e, funcWrapper(mapping, 1)) 1624 | } 1625 | 1626 | // Replace replaces rows in the database. Accepts a JSON document or a RQL 1627 | // expression, and replaces the original document with the new one. The new 1628 | // row must have the same primary key as the original document. 1629 | // 1630 | // Example usage: 1631 | // 1632 | // var response r.WriteResponse 1633 | // 1634 | // // Replace a single row by id 1635 | // id := "05679c96-9a05-4f42-a2f6-a9e47c45a5ae" 1636 | // replacement := r.Map{"id": r.Row.Attr("id"), "name": "Thing"} 1637 | // err := r.Table("heroes").GetById(id).Replace(replacement).Run(session).One(&response) 1638 | // 1639 | // // Replace all rows in a table 1640 | // err := r.Table("heroes").Replace(replacement).Run(session).One(&response) 1641 | func (e Exp) Replace(mapping interface{}) Exp { 1642 | return naryOperator(replaceKind, e, funcWrapper(mapping, 1)) 1643 | } 1644 | 1645 | // Delete removes one or more rows from the database. 1646 | // 1647 | // Example usage: 1648 | // 1649 | // var response r.WriteResponse 1650 | // 1651 | // // Delete a single row by id 1652 | // id := "5d93edbb-2882-4594-8163-f64d8695e575" 1653 | // err := r.Table("heroes").GetById(id).Delete().Run(session).One(&response) 1654 | // 1655 | // // Delete all rows in a table 1656 | // err := r.Table("heroes").Delete().Run(session).One(&response) 1657 | // 1658 | // // Find a row, then delete it 1659 | // row := r.Map{"real_name": "Peter Benjamin Parker"} 1660 | // err := r.Table("heroes").Filter(row).Delete().Run(session).One(&response) 1661 | func (e Exp) Delete() Exp { 1662 | return naryOperator(deleteKind, e) 1663 | } 1664 | 1665 | // ForEach runs a given write query for each row of a sequence. 1666 | // 1667 | // Example usage: 1668 | // 1669 | // // Delete all rows with the given ids 1670 | // var response r.WriteResponse 1671 | // // Delete multiple rows by primary key 1672 | // heroNames := []string{"Iron Man", "Colossus"} 1673 | // deleteHero := func (name r.Exp) r.Query { 1674 | // return r.Table("heroes").Get(name, "name").Delete() 1675 | // } 1676 | // err := r.Expr(heroNames).ForEach(deleteHero).Run(session).One(&response) 1677 | // 1678 | // Example response: 1679 | // 1680 | // { 1681 | // "deleted": 2 1682 | // } 1683 | func (e Exp) ForEach(queryFunc interface{}) Exp { 1684 | return naryOperator(forEachKind, e, funcWrapper(queryFunc, 1)) 1685 | } 1686 | 1687 | // Do evalutes the last argument (a function) using all previous arguments as the arguments to the function. 1688 | // 1689 | // For instance, Do(a, b, c, f) will be run as f(a, b, c). 1690 | // 1691 | // Example usage: 1692 | // 1693 | // var response interface{} 1694 | // err := r.Do(1, 2, 3, func(a, b, c r.Exp) interface{} { 1695 | // return r.List{a, b, c} 1696 | // }).Run(session).One(&response) 1697 | // 1698 | // Example response: 1699 | // 1700 | // [1,2,3] 1701 | func Do(operands ...interface{}) Exp { 1702 | // last argument is a function 1703 | f := operands[len(operands)-1] 1704 | operands = operands[:len(operands)-1] 1705 | return naryOperator(funcallKind, funcWrapper(f, -1), operands...) 1706 | } 1707 | 1708 | // TypeOf returns the type of the expression. 1709 | // 1710 | // Example usage: 1711 | // 1712 | // var response string 1713 | // err := r.Expr(1).TypeOf().Run(session).One(&response) 1714 | // 1715 | // Example response: 1716 | // 1717 | // "NUMBER" 1718 | func (e Exp) TypeOf() Exp { 1719 | return naryOperator(typeOfKind, e) 1720 | } 1721 | 1722 | // Info returns information about the expression. Often used on tables. 1723 | // 1724 | // Example usage: 1725 | // 1726 | // var response string 1727 | // err := r.Table("heroes").Info().Run(session).One(&response) 1728 | // 1729 | // Example response: 1730 | // 1731 | // "NUMBER" 1732 | func (e Exp) Info() Exp { 1733 | return naryOperator(infoKind, e) 1734 | } 1735 | 1736 | // CoerceTo converts a value of one type to another type. 1737 | // 1738 | // You can convert: a selection, sequence, or object into an ARRAY, an array of 1739 | // pairs into an OBJECT, and any DATUM into a STRING. 1740 | // 1741 | // Example usage: 1742 | // 1743 | // var response string 1744 | // err := r.Expr(1).CoerceTo("string").Run(session).One(&response) 1745 | // 1746 | // Example response: 1747 | // 1748 | // "1" 1749 | func (e Exp) CoerceTo(typename string) Exp { 1750 | return naryOperator(coerceToKind, e, typename) 1751 | } 1752 | 1753 | // WithFields filters an array to only include objects with all specified 1754 | // fields, then removes all extra fields from each object. 1755 | // 1756 | // Example usage: 1757 | // 1758 | // objects := r.List{ 1759 | // r.Map{"name": "Mono", "sexiness": "maximum"}, 1760 | // r.Map{"name": "Agro", "horseyness": "maximum"}, 1761 | // } 1762 | // var response []interface{} 1763 | // r.Expr(objects).WithFields("name", "horseyness").Run(session).One(&response) 1764 | // 1765 | // Example response: 1766 | // 1767 | // {"name": "Agro", "horseyness": "maximum"} 1768 | func (e Exp) WithFields(fields ...string) Exp { 1769 | return naryOperator(withFieldsKind, e, stringsToInterfaces(fields)...) 1770 | } 1771 | 1772 | // Prepend inserts a value at the beginning of an array. 1773 | // 1774 | // Example usage: 1775 | // 1776 | // var response []string 1777 | // r.Expr(r.List{"a", "b"}).Prepend("z").Run(session).One(&response) 1778 | // 1779 | // Example response: 1780 | // 1781 | // ["z", "a", "b"] 1782 | func (e Exp) Prepend(value interface{}) Exp { 1783 | return naryOperator(prependKind, e, value) 1784 | } 1785 | 1786 | // InsertAt inserts a single value into an array at the given index. 1787 | // 1788 | // Example usage: 1789 | // 1790 | // var response []string 1791 | // r.Expr(r.List{"a", "b"}).InsertAt(1, "c").Run(session).One(&response) 1792 | // 1793 | // Example response: 1794 | // 1795 | // ["a", "c", "b"] 1796 | func (e Exp) InsertAt(index, value interface{}) Exp { 1797 | return naryOperator(insertAtKind, e, index, value) 1798 | } 1799 | 1800 | // SpliceAt inserts multiple values into an array at the given index 1801 | // 1802 | // Example usage: 1803 | // 1804 | // var response []string 1805 | // r.Expr(r.List{"a", "b"}).SpliceAt(1, r.List{"c", "a", "t"}).Run(session).One(&response) 1806 | // 1807 | // Example response: 1808 | // 1809 | // ["a", "c", "a", "t", "b"] 1810 | func (e Exp) SpliceAt(index, value interface{}) Exp { 1811 | return naryOperator(spliceAtKind, e, index, value) 1812 | } 1813 | 1814 | // DeleteAt removes an element from an array from the given start index to the 1815 | // end index. If end index is set to nil DeleteAt will only delete 1816 | // the element at start index. 1817 | // 1818 | // Example usage: 1819 | // 1820 | // var response []string 1821 | // r.Expr(r.List{"a", "b", "c"}).DeleteAt(1, 2).Run(session).One(&response) 1822 | // 1823 | // Example response: 1824 | // 1825 | // ["a"] 1826 | func (e Exp) DeleteAt(startIndex, endIndex interface{}) Exp { 1827 | if endIndex == nil { 1828 | return naryOperator(deleteAtKind, e, startIndex) 1829 | } 1830 | return naryOperator(deleteAtKind, e, startIndex, endIndex) 1831 | } 1832 | 1833 | // ChangeAt replaces an element of an array at a given index. 1834 | // 1835 | // Example usage: 1836 | // 1837 | // var response []string 1838 | // r.Expr(r.List{"a", "b", "c"}).ChangeAt(1, "x").Run(session).One(&response) 1839 | // 1840 | // Example response: 1841 | // 1842 | // ["a", "x", "c"] 1843 | func (e Exp) ChangeAt(index, value interface{}) Exp { 1844 | return naryOperator(changeAtKind, e, index, value) 1845 | } 1846 | 1847 | // Difference removes values from an array. 1848 | // 1849 | // Example usage: 1850 | // 1851 | // var response []string 1852 | // r.Expr(r.List{"a", "b", "b", "c"}).Difference(r.List{"a", "b", "d"}).Run(session).One(&response) 1853 | // 1854 | // Example response: 1855 | // 1856 | // ["c"] 1857 | func (e Exp) Difference(value interface{}) Exp { 1858 | return naryOperator(differenceKind, e, value) 1859 | } 1860 | 1861 | // IndexesOf gets the indexes where either a specific value appears, or else all 1862 | // indexes where the given function returns true. 1863 | // 1864 | // Example usage: 1865 | // 1866 | // var response []int 1867 | // r.Expr(r.List{"a", "b", "b", "a"}).IndexesOf("b").Run(session).One(&response) 1868 | // 1869 | // Example response: 1870 | // 1871 | // [1, 2] 1872 | // 1873 | // Example usage with function: 1874 | // var response []int 1875 | // r.Expr(r.List{"a", "b", "b", "a"}).IndexesOf(func(row r.Exp) r.Exp { 1876 | // return r.Expr(row.Eq("b")) 1877 | // }).Run(session).One(&response) 1878 | func (e Exp) IndexesOf(operand interface{}) Exp { 1879 | return naryOperator(indexesOfKind, e, funcWrapper(operand, 1)) 1880 | } 1881 | 1882 | // Keys returns an array of all the keys on an object. 1883 | // 1884 | // Example usage: 1885 | // 1886 | // var response []string 1887 | // expr := r.Expr(r.Map{"name": "rethinkdb", "type": "database"}) 1888 | // expr.Keys().Run(session).One(&response) 1889 | // 1890 | // Example response: 1891 | // 1892 | // ["name", "type"] 1893 | func (e Exp) Keys() Exp { 1894 | return naryOperator(keysKind, e) 1895 | } 1896 | 1897 | // IsEmpty returns true if the sequence is empty. 1898 | // 1899 | // Example usage: 1900 | // 1901 | // var response bool 1902 | // r.Expr(r.List{}).IsEmpty().Run(session).One(&response) 1903 | // 1904 | // Example response: 1905 | // 1906 | // true 1907 | func (e Exp) IsEmpty() Exp { 1908 | return naryOperator(isEmptyKind, e) 1909 | } 1910 | 1911 | // SetInsert adds a value to an array and returns the unique values of the resulting array. 1912 | // 1913 | // Example usage: 1914 | // 1915 | // var response []string 1916 | // err = r.Expr(r.List{"a", "b", "b"}).SetInsert("b").SetInsert("c").Run(session).One(&response) 1917 | // 1918 | // Example response: 1919 | // 1920 | // ["a", "b", "c"] 1921 | func (e Exp) SetInsert(value interface{}) Exp { 1922 | return naryOperator(setInsertKind, e, value) 1923 | } 1924 | 1925 | // SetUnion adds multiple values to an array and returns the unique values of the resulting array. 1926 | // 1927 | // Example usage: 1928 | // 1929 | // var response []string 1930 | // err = r.Expr(r.List{"a", "b", "b"}).SetUnion(r.List{"b", "c"}).Run(session).One(&response) 1931 | // 1932 | // Example response: 1933 | // 1934 | // ["a", "b", "c"] 1935 | func (e Exp) SetUnion(values interface{}) Exp { 1936 | return naryOperator(setUnionKind, e, values) 1937 | } 1938 | 1939 | // SetDifference removes the given values from an array and returns the unique values of the resulting array. 1940 | // 1941 | // Example usage: 1942 | // 1943 | // var response []string 1944 | // err = r.Expr(r.List{"a", "b", "b"}).SetDifference(r.List{"b", "c"}).Run(session).One(&response) 1945 | // 1946 | // Example response: 1947 | // 1948 | // ["a"] 1949 | func (e Exp) SetDifference(values interface{}) Exp { 1950 | return naryOperator(setDifferenceKind, e, values) 1951 | } 1952 | 1953 | // SetIntersection returns all the unique values that appear in both arrays. 1954 | // 1955 | // Example usage: 1956 | // 1957 | // var response []string 1958 | // err = r.Expr(r.List{"a", "b", "b"}).SetIntersection(r.List{"b", "c"}).Run(session).One(&response) 1959 | // 1960 | // Example response: 1961 | // 1962 | // ["b"] 1963 | func (e Exp) SetIntersection(values interface{}) Exp { 1964 | return naryOperator(setIntersectionKind, e, values) 1965 | } 1966 | 1967 | // Contains returns true if all the specified values appear in the array, false 1968 | // otherwise. 1969 | // 1970 | // Example usage: 1971 | // 1972 | // var response bool 1973 | // err = r.Expr(r.List{"a", "b", "c"}).Contains("a", "b").Run(session).One(&response) 1974 | // 1975 | // Example response: 1976 | // 1977 | // true 1978 | func (e Exp) Contains(values ...interface{}) Exp { 1979 | return naryOperator(containsKind, e, values...) 1980 | } 1981 | 1982 | // Sample selects a given number of elements from an array randomly with a 1983 | // uniform distribution. 1984 | // 1985 | // Example usage: 1986 | // 1987 | // var response []string 1988 | // err = r.Expr(r.List{"a", "b", "c"}).Sample(1).Run(session).One(&response) 1989 | // 1990 | // Example response: 1991 | // 1992 | // ["a"] (maybe) 1993 | func (e Exp) Sample(count interface{}) Exp { 1994 | return naryOperator(sampleKind, e, count) 1995 | } 1996 | 1997 | // Match matches a regular expression against a string. The regular expression 1998 | // syntax is RE2, which is the same used by the "regexp" package. 1999 | // 2000 | // Example usage: 2001 | // 2002 | // var response interface{} 2003 | // err = r.Expr("3.14159").Match("[0-9]+").Run(session).One(&response) 2004 | // 2005 | // Example response: 2006 | // 2007 | // {"str": "3", "start": 0, "end": 1, "groups": []} 2008 | func (e Exp) Match(regularExpression string) Exp { 2009 | return naryOperator(matchKind, e, regularExpression) 2010 | } 2011 | 2012 | // Default specifies the default value for an expression if the expression 2013 | // evaluates to null or if it raises an error (for instance, a non-existent 2014 | // property is accessed). 2015 | // 2016 | // Example usage: 2017 | // 2018 | // var response interface{} 2019 | // r.Expr(r.Map{"a": "b"}).Attr("c").Default("oops").Run(session).One(&response) 2020 | // 2021 | // Example response: 2022 | // 2023 | // "oops" 2024 | func (e Exp) Default(value interface{}) Exp { 2025 | return naryOperator(defaultKind, e, value) 2026 | } 2027 | 2028 | // ReturnValues tells the server, when performing a single row insert/update/delete/upsert, to return the new and old values on single row 2029 | // 2030 | // Example usage: 2031 | // 2032 | // var response interface{} 2033 | // 2034 | // Example response: 2035 | // 2036 | func (e Exp) ReturnValues() Exp { 2037 | return naryOperator(returnValuesKind, e) 2038 | } 2039 | -------------------------------------------------------------------------------- /responses.go: -------------------------------------------------------------------------------- 1 | package rethinkgo 2 | 3 | // WriteResponse is a type that can be used to read responses to write queries, such as .Insert() 4 | // 5 | // Example usage: 6 | // 7 | // var response r.WriteResponse 8 | // err := r.Table("heroes").Insert(r.Map{"name": "Professor X"}).Run(session).One(&response) 9 | // fmt.Println("inserted", response.Inserted, "rows") 10 | type WriteResponse struct { 11 | Inserted int 12 | Errors int 13 | Updated int 14 | Unchanged int 15 | Replaced int 16 | Deleted int 17 | GeneratedKeys []string `json:"generated_keys"` 18 | FirstError string `json:"first_error"` // populated if Errors > 0 19 | NewValue interface{} `json:"new_val"` 20 | OldValue interface{} `json:"old_val"` 21 | } 22 | -------------------------------------------------------------------------------- /rows.go: -------------------------------------------------------------------------------- 1 | package rethinkgo 2 | 3 | import ( 4 | "code.google.com/p/goprotobuf/proto" 5 | "errors" 6 | "fmt" 7 | "reflect" 8 | p "github.com/christopherhesse/rethinkgo/ql2" 9 | ) 10 | 11 | // Rows is an iterator to move through the rows returned by the database, call 12 | // rows.Scan(&dest) in a loop to scan a row into the variable `dest`, 13 | // rows.Next() returns false when there is an error or no more rows left. 14 | // 15 | // There are three methods on the rows object that can be used to avoid 16 | // iterating in this manner. These three methods correspond to the return types 17 | // of a query: 18 | // 19 | // .Exec() for an empty response (or to ignore the response): 20 | // 21 | // err := r.Db("marvel").TableCreate("heroes").Exec() 22 | // 23 | // .One(&dest) for a response that always returns a single result: 24 | // 25 | // var response string 26 | // err := r.Table("heroes").Get("Omega Red", "name").Run(session).One(&response) 27 | // 28 | // .All(&dest) for a list of results: 29 | // 30 | // var response []string 31 | // err := r.Db("marvel").TableList().Run(session).All(&response) 32 | // 33 | // .All() may perform multiple network requests to get all of the results of 34 | // the query. Use .Limit() if you only need a certain number. 35 | // 36 | // All three of these methods will return errors if used on a query response 37 | // that does not match the expected type (ErrWrongResponseType). 38 | type Rows struct { 39 | session *Session 40 | closed bool 41 | buffer []*p.Datum 42 | current *p.Datum 43 | complete bool // We have retrieved all the results for a query 44 | lasterr error 45 | token int64 46 | responseType p.Response_ResponseType 47 | } 48 | 49 | // continueQuery creates a query that will cause this query to continue 50 | func (rows *Rows) continueQuery() error { 51 | queryProto := &p.Query{ 52 | Type: p.Query_CONTINUE.Enum(), 53 | Token: proto.Int64(rows.token), 54 | } 55 | buffer, responseType, err := rows.session.conn.executeQuery(queryProto, rows.session.timeout) 56 | if err != nil { 57 | return err 58 | } 59 | 60 | switch responseType { 61 | case p.Response_SUCCESS_PARTIAL: 62 | // continuation of a stream of rows 63 | rows.buffer = buffer 64 | case p.Response_SUCCESS_SEQUENCE: 65 | // end of a stream of rows, there's no more after this 66 | rows.buffer = buffer 67 | rows.complete = true 68 | default: 69 | return fmt.Errorf("rethinkdb: Unexpected response type: %v", responseType) 70 | } 71 | return nil 72 | } 73 | 74 | // Next moves the iterator forward by one document, returns false if there are 75 | // no more rows or some sort of error has occurred (use .Err() to get the last 76 | // error). `dest` must be passed by reference. 77 | // 78 | // Example usage: 79 | // 80 | // rows := r.Table("heroes").Run(session) 81 | // for rows.Next() { 82 | // var hero interface{} 83 | // rows.Scan(&hero) 84 | // fmt.Println("hero:", hero) 85 | // } 86 | // if rows.Err() != nil { 87 | // ... 88 | // } 89 | func (rows *Rows) Next() bool { 90 | if rows.closed { 91 | return false 92 | } 93 | 94 | if rows.lasterr != nil { 95 | return false 96 | } 97 | 98 | if len(rows.buffer) == 0 { 99 | // we're out of results, may need to fetch some more 100 | if rows.complete { 101 | rows.closed = true 102 | return false 103 | } else { 104 | // more rows to get, fetch 'em 105 | err := rows.continueQuery() 106 | if err != nil { 107 | rows.lasterr = err 108 | return false 109 | } 110 | } 111 | } 112 | 113 | if len(rows.buffer) > 0 { 114 | rows.current = rows.buffer[0] 115 | rows.buffer = rows.buffer[1:len(rows.buffer)] 116 | } 117 | 118 | return true 119 | } 120 | 121 | // Scan writes the current row into the provided variable, which must be passed 122 | // by reference. 123 | // 124 | // NOTE: Scan uses json.Unmarshal internally and will not clear the destination 125 | // before writing the next row. Make sure to create a new destination or clear 126 | // it before calling .Scan(&dest). 127 | func (rows *Rows) Scan(dest interface{}) error { 128 | return datumUnmarshal(rows.current, dest) 129 | } 130 | 131 | // Err returns the last error encountered, for example, a network error while 132 | // contacting the database server, or while parsing. 133 | // 134 | // Example usage: 135 | // 136 | // err := r.Table("heroes").Run(session).Err() 137 | func (rows *Rows) Err() error { 138 | return rows.lasterr 139 | } 140 | 141 | // All fetches all the results from an iterator into a reference to a slice. It 142 | // may perform multiple network requests to the server until it has retrieved 143 | // all results. 144 | // 145 | // Example usage: 146 | // 147 | // var result []interface{} 148 | // err := r.Table("heroes").Run(session).All(&result) 149 | func (rows *Rows) All(slice interface{}) error { 150 | if rows.Err() != nil { 151 | return rows.Err() 152 | } 153 | 154 | slicePointerValue := reflect.ValueOf(slice) 155 | if slicePointerValue.Kind() != reflect.Ptr { 156 | return errors.New("rethinkdb: `slice` should probably should be a pointer to a slice") 157 | } 158 | 159 | sliceValue := slicePointerValue.Elem() 160 | if sliceValue.Kind() != reflect.Slice { 161 | return errors.New("rethinkdb: A slice type must be provided") 162 | } 163 | 164 | if rows.responseType == p.Response_SUCCESS_PARTIAL || rows.responseType == p.Response_SUCCESS_SEQUENCE { 165 | // create a new slice to hold the results 166 | newSliceValue := reflect.MakeSlice(sliceValue.Type(), 0, 0) 167 | for rows.Next() { 168 | // create a new element of the kind that the slice holds so we can scan 169 | // into it 170 | elemValue := reflect.New(sliceValue.Type().Elem()) 171 | if err := rows.Scan(elemValue.Interface()); err != nil { 172 | return err 173 | } 174 | newSliceValue = reflect.Append(newSliceValue, elemValue.Elem()) 175 | } 176 | 177 | if rows.Err() != nil { 178 | return rows.Err() 179 | } 180 | 181 | sliceValue.Set(newSliceValue) 182 | return nil 183 | } else if rows.responseType == p.Response_SUCCESS_ATOM { 184 | // if we got a single datum from the server, try to read it into the slice we got 185 | if rows.Next() { 186 | if err := rows.Scan(slicePointerValue.Interface()); err != nil { 187 | return err 188 | } 189 | } 190 | 191 | if rows.Err() != nil { 192 | return rows.Err() 193 | } 194 | return nil 195 | } 196 | return ErrWrongResponseType{} 197 | } 198 | 199 | // One gets the first result from a query response. 200 | // 201 | // Example usage: 202 | // 203 | // var result interface{} 204 | // err := r.Table("villains").Get("Galactus", "name").Run(session).One(&result) 205 | func (rows *Rows) One(row interface{}) error { 206 | if rows.Err() != nil { 207 | return rows.Err() 208 | } 209 | 210 | if rows.responseType != p.Response_SUCCESS_ATOM { 211 | return ErrWrongResponseType{} 212 | } 213 | 214 | rows.Next() 215 | if err := rows.Scan(row); err != nil { 216 | return err 217 | } 218 | 219 | return rows.Err() 220 | } 221 | 222 | // Exec is for queries for which you wish to ignore the result. For instance, 223 | // creating a table. 224 | // 225 | // Example usage: 226 | // 227 | // err := r.TableCreate("villains").Run(session).Exec() 228 | func (rows *Rows) Exec() error { 229 | if rows.Err() != nil { 230 | return rows.Err() 231 | } 232 | 233 | return nil 234 | } 235 | -------------------------------------------------------------------------------- /session.go: -------------------------------------------------------------------------------- 1 | package rethinkgo 2 | 3 | import ( 4 | "code.google.com/p/goprotobuf/proto" 5 | "fmt" 6 | p "github.com/christopherhesse/rethinkgo/ql2" 7 | "sync/atomic" 8 | "time" 9 | ) 10 | 11 | // Session represents a connection to a server, use it to run queries against a 12 | // database, with either sess.Run(query) or query.Run(session). Do not share a 13 | // session between goroutines, create a new one for each goroutine. 14 | type Session struct { 15 | // current query identifier, just needs to be unique for each query, so we 16 | // can match queries with responses, e.g. 4782371 17 | token int64 18 | // address of server, e.g. "localhost:28015" 19 | address string 20 | // database to use if no database is specified in query, e.g. "test" 21 | database string 22 | // maximum duration of a single query 23 | timeout time.Duration 24 | // authorization key for servers configured to check this 25 | authkey string 26 | 27 | conn *connection 28 | closed bool 29 | } 30 | 31 | // Connect creates a new database session. 32 | // 33 | // NOTE: You probably should not share sessions between goroutines. 34 | // 35 | // Example usage: 36 | // 37 | // sess, err := r.Connect("localhost:28015", "test") 38 | func Connect(address, database string) (*Session, error) { 39 | s := &Session{address: address, database: database, closed: true} 40 | err := s.Reconnect() 41 | return s, err 42 | } 43 | 44 | // ConnectWithAuth is the same as Connect, but also sets the authorization key 45 | // used to connect to the server. 46 | func ConnectWithAuth(address, database, authkey string) (*Session, error) { 47 | s := &Session{address: address, database: database, authkey: authkey, closed: true} 48 | err := s.Reconnect() 49 | return s, err 50 | } 51 | 52 | // Reconnect closes and re-opens a session. 53 | // 54 | // Example usage: 55 | // 56 | // err := sess.Reconnect() 57 | func (s *Session) Reconnect() error { 58 | if err := s.Close(); err != nil { 59 | return err 60 | } 61 | 62 | s.closed = false 63 | var err error 64 | s.conn, err = serverConnect(s.address, s.authkey) 65 | return err 66 | } 67 | 68 | // Close closes the session, freeing any associated resources. 69 | // 70 | // Example usage: 71 | // 72 | // err := sess.Close() 73 | func (s *Session) Close() error { 74 | if s.closed { 75 | return nil 76 | } 77 | 78 | err := s.conn.Close() 79 | s.closed = true 80 | 81 | return err 82 | } 83 | 84 | // SetTimeout causes any future queries that are run on this session to timeout 85 | // after the given duration, returning a timeout error. Set to zero to disable. 86 | // 87 | // The timeout is global to all queries run on a single Session and does not 88 | // apply to any query currently in progress. 89 | // 90 | // Example usage: 91 | // 92 | // sess.SetTimeout(1 * time.Second) 93 | func (s *Session) SetTimeout(timeout time.Duration) { 94 | s.timeout = timeout 95 | } 96 | 97 | // Use changes the default database for a connection. This is the database that 98 | // will be used when a query is created without an explicit database. This 99 | // should not be used if the session is shared between goroutines, confusion 100 | // would result. 101 | // 102 | // Example usage: 103 | // 104 | // sess.Use("dave") 105 | // rows := r.Table("employees").Run(session) // uses database "dave" 106 | func (s *Session) Use(database string) { 107 | s.database = database 108 | } 109 | 110 | // getToken generates the next query token, used to number requests and match 111 | // responses with requests. 112 | func (s *Session) getToken() int64 { 113 | return atomic.AddInt64(&s.token, 1) 114 | } 115 | 116 | // Run executes a query directly on a specific session and returns an iterator 117 | // that moves through the resulting JSON rows with rows.Next() and 118 | // rows.Scan(&dest). See the documentation for the Rows object for other 119 | // options. 120 | // 121 | // Example usage: 122 | // 123 | // rows := session.Run(query) 124 | // for rows.Next() { 125 | // var row map[string]interface{} 126 | // rows.Scan(&row) 127 | // fmt.Println("row:", row) 128 | // } 129 | // if rows.Err() { 130 | // ... 131 | // } 132 | func (s *Session) Run(query Exp) *Rows { 133 | queryProto, err := s.getContext().buildProtobuf(query) 134 | if err != nil { 135 | return &Rows{lasterr: err} 136 | } 137 | 138 | queryProto.Token = proto.Int64(s.getToken()) 139 | buffer, responseType, err := s.conn.executeQuery(queryProto, s.timeout) 140 | if err != nil { 141 | return &Rows{lasterr: err} 142 | } 143 | 144 | switch responseType { 145 | case p.Response_SUCCESS_ATOM: 146 | // single document (or json) response, return an iterator anyway for 147 | // consistency of types 148 | return &Rows{ 149 | buffer: buffer, 150 | complete: true, 151 | responseType: responseType, 152 | } 153 | case p.Response_SUCCESS_PARTIAL: 154 | // beginning of stream of rows, there are more results available from the 155 | // server than the ones we just received, so save the session we used in 156 | // case the user wants more 157 | return &Rows{ 158 | session: s, 159 | buffer: buffer, 160 | token: queryProto.GetToken(), 161 | responseType: responseType, 162 | } 163 | case p.Response_SUCCESS_SEQUENCE: 164 | // end of a stream of rows, since we got this on the initial query this means 165 | // that we got a stream response, but the number of results was less than the 166 | // number required to break the response into chunks. we can just return all 167 | // the results in one go, as this is the only response 168 | return &Rows{ 169 | buffer: buffer, 170 | complete: true, 171 | responseType: responseType, 172 | } 173 | } 174 | return &Rows{lasterr: fmt.Errorf("rethinkdb: Unexpected response type from server: %v", responseType)} 175 | } 176 | 177 | func (s *Session) getContext() context { 178 | return context{databaseName: s.database, atomic: true} 179 | } 180 | 181 | // Run runs a query using the given session, there is one Run() 182 | // method for each type of query. 183 | func (e Exp) Run(session *Session) *Rows { 184 | return session.Run(e) 185 | } 186 | -------------------------------------------------------------------------------- /utils.go: -------------------------------------------------------------------------------- 1 | package rethinkgo 2 | 3 | import ( 4 | "code.google.com/p/goprotobuf/proto" 5 | "strings" 6 | ) 7 | 8 | func protoStringOrNil(s string) *string { 9 | if s == "" { 10 | return nil 11 | } 12 | return proto.String(s) 13 | } 14 | 15 | func protoInt64OrNil(i int64) *int64 { 16 | if i == 0 { 17 | return nil 18 | } 19 | return proto.Int64(i) 20 | } 21 | 22 | func prefixLines(s string, prefix string) (result string) { 23 | for _, line := range strings.Split(s, "\n") { 24 | result += prefix + line + "\n" 25 | } 26 | return 27 | } 28 | 29 | func protobufToString(p proto.Message, indentLevel int) string { 30 | return prefixLines(proto.MarshalTextString(p), strings.Repeat(" ", indentLevel)) 31 | } 32 | --------------------------------------------------------------------------------