pax_global_header00006660000000000000000000000064127204263000014506gustar00rootroot0000000000000052 comment=de072a9c0eff5bd52ef81fe45b6abeccde74bd13 gorethink-2.0.4/000077500000000000000000000000001272042630000135035ustar00rootroot00000000000000gorethink-2.0.4/.gitignore000066400000000000000000000004271272042630000154760ustar00rootroot00000000000000# Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a *.so #vim *.swp *.swo # Folders _obj _test # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe .wercker gorethink-2.0.4/.travis.yml000066400000000000000000000014341272042630000156160ustar00rootroot00000000000000language: go go: - 1.4 - 1.5 cache: apt go_import_path: gopkg.in/dancannon/gorethink.v2 before_script: - source /etc/lsb-release && echo "deb http://download.rethinkdb.com/apt $DISTRIB_CODENAME main" | sudo tee /etc/apt/sources.list.d/rethinkdb.list - wget -qO- http://download.rethinkdb.com/apt/pubkey.gpg | sudo apt-key add - - sudo apt-get update - sudo apt-get install rethinkdb - rethinkdb > /dev/null 2>&1 & - rethinkdb --port-offset 1 --directory rethinkdb_data1 --join localhost:29016 > /dev/null 2>&1 & - rethinkdb --port-offset 2 --directory rethinkdb_data2 --join localhost:29016 > /dev/null 2>&1 & - rethinkdb --port-offset 3 --directory rethinkdb_data3 --join localhost:29016 > /dev/null 2>&1 & script: go test -tags='cluster' -short -race -check.v -v ./... gorethink-2.0.4/CHANGELOG.md000066400000000000000000000464621272042630000153300ustar00rootroot00000000000000# Change Log All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). ## v2.0.4 - 2016-05-22 ### Changed - Changed `Connect` to return the reason for connections failing (instead of just "no connections were made when creating the session") ### Fixed - Fixed queries not being retried when using `Query()`, queries are now retried if the request failed due to a bad connection. - Fixed `Cursor` methods panicking if using a nil cursor, please note that you should still always check if your queries return an error. ## v2.0.3 - 2016-05-12 ### Added - Added constants for system database and table names. ### Changed - Re-enabled keep alive by default. ## v2.0.2 - 2016-04-18 ### Fixed - Fixed issue which prevented anonymous `time.Time` values from being encoded when used in a struct. - Fixed panic when attempting to run a query with a nil session ## v2.0.1 - 2016-04-14 ### Added - Added `UnionWithOpts` term which allows `Union` to be called with optional arguments (such as `Interleave`) - Added `IncludeOffsets` and `IncludeTypes` optional arguments to `ChangesOpts` - Added `Conflict` optional argument to `InsertOpts` ### Fixed - Fixed error when connecting to database as non-admin user, please note that `DiscoverHosts` will not work with user authentication at this time due to the fact that RethinkDB restricts access to the required system tables. ## v2.0.0 - 2016-04-13 ### Changed - GoRethink now uses the v1.0 RethinkDB protocol which supports RethinkDB v2.3 and above. If you are using RethinkDB 2.2 or older please set `HandshakeVersion` when creating a session. For example: ```go r.Connect( ... HandshakeVersion: r.HandshakeV0_4, ... ) ``` ### Added - Added support for username/password authentication. To login pass your username and password when creating a session using the `Username` and `Password` fields in the `ConnectOpts`. - Added the `Grant` term - Added the `Ordered` optional argument to `EqJoin` - Added the `Fold` term and examples - Added the `ReadOne` and `ReadAll` helper functions for quickly executing a query and scanning the result into a variable. For examples see the godocs. - Added the `Peek` and `Skip` functions to the `Cursor`. - Added support for referential arrays in structs - Added the `Durability` argument to `RunOpts`/`ExecOpts` ### Deprecated - Deprecated the root `Wait` term, `r.Table(...).Wait()` should now be used instead. - Deprecated session authentication using `AuthKey` ### Fixed - Fixed issue with `ReconfigureOpts` field `PrimaryTag` ## v1.4.1 - 2016-04-02 ### Fixed - Fixed panic when closing a connection at the same time as using a changefeed. - Update imports to correctly use gopkg.in - Fixed race condition when using anonymous functions - Fixed IsConflictErr and IsTypeErr panicking when passed nil errors - RunWrite no longer misformats errors with formatting directives in them ## v1.4.0 - 2016-03-15 ### Added - Added the ability to reference subdocuments when inserting new documents, for more information see the documentation in the readme. - Added the `SetTags` function which allows GoRethink to override which tags are used when working with structs. For example to support the `json` add the following call `SetTags("gorethink", "json")`. - Added helper functions for checking the error type of a write query, this is useful when calling `RunWrite`. + Added `IsConflictErr` which returns true when RethinkDB returns a duplicate key error. + Added `IsTypeErr` which returns true when RethinkDB returns an unexpected type error. - Added the `RawQuery` term which can be used to execute a raw JSON query, for more information about this query see the godoc. - Added the `NextResponse` function to `Cursor` which will return the next raw JSON response in the result set. - Added ability to set the keep alive period by setting the `KeepAlivePeriod` field in `ConnectOpts`. ### Fixed - Fixed an issue that could prevent bad connections from being removed from the connection pool. - Fixed certain connection errors not being returned as `RqlConnectionError` when calling `Run`, `Exec` or `RunWrite`. - Fixed potential dead lock in connection code caused when building the query. ## v1.3.2 - 2015-02-01 ### Fixed - Fixed race condition in cursor which caused issues when closing a cursor that is in the process of fetching data. ## v1.3.1 - 2015-01-22 ### Added - Added more documentation and examples for `GetAll`. ### Fixed - Fixed `RunWrite` not defering its call to `Cursor.Close()`. This could cause issues if an error occurred when decoding the result. - Fixed panic when calling `Error()` on a GoRethink `rqlError`. ## v1.3.0 - 2016-01-11 ### Added - Added new error types, the following error types can now be returned: `RQLClientError`, `RQLCompileError`, `RQLDriverCompileError`, `RQLServerCompileError`, `RQLAuthError`, `RQLRuntimeError`, `RQLQueryLogicError`, `RQLNonExistenceError`, `RQLResourceLimitError`, `RQLUserError`, `RQLInternalError`, `RQLTimeoutError`, `RQLAvailabilityError`, `RQLOpFailedError`, `RQLOpIndeterminateError`, `RQLDriverError`, `RQLConnectionError`. Please note that some other errors can be returned. - Added `IsConnected` function to `Session`. ### Fixed - Fixed panic when scanning through results caused by incorrect queue implementation. ## v1.2.0 - 2015-11-19 ### Added - Added `UUID` term - Added `Values` term - Added `IncludeInitial` and `ChangefeedQueueSize` to `ChangesOpts` - Added `UseJSONNumber` to `ConnectOpts` which changes the way the JSON unmarshal works when deserializing JSON with interface{}, it's preferred to use json.Number instead float64 as it preserves the original precision. - Added `HostDecayDuration` to `ConnectOpts` to configure how hosts are selected. For more information see the godoc. ### Changed - Timezones from `time.Time` are now stored in the database, before all times were stored as UTC. To convert a go `time.Time` back to UTC you can call `t.In(time.UTC)`. - Improved host selection to use `hailocab/go-hostpool` to select nodes based on recent responses and timings. - Changed connection pool to use `fatih/pool` instead of a custom connection pool, this has caused some internal API changes and the behaviour of `MaxIdle` and `MaxOpen` has slightly changed. This change was made mostly to make driver maintenance easier. + `MaxIdle` now configures the initial size of the pool, the name of this field will likely change in the future. + Not setting `MaxOpen` no longer creates an unbounded connection pool per host but instead creates a pool with a maximum capacity of 2 per host. ### Deprecated - Deprecated the option `NodeRefreshInterval` in `ConnectOpts` - Deprecated `SetMaxIdleConns` and `SetMaxOpenConns`, these options should now only be set when creating the session. ### Fixed - Fixed some type aliases not being correctly encoded when using `Expr`. ## v1.1.4 - 2015-10-02 ### Added - Added root table terms (`r.TableCreate`, `r.TableList` and `r.TableDrop`) ### Removed - Removed `ReadMode` option from `RunOpts` and `ExecOpts` (incorrectly added in v1.1.0) ### Fixed - Fixed `Decode` no longer setting pointer to nil on document not found - Fixed panic when `fetchMore` returns an error - Fixed deadlock when closing changefeed - Fixed stop query incorrectly waiting for response - Fixed pointers not to be properly decoded ## v1.1.3 - 2015-09-06 ### Fixed - Fixed pointers not to be properly decoded - Fixed queries always timing out when Timeout ConnectOpt is set. ## v1.1.2 - 2015-08-28 ### Fixed - Fixed issue when encoding some maps ## v1.1.1 - 2015-08-21 ### Fixed - Corrected protobuf import - Fixed documentation - Fixed issues with time pseudotype conversion that caused issues with milliseconds ## v1.1.0 - 2015-08-19 ### Added - Replaced `UseOutdated` with `ReadMode` - Added `EmergencyRepair` and `NonVotingReplicaTags` to `ReconfigureOpts` - Added `Union` as a root term - Added `Branch` as a root term - Added `ReadTimeout` and `WriteTimeout` to `RunOpts` and `ExecOpts` - Exported `github.com/Sirupsen/logrus.Logger` as `Log` - Added support for encoding maps with non-string keys - Added 'Round', 'Ceil' and 'Floor' terms - Added race detector to CI ### Changed - Changed `Timeout` connect argument to only configure the connection timeout. - Replaced `Db` with `DB` in `RunOpts` and `ExecOpts` (`Db` still works for now) - Made `Cursor` and `Session` safe for concurrent use - Replaced `ErrClusterClosed` with `ErrConnectionClosed` ## Deprecated - Deprecated `UseOutdated` optional argument - Deprecated `Db` in `RunOpt` ### Fixed - Fixed race condition in node pool - Fixed node refresh issue with RethinkDB 2.1 due to an API change - Fixed encoding errors not being returned when running queries ## v1.0.0 - 2015-06-27 1.0.0 is finally here, This is the first stable production ready release of GoRethink! ![GoRethink Logo](https://raw.github.com/wiki/dancannon/gorethink/gopher-and-thinker.png "Golang Gopher and RethinkDB Thinker") In an attempt to make this library more "idiomatic" some functions have been renamed, for the full list of changes and bug fixes see below. ### Added - Added more documentation. - Added `Shards`, `Replicas` and `PrimaryReplicaTag` optional arguments in `TableCreateOpts`. - Added `MultiGroup` and `MultiGroupByIndex` which are equivalent to the running `group` with the `multi` optional argument set to true. ### Changed - Renamed `Db` to `DB`. - Renamed `DbCreate` to `DBCreate`. - Renamed `DbDrop` to `DBDrop`. - Renamed `RqlConnectionError` to `RQLConnectionError`. - Renamed `RqlDriverError` to `RQLDriverError`. - Renamed `RqlClientError` to `RQLClientError`. - Renamed `RqlRuntimeError` to `RQLRuntimeError`. - Renamed `RqlCompileError` to `RQLCompileError`. - Renamed `Js` to `JS`. - Renamed `Json` to `JSON`. - Renamed `Http` to `HTTP`. - Renamed `GeoJson` to `GeoJSON`. - Renamed `ToGeoJson` to `ToGeoJSON`. - Renamed `WriteChanges` to `ChangeResponse`, this is now a general type and can be used when dealing with changefeeds. - Removed depth limit when encoding values using `Expr` ### Fixed - Fixed issue causing errors when closing a changefeed cursor (#191) - Fixed issue causing nodes to remain unhealthy when host discovery is disabled (#195) - Fixed issue causing driver to fail when connecting to DB which did not have its canonical address set correctly (#200). - Fixed ongoing queries not being properly stopped when closing the cursor. ### Removed - Removed `CacheSize` and `DataCenter` optional arguments in `TableCreateOpts`. - Removed `CacheSize` optional argument from `InsertOpts` ## v0.7.2 - 2015-05-05 ### Added - Added support for connecting to a server using TLS (#179) ### Fixed - Fixed issue causing driver to fail to connect to servers with the HTTP admin interface disabled (#181) - Fixed errors in documentation (#182, #184) - Fixed RunWrite not closing the cursor (#185) ## v0.7.1 - 2015-04-19 ### Changed - Improved logging of connection errors. ### Fixed - Fixed bug causing empty times to be inserted into the DB even when the omitempty tag was set. - Fixed node status refresh loop leaking goroutines. ## v0.7.0 - 2015-03-30 This release includes support for RethinkDB 2.0 and connecting to clusters. To connect to a cluster you should use the new `Addresses` field in `ConnectOpts`, for example: ```go session, err := r.Connect(r.ConnectOpts{ Addresses: []string{"localhost:28015", "localhost:28016"}, }) if err != nil { log.Fatalln(err.Error()) } ``` Also added was the ability to read from a cursor using a channel, this is especially useful when using changefeeds. For more information see this [gist](https://gist.github.com/dancannon/2865686d163ed78bbc3c) ```go cursor, err := r.Table("items").Changes() ch := make(chan map[string]interface{}) cursor.Listen(ch) ``` For more details checkout the [README](https://github.com/dancannon/gorethink/blob/master/README.md) and [godoc](https://godoc.org/github.com/dancannon/gorethink). As always if you have any further questions send me a message on [Gitter](https://gitter.im/dancannon/gorethink). - Added the ability to connect to multiple nodes, queries are then distributed between these nodes. If a node stops responding then queries stop being sent to this node. - Added the `DiscoverHosts` optional argument to `ConnectOpts`, when this value is `true` the driver will listen for new nodes added to the cluster. - Added the `Addresses` optional argument to `ConnectOpts`, this allows the driver to connect to multiple nodes in a cluster. - Added the `IncludeStates` optional argument to `Changes`. - Added `MinVal` and `MaxVal` which represent the smallest and largest possible values. - Added the `Listen` cursor helper function which publishes database results to a channel. - Added support for optional arguments for the `Wait` function. - Added the `Type` function to the `Cursor`, by default this value will be "Cursor" unless using a changefeed. - Changed the `IndexesOf` function to `OffsetsOf` . - Changed driver to use the v0.4 protocol (used to use v0.3). - Fixed geometry tests not properly checking the expected results. - Fixed bug causing nil pointer panics when using an `Unmarshaler` - Fixed dropped millisecond precision if given value is too old ## v0.6.3 - 2015-03-04 ### Added - Add `IdentifierFormat` optarg to `TableOpts` (#158) ### Fixed - Fix struct alignment for ARM and x86-32 builds (#153) - Fix sprintf format for geometry error message (#157) - Fix duplicate if block (#159) - Fix incorrect assertion in decoder tests ## v0.6.2 - 2015-02-15 - Fixed `writeQuery` being too small when sending large queries ## v0.6.1 - 2015-02-13 - Reduce GC by using buffers when reading and writing - Fixed encoding `time.Time` ignoring millseconds - Fixed pointers in structs that implement the `Marshaler`/`Unmarshaler` interfaces being ignored ## v0.6.0 - 2015-01-01 There are some major changes to the driver with this release that are not related to the RethinkDB v1.16 release. Please have a read through them: - Improvements to result decoding by caching reflection calls. - Finished implementing the `Marshaler`/`Unmarshaler` interfaces - Connection pool overhauled. There were a couple of issues with connections in the previous releases so this release replaces the `fatih/pool` package with a connection pool based on the `database/sql` connection pool. - Another change is the removal of the prefetching mechanism as the connection+cursor logic was becoming quite complex and causing bugs, hopefully this will be added back in the near future but for now I am focusing my efforts on ensuring the driver is as stable as possible #130 #137 - Due to the above change the API for connecting has changed slightly (The API is now closer to the `database/sql` API. `ConnectOpts` changes: - `MaxActive` renamed to `MaxOpen` - `IdleTimeout` renamed to `Timeout` - `Cursor`s are now only closed automatically when calling either `All` or `One` - `Exec` now takes `ExecOpts` instead of `RunOpts`. The only difference is that `Exec` has the `NoReply` field With that out the way here are the v1.16 changes: - Added `Range` which generates all numbers from a given range - Added an optional squash argument to the changes command, which lets the server combine multiple changes to the same document (defaults to true) - Added new admin functions (`Config`, `Rebalance`, `Reconfigure`, `Status`, `Wait`) - Added support for `SUCCESS_ATOM_FEED` - Added `MinIndex` + `MaxInde`x functions - Added `ToJSON` function - Updated `WriteResponse` type Since this release has a lot of changes and although I have tested these changes sometimes things fall through the gaps. If you discover any bugs please let me know and I will try to fix them as soon as possible. ## v.0.5.1 - 2014-12-14 - Fixed empty slices being returned as `[]T(nil)` not `[]T{}` #138 ## v0.5.0 - 2014-10-06 - Added geospatial terms (`Circle`, `Distance`, `Fill`, `Geojson`, `ToGeojson`, `GetIntersecting`, `GetNearest`, `Includes`, `Intersects`, `Line`, `Point`, `Polygon`, `PolygonSub`) - Added `UUID` term for generating unique IDs - Added `AtIndex` term, combines `Nth` and `GetField` - Added the `Geometry` type, see the types package - Updated the `BatchConf` field in `RunOpts`, now uses the `BatchOpts` type - Removed support for the `FieldMapper` interface Internal Changes - Fixed encoding performance issues, greatly improves writes/second - Updated `Next` to zero the destination value every time it is called. ## v0.4.2 - 2014-09-06 - Fixed issue causing `Close` to start an infinite loop - Tidied up connection closing logic ## v0.4.1 - 2014-09-05 - Fixed bug causing Pseudotypes to not be decoded properly (#117) - Updated github.com/fatih/pool to v2 (#118) ## v0.4.0 - 2014-08-13 - Updated the driver to support RethinkDB v1.14 (#116) - Added the Binary data type - Added the Binary command which takes a `[]byte` or `bytes.Buffer{}` as an argument. - Added the `BinaryFormat` optional argument to `RunOpts` - Added the `GroupFormat` optional argument to `RunOpts` - Added the `ArrayLimit` optional argument to `RunOpts` - Renamed the `ReturnVals` optional argument to `ReturnChanges` - Renamed the `Upsert` optional argument to `Conflict` - Added the `IndexRename` command - Updated `Distinct` to now take the `Index` optional argument (using `DistinctOpts`) Internal Changes - Updated to use the new JSON protocol - Switched the connection pool code to use github.com/fatih/pool - Added some benchmarks ## v0.3.2 - 2014-08-17 - Fixed issue causing connections not to be closed correctly (#109) - Fixed issue causing terms in optional arguments to be encoded incorrectly (#114) ## v0.3.1 - 2014-06-14 - Fixed "Token ## not in stream cache" error (#103) - Changed Exec to no longer use NoReply. It now waits for the server to respond. ## v0.3.0 - 2014-06-26 - Replaced `ResultRows`/`ResultRow` with `Cursor`, `Cursor` has the `Next`, `All` and `One` methods which stores the relevant value in the value pointed at by result. For more information check the examples. - Changed the time constants (Days and Months) to package globals instead of functions - Added the `Args` term and changed the arguments for many terms to `args ...interface{}` to allow argument splicing - Added the `Changes` term and support for the feed response type - Added the `Random` term - Added the `Http` term - The second argument for `Slice` is now optional - `EqJoin` now accepts a function as its first argument - `Nth` now returns a selection ## v0.2.0 - 2014-04-13 * Changed `Connect` to use `ConnectOpts` instead of `map[string]interface{}` * Migrated to new `Group`/`Ungroup` functions, these replace `GroupedMapReduce` and `GroupBy` * Added new aggregators * Removed base parameter for `Reduce` * Added `Object` function * Added `Upcase`, `Downcase` and `Split` string functions * Added `GROUPED_DATA` pseudotype * Fixed query printing ## v0.1.0 - 2013-11-27 * Added noreply writes * Added the new terms `index_status`, `index_wait` and `sync` * Added the profile flag to the run functions * Optional arguments are now structs instead of key, pair strings. Almost all of the struct fields are of type interface{} as they can have terms inside them. For example: `r.TableCreateOpts{ PrimaryKey: r.Expr("index") }` * Returned arrays are now properly loaded into ResultRows. In the past when running `r.Expr([]interface{}{1,2,3})` would require you to use `RunRow` followed by `Scan`. You can now use `Run` followed by `ScanAll` gorethink-2.0.4/LICENSE000066400000000000000000000240411272042630000145110ustar00rootroot00000000000000Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: You must give any other recipients of the Work or Derivative Works a copy of this License; and You must cause any modified files to carry prominent notices stating that You changed the files; and You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright {yyyy} {name of copyright owner} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. gorethink-2.0.4/README.md000066400000000000000000000336751272042630000150000ustar00rootroot00000000000000# GoRethink - RethinkDB Driver for Go [![GitHub tag](https://img.shields.io/github/tag/dancannon/gorethink.svg?style=flat)](https://github.com/dancannon/gorethink/releases) [![GoDoc](https://godoc.org/github.com/dancannon/gorethink?status.png)](https://godoc.org/github.com/dancannon/gorethink) [![build status](https://img.shields.io/travis/dancannon/gorethink/master.svg "build status")](https://travis-ci.org/dancannon/gorethink) [Go](http://golang.org/) driver for [RethinkDB](http://www.rethinkdb.com/) ![GoRethink Logo](https://raw.github.com/wiki/dancannon/gorethink/gopher-and-thinker-s.png "Golang Gopher and RethinkDB Thinker") Current version: v2.0.4 (RethinkDB v2.3) Please note that this version of the driver only supports versions of RethinkDB using the v0.4 protocol (any versions of the driver older than RethinkDB 2.0 will not work). If you need any help you can find me on the [RethinkDB slack](http://slack.rethinkdb.com/) in the #gorethink channel. ## Installation ``` go get gopkg.in/dancannon/gorethink.v2 ``` (Or v1) ```sh go get gopkg.in/dancannon/gorethink.v1 ``` ## Connection ### Basic Connection Setting up a basic connection with RethinkDB is simple: ```go import ( r "github.com/dancannon/gorethink" "log" ) var session *r.Session session, err := r.Connect(r.ConnectOpts{ Address: "localhost:28015", }) if err != nil { log.Fatalln(err.Error()) } ``` See the [documentation](http://godoc.org/github.com/dancannon/gorethink#Connect) for a list of supported arguments to Connect(). ### Connection Pool The driver uses a connection pool at all times, by default it creates and frees connections automatically. It's safe for concurrent use by multiple goroutines. To configure the connection pool `MaxIdle`, `MaxOpen` and `Timeout` can be specified during connection. If you wish to change the value of `MaxIdle` or `MaxOpen` during runtime then the functions `SetMaxIdleConns` and `SetMaxOpenConns` can be used. ```go var session *r.Session session, err := r.Connect(r.ConnectOpts{ Address: "localhost:28015", Database: "test", MaxIdle: 10, MaxOpen: 10, }) if err != nil { log.Fatalln(err.Error()) } session.SetMaxOpenConns(5) ``` ### Connect to a cluster To connect to a RethinkDB cluster which has multiple nodes you can use the following syntax. When connecting to a cluster with multiple nodes queries will be distributed between these nodes. ```go var session *r.Session session, err := r.Connect(r.ConnectOpts{ Addresses: []string{"localhost:28015", "localhost:28016"}, Database: "test", AuthKey: "14daak1cad13dj", DiscoverHosts: true, }) if err != nil { log.Fatalln(err.Error()) } ``` When `DiscoverHosts` is true any nodes are added to the cluster after the initial connection then the new node will be added to the pool of available nodes used by GoRethink. Unfortunately the canonical address of each server in the cluster **MUST** be set as otherwise clients will try to connect to the database nodes locally. For more information about how to set a RethinkDB servers canonical address set this page http://www.rethinkdb.com/docs/config-file/. ## User Authentication To login with a username and password you should first create a user, this can be done by writing to the `users` system table and then grant that user access to any tables or databases they need access to. This queries can also be executed in the RethinkDB admin console. ```go err := r.DB("rethinkdb").Table("users").Insert(map[string]string{ "id": "john", "password": "p455w0rd", }).Exec(session) ... err = r.DB("blog").Table("posts").Grant("john", map[string]bool{ "read": true, "write": true, }).Exec(session) ... ``` Finally the username and password should be passed to `Connect` when creating your session, for example: ```go session, err := r.Connect(r.ConnectOpts{ Address: "localhost:28015", Database: "blog", Username: "john", Password: "p455w0rd", }) ``` Please note that `DiscoverHosts` will not work with user authentication at this time due to the fact that RethinkDB restricts access to the required system tables. ## Query Functions This library is based on the official drivers so the code on the [API](http://www.rethinkdb.com/api/) page should require very few changes to work. To view full documentation for the query functions check the [API reference](https://github.com/dancannon/gorethink/wiki/Go-ReQL-command-reference) or [GoDoc](http://godoc.org/github.com/dancannon/gorethink#Term) Slice Expr Example ```go r.Expr([]interface{}{1, 2, 3, 4, 5}).Run(session) ``` Map Expr Example ```go r.Expr(map[string]interface{}{"a": 1, "b": 2, "c": 3}).Run(session) ``` Get Example ```go r.DB("database").Table("table").Get("GUID").Run(session) ``` Map Example (Func) ```go r.Expr([]interface{}{1, 2, 3, 4, 5}).Map(func (row Term) interface{} { return row.Add(1) }).Run(session) ``` Map Example (Implicit) ```go r.Expr([]interface{}{1, 2, 3, 4, 5}).Map(r.Row.Add(1)).Run(session) ``` Between (Optional Args) Example ```go r.DB("database").Table("table").Between(1, 10, r.BetweenOpts{ Index: "num", RightBound: "closed", }).Run(session) ``` ### Optional Arguments As shown above in the Between example optional arguments are passed to the function as a struct. Each function that has optional arguments as a related struct. This structs are named in the format FunctionNameOpts, for example BetweenOpts is the related struct for Between. ## Results Different result types are returned depending on what function is used to execute the query. - `Run` returns a cursor which can be used to view all rows returned. - `RunWrite` returns a WriteResponse and should be used for queries such as Insert, Update, etc... - `Exec` sends a query to the server and closes the connection immediately after reading the response from the database. If you do not wish to wait for the response then you can set the `NoReply` flag. Example: ```go res, err := r.DB("database").Table("tablename").Get(key).Run(session) if err != nil { // error } defer res.Close() // Always ensure you close the cursor to ensure connections are not leaked ``` Cursors have a number of methods available for accessing the query results - `Next` retrieves the next document from the result set, blocking if necessary. - `All` retrieves all documents from the result set into the provided slice. - `One` retrieves the first document from the result set. Examples: ```go var row interface{} for res.Next(&row) { // Do something with row } if res.Err() != nil { // error } ``` ```go var rows []interface{} err := res.All(&rows) if err != nil { // error } ``` ```go var row interface{} err := res.One(&row) if err == r.ErrEmptyResult { // row not found } if err != nil { // error } ``` ## Encoding/Decoding When passing structs to Expr(And functions that use Expr such as Insert, Update) the structs are encoded into a map before being sent to the server. Each exported field is added to the map unless - the field's tag is "-", or - the field is empty and its tag specifies the "omitempty" option. Each fields default name in the map is the field name but can be specified in the struct field's tag value. The "gorethink" key in the struct field's tag value is the key name, followed by an optional comma and options. Examples: ```go // Field is ignored by this package. Field int `gorethink:"-"` // Field appears as key "myName". Field int `gorethink:"myName"` // Field appears as key "myName" and // the field is omitted from the object if its value is empty, // as defined above. Field int `gorethink:"myName,omitempty"` // Field appears as key "Field" (the default), but // the field is skipped if empty. // Note the leading comma. Field int `gorethink:",omitempty"` ``` **NOTE:** It is strongly recommended that struct tags are used to explicitly define the mapping between your Go type and how the data is stored by RethinkDB. This is especially important when using an `Id` field as by default RethinkDB will create a field named `id` as the primary key (note that the RethinkDB field is lowercase but the Go version starts with a capital letter). When encoding maps with non-string keys the key values are automatically converted to strings where possible, however it is recommended that you use strings where possible (for example `map[string]T`). If you wish to use the `json` tags for GoRethink then you can call `SetTags("gorethink", "json")` when starting your program, this will cause GoRethink to check for `json` tags after checking for `gorethink` tags. By default this feature is disabled. This function will also let you support any other tags, the driver will check for tags in the same order as the parameters. ### References Sometimes you may want to use a Go struct that references a document in another table, instead of creating a new struct which is just used when writing to RethinkDB you can annotate your struct with the reference tag option. This will tell GoRethink that when encoding your data it should "pluck" the ID field from the nested document and use that instead. This is all quite complicated so hopefully this example should help. First lets assume you have two types `Author` and `Book` and you want to insert a new book into your database however you dont want to include the entire author struct in the books table. As you can see the `Author` field in the `Book` struct has some extra tags, firstly we have added the `reference` tag option which tells GoRethink to pluck a field from the `Author` struct instead of inserting the whole author document. We also have the `gorethink_ref` tag which tells GoRethink to look for the `id` field in the `Author` document, without this tag GoRethink would instead look for the `author_id` field. ```go type Author struct { ID string `gorethink:"id,omitempty"` Name string `gorethink:"name"` } type Book struct { ID string `gorethink:"id,omitempty"` Title string `gorethink:"title"` Author Author `gorethink:"author_id,reference" gorethink_ref:"id"` } ``` The resulting data in RethinkDB should look something like this: ```json { "author_id": "c2182a10-6b9d-4ea1-a70c-d6649bb5f8d7", "id": "eeb006d6-7fec-46c8-9d29-45b83f07ca14", "title": "The Hobbit" } ``` If you wanted to read back the book with the author included then you could run the following GoRethink query: ```go r.Table("books").Get("1").Merge(func(p r.Term) interface{} { return map[string]interface{}{ "author_id": r.Table("authors").Get(p.Field("author_id")), } }).Run(session) ``` ## Logging By default the driver logs errors when it fails to connect to the database. If you would like more verbose error logging you can call `r.SetVerbose(true)`. Alternatively if you wish to modify the logging behaviour you can modify the logger provided by `github.com/Sirupsen/logrus`. For example the following code completely disable the logger: ```go r.Log.Out = ioutil.Discard ``` ## Benchmarks Everyone wants their project's benchmarks to be speedy. And while we know that rethinkDb and the gorethink driver are quite fast, our primary goal is for our benchmarks to be correct. They are designed to give you, the user, an accurate picture of writes per second (w/s). If you come up with a accurate test that meets this aim, submit a pull request please. Thanks to @jaredfolkins for the contribution. | Type | Value | | --- | --- | | **Model Name** | MacBook Pro | | **Model Identifier** | MacBookPro11,3 | | **Processor Name** | Intel Core i7 | | **Processor Speed** | 2.3 GHz | | **Number of Processors** | 1 | | **Total Number of Cores** | 4 | | **L2 Cache (per Core)** | 256 KB | | **L3 Cache** | 6 MB | | **Memory** | 16 GB | ```bash BenchmarkBatch200RandomWrites 20 557227775 ns/op BenchmarkBatch200RandomWritesParallel10 30 354465417 ns/op BenchmarkBatch200SoftRandomWritesParallel10 100 761639276 ns/op BenchmarkRandomWrites 100 10456580 ns/op BenchmarkRandomWritesParallel10 1000 1614175 ns/op BenchmarkRandomSoftWrites 3000 589660 ns/op BenchmarkRandomSoftWritesParallel10 10000 247588 ns/op BenchmarkSequentialWrites 50 24408285 ns/op BenchmarkSequentialWritesParallel10 1000 1755373 ns/op BenchmarkSequentialSoftWrites 3000 631211 ns/op BenchmarkSequentialSoftWritesParallel10 10000 263481 ns/op ``` ## Examples Many functions have examples and are viewable in the godoc, alternatively view some more full features examples on the [wiki](https://github.com/dancannon/gorethink/wiki/Examples). ## Further reading - [GoRethink Goes 1.0](https://www.compose.io/articles/gorethink-goes-1-0/) - [Go, RethinkDB & Changefeeds](https://www.compose.io/articles/go-rethinkdb-and-changefeeds-part-1/) - [Build an IRC bot in Go with RethinkDB changefeeds](http://rethinkdb.com/blog/go-irc-bot/) ## License Copyright 2013 Daniel Cannon Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ## Donations [![Donations](https://pledgie.com/campaigns/29517.png "Donations")](https://pledgie.com/campaigns/29517) gorethink-2.0.4/benchmarks_test.go000066400000000000000000000134201272042630000172060ustar00rootroot00000000000000package gorethink import ( "math/rand" "strconv" "sync" "testing" "time" ) func BenchmarkBatch200RandomWrites(b *testing.B) { var term Term var data []map[string]interface{} for i := 0; i < b.N; i++ { for is := 0; is < 200; is++ { r := rand.New(rand.NewSource(time.Now().UnixNano())) cid := map[string]interface{}{ "customer_id": strconv.FormatInt(r.Int63(), 10), } data = append(data, cid) } // Insert the new item into the database term = DB("benchmarks").Table("benchmarks").Insert(data) // Insert the new item into the database _, err := term.RunWrite(session, RunOpts{ MinBatchRows: 200, MaxBatchRows: 200, }) if err != nil { b.Errorf("insert failed [%s] ", err) } } } func BenchmarkBatch200RandomWritesParallel10(b *testing.B) { var term Term var data []map[string]interface{} b.SetParallelism(10) b.RunParallel(func(pb *testing.PB) { for pb.Next() { for is := 0; is < 200; is++ { r := rand.New(rand.NewSource(time.Now().UnixNano())) cid := map[string]interface{}{ "customer_id": strconv.FormatInt(r.Int63(), 10), } data = append(data, cid) } // Insert the new item into the database term = DB("benchmarks").Table("benchmarks").Insert(data) // Insert the new item into the database _, err := term.RunWrite(session, RunOpts{ MinBatchRows: 200, MaxBatchRows: 200, }) if err != nil { b.Errorf("insert failed [%s] ", err) } } }) } func BenchmarkBatch200SoftRandomWritesParallel10(b *testing.B) { var term Term var data []map[string]interface{} b.SetParallelism(10) b.RunParallel(func(pb *testing.PB) { for pb.Next() { opts := InsertOpts{Durability: "soft"} for is := 0; is < 200; is++ { r := rand.New(rand.NewSource(time.Now().UnixNano())) cid := map[string]interface{}{ "customer_id": strconv.FormatInt(r.Int63(), 10), } data = append(data, cid) } // Insert the new item into the database term = DB("benchmarks").Table("benchmarks").Insert(data, opts) // Insert the new item into the database _, err := term.RunWrite(session, RunOpts{ MinBatchRows: 200, MaxBatchRows: 200, }) if err != nil { b.Errorf("insert failed [%s] ", err) } } }) } func BenchmarkRandomWrites(b *testing.B) { for i := 0; i < b.N; i++ { r := rand.New(rand.NewSource(time.Now().UnixNano())) data := map[string]interface{}{ "customer_id": strconv.FormatInt(r.Int63(), 10), } // Insert the new item into the database _, err := DB("benchmarks").Table("benchmarks").Insert(data).RunWrite(session) if err != nil { b.Errorf("insert failed [%s] ", err) } } } func BenchmarkRandomWritesParallel10(b *testing.B) { // p*GOMAXPROCS b.SetParallelism(10) b.RunParallel(func(pb *testing.PB) { for pb.Next() { r := rand.New(rand.NewSource(time.Now().UnixNano())) data := map[string]interface{}{ "customer_id": strconv.FormatInt(r.Int63(), 10), } // Insert the new item into the database _, err := DB("benchmarks").Table("benchmarks").Insert(data).RunWrite(session) if err != nil { b.Errorf("insert failed [%s] ", err) } } }) } func BenchmarkRandomSoftWrites(b *testing.B) { for i := 0; i < b.N; i++ { data := map[string]interface{}{ "customer_id": strconv.FormatInt(rand.Int63(), 10), } // Insert the new item into the database opts := InsertOpts{Durability: "soft"} _, err := DB("benchmarks").Table("benchmarks").Insert(data, opts).RunWrite(session) if err != nil { b.Errorf("insert failed [%s] ", err) } } } func BenchmarkRandomSoftWritesParallel10(b *testing.B) { // p*GOMAXPROCS b.SetParallelism(10) b.RunParallel(func(pb *testing.PB) { for pb.Next() { r := rand.New(rand.NewSource(time.Now().UnixNano())) data := map[string]interface{}{ "customer_id": strconv.FormatInt(r.Int63(), 10), } // Insert the new item into the database opts := InsertOpts{Durability: "soft"} _, err := DB("benchmarks").Table("benchmarks").Insert(data, opts).RunWrite(session) if err != nil { b.Errorf("insert failed [%s] ", err) } } }) } func BenchmarkSequentialWrites(b *testing.B) { si := 0 for i := 0; i < b.N; i++ { si++ data := map[string]interface{}{ "customer_id": si, } // Insert the new item into the database _, err := DB("benchmarks").Table("benchmarks").Insert(data).RunWrite(session) if err != nil { b.Errorf("insert failed [%s] ", err) return } } } func BenchmarkSequentialWritesParallel10(b *testing.B) { var mu sync.Mutex si := 0 // p*GOMAXPROCS b.SetParallelism(10) b.RunParallel(func(pb *testing.PB) { for pb.Next() { mu.Lock() si++ mu.Unlock() data := map[string]interface{}{ "customer_id": si, } // Insert the new item into the database _, err := DB("benchmarks").Table("benchmarks").Insert(data).RunWrite(session) if err != nil { b.Errorf("insert failed [%s] ", err) return } } }) } func BenchmarkSequentialSoftWrites(b *testing.B) { opts := InsertOpts{Durability: "soft"} si := 0 for i := 0; i < b.N; i++ { si++ data := map[string]interface{}{ "customer_id": si, } // Insert the new item into the database _, err := Table("benchmarks").Insert(data, opts).RunWrite(session) if err != nil { b.Errorf("insert failed [%s] ", err) return } } } func BenchmarkSequentialSoftWritesParallel10(b *testing.B) { var mu sync.Mutex si := 0 // p*GOMAXPROCS b.SetParallelism(10) b.RunParallel(func(pb *testing.PB) { for pb.Next() { mu.Lock() si++ mu.Unlock() data := map[string]interface{}{ "customer_id": si, } opts := InsertOpts{Durability: "soft"} // Insert the new item into the database _, err := Table("benchmarks").Insert(data, opts).RunWrite(session) if err != nil { b.Errorf("insert failed [%s] ", err) return } } }) } gorethink-2.0.4/checkers_test.go000066400000000000000000000105561272042630000166670ustar00rootroot00000000000000package gorethink import ( "encoding/json" "fmt" test "gopkg.in/check.v1" "gopkg.in/dancannon/gorethink.v2/types" ) type jsonChecker struct { *test.CheckerInfo } func (j jsonChecker) Check(params []interface{}, names []string) (result bool, error string) { var jsonParams []interface{} for _, param := range params { jsonParam, err := json.Marshal(param) if err != nil { return false, err.Error() } jsonParams = append(jsonParams, jsonParam) } return test.DeepEquals.Check(jsonParams, names) } // jsonEquals compares two interface{} objects by converting them to JSON and // seeing if the strings match var jsonEquals = &jsonChecker{ &test.CheckerInfo{Name: "jsonEquals", Params: []string{"obtained", "expected"}}, } type geometryChecker struct { *test.CheckerInfo } func (j geometryChecker) Check(params []interface{}, names []string) (result bool, error string) { obtained, ok := params[0].(types.Geometry) if !ok { return false, "obtained must be a Geometry" } expectedType, ok := params[1].(string) if !ok { return false, "expectedType must be a string" } switch expectedType { case "Polygon": expectedCoords, ok := params[2].([][][]float64) if !ok { return false, "expectedCoords must be a [][][]float64" } return comparePolygon(expectedCoords, obtained) case "LineString": expectedCoords, ok := params[2].([][]float64) if !ok { return false, "expectedCoords must be a [][]float64" } return compareLineString(expectedCoords, obtained) case "Point": expectedCoords, ok := params[2].([]float64) if !ok { return false, "expectedCoords must be a []float64" } return comparePoint(expectedCoords, obtained) default: return false, "unknown expectedType" } } // geometryEquals compares two geometry values, all coordinates are compared with a small amount of tolerance var geometryEquals = &geometryChecker{ &test.CheckerInfo{Name: "geometryEquals", Params: []string{"obtained", "expectedType", "expectedCoords"}}, } /* BEGIN FLOAT HELPERS */ // totally ripped off from math/all_test.go // https://github.com/golang/go/blob/master/src/math/all_test.go#L1723-L1749 func tolerance(a, b, e float64) bool { d := a - b if d < 0 { d = -d } if a != 0 { e = e * a if e < 0 { e = -e } } return d < e } func mehclose(a, b float64) bool { return tolerance(a, b, 1e-2) } func kindaclose(a, b float64) bool { return tolerance(a, b, 1e-8) } func prettyclose(a, b float64) bool { return tolerance(a, b, 1e-14) } func veryclose(a, b float64) bool { return tolerance(a, b, 4e-16) } func soclose(a, b, e float64) bool { return tolerance(a, b, e) } func comparePolygon(expected [][][]float64, obtained types.Geometry) (result bool, error string) { if obtained.Type != "Polygon" { return false, fmt.Sprintf("obtained geometry has incorrect type, has %s but expected Polygon", obtained.Type) } for i, line := range obtained.Lines { for j, point := range line { ok, err := assertPointsEqual( expected[i][j][0], point.Lon, // Lon expected[i][j][1], point.Lat, // Lat ) if !ok { return false, err } } } return true, "" } func compareLineString(expected [][]float64, obtained types.Geometry) (result bool, error string) { if obtained.Type != "LineString" { return false, fmt.Sprintf("obtained geometry has incorrect type, has %s but expected LineString", obtained.Type) } for j, point := range obtained.Line { ok, err := assertPointsEqual( expected[j][0], point.Lon, // Lon expected[j][1], point.Lat, // Lat ) if !ok { return false, err } } return true, "" } func comparePoint(expected []float64, obtained types.Geometry) (result bool, error string) { if obtained.Type != "Point" { return false, fmt.Sprintf("obtained geometry has incorrect type, has %s but expected Point", obtained.Type) } return assertPointsEqual( expected[0], obtained.Point.Lon, // Lon expected[1], obtained.Point.Lat, // Lat ) } func assertPointsEqual(expectedLon, obtainedLon, expectedLat, obtainedLat float64) (result bool, error string) { if !kindaclose(expectedLon, obtainedLon) { return false, fmt.Sprintf("the deviation between the compared floats is too great [%v:%v]", expectedLon, obtainedLon) } if !kindaclose(expectedLat, obtainedLat) { return false, fmt.Sprintf("the deviation between the compared floats is too great [%v:%v]", expectedLat, obtainedLat) } return true, "" } /* END FLOAT HELPERS */ gorethink-2.0.4/cluster.go000066400000000000000000000244361272042630000155240ustar00rootroot00000000000000package gorethink import ( "fmt" "strings" "sync" "sync/atomic" "time" "github.com/Sirupsen/logrus" "github.com/cenkalti/backoff" "github.com/hailocab/go-hostpool" ) // A Cluster represents a connection to a RethinkDB cluster, a cluster is created // by the Session and should rarely be created manually. // // The cluster keeps track of all nodes in the cluster and if requested can listen // for cluster changes and start tracking a new node if one appears. Currently // nodes are removed from the pool if they become unhealthy (100 failed queries). // This should hopefully soon be replaced by a backoff system. type Cluster struct { opts *ConnectOpts mu sync.RWMutex seeds []Host // Initial host nodes specified by user. hp hostpool.HostPool nodes map[string]*Node // Active nodes in cluster. closed bool nodeIndex int64 } // NewCluster creates a new cluster by connecting to the given hosts. func NewCluster(hosts []Host, opts *ConnectOpts) (*Cluster, error) { c := &Cluster{ hp: hostpool.NewEpsilonGreedy([]string{}, opts.HostDecayDuration, &hostpool.LinearEpsilonValueCalculator{}), seeds: hosts, opts: opts, } // Attempt to connect to each host and discover any additional hosts if host // discovery is enabled if err := c.connectNodes(c.getSeeds()); err != nil { return nil, err } if !c.IsConnected() { return nil, ErrNoConnectionsStarted } if opts.DiscoverHosts { go c.discover() } return c, nil } // Query executes a ReQL query using the cluster to connect to the database func (c *Cluster) Query(q Query) (cursor *Cursor, err error) { node, hpr, err := c.GetNextNode() if err != nil { return nil, err } cursor, err = node.Query(q) hpr.Mark(err) return cursor, err } // Exec executes a ReQL query using the cluster to connect to the database func (c *Cluster) Exec(q Query) (err error) { node, hpr, err := c.GetNextNode() if err != nil { return err } err = node.Exec(q) hpr.Mark(err) return err } // Server returns the server name and server UUID being used by a connection. func (c *Cluster) Server() (response ServerResponse, err error) { node, hpr, err := c.GetNextNode() if err != nil { return ServerResponse{}, err } response, err = node.Server() hpr.Mark(err) return response, err } // SetMaxIdleConns sets the maximum number of connections in the idle // connection pool. func (c *Cluster) SetMaxIdleConns(n int) { for _, node := range c.GetNodes() { node.SetMaxIdleConns(n) } } // SetMaxOpenConns sets the maximum number of open connections to the database. func (c *Cluster) SetMaxOpenConns(n int) { for _, node := range c.GetNodes() { node.SetMaxOpenConns(n) } } // Close closes the cluster func (c *Cluster) Close(optArgs ...CloseOpts) error { if c.closed { return nil } for _, node := range c.GetNodes() { err := node.Close(optArgs...) if err != nil { return err } } c.closed = true return nil } // discover attempts to find new nodes in the cluster using the current nodes func (c *Cluster) discover() { // Keep retrying with exponential backoff. b := backoff.NewExponentialBackOff() // Never finish retrying (max interval is still 60s) b.MaxElapsedTime = 0 // Keep trying to discover new nodes for { backoff.RetryNotify(func() error { // If no hosts try seeding nodes if len(c.GetNodes()) == 0 { c.connectNodes(c.getSeeds()) } return c.listenForNodeChanges() }, b, func(err error, wait time.Duration) { Log.Debugf("Error discovering hosts %s, waiting: %s", err, wait) }) } } // listenForNodeChanges listens for changes to node status using change feeds. // This function will block until the query fails func (c *Cluster) listenForNodeChanges() error { // Start listening to changes from a random active node node, hpr, err := c.GetNextNode() if err != nil { return err } q, err := newQuery( DB("rethinkdb").Table("server_status").Changes(), map[string]interface{}{}, c.opts, ) if err != nil { return fmt.Errorf("Error building query: %s", err) } cursor, err := node.Query(q) if err != nil { hpr.Mark(err) return err } // Keep reading node status updates from changefeed var result struct { NewVal nodeStatus `gorethink:"new_val"` OldVal nodeStatus `gorethink:"old_val"` } for cursor.Next(&result) { addr := fmt.Sprintf("%s:%d", result.NewVal.Network.Hostname, result.NewVal.Network.ReqlPort) addr = strings.ToLower(addr) switch result.NewVal.Status { case "connected": // Connect to node using exponential backoff (give up after waiting 5s) // to give the node time to start-up. b := backoff.NewExponentialBackOff() b.MaxElapsedTime = time.Second * 5 backoff.Retry(func() error { node, err := c.connectNodeWithStatus(result.NewVal) if err == nil { if !c.nodeExists(node) { c.addNode(node) Log.WithFields(logrus.Fields{ "id": node.ID, "host": node.Host.String(), }).Debug("Connected to node") } } return err }, b) } } err = cursor.Err() hpr.Mark(err) return err } func (c *Cluster) connectNodes(hosts []Host) error { // Add existing nodes to map nodeSet := map[string]*Node{} for _, node := range c.GetNodes() { nodeSet[node.ID] = node } var attemptErr error // Attempt to connect to each seed host for _, host := range hosts { conn, err := NewConnection(host.String(), c.opts) if err != nil { attemptErr = err Log.Warnf("Error creating connection: %s", err.Error()) continue } defer conn.Close() if c.opts.DiscoverHosts { q, err := newQuery( DB("rethinkdb").Table("server_status"), map[string]interface{}{}, c.opts, ) if err != nil { Log.Warnf("Error building query: %s", err) continue } _, cursor, err := conn.Query(q) if err != nil { attemptErr = err Log.Warnf("Error fetching cluster status: %s", err) continue } var results []nodeStatus err = cursor.All(&results) if err != nil { attemptErr = err continue } for _, result := range results { node, err := c.connectNodeWithStatus(result) if err == nil { if _, ok := nodeSet[node.ID]; !ok { Log.WithFields(logrus.Fields{ "id": node.ID, "host": node.Host.String(), }).Debug("Connected to node") nodeSet[node.ID] = node } } else { attemptErr = err Log.Warnf("Error connecting to node: %s", err) } } } else { svrRsp, err := conn.Server() if err != nil { attemptErr = err Log.Warnf("Error fetching server ID: %s", err) continue } node, err := c.connectNode(svrRsp.ID, []Host{host}) if err == nil { if _, ok := nodeSet[node.ID]; !ok { Log.WithFields(logrus.Fields{ "id": node.ID, "host": node.Host.String(), }).Debug("Connected to node") nodeSet[node.ID] = node } } else { attemptErr = err Log.Warnf("Error connecting to node: %s", err) } } } // If no nodes were contactable then return the last error, this does not // include driver errors such as if there was an issue building the // query if len(nodeSet) == 0 { return attemptErr } nodes := []*Node{} for _, node := range nodeSet { nodes = append(nodes, node) } c.setNodes(nodes) return nil } func (c *Cluster) connectNodeWithStatus(s nodeStatus) (*Node, error) { aliases := make([]Host, len(s.Network.CanonicalAddresses)) for i, aliasAddress := range s.Network.CanonicalAddresses { aliases[i] = NewHost(aliasAddress.Host, int(s.Network.ReqlPort)) } return c.connectNode(s.ID, aliases) } func (c *Cluster) connectNode(id string, aliases []Host) (*Node, error) { var pool *Pool var err error for len(aliases) > 0 { pool, err = NewPool(aliases[0], c.opts) if err != nil { aliases = aliases[1:] continue } err = pool.Ping() if err != nil { aliases = aliases[1:] continue } // Ping successful so break out of loop break } if err != nil { return nil, err } if len(aliases) == 0 { return nil, ErrInvalidNode } return newNode(id, aliases, c, pool), nil } // IsConnected returns true if cluster has nodes and is not already closed. func (c *Cluster) IsConnected() bool { c.mu.RLock() closed := c.closed c.mu.RUnlock() return (len(c.GetNodes()) > 0) && !closed } // AddSeeds adds new seed hosts to the cluster. func (c *Cluster) AddSeeds(hosts []Host) { c.mu.Lock() c.seeds = append(c.seeds, hosts...) c.mu.Unlock() } func (c *Cluster) getSeeds() []Host { c.mu.RLock() seeds := c.seeds c.mu.RUnlock() return seeds } // GetNextNode returns a random node on the cluster func (c *Cluster) GetNextNode() (*Node, hostpool.HostPoolResponse, error) { if !c.IsConnected() { return nil, nil, ErrNoConnections } c.mu.RLock() defer c.mu.RUnlock() nodes := c.nodes hpr := c.hp.Get() if n, ok := nodes[hpr.Host()]; ok { if !n.Closed() { return n, hpr, nil } } return nil, nil, ErrNoConnections } // GetNodes returns a list of all nodes in the cluster func (c *Cluster) GetNodes() []*Node { c.mu.RLock() nodes := make([]*Node, 0, len(c.nodes)) for _, n := range c.nodes { nodes = append(nodes, n) } c.mu.RUnlock() return nodes } func (c *Cluster) nodeExists(search *Node) bool { for _, node := range c.GetNodes() { if node.ID == search.ID { return true } } return false } func (c *Cluster) addNode(node *Node) { c.mu.RLock() nodes := append(c.GetNodes(), node) c.mu.RUnlock() c.setNodes(nodes) } func (c *Cluster) addNodes(nodesToAdd []*Node) { c.mu.RLock() nodes := append(c.GetNodes(), nodesToAdd...) c.mu.RUnlock() c.setNodes(nodes) } func (c *Cluster) setNodes(nodes []*Node) { nodesMap := make(map[string]*Node, len(nodes)) hosts := make([]string, len(nodes)) for i, node := range nodes { host := node.Host.String() nodesMap[host] = node hosts[i] = host } c.mu.Lock() c.nodes = nodesMap c.hp.SetHosts(hosts) c.mu.Unlock() } func (c *Cluster) removeNode(nodeID string) { nodes := c.GetNodes() nodeArray := make([]*Node, len(nodes)-1) count := 0 // Add nodes that are not in remove list. for _, n := range nodes { if n.ID != nodeID { nodeArray[count] = n count++ } } // Do sanity check to make sure assumptions are correct. if count < len(nodeArray) { // Resize array. nodeArray2 := make([]*Node, count) copy(nodeArray2, nodeArray) nodeArray = nodeArray2 } c.setNodes(nodeArray) } func (c *Cluster) nextNodeIndex() int64 { return atomic.AddInt64(&c.nodeIndex, 1) } gorethink-2.0.4/cluster_integration_test.go000066400000000000000000000042151272042630000211570ustar00rootroot00000000000000// +build cluster // +build integration package gorethink import ( "time" test "gopkg.in/check.v1" ) func (s *RethinkSuite) TestClusterDetectNewNode(c *test.C) { session, err := Connect(ConnectOpts{ Addresses: []string{url, url2}, DiscoverHosts: true, NodeRefreshInterval: time.Second, }) c.Assert(err, test.IsNil) t := time.NewTimer(time.Second * 30) for { select { // Fail if deadline has passed case <-t.C: c.Fatal("No node was added to the cluster") default: // Pass if another node was added if len(session.cluster.GetNodes()) >= 3 { return } } } } func (s *RethinkSuite) TestClusterRecoverAfterNoNodes(c *test.C) { session, err := Connect(ConnectOpts{ Addresses: []string{url, url2}, DiscoverHosts: true, NodeRefreshInterval: time.Second, }) c.Assert(err, test.IsNil) t := time.NewTimer(time.Second * 30) hasHadZeroNodes := false for { select { // Fail if deadline has passed case <-t.C: c.Fatal("No node was added to the cluster") default: // Check if there are no nodes if len(session.cluster.GetNodes()) == 0 { hasHadZeroNodes = true } // Pass if another node was added if len(session.cluster.GetNodes()) >= 1 && hasHadZeroNodes { return } } } } func (s *RethinkSuite) TestClusterNodeHealth(c *test.C) { session, err := Connect(ConnectOpts{ Addresses: []string{url1, url2, url3}, DiscoverHosts: true, NodeRefreshInterval: time.Second, MaxIdle: 50, MaxOpen: 200, }) c.Assert(err, test.IsNil) attempts := 0 failed := 0 seconds := 0 t := time.NewTimer(time.Second * 30) tick := time.NewTicker(time.Second) for { select { // Fail if deadline has passed case <-tick.C: seconds++ c.Logf("%ds elapsed", seconds) case <-t.C: // Execute queries for 10s and check that at most 5% of the queries fail c.Logf("%d of the %d(%d%%) queries failed", failed, attempts, (failed / attempts)) c.Assert(failed <= 100, test.Equals, true) return default: attempts++ if err := Expr(1).Exec(session); err != nil { c.Logf("Query failed, %s", err) failed++ } } } } gorethink-2.0.4/cluster_test.go000066400000000000000000000027101272042630000165520ustar00rootroot00000000000000// +build cluster package gorethink import ( "fmt" "time" test "gopkg.in/check.v1" ) func (s *RethinkSuite) TestClusterConnect(c *test.C) { session, err := Connect(ConnectOpts{ Addresses: []string{url1, url2, url3}, }) c.Assert(err, test.IsNil) row, err := Expr("Hello World").Run(session) c.Assert(err, test.IsNil) var response string err = row.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, "Hello World") } func (s *RethinkSuite) TestClusterMultipleQueries(c *test.C) { session, err := Connect(ConnectOpts{ Addresses: []string{url1, url2, url3}, }) c.Assert(err, test.IsNil) for i := 0; i < 1000; i++ { row, err := Expr(fmt.Sprintf("Hello World", i)).Run(session) c.Assert(err, test.IsNil) var response string err = row.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, fmt.Sprintf("Hello World", i)) } } func (s *RethinkSuite) TestClusterConnectError(c *test.C) { var err error _, err = Connect(ConnectOpts{ Addresses: []string{"nonexistanturl"}, Timeout: time.Second, }) c.Assert(err, test.NotNil) } func (s *RethinkSuite) TestClusterConnectDatabase(c *test.C) { session, err := Connect(ConnectOpts{ Addresses: []string{url1, url2, url3}, Database: "test2", }) c.Assert(err, test.IsNil) _, err = Table("test2").Run(session) c.Assert(err, test.NotNil) c.Assert(err.Error(), test.Equals, "gorethink: Database `test2` does not exist. in: \nr.Table(\"test2\")") } gorethink-2.0.4/connection.go000066400000000000000000000224641272042630000162010ustar00rootroot00000000000000package gorethink import ( "crypto/tls" "encoding/binary" "encoding/json" "net" "sync" "sync/atomic" "time" p "gopkg.in/dancannon/gorethink.v2/ql2" ) const ( respHeaderLen = 12 ) // Response represents the raw response from a query, most of the time you // should instead use a Cursor when reading from the database. type Response struct { Token int64 Type p.Response_ResponseType `json:"t"` ErrorType p.Response_ErrorType `json:"e"` Notes []p.Response_ResponseNote `json:"n"` Responses []json.RawMessage `json:"r"` Backtrace []interface{} `json:"b"` Profile interface{} `json:"p"` } // Connection is a connection to a rethinkdb database. Connection is not thread // safe and should only be accessed be a single goroutine type Connection struct { net.Conn address string opts *ConnectOpts _ [4]byte mu sync.Mutex token int64 cursors map[int64]*Cursor bad bool closed bool } // NewConnection creates a new connection to the database server func NewConnection(address string, opts *ConnectOpts) (*Connection, error) { var err error c := &Connection{ address: address, opts: opts, cursors: make(map[int64]*Cursor), } // Connect to Server nd := net.Dialer{Timeout: c.opts.Timeout, KeepAlive: opts.KeepAlivePeriod} if c.opts.TLSConfig == nil { c.Conn, err = nd.Dial("tcp", address) } else { c.Conn, err = tls.DialWithDialer(&nd, "tcp", address, c.opts.TLSConfig) } if err != nil { return nil, RQLConnectionError{rqlError(err.Error())} } // Enable TCP Keepalives on TCP connections if tc, ok := c.Conn.(*net.TCPConn); ok { if err := tc.SetKeepAlive(true); err != nil { // Don't send COM_QUIT before handshake. c.Conn.Close() c.Conn = nil return nil, err } } // Send handshake handshake, err := c.handshake(opts.HandshakeVersion) if err != nil { return nil, err } if err = handshake.Send(); err != nil { return nil, err } return c, nil } // Close closes the underlying net.Conn func (c *Connection) Close() error { c.mu.Lock() defer c.mu.Unlock() var err error if !c.closed { err = c.Conn.Close() c.closed = true c.cursors = make(map[int64]*Cursor) } return err } // Query sends a Query to the database, returning both the raw Response and a // Cursor which should be used to view the query's response. // // This function is used internally by Run which should be used for most queries. func (c *Connection) Query(q Query) (*Response, *Cursor, error) { if c == nil { return nil, nil, ErrConnectionClosed } c.mu.Lock() if c.Conn == nil { c.bad = true c.mu.Unlock() return nil, nil, ErrConnectionClosed } // Add token if query is a START/NOREPLY_WAIT if q.Type == p.Query_START || q.Type == p.Query_NOREPLY_WAIT || q.Type == p.Query_SERVER_INFO { q.Token = c.nextToken() } if q.Type == p.Query_START || q.Type == p.Query_NOREPLY_WAIT { if c.opts.Database != "" { var err error q.Opts["db"], err = DB(c.opts.Database).build() if err != nil { c.mu.Unlock() return nil, nil, RQLDriverError{rqlError(err.Error())} } } } c.mu.Unlock() err := c.sendQuery(q) if err != nil { return nil, nil, err } if noreply, ok := q.Opts["noreply"]; ok && noreply.(bool) { return nil, nil, nil } for { response, err := c.readResponse() if err != nil { return nil, nil, err } if response.Token == q.Token { // If this was the requested response process and return return c.processResponse(q, response) } else if _, ok := c.cursors[response.Token]; ok { // If the token is in the cursor cache then process the response c.processResponse(q, response) } else { putResponse(response) } } } type ServerResponse struct { ID string `gorethink:"id"` Name string `gorethink:"name"` } // Server returns the server name and server UUID being used by a connection. func (c *Connection) Server() (ServerResponse, error) { var response ServerResponse _, cur, err := c.Query(Query{ Type: p.Query_SERVER_INFO, }) if err != nil { return response, err } if err = cur.One(&response); err != nil { return response, err } if err = cur.Close(); err != nil { return response, err } return response, nil } // sendQuery marshals the Query and sends the JSON to the server. func (c *Connection) sendQuery(q Query) error { // Build query b, err := json.Marshal(q.build()) if err != nil { return RQLDriverError{rqlError("Error building query")} } // Set timeout if c.opts.WriteTimeout == 0 { c.Conn.SetWriteDeadline(time.Time{}) } else { c.Conn.SetWriteDeadline(time.Now().Add(c.opts.WriteTimeout)) } // Send the JSON encoding of the query itself. if err = c.writeQuery(q.Token, b); err != nil { c.bad = true return RQLConnectionError{rqlError(err.Error())} } return nil } // getToken generates the next query token, used to number requests and match // responses with requests. func (c *Connection) nextToken() int64 { // requires c.token to be 64-bit aligned on ARM return atomic.AddInt64(&c.token, 1) } // readResponse attempts to read a Response from the server, if no response // could be read then an error is returned. func (c *Connection) readResponse() (*Response, error) { // Set timeout if c.opts.ReadTimeout == 0 { c.Conn.SetReadDeadline(time.Time{}) } else { c.Conn.SetReadDeadline(time.Now().Add(c.opts.ReadTimeout)) } // Read response header (token+length) headerBuf := [respHeaderLen]byte{} if _, err := c.read(headerBuf[:], respHeaderLen); err != nil { c.bad = true return nil, RQLConnectionError{rqlError(err.Error())} } responseToken := int64(binary.LittleEndian.Uint64(headerBuf[:8])) messageLength := binary.LittleEndian.Uint32(headerBuf[8:]) // Read the JSON encoding of the Response itself. b := make([]byte, int(messageLength)) if _, err := c.read(b, int(messageLength)); err != nil { c.bad = true return nil, RQLConnectionError{rqlError(err.Error())} } // Decode the response var response = newCachedResponse() if err := json.Unmarshal(b, response); err != nil { c.bad = true return nil, RQLDriverError{rqlError(err.Error())} } response.Token = responseToken return response, nil } func (c *Connection) processResponse(q Query, response *Response) (*Response, *Cursor, error) { switch response.Type { case p.Response_CLIENT_ERROR: return c.processErrorResponse(q, response, RQLClientError{rqlServerError{response, q.Term}}) case p.Response_COMPILE_ERROR: return c.processErrorResponse(q, response, RQLCompileError{rqlServerError{response, q.Term}}) case p.Response_RUNTIME_ERROR: return c.processErrorResponse(q, response, createRuntimeError(response.ErrorType, response, q.Term)) case p.Response_SUCCESS_ATOM, p.Response_SERVER_INFO: return c.processAtomResponse(q, response) case p.Response_SUCCESS_PARTIAL: return c.processPartialResponse(q, response) case p.Response_SUCCESS_SEQUENCE: return c.processSequenceResponse(q, response) case p.Response_WAIT_COMPLETE: return c.processWaitResponse(q, response) default: putResponse(response) return nil, nil, RQLDriverError{rqlError("Unexpected response type")} } } func (c *Connection) processErrorResponse(q Query, response *Response, err error) (*Response, *Cursor, error) { c.mu.Lock() cursor := c.cursors[response.Token] delete(c.cursors, response.Token) c.mu.Unlock() return response, cursor, err } func (c *Connection) processAtomResponse(q Query, response *Response) (*Response, *Cursor, error) { // Create cursor cursor := newCursor(c, "Cursor", response.Token, q.Term, q.Opts) cursor.profile = response.Profile cursor.extend(response) return response, cursor, nil } func (c *Connection) processPartialResponse(q Query, response *Response) (*Response, *Cursor, error) { cursorType := "Cursor" if len(response.Notes) > 0 { switch response.Notes[0] { case p.Response_SEQUENCE_FEED: cursorType = "Feed" case p.Response_ATOM_FEED: cursorType = "AtomFeed" case p.Response_ORDER_BY_LIMIT_FEED: cursorType = "OrderByLimitFeed" case p.Response_UNIONED_FEED: cursorType = "UnionedFeed" case p.Response_INCLUDES_STATES: cursorType = "IncludesFeed" } } c.mu.Lock() cursor, ok := c.cursors[response.Token] if !ok { // Create a new cursor if needed cursor = newCursor(c, cursorType, response.Token, q.Term, q.Opts) cursor.profile = response.Profile c.cursors[response.Token] = cursor } c.mu.Unlock() cursor.extend(response) return response, cursor, nil } func (c *Connection) processSequenceResponse(q Query, response *Response) (*Response, *Cursor, error) { c.mu.Lock() cursor, ok := c.cursors[response.Token] if !ok { // Create a new cursor if needed cursor = newCursor(c, "Cursor", response.Token, q.Term, q.Opts) cursor.profile = response.Profile } delete(c.cursors, response.Token) c.mu.Unlock() cursor.extend(response) return response, cursor, nil } func (c *Connection) processWaitResponse(q Query, response *Response) (*Response, *Cursor, error) { c.mu.Lock() delete(c.cursors, response.Token) c.mu.Unlock() return response, nil, nil } func (c *Connection) isBad() bool { c.mu.Lock() defer c.mu.Unlock() return c.bad } var responseCache = make(chan *Response, 16) func newCachedResponse() *Response { select { case r := <-responseCache: return r default: return new(Response) } } func putResponse(r *Response) { *r = Response{} // zero it select { case responseCache <- r: default: } } gorethink-2.0.4/connection_handshake.go000066400000000000000000000256541272042630000202130ustar00rootroot00000000000000package gorethink import ( "bufio" "crypto/hmac" "crypto/rand" "crypto/sha256" "encoding/base64" "encoding/binary" "encoding/json" "fmt" "hash" "io" "strconv" "strings" "golang.org/x/crypto/pbkdf2" p "gopkg.in/dancannon/gorethink.v2/ql2" ) type HandshakeVersion int const ( HandshakeV1_0 HandshakeVersion = iota HandshakeV0_4 ) type connectionHandshake interface { Send() error } func (c *Connection) handshake(version HandshakeVersion) (connectionHandshake, error) { switch version { case HandshakeV0_4: return &connectionHandshakeV0_4{conn: c}, nil case HandshakeV1_0: return &connectionHandshakeV1_0{conn: c}, nil default: return nil, fmt.Errorf("Unrecognised handshake version") } } type connectionHandshakeV0_4 struct { conn *Connection } func (c *connectionHandshakeV0_4) Send() error { // Send handshake request if err := c.writeHandshakeReq(); err != nil { c.conn.Close() return RQLConnectionError{rqlError(err.Error())} } // Read handshake response if err := c.readHandshakeSuccess(); err != nil { c.conn.Close() return RQLConnectionError{rqlError(err.Error())} } return nil } func (c *connectionHandshakeV0_4) writeHandshakeReq() error { pos := 0 dataLen := 4 + 4 + len(c.conn.opts.AuthKey) + 4 data := make([]byte, dataLen) // Send the protocol version to the server as a 4-byte little-endian-encoded integer binary.LittleEndian.PutUint32(data[pos:], uint32(p.VersionDummy_V0_4)) pos += 4 // Send the length of the auth key to the server as a 4-byte little-endian-encoded integer binary.LittleEndian.PutUint32(data[pos:], uint32(len(c.conn.opts.AuthKey))) pos += 4 // Send the auth key as an ASCII string if len(c.conn.opts.AuthKey) > 0 { pos += copy(data[pos:], c.conn.opts.AuthKey) } // Send the protocol type as a 4-byte little-endian-encoded integer binary.LittleEndian.PutUint32(data[pos:], uint32(p.VersionDummy_JSON)) pos += 4 return c.conn.writeData(data) } func (c *connectionHandshakeV0_4) readHandshakeSuccess() error { reader := bufio.NewReader(c.conn.Conn) line, err := reader.ReadBytes('\x00') if err != nil { if err == io.EOF { return fmt.Errorf("Unexpected EOF: %s", string(line)) } return err } // convert to string and remove trailing NUL byte response := string(line[:len(line)-1]) if response != "SUCCESS" { response = strings.TrimSpace(response) // we failed authorization or something else terrible happened return RQLDriverError{rqlError(fmt.Sprintf("Server dropped connection with message: \"%s\"", response))} } return nil } const ( handshakeV1_0_protocolVersionNumber = 0 handshakeV1_0_authenticationMethod = "SCRAM-SHA-256" ) type connectionHandshakeV1_0 struct { conn *Connection reader *bufio.Reader authMsg string } func (c *connectionHandshakeV1_0) Send() error { c.reader = bufio.NewReader(c.conn.Conn) // Generate client nonce clientNonce, err := c.generateNonce() if err != nil { c.conn.Close() return RQLDriverError{rqlError(fmt.Sprintf("Failed to generate client nonce: %s", err))} } // Send client first message if err := c.writeFirstMessage(clientNonce); err != nil { c.conn.Close() return err } // Read status if err := c.checkServerVersions(); err != nil { c.conn.Close() return err } // Read server first message i, salt, serverNonce, err := c.readFirstMessage() if err != nil { c.conn.Close() return err } // Check server nonce if !strings.HasPrefix(serverNonce, clientNonce) { return RQLAuthError{RQLDriverError{rqlError("Invalid nonce from server")}} } // Generate proof saltedPass := c.saltPassword(i, salt) clientProof := c.calculateProof(saltedPass, clientNonce, serverNonce) serverSignature := c.serverSignature(saltedPass) // Send client final message if err := c.writeFinalMessage(serverNonce, clientProof); err != nil { c.conn.Close() return err } // Read server final message if err := c.readFinalMessage(serverSignature); err != nil { c.conn.Close() return err } return nil } func (c *connectionHandshakeV1_0) writeFirstMessage(clientNonce string) error { // Default username to admin if not set username := "admin" if c.conn.opts.Username != "" { username = c.conn.opts.Username } c.authMsg = fmt.Sprintf("n=%s,r=%s", username, clientNonce) msg := fmt.Sprintf( `{"protocol_version": %d,"authentication": "n,,%s","authentication_method": "%s"}`, handshakeV1_0_protocolVersionNumber, c.authMsg, handshakeV1_0_authenticationMethod, ) pos := 0 dataLen := 4 + len(msg) + 1 data := make([]byte, dataLen) // Send the protocol version to the server as a 4-byte little-endian-encoded integer binary.LittleEndian.PutUint32(data[pos:], uint32(p.VersionDummy_V1_0)) pos += 4 // Send the auth message as an ASCII string pos += copy(data[pos:], msg) // Add null terminating byte data[pos] = '\x00' return c.writeData(data) } func (c *connectionHandshakeV1_0) checkServerVersions() error { b, err := c.readResponse() if err != nil { return err } // Read status type versionsResponse struct { Success bool `json:"success"` MinProtocolVersion int `json:"min_protocol_version"` MaxProtocolVersion int `json:"max_protocol_version"` ServerVersion string `json:"server_version"` ErrorCode int `json:"error_code"` Error string `json:"error"` } var rsp *versionsResponse statusStr := string(b) if err := json.Unmarshal(b, &rsp); err != nil { if strings.HasPrefix(statusStr, "ERROR: ") { statusStr = strings.TrimPrefix(statusStr, "ERROR: ") return RQLConnectionError{rqlError(statusStr)} } return RQLDriverError{rqlError(fmt.Sprintf("Error reading versions: %s", err))} } if !rsp.Success { return c.handshakeError(rsp.ErrorCode, rsp.Error) } if rsp.MinProtocolVersion > handshakeV1_0_protocolVersionNumber || rsp.MaxProtocolVersion < handshakeV1_0_protocolVersionNumber { return RQLDriverError{rqlError( fmt.Sprintf( "Unsupported protocol version %d, expected between %d and %d.", handshakeV1_0_protocolVersionNumber, rsp.MinProtocolVersion, rsp.MaxProtocolVersion, ), )} } return nil } func (c *connectionHandshakeV1_0) readFirstMessage() (i int64, salt []byte, serverNonce string, err error) { b, err2 := c.readResponse() if err2 != nil { err = err2 return } // Read server message type firstMessageResponse struct { Success bool `json:"success"` Authentication string `json:"authentication"` ErrorCode int `json:"error_code"` Error string `json:"error"` } var rsp *firstMessageResponse if err2 := json.Unmarshal(b, &rsp); err2 != nil { err = RQLDriverError{rqlError(fmt.Sprintf("Error parsing auth response: %s", err2))} return } if !rsp.Success { err = c.handshakeError(rsp.ErrorCode, rsp.Error) return } c.authMsg += "," c.authMsg += rsp.Authentication // Parse authentication field auth := map[string]string{} parts := strings.Split(rsp.Authentication, ",") for _, part := range parts { i := strings.Index(part, "=") if i != -1 { auth[part[:i]] = part[i+1:] } } // Extract return values if v, ok := auth["i"]; ok { i, err = strconv.ParseInt(v, 10, 64) if err != nil { return } } if v, ok := auth["s"]; ok { salt, err = base64.StdEncoding.DecodeString(v) if err != nil { return } } if v, ok := auth["r"]; ok { serverNonce = v } return } func (c *connectionHandshakeV1_0) writeFinalMessage(serverNonce, clientProof string) error { authMsg := "c=biws,r=" authMsg += serverNonce authMsg += ",p=" authMsg += clientProof msg := fmt.Sprintf(`{"authentication": "%s"}`, authMsg) pos := 0 dataLen := len(msg) + 1 data := make([]byte, dataLen) // Send the auth message as an ASCII string pos += copy(data[pos:], msg) // Add null terminating byte data[pos] = '\x00' return c.writeData(data) } func (c *connectionHandshakeV1_0) readFinalMessage(serverSignature string) error { b, err := c.readResponse() if err != nil { return err } // Read server message type finalMessageResponse struct { Success bool `json:"success"` Authentication string `json:"authentication"` ErrorCode int `json:"error_code"` Error string `json:"error"` } var rsp *finalMessageResponse if err := json.Unmarshal(b, &rsp); err != nil { return RQLDriverError{rqlError(fmt.Sprintf("Error parsing auth response: %s", err))} } if !rsp.Success { return c.handshakeError(rsp.ErrorCode, rsp.Error) } // Parse authentication field auth := map[string]string{} parts := strings.Split(rsp.Authentication, ",") for _, part := range parts { i := strings.Index(part, "=") if i != -1 { auth[part[:i]] = part[i+1:] } } // Validate server response if serverSignature != auth["v"] { return RQLAuthError{RQLDriverError{rqlError("Invalid server signature")}} } return nil } func (c *connectionHandshakeV1_0) writeData(data []byte) error { if err := c.conn.writeData(data); err != nil { return RQLConnectionError{rqlError(err.Error())} } return nil } func (c *connectionHandshakeV1_0) readResponse() ([]byte, error) { line, err := c.reader.ReadBytes('\x00') if err != nil { if err == io.EOF { return nil, RQLConnectionError{rqlError(fmt.Sprintf("Unexpected EOF: %s", string(line)))} } return nil, RQLConnectionError{rqlError(err.Error())} } // Strip null byte and return return line[:len(line)-1], nil } func (c *connectionHandshakeV1_0) generateNonce() (string, error) { const nonceSize = 24 b := make([]byte, nonceSize) _, err := rand.Read(b) if err != nil { return "", err } return base64.StdEncoding.EncodeToString(b), nil } func (c *connectionHandshakeV1_0) saltPassword(iter int64, salt []byte) []byte { pass := []byte(c.conn.opts.Password) return pbkdf2.Key(pass, salt, int(iter), sha256.Size, sha256.New) } func (c *connectionHandshakeV1_0) calculateProof(saltedPass []byte, clientNonce, serverNonce string) string { // Generate proof c.authMsg += ",c=biws,r=" + serverNonce mac := hmac.New(c.hashFunc(), saltedPass) mac.Write([]byte("Client Key")) clientKey := mac.Sum(nil) hash := c.hashFunc()() hash.Write(clientKey) storedKey := hash.Sum(nil) mac = hmac.New(c.hashFunc(), storedKey) mac.Write([]byte(c.authMsg)) clientSignature := mac.Sum(nil) clientProof := make([]byte, len(clientKey)) for i, _ := range clientKey { clientProof[i] = clientKey[i] ^ clientSignature[i] } return base64.StdEncoding.EncodeToString(clientProof) } func (c *connectionHandshakeV1_0) serverSignature(saltedPass []byte) string { mac := hmac.New(c.hashFunc(), saltedPass) mac.Write([]byte("Server Key")) serverKey := mac.Sum(nil) mac = hmac.New(c.hashFunc(), serverKey) mac.Write([]byte(c.authMsg)) serverSignature := mac.Sum(nil) return base64.StdEncoding.EncodeToString(serverSignature) } func (c *connectionHandshakeV1_0) handshakeError(code int, message string) error { if code >= 10 || code <= 20 { return RQLAuthError{RQLDriverError{rqlError(message)}} } return RQLDriverError{rqlError(message)} } func (c *connectionHandshakeV1_0) hashFunc() func() hash.Hash { return sha256.New } gorethink-2.0.4/connection_helper.go000066400000000000000000000016501272042630000175320ustar00rootroot00000000000000package gorethink import "encoding/binary" // Write 'data' to conn func (c *Connection) writeData(data []byte) error { _, err := c.Conn.Write(data[:]) return err } func (c *Connection) read(buf []byte, length int) (total int, err error) { var n int for total < length { if n, err = c.Conn.Read(buf[total:length]); err != nil { break } total += n } return total, err } func (c *Connection) writeQuery(token int64, q []byte) error { pos := 0 dataLen := 8 + 4 + len(q) data := make([]byte, dataLen) // Send the protocol version to the server as a 4-byte little-endian-encoded integer binary.LittleEndian.PutUint64(data[pos:], uint64(token)) pos += 8 // Send the length of the auth key to the server as a 4-byte little-endian-encoded integer binary.LittleEndian.PutUint32(data[pos:], uint32(len(q))) pos += 4 // Send the auth key as an ASCII string pos += copy(data[pos:], q) return c.writeData(data) } gorethink-2.0.4/cursor.go000066400000000000000000000346051272042630000153570ustar00rootroot00000000000000package gorethink import ( "bytes" "encoding/json" "errors" "reflect" "sync" "gopkg.in/dancannon/gorethink.v2/encoding" p "gopkg.in/dancannon/gorethink.v2/ql2" ) var ( errNilCursor = errors.New("cursor is nil") errCursorClosed = errors.New("connection closed, cannot read cursor") ) func newCursor(conn *Connection, cursorType string, token int64, term *Term, opts map[string]interface{}) *Cursor { if cursorType == "" { cursorType = "Cursor" } cursor := &Cursor{ conn: conn, token: token, cursorType: cursorType, term: term, opts: opts, buffer: make([]interface{}, 0), responses: make([]json.RawMessage, 0), } return cursor } // Cursor is the result of a query. Its cursor starts before the first row // of the result set. A Cursor is not thread safe and should only be accessed // by a single goroutine at any given time. Use Next to advance through the // rows: // // cursor, err := query.Run(session) // ... // defer cursor.Close() // // var response interface{} // for cursor.Next(&response) { // ... // } // err = cursor.Err() // get any error encountered during iteration // ... type Cursor struct { releaseConn func() error conn *Connection token int64 cursorType string term *Term opts map[string]interface{} mu sync.RWMutex lastErr error fetching bool closed bool finished bool isAtom bool pendingSkips int buffer []interface{} responses []json.RawMessage profile interface{} } // Profile returns the information returned from the query profiler. func (c *Cursor) Profile() interface{} { if c == nil { return nil } c.mu.RLock() defer c.mu.RUnlock() return c.profile } // Type returns the cursor type (by default "Cursor") func (c *Cursor) Type() string { if c == nil { return "Cursor" } c.mu.RLock() defer c.mu.RUnlock() return c.cursorType } // Err returns nil if no errors happened during iteration, or the actual // error otherwise. func (c *Cursor) Err() error { if c == nil { return errNilCursor } c.mu.RLock() defer c.mu.RUnlock() return c.lastErr } // Close closes the cursor, preventing further enumeration. If the end is // encountered, the cursor is closed automatically. Close is idempotent. func (c *Cursor) Close() error { if c == nil { return errNilCursor } c.mu.Lock() defer c.mu.Unlock() var err error // If cursor is already closed return immediately closed := c.closed if closed { return nil } // Get connection and check its valid, don't need to lock as this is only // set when the cursor is created conn := c.conn if conn == nil { return nil } if conn.Conn == nil { return nil } // Stop any unfinished queries if !c.finished { q := Query{ Type: p.Query_STOP, Token: c.token, Opts: map[string]interface{}{ "noreply": true, }, } _, _, err = conn.Query(q) } if c.releaseConn != nil { if err := c.releaseConn(); err != nil { return err } } c.closed = true c.conn = nil c.buffer = nil c.responses = nil return err } // Next retrieves the next document from the result set, blocking if necessary. // This method will also automatically retrieve another batch of documents from // the server when the current one is exhausted, or before that in background // if possible. // // Next returns true if a document was successfully unmarshalled onto result, // and false at the end of the result set or if an error happened. // When Next returns false, the Err method should be called to verify if // there was an error during iteration. // // Also note that you are able to reuse the same variable multiple times as // `Next` zeroes the value before scanning in the result. func (c *Cursor) Next(dest interface{}) bool { if c == nil { return false } c.mu.Lock() if c.closed { c.mu.Unlock() return false } hasMore, err := c.nextLocked(dest, true) if c.handleErrorLocked(err) != nil { c.mu.Unlock() c.Close() return false } c.mu.Unlock() if !hasMore { c.Close() } return hasMore } func (c *Cursor) nextLocked(dest interface{}, progressCursor bool) (bool, error) { for { if err := c.seekCursor(true); err != nil { return false, err } if len(c.buffer) == 0 && c.finished { return false, nil } if len(c.buffer) > 0 { data := c.buffer[0] if progressCursor { c.buffer = c.buffer[1:] } err := encoding.Decode(dest, data) if err != nil { return false, err } return true, nil } } } // Peek behaves similarly to Next, retreiving the next document from the result set // and blocking if necessary. Peek, however, does not progress the position of the cursor. // This can be useful for expressions which can return different types to attempt to // decode them into different interfaces. // // Like Next, it will also automatically retrieve another batch of documents from // the server when the current one is exhausted, or before that in background // if possible. // // Unlike Next, Peek does not progress the position of the cursor. Peek // will return errors from decoding, but they will not be persisted in the cursor // and therefore will not be available on cursor.Err(). This can be useful for // expressions that can return different types to attempt to decode them into // different interfaces. // // Peek returns true if a document was successfully unmarshalled onto result, // and false at the end of the result set or if an error happened. Peek also // returns the error (if any) that occured func (c *Cursor) Peek(dest interface{}) (bool, error) { if c == nil { return false, errNilCursor } c.mu.Lock() if c.closed { c.mu.Unlock() return false, nil } hasMore, err := c.nextLocked(dest, false) if _, isDecodeErr := err.(*encoding.DecodeTypeError); isDecodeErr { c.mu.Unlock() return false, err } if c.handleErrorLocked(err) != nil { c.mu.Unlock() c.Close() return false, err } c.mu.Unlock() return hasMore, nil } // Skip progresses the cursor by one record. It is useful after a successful // Peek to avoid duplicate decoding work. func (c *Cursor) Skip() { if c == nil { return } c.mu.Lock() defer c.mu.Unlock() c.pendingSkips++ } // NextResponse retrieves the next raw response from the result set, blocking if necessary. // Unlike Next the returned response is the raw JSON document returned from the // database. // // NextResponse returns false (and a nil byte slice) at the end of the result // set or if an error happened. func (c *Cursor) NextResponse() ([]byte, bool) { if c == nil { return nil, false } c.mu.Lock() if c.closed { c.mu.Unlock() return nil, false } b, hasMore, err := c.nextResponseLocked() if c.handleErrorLocked(err) != nil { c.mu.Unlock() c.Close() return nil, false } c.mu.Unlock() if !hasMore { c.Close() } return b, hasMore } func (c *Cursor) nextResponseLocked() ([]byte, bool, error) { for { if err := c.seekCursor(false); err != nil { return nil, false, err } if len(c.responses) == 0 && c.finished { return nil, false, nil } if len(c.responses) > 0 { var response json.RawMessage response, c.responses = c.responses[0], c.responses[1:] return []byte(response), true, nil } } } // All retrieves all documents from the result set into the provided slice // and closes the cursor. // // The result argument must necessarily be the address for a slice. The slice // may be nil or previously allocated. // // Also note that you are able to reuse the same variable multiple times as // `All` zeroes the value before scanning in the result. It also attempts // to reuse the existing slice without allocating any more space by either // resizing or returning a selection of the slice if necessary. func (c *Cursor) All(result interface{}) error { if c == nil { return errNilCursor } resultv := reflect.ValueOf(result) if resultv.Kind() != reflect.Ptr || resultv.Elem().Kind() != reflect.Slice { panic("result argument must be a slice address") } slicev := resultv.Elem() slicev = slicev.Slice(0, slicev.Cap()) elemt := slicev.Type().Elem() i := 0 for { if slicev.Len() == i { elemp := reflect.New(elemt) if !c.Next(elemp.Interface()) { break } slicev = reflect.Append(slicev, elemp.Elem()) slicev = slicev.Slice(0, slicev.Cap()) } else { if !c.Next(slicev.Index(i).Addr().Interface()) { break } } i++ } resultv.Elem().Set(slicev.Slice(0, i)) if err := c.Err(); err != nil { c.Close() return err } if err := c.Close(); err != nil { return err } return nil } // One retrieves a single document from the result set into the provided // slice and closes the cursor. // // Also note that you are able to reuse the same variable multiple times as // `One` zeroes the value before scanning in the result. func (c *Cursor) One(result interface{}) error { if c == nil { return errNilCursor } if c.IsNil() { c.Close() return ErrEmptyResult } hasResult := c.Next(result) if err := c.Err(); err != nil { c.Close() return err } if err := c.Close(); err != nil { return err } if !hasResult { return ErrEmptyResult } return nil } // Listen listens for rows from the database and sends the result onto the given // channel. The type that the row is scanned into is determined by the element // type of the channel. // // Also note that this function returns immediately. // // cursor, err := r.Expr([]int{1,2,3}).Run(session) // if err != nil { // panic(err) // } // // ch := make(chan int) // cursor.Listen(ch) // <- ch // 1 // <- ch // 2 // <- ch // 3 func (c *Cursor) Listen(channel interface{}) { go func() { channelv := reflect.ValueOf(channel) if channelv.Kind() != reflect.Chan { panic("input argument must be a channel") } elemt := channelv.Type().Elem() for { elemp := reflect.New(elemt) if !c.Next(elemp.Interface()) { break } channelv.Send(elemp.Elem()) } c.Close() channelv.Close() }() } // IsNil tests if the current row is nil. func (c *Cursor) IsNil() bool { if c == nil { return true } c.mu.RLock() defer c.mu.RUnlock() if len(c.buffer) > 0 { return c.buffer[0] == nil } if len(c.responses) > 0 { response := c.responses[0] if response == nil { return true } if string(response) == "null" { return true } return false } return true } // fetchMore fetches more rows from the database. // // If wait is true then it will wait for the database to reply otherwise it // will return after sending the continue query. func (c *Cursor) fetchMore() error { var err error fetching := c.fetching closed := c.closed if !fetching { c.fetching = true if closed { return errCursorClosed } q := Query{ Type: p.Query_CONTINUE, Token: c.token, } c.mu.Unlock() _, _, err = c.conn.Query(q) c.mu.Lock() } return err } // handleError sets the value of lastErr to err if lastErr is not yet set. func (c *Cursor) handleError(err error) error { c.mu.Lock() defer c.mu.Unlock() return c.handleErrorLocked(err) } func (c *Cursor) handleErrorLocked(err error) error { if c.lastErr == nil { c.lastErr = err } return c.lastErr } // extend adds the result of a continue query to the cursor. func (c *Cursor) extend(response *Response) { c.mu.Lock() defer c.mu.Unlock() c.extendLocked(response) } func (c *Cursor) extendLocked(response *Response) { c.responses = append(c.responses, response.Responses...) c.finished = response.Type != p.Response_SUCCESS_PARTIAL c.fetching = false c.isAtom = response.Type == p.Response_SUCCESS_ATOM putResponse(response) } // seekCursor takes care of loading more data if needed and applying pending skips // // bufferResponse determines whether the response will be parsed into the buffer func (c *Cursor) seekCursor(bufferResponse bool) error { if c.lastErr != nil { return c.lastErr } if len(c.buffer) == 0 && len(c.responses) == 0 && c.closed { return errCursorClosed } // Loop over loading data, applying skips as necessary and loading more data as needed // until either the cursor is closed or finished, or we have applied all outstanding // skips and data is available for { c.applyPendingSkips(bufferResponse) // if we are buffering the responses, skip can drain from the buffer if bufferResponse && len(c.buffer) == 0 && len(c.responses) > 0 { if err := c.bufferNextResponse(); err != nil { return err } continue // go around the loop again to re-apply pending skips } else if len(c.buffer) == 0 && len(c.responses) == 0 && !c.finished && !c.closed { // We skipped all of our data, load some more if err := c.fetchMore(); err != nil { return err } continue // go around the loop again to re-apply pending skips } return nil } } // applyPendingSkips applies all pending skips to the buffer and // returns whether there are more pending skips to be applied // // if drainFromBuffer is true, we will drain from the buffer, otherwise // we drain from the responses func (c *Cursor) applyPendingSkips(drainFromBuffer bool) (stillPending bool) { if c.pendingSkips == 0 { return false } if drainFromBuffer { if len(c.buffer) > c.pendingSkips { c.buffer = c.buffer[c.pendingSkips:] c.pendingSkips = 0 return false } c.pendingSkips -= len(c.buffer) c.buffer = c.buffer[:0] return c.pendingSkips > 0 } if len(c.responses) > c.pendingSkips { c.responses = c.responses[c.pendingSkips:] c.pendingSkips = 0 return false } c.pendingSkips -= len(c.responses) c.responses = c.responses[:0] return c.pendingSkips > 0 } // bufferResponse reads a single response and stores the result into the buffer // if the response is from an atomic response, it will check if the // response contains multiple records and store them all into the buffer func (c *Cursor) bufferNextResponse() error { // If there are no responses, nothing to do if len(c.responses) == 0 { return nil } response := c.responses[0] c.responses = c.responses[1:] var value interface{} decoder := json.NewDecoder(bytes.NewBuffer(response)) if c.conn.opts.UseJSONNumber { decoder.UseNumber() } err := decoder.Decode(&value) if err != nil { return err } value, err = recursivelyConvertPseudotype(value, c.opts) if err != nil { return err } // If response is an ATOM then try and convert to an array if data, ok := value.([]interface{}); ok && c.isAtom { c.buffer = append(c.buffer, data...) } else if value == nil { c.buffer = append(c.buffer, nil) } else { c.buffer = append(c.buffer, value) } return nil } gorethink-2.0.4/cursor_test.go000066400000000000000000000303051272042630000164070ustar00rootroot00000000000000package gorethink import ( "fmt" "time" test "gopkg.in/check.v1" ) type object struct { ID int64 `gorethink:"id,omitempty"` Name string `gorethink:"name"` Attrs []attr } type attr struct { Name string Value interface{} } func (s *RethinkSuite) TestCursorLiteral(c *test.C) { res, err := Expr(5).Run(session) c.Assert(err, test.IsNil) c.Assert(res.Type(), test.Equals, "Cursor") var response interface{} err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, 5) } func (s *RethinkSuite) TestCursorSlice(c *test.C) { res, err := Expr([]interface{}{1, 2, 3, 4, 5}).Run(session) c.Assert(err, test.IsNil) c.Assert(res.Type(), test.Equals, "Cursor") var response []interface{} err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{1, 2, 3, 4, 5}) } func (s *RethinkSuite) TestCursorPartiallyNilSlice(c *test.C) { res, err := Expr(map[string]interface{}{ "item": []interface{}{ map[string]interface{}{"num": 1}, nil, }, }).Run(session) c.Assert(err, test.IsNil) c.Assert(res.Type(), test.Equals, "Cursor") var response map[string]interface{} err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, map[string]interface{}{ "item": []interface{}{ map[string]interface{}{"num": 1}, nil, }, }) } func (s *RethinkSuite) TestCursorMap(c *test.C) { res, err := Expr(map[string]interface{}{ "id": 2, "name": "Object 1", }).Run(session) c.Assert(err, test.IsNil) c.Assert(res.Type(), test.Equals, "Cursor") var response map[string]interface{} err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, map[string]interface{}{ "id": 2, "name": "Object 1", }) } func (s *RethinkSuite) TestCursorMapIntoInterface(c *test.C) { res, err := Expr(map[string]interface{}{ "id": 2, "name": "Object 1", }).Run(session) c.Assert(err, test.IsNil) c.Assert(res.Type(), test.Equals, "Cursor") var response interface{} err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, map[string]interface{}{ "id": 2, "name": "Object 1", }) } func (s *RethinkSuite) TestCursorMapNested(c *test.C) { res, err := Expr(map[string]interface{}{ "id": 2, "name": "Object 1", "attr": []interface{}{map[string]interface{}{ "name": "attr 1", "value": "value 1", }}, }).Run(session) c.Assert(err, test.IsNil) c.Assert(res.Type(), test.Equals, "Cursor") var response interface{} err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, map[string]interface{}{ "id": 2, "name": "Object 1", "attr": []interface{}{map[string]interface{}{ "name": "attr 1", "value": "value 1", }}, }) } func (s *RethinkSuite) TestCursorStruct(c *test.C) { res, err := Expr(map[string]interface{}{ "id": 2, "name": "Object 1", "Attrs": []interface{}{map[string]interface{}{ "Name": "attr 1", "Value": "value 1", }}, }).Run(session) c.Assert(err, test.IsNil) c.Assert(res.Type(), test.Equals, "Cursor") var response object err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.DeepEquals, object{ ID: 2, Name: "Object 1", Attrs: []attr{attr{ Name: "attr 1", Value: "value 1", }}, }) } func (s *RethinkSuite) TestCursorStructPseudoTypes(c *test.C) { var zeroTime time.Time t := time.Now() res, err := Expr(map[string]interface{}{ "T": time.Unix(t.Unix(), 0).In(time.UTC), "Z": zeroTime, "B": []byte("hello"), }).Run(session) c.Assert(err, test.IsNil) var response PseudoTypes err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(res.Type(), test.Equals, "Cursor") c.Assert(response.T.Equal(time.Unix(t.Unix(), 0)), test.Equals, true) c.Assert(response.Z.Equal(zeroTime), test.Equals, true) c.Assert(response.B, jsonEquals, []byte("hello")) } func (s *RethinkSuite) TestCursorAtomString(c *test.C) { res, err := Expr("a").Run(session) c.Assert(err, test.IsNil) c.Assert(res.Type(), test.Equals, "Cursor") var response string err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, "a") } func (s *RethinkSuite) TestCursorAtomArray(c *test.C) { res, err := Expr([]interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}).Run(session) c.Assert(err, test.IsNil) c.Assert(res.Type(), test.Equals, "Cursor") var response []int err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, test.DeepEquals, []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}) } func (s *RethinkSuite) TestEmptyResults(c *test.C) { DBCreate("test").Exec(session) DB("test").TableCreate("test").Exec(session) res, err := DB("test").Table("test").Get("missing value").Run(session) c.Assert(err, test.IsNil) c.Assert(res.IsNil(), test.Equals, true) res, err = DB("test").Table("test").Get("missing value").Run(session) c.Assert(err, test.IsNil) var response interface{} err = res.One(&response) c.Assert(err, test.Equals, ErrEmptyResult) c.Assert(res.IsNil(), test.Equals, true) res, err = Expr(nil).Run(session) c.Assert(err, test.IsNil) c.Assert(res.IsNil(), test.Equals, true) res, err = DB("test").Table("test").Get("missing value").Run(session) c.Assert(err, test.IsNil) c.Assert(res.IsNil(), test.Equals, true) res, err = DB("test").Table("test").GetAll("missing value", "another missing value").Run(session) c.Assert(err, test.IsNil) c.Assert(res.Next(&response), test.Equals, false) var obj object obj.Name = "missing value" res, err = DB("test").Table("test").Filter(obj).Run(session) c.Assert(err, test.IsNil) c.Assert(res.IsNil(), test.Equals, true) var objP *object res, err = DB("test").Table("test").Get("missing value").Run(session) res.Next(&objP) c.Assert(err, test.IsNil) c.Assert(objP, test.IsNil) } func (s *RethinkSuite) TestCursorAll(c *test.C) { // Ensure table + database exist DBCreate("test").Exec(session) DB("test").TableDrop("Table3").Exec(session) DB("test").TableCreate("Table3").Exec(session) DB("test").Table("Table3").IndexCreate("num").Exec(session) DB("test").Table("Table3").IndexWait().Exec(session) // Insert rows DB("test").Table("Table3").Insert([]interface{}{ map[string]interface{}{ "id": 2, "name": "Object 1", "Attrs": []interface{}{map[string]interface{}{ "Name": "attr 1", "Value": "value 1", }}, }, map[string]interface{}{ "id": 3, "name": "Object 2", "Attrs": []interface{}{map[string]interface{}{ "Name": "attr 1", "Value": "value 1", }}, }, }).Exec(session) // Test query query := DB("test").Table("Table3").OrderBy("id") res, err := query.Run(session) c.Assert(err, test.IsNil) var response []object err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, test.HasLen, 2) c.Assert(response, test.DeepEquals, []object{ object{ ID: 2, Name: "Object 1", Attrs: []attr{attr{ Name: "attr 1", Value: "value 1", }}, }, object{ ID: 3, Name: "Object 2", Attrs: []attr{attr{ Name: "attr 1", Value: "value 1", }}, }, }) } func (s *RethinkSuite) TestCursorListen(c *test.C) { // Ensure table + database exist DBCreate("test").Exec(session) DB("test").TableDrop("Table3").Exec(session) DB("test").TableCreate("Table3").Exec(session) DB("test").Table("Table3").IndexCreate("num").Exec(session) DB("test").Table("Table3").IndexWait().Exec(session) // Insert rows DB("test").Table("Table3").Insert([]interface{}{ map[string]interface{}{ "id": 2, "name": "Object 1", "Attrs": []interface{}{map[string]interface{}{ "Name": "attr 1", "Value": "value 1", }}, }, map[string]interface{}{ "id": 3, "name": "Object 2", "Attrs": []interface{}{map[string]interface{}{ "Name": "attr 1", "Value": "value 1", }}, }, }).Exec(session) // Test query query := DB("test").Table("Table3").OrderBy("id") res, err := query.Run(session) c.Assert(err, test.IsNil) ch := make(chan object) res.Listen(ch) var response []object for v := range ch { response = append(response, v) } c.Assert(response, test.HasLen, 2) c.Assert(response, test.DeepEquals, []object{ object{ ID: 2, Name: "Object 1", Attrs: []attr{attr{ Name: "attr 1", Value: "value 1", }}, }, object{ ID: 3, Name: "Object 2", Attrs: []attr{attr{ Name: "attr 1", Value: "value 1", }}, }, }) } func (s *RethinkSuite) TestCursorChangesClose(c *test.C) { // Ensure table + database exist DBCreate("test").Exec(session) DB("test").TableDrop("Table3").Exec(session) DB("test").TableCreate("Table3").Exec(session) // Test query // res, err := DB("test").Table("Table3").Changes().Run(session) res, err := DB("test").Table("Table3").Changes().Run(session) c.Assert(err, test.IsNil) c.Assert(res, test.NotNil) // Ensure that the cursor can be closed err = res.Close() c.Assert(err, test.IsNil) } func (s *RethinkSuite) TestCursorReuseResult(c *test.C) { // Test query query := Expr([]interface{}{ map[string]interface{}{ "A": "a", }, map[string]interface{}{ "B": 1, }, map[string]interface{}{ "A": "a", }, map[string]interface{}{ "B": 1, }, map[string]interface{}{ "A": "a", "B": 1, }, }) res, err := query.Run(session) c.Assert(err, test.IsNil) var i int var result SimpleT for res.Next(&result) { switch i { case 0: c.Assert(result, test.DeepEquals, SimpleT{ A: "a", B: 0, }) case 1: c.Assert(result, test.DeepEquals, SimpleT{ A: "", B: 1, }) case 2: c.Assert(result, test.DeepEquals, SimpleT{ A: "a", B: 0, }) case 3: c.Assert(result, test.DeepEquals, SimpleT{ A: "", B: 1, }) case 4: c.Assert(result, test.DeepEquals, SimpleT{ A: "a", B: 1, }) default: c.Fatalf("Unexpected number of results") } i++ } c.Assert(res.Err(), test.IsNil) } func (s *RethinkSuite) TestCursorNextResponse(c *test.C) { res, err := Expr(5).Run(session) c.Assert(err, test.IsNil) c.Assert(res.Type(), test.Equals, "Cursor") b, ok := res.NextResponse() c.Assert(ok, test.Equals, true) c.Assert(b, jsonEquals, []byte(`5`)) } func (s *RethinkSuite) TestCursorNextResponse_object(c *test.C) { res, err := Expr(map[string]string{"foo": "bar"}).Run(session) c.Assert(err, test.IsNil) c.Assert(res.Type(), test.Equals, "Cursor") b, ok := res.NextResponse() c.Assert(ok, test.Equals, true) c.Assert(b, jsonEquals, []byte(`{"foo":"bar"}`)) } func (s *RethinkSuite) TestCursorPeek_idempotency(c *test.C) { res, err := Expr([]int{1, 2, 3}).Run(session) c.Assert(err, test.IsNil) var result int // Test idempotency for i := 0; i < 2; i++ { hasMore, err := res.Peek(&result) c.Assert(err, test.IsNil) c.Assert(result, test.Equals, 1) c.Assert(hasMore, test.Equals, true) } } func (s *RethinkSuite) TestCursorPeek_wrong_type(c *test.C) { res, err := Expr([]int{1, 2, 3}).Run(session) c.Assert(err, test.IsNil) // Test that wrongType doesn't break the cursor wrongType := struct { Name string Age int }{} hasMore, err := res.Peek(&wrongType) c.Assert(err, test.NotNil) c.Assert(hasMore, test.Equals, false) c.Assert(res.Err(), test.IsNil) } func (s *RethinkSuite) TestCursorPeek_usage(c *test.C) { res, err := Expr([]int{1, 2, 3}).Run(session) c.Assert(err, test.IsNil) var result int // Test that Skip progresses our cursor res.Skip() hasMore, err := res.Peek(&result) c.Assert(err, test.IsNil) c.Assert(result, test.Equals, 2) c.Assert(hasMore, test.Equals, true) // Test that we can use Next afterwards and we get the same result hasMore = res.Next(&result) c.Assert(result, test.Equals, 2) c.Assert(hasMore, test.Equals, true) } func (s *RethinkSuite) TestCursorSkip(c *test.C) { res, err := Expr([]int{1, 2, 3}).Run(session) c.Assert(err, test.IsNil) res.Skip() var result int hasMore := res.Next(&result) c.Assert(result, test.Equals, 2) c.Assert(hasMore, test.Equals, true) } func ExampleCursor_Peek() { res, err := Expr([]int{1, 2, 3}).Run(session) if err != nil { fmt.Print(err) return } var result, altResult int wasRead, err := res.Peek(&result) // Result is now 1 if err != nil { fmt.Print(err) return } else if !wasRead { fmt.Print("No data to read!") } res.Next(&altResult) // altResult is also 1, peek didn't progress the cursor res.Skip() // progress the cursor, skipping 2 res.Peek(&result) // result is now 3 } gorethink-2.0.4/doc.go000066400000000000000000000003521272042630000145770ustar00rootroot00000000000000// Package gorethink implements a Go driver for RethinkDB // // Current version: v2.0.4 (RethinkDB v2.3) // For more in depth information on how to use RethinkDB check out the API docs // at http://rethinkdb.com/api package gorethink gorethink-2.0.4/encoding/000077500000000000000000000000001272042630000152715ustar00rootroot00000000000000gorethink-2.0.4/encoding/cache.go000066400000000000000000000154121272042630000166660ustar00rootroot00000000000000// This code is based on encoding/json and gorilla/schema package encoding import ( "reflect" "sort" "sync" "time" ) // A field represents a single field found in a struct. type field struct { name string nameBytes []byte // []byte(name) equalFold func(s, t []byte) bool tag bool index []int typ reflect.Type omitEmpty bool quoted bool reference bool refName string } func fillField(f field) field { f.nameBytes = []byte(f.name) f.equalFold = foldFunc(f.nameBytes) return f } // byName sorts field by name, breaking ties with depth, // then breaking ties with "name came from tag", then // breaking ties with index sequence. type byName []field func (x byName) Len() int { return len(x) } func (x byName) Swap(i, j int) { x[i], x[j] = x[j], x[i] } func (x byName) Less(i, j int) bool { if x[i].name != x[j].name { return x[i].name < x[j].name } if len(x[i].index) != len(x[j].index) { return len(x[i].index) < len(x[j].index) } if x[i].tag != x[j].tag { return x[i].tag } return byIndex(x).Less(i, j) } // byIndex sorts field by index sequence. type byIndex []field func (x byIndex) Len() int { return len(x) } func (x byIndex) Swap(i, j int) { x[i], x[j] = x[j], x[i] } func (x byIndex) Less(i, j int) bool { for k, xik := range x[i].index { if k >= len(x[j].index) { return false } if xik != x[j].index[k] { return xik < x[j].index[k] } } return len(x[i].index) < len(x[j].index) } // typeFields returns a list of fields that should be recognized for the given type. // The algorithm is breadth-first search over the set of structs to include - the top struct // and then any reachable anonymous structs. func typeFields(t reflect.Type) []field { // Anonymous fields to explore at the current level and the next. current := []field{} next := []field{{typ: t}} // Count of queued names for current level and the next. count := map[reflect.Type]int{} nextCount := map[reflect.Type]int{} // Types already visited at an earlier level. visited := map[reflect.Type]bool{} // Fields found. var fields []field for len(next) > 0 { current, next = next, current[:0] count, nextCount = nextCount, map[reflect.Type]int{} for _, f := range current { if visited[f.typ] { continue } visited[f.typ] = true // Scan f.typ for fields to include. for i := 0; i < f.typ.NumField(); i++ { sf := f.typ.Field(i) if sf.PkgPath != "" && !sf.Anonymous { // unexported continue } // Extract field name from tag tag := getTag(sf) if tag == "-" { continue } name, opts := parseTag(tag) if !isValidTag(name) { name = "" } // Extract referenced field from tags refTag := getRefTag(sf) ref, _ := parseTag(refTag) if !isValidTag(ref) { ref = "" } index := make([]int, len(f.index)+1) copy(index, f.index) index[len(f.index)] = i ft := sf.Type if ft.Name() == "" && ft.Kind() == reflect.Ptr { // Follow pointer. ft = ft.Elem() } // Record found field and index sequence. if name != "" || !sf.Anonymous || ft.Kind() != reflect.Struct || isPseudoType(ft) { tagged := name != "" if name == "" { name = sf.Name } fields = append(fields, fillField(field{ name: name, tag: tagged, index: index, typ: ft, omitEmpty: opts.Contains("omitempty"), reference: opts.Contains("reference"), refName: ref, })) if count[f.typ] > 1 { // If there were multiple instances, add a second, // so that the annihilation code will see a duplicate. // It only cares about the distinction between 1 or 2, // so don't bother generating any more copies. fields = append(fields, fields[len(fields)-1]) } continue } // Record new anonymous struct to explore in next round. nextCount[ft]++ if nextCount[ft] == 1 { next = append(next, fillField(field{name: ft.Name(), index: index, typ: ft})) } } } } sort.Sort(byName(fields)) // Delete all fields that are hidden by the Go rules for embedded fields, // except that fields with valid tags are promoted. // The fields are sorted in primary order of name, secondary order // of field index length. Loop over names; for each name, delete // hidden fields by choosing the one dominant field that survives. out := fields[:0] for advance, i := 0, 0; i < len(fields); i += advance { // One iteration per name. // Find the sequence of fields with the name of this first field. fi := fields[i] name := fi.name for advance = 1; i+advance < len(fields); advance++ { fj := fields[i+advance] if fj.name != name { break } } if advance == 1 { // Only one field with this name out = append(out, fi) continue } dominant, ok := dominantField(fields[i : i+advance]) if ok { out = append(out, dominant) } } fields = out sort.Sort(byIndex(fields)) return fields } func isPseudoType(t reflect.Type) bool { return t == reflect.TypeOf(time.Time{}) } // dominantField looks through the fields, all of which are known to // have the same name, to find the single field that dominates the // others using Go's embedding rules, modified by the presence of // valid tags. If there are multiple top-level fields, the boolean // will be false: This condition is an error in Go and we skip all // the fields. func dominantField(fields []field) (field, bool) { // The fields are sorted in increasing index-length order. The winner // must therefore be one with the shortest index length. Drop all // longer entries, which is easy: just truncate the slice. length := len(fields[0].index) tagged := -1 // Index of first tagged field. for i, f := range fields { if len(f.index) > length { fields = fields[:i] break } if f.tag { if tagged >= 0 { // Multiple tagged fields at the same level: conflict. // Return no field. return field{}, false } tagged = i } } if tagged >= 0 { return fields[tagged], true } // All remaining fields have the same length. If there's more than one, // we have a conflict (two fields named "X" at the same level) and we // return no field. if len(fields) > 1 { return field{}, false } return fields[0], true } var fieldCache struct { sync.RWMutex m map[reflect.Type][]field } // cachedTypeFields is like typeFields but uses a cache to avoid repeated work. func cachedTypeFields(t reflect.Type) []field { fieldCache.RLock() f := fieldCache.m[t] fieldCache.RUnlock() if f != nil { return f } // Compute fields without lock. // Might duplicate effort but won't hold other computations back. f = typeFields(t) if f == nil { f = []field{} } fieldCache.Lock() if fieldCache.m == nil { fieldCache.m = map[reflect.Type][]field{} } fieldCache.m[t] = f fieldCache.Unlock() return f } gorethink-2.0.4/encoding/decoder.go000066400000000000000000000057621272042630000172370ustar00rootroot00000000000000package encoding import ( "errors" "reflect" "runtime" "sync" ) var byteSliceType = reflect.TypeOf([]byte(nil)) type decoderFunc func(dv reflect.Value, sv reflect.Value) // Decode decodes map[string]interface{} into a struct. The first parameter // must be a pointer. func Decode(dst interface{}, src interface{}) (err error) { defer func() { if r := recover(); r != nil { if _, ok := r.(runtime.Error); ok { panic(r) } if v, ok := r.(string); ok { err = errors.New(v) } else { err = r.(error) } } }() dv := reflect.ValueOf(dst) sv := reflect.ValueOf(src) if dv.Kind() != reflect.Ptr { return &DecodeTypeError{ DestType: dv.Type(), SrcType: sv.Type(), Reason: "must be a pointer", } } dv = dv.Elem() if !dv.CanAddr() { return &DecodeTypeError{ DestType: dv.Type(), SrcType: sv.Type(), Reason: "must be addressable", } } decode(dv, sv) return nil } // decode decodes the source value into the destination value func decode(dv, sv reflect.Value) { valueDecoder(dv, sv)(dv, sv) } type decoderCacheKey struct { dt, st reflect.Type } var decoderCache struct { sync.RWMutex m map[decoderCacheKey]decoderFunc } func valueDecoder(dv, sv reflect.Value) decoderFunc { if !sv.IsValid() { return invalidValueDecoder } if dv.IsValid() { dv = indirect(dv, false) dv.Set(reflect.Zero(dv.Type())) } return typeDecoder(dv.Type(), sv.Type()) } func typeDecoder(dt, st reflect.Type) decoderFunc { decoderCache.RLock() f := decoderCache.m[decoderCacheKey{dt, st}] decoderCache.RUnlock() if f != nil { return f } // To deal with recursive types, populate the map with an // indirect func before we build it. This type waits on the // real func (f) to be ready and then calls it. This indirect // func is only used for recursive types. decoderCache.Lock() var wg sync.WaitGroup wg.Add(1) decoderCache.m[decoderCacheKey{dt, st}] = func(dv, sv reflect.Value) { wg.Wait() f(dv, sv) } decoderCache.Unlock() // Compute fields without lock. // Might duplicate effort but won't hold other computations back. f = newTypeDecoder(dt, st) wg.Done() decoderCache.Lock() decoderCache.m[decoderCacheKey{dt, st}] = f decoderCache.Unlock() return f } // indirect walks down v allocating pointers as needed, // until it gets to a non-pointer. func indirect(v reflect.Value, decodeNull bool) reflect.Value { // If v is a named type and is addressable, // start with its address, so that if the type has pointer methods, // we find them. if v.Kind() != reflect.Ptr && v.Type().Name() != "" && v.CanAddr() { v = v.Addr() } for { // Load value from interface, but only if the result will be // usefully addressable. if v.Kind() == reflect.Interface && !v.IsNil() { e := v.Elem() if e.Kind() == reflect.Ptr && !e.IsNil() && (!decodeNull || e.Elem().Kind() == reflect.Ptr) { v = e continue } } if v.Kind() != reflect.Ptr { break } if v.IsNil() { v.Set(reflect.New(v.Type().Elem())) } v = v.Elem() } return v } gorethink-2.0.4/encoding/decoder_test.go000066400000000000000000000231741272042630000202730ustar00rootroot00000000000000package encoding import ( "bytes" "encoding/json" "image" "reflect" "testing" ) type T struct { X string Y int Z int `gorethink:"-"` } type U struct { Alphabet string `gorethink:"alpha"` } type V struct { F1 interface{} F2 int32 F3 string } type tx struct { x int } var txType = reflect.TypeOf((*tx)(nil)).Elem() // Test data structures for anonymous fields. type Point struct { Z int } type Top struct { Level0 int Embed0 *Embed0a *Embed0b `gorethink:"e,omitempty"` // treated as named Embed0c `gorethink:"-"` // ignored Loop Embed0p // has Point with X, Y, used Embed0q // has Point with Z, used } type Embed0 struct { Level1a int // overridden by Embed0a's Level1a with tag Level1b int // used because Embed0a's Level1b is renamed Level1c int // used because Embed0a's Level1c is ignored Level1d int // annihilated by Embed0a's Level1d Level1e int `gorethink:"x"` // annihilated by Embed0a.Level1e } type Embed0a struct { Level1a int `gorethink:"Level1a,omitempty"` Level1b int `gorethink:"LEVEL1B,omitempty"` Level1c int `gorethink:"-"` Level1d int // annihilated by Embed0's Level1d Level1f int `gorethink:"x"` // annihilated by Embed0's Level1e } type Embed0b Embed0 type Embed0c Embed0 type Embed0p struct { image.Point } type Embed0q struct { Point } type Loop struct { Loop1 int `gorethink:",omitempty"` Loop2 int `gorethink:",omitempty"` *Loop } // From reflect test: // The X in S6 and S7 annihilate, but they also block the X in S8.S9. type S5 struct { S6 S7 S8 } type S6 struct { X int } type S7 S6 type S8 struct { S9 } type S9 struct { X int Y int } // From reflect test: // The X in S11.S6 and S12.S6 annihilate, but they also block the X in S13.S8.S9. type S10 struct { S11 S12 S13 } type S11 struct { S6 } type S12 struct { S6 } type S13 struct { S8 } type PointerBasic struct { X int Y *int } type Pointer struct { PPoint *Point Point Point } type decodeTest struct { in interface{} ptr interface{} out interface{} err error } type Ambig struct { // Given "hello", the first match should win. First int `gorethink:"HELLO"` Second int `gorethink:"Hello"` } // Decode test helper vars var ( sampleInt = 2 ) var decodeTests = []decodeTest{ // basic types {in: true, ptr: new(bool), out: true}, {in: 1, ptr: new(int), out: 1}, {in: 1.2, ptr: new(float64), out: 1.2}, {in: -5, ptr: new(int16), out: int16(-5)}, {in: 2, ptr: new(string), out: string("2")}, {in: float64(2.0), ptr: new(interface{}), out: float64(2.0)}, {in: string("2"), ptr: new(interface{}), out: string("2")}, {in: "a\u1234", ptr: new(string), out: "a\u1234"}, {in: []interface{}{}, ptr: new([]string), out: []string{}}, {in: map[string]interface{}{"X": []interface{}{1, 2, 3}, "Y": 4}, ptr: new(T), out: T{}, err: &DecodeTypeError{reflect.TypeOf(""), reflect.TypeOf([]interface{}{}), ""}}, {in: map[string]interface{}{"x": 1}, ptr: new(tx), out: tx{}}, {in: map[string]interface{}{"F1": float64(1), "F2": 2, "F3": 3}, ptr: new(V), out: V{F1: float64(1), F2: int32(2), F3: string("3")}}, {in: map[string]interface{}{"F1": string("1"), "F2": 2, "F3": 3}, ptr: new(V), out: V{F1: string("1"), F2: int32(2), F3: string("3")}}, { in: map[string]interface{}{"k1": int64(1), "k2": "s", "k3": []interface{}{int64(1), 2.0, 3e-3}, "k4": map[string]interface{}{"kk1": "s", "kk2": int64(2)}}, out: map[string]interface{}{"k1": int64(1), "k2": "s", "k3": []interface{}{int64(1), 2.0, 3e-3}, "k4": map[string]interface{}{"kk1": "s", "kk2": int64(2)}}, ptr: new(interface{}), }, // Z has a "-" tag. {in: map[string]interface{}{"Y": 1, "Z": 2}, ptr: new(T), out: T{Y: 1}}, {in: map[string]interface{}{"alpha": "abc", "alphabet": "xyz"}, ptr: new(U), out: U{Alphabet: "abc"}}, {in: map[string]interface{}{"alpha": "abc"}, ptr: new(U), out: U{Alphabet: "abc"}}, {in: map[string]interface{}{"alphabet": "xyz"}, ptr: new(U), out: U{}}, // array tests {in: []interface{}{1, 2, 3}, ptr: new([3]int), out: [3]int{1, 2, 3}}, {in: []interface{}{1, 2, 3}, ptr: new([1]int), out: [1]int{1}}, {in: []interface{}{1, 2, 3}, ptr: new([5]int), out: [5]int{1, 2, 3, 0, 0}}, // empty array to interface test {in: map[string]interface{}{"T": []interface{}{}}, ptr: new(map[string]interface{}), out: map[string]interface{}{"T": []interface{}{}}}, { in: map[string]interface{}{ "Level0": 1, "Level1b": 2, "Level1c": 3, "level1d": 4, "Level1a": 5, "LEVEL1B": 6, "e": map[string]interface{}{ "Level1a": 8, "Level1b": 9, "Level1c": 10, "Level1d": 11, "x": 12, }, "Loop1": 13, "Loop2": 14, "X": 15, "Y": 16, "Z": 17, }, ptr: new(Top), out: Top{ Level0: 1, Embed0: Embed0{ Level1b: 2, Level1c: 3, }, Embed0a: &Embed0a{ Level1a: 5, Level1b: 6, }, Embed0b: &Embed0b{ Level1a: 8, Level1b: 9, Level1c: 10, Level1d: 11, }, Loop: Loop{ Loop1: 13, Loop2: 14, }, Embed0p: Embed0p{ Point: image.Point{X: 15, Y: 16}, }, Embed0q: Embed0q{ Point: Point{Z: 17}, }, }, }, { in: map[string]interface{}{"hello": 1}, ptr: new(Ambig), out: Ambig{First: 1}, }, { in: map[string]interface{}{"X": 1, "Y": 2}, ptr: new(S5), out: S5{S8: S8{S9: S9{Y: 2}}}, }, { in: map[string]interface{}{"X": 1, "Y": 2}, ptr: new(S10), out: S10{S13: S13{S8: S8{S9: S9{Y: 2}}}}, }, { in: map[string]interface{}{"PPoint": map[string]interface{}{"Z": 1}, "Point": map[string]interface{}{"Z": 2}}, ptr: new(Pointer), out: Pointer{PPoint: &Point{Z: 1}, Point: Point{Z: 2}}, }, { in: map[string]interface{}{"Point": map[string]interface{}{"Z": 2}}, ptr: new(Pointer), out: Pointer{PPoint: nil, Point: Point{Z: 2}}, }, { in: map[string]interface{}{"x": 2}, ptr: new(PointerBasic), out: PointerBasic{X: 2, Y: nil}, }, { in: map[string]interface{}{"x": 2, "y": 2}, ptr: new(PointerBasic), out: PointerBasic{X: 2, Y: &sampleInt}, }, } func TestDecode(t *testing.T) { for i, tt := range decodeTests { if tt.ptr == nil { continue } // v = new(right-type) v := reflect.New(reflect.TypeOf(tt.ptr).Elem()) err := Decode(v.Interface(), tt.in) if !jsonEqual(err, tt.err) { t.Errorf("#%d: got error %v want %v", i, err, tt.err) continue } if tt.err == nil && !jsonEqual(v.Elem().Interface(), tt.out) { t.Errorf("#%d: mismatch\nhave: %+v\nwant: %+v", i, v.Elem().Interface(), tt.out) continue } // Check round trip. if tt.err == nil { enc, err := Encode(v.Interface()) if err != nil { t.Errorf("#%d: error re-marshaling: %v", i, err) continue } vv := reflect.New(reflect.TypeOf(tt.ptr).Elem()) if err := Decode(vv.Interface(), enc); err != nil { t.Errorf("#%d: error re-decodeing: %v", i, err) continue } if !jsonEqual(v.Elem().Interface(), vv.Elem().Interface()) { t.Errorf("#%d: mismatch\nhave: %#+v\nwant: %#+v", i, v.Elem().Interface(), vv.Elem().Interface()) continue } } } } func TestStringKind(t *testing.T) { type aMap map[string]int var m1, m2 map[string]int m1 = map[string]int{ "foo": 42, } data, err := Encode(m1) if err != nil { t.Errorf("Unexpected error encoding: %v", err) } err = Decode(&m2, data) if err != nil { t.Errorf("Unexpected error decoding: %v", err) } if !jsonEqual(m1, m2) { t.Error("Items should be equal after encoding and then decoding") } } // Test handling of unexported fields that should be ignored. type unexportedFields struct { Name string m map[string]interface{} `gorethink:"-"` m2 map[string]interface{} `gorethink:"abcd"` } func TestDecodeUnexported(t *testing.T) { input := map[string]interface{}{ "Name": "Bob", "m": map[string]interface{}{ "x": 123, }, "m2": map[string]interface{}{ "y": 123, }, "abcd": map[string]interface{}{ "z": 789, }, } want := &unexportedFields{Name: "Bob"} out := &unexportedFields{} err := Decode(out, input) if err != nil { t.Errorf("got error %v, expected nil", err) } if !jsonEqual(out, want) { t.Errorf("got %q, want %q", out, want) } } type Foo struct { FooBar interface{} `gorethink:"foobar"` } type Bar struct { Baz int `gorethink:"baz"` } type UnmarshalerPointer struct { Value *UnmarshalerValue } type UnmarshalerValue struct { ValueInt int64 ValueString string } func (v *UnmarshalerValue) MarshalRQL() (interface{}, error) { if v.ValueInt != int64(0) { return Encode(v.ValueInt) } if v.ValueString != "" { return Encode(v.ValueString) } return Encode(nil) } func (v *UnmarshalerValue) UnmarshalRQL(b interface{}) (err error) { n, s := int64(0), "" if err = Decode(&s, b); err == nil { v.ValueString = s return } if err = Decode(&n, b); err == nil { v.ValueInt = n } return } func TestDecodeUnmarshalerPointer(t *testing.T) { input := map[string]interface{}{ "Value": "abc", } want := &UnmarshalerPointer{ Value: &UnmarshalerValue{ValueString: "abc"}, } out := &UnmarshalerPointer{} err := Decode(out, input) if err != nil { t.Errorf("got error %v, expected nil", err) } if !jsonEqual(out, want) { t.Errorf("got %q, want %q", out, want) } } func TestDecodeMapIntKeys(t *testing.T) { input := map[string]int{"1": 1, "2": 2, "3": 3} want := map[int]int{1: 1, 2: 2, 3: 3} out := map[int]int{} err := Decode(&out, input) if err != nil { t.Errorf("got error %v, expected nil", err) } if !jsonEqual(out, want) { t.Errorf("got %q, want %q", out, want) } } func jsonEqual(a, b interface{}) bool { // First check using reflect.DeepEqual if reflect.DeepEqual(a, b) { return true } // Then use jsonEqual ba, err := json.Marshal(a) if err != nil { panic(err) } bb, err := json.Marshal(b) if err != nil { panic(err) } return bytes.Compare(ba, bb) == 0 } gorethink-2.0.4/encoding/decoder_types.go000066400000000000000000000275521272042630000204640ustar00rootroot00000000000000package encoding import ( "bytes" "fmt" "reflect" "strconv" ) // newTypeDecoder constructs an decoderFunc for a type. func newTypeDecoder(dt, st reflect.Type) decoderFunc { if reflect.PtrTo(dt).Implements(unmarshalerType) || dt.Implements(unmarshalerType) { return unmarshalerDecoder } if st.Kind() == reflect.Interface { return interfaceAsTypeDecoder } switch dt.Kind() { case reflect.Bool: switch st.Kind() { case reflect.Bool: return boolAsBoolDecoder case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return intAsBoolDecoder case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return uintAsBoolDecoder case reflect.Float32, reflect.Float64: return floatAsBoolDecoder case reflect.String: return stringAsBoolDecoder default: return decodeTypeError } case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: switch st.Kind() { case reflect.Bool: return boolAsIntDecoder case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return intAsIntDecoder case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return uintAsIntDecoder case reflect.Float32, reflect.Float64: return floatAsIntDecoder case reflect.String: return stringAsIntDecoder default: return decodeTypeError } case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: switch st.Kind() { case reflect.Bool: return boolAsUintDecoder case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return intAsUintDecoder case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return uintAsUintDecoder case reflect.Float32, reflect.Float64: return floatAsUintDecoder case reflect.String: return stringAsUintDecoder default: return decodeTypeError } case reflect.Float32, reflect.Float64: switch st.Kind() { case reflect.Bool: return boolAsFloatDecoder case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return intAsFloatDecoder case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return uintAsFloatDecoder case reflect.Float32, reflect.Float64: return floatAsFloatDecoder case reflect.String: return stringAsFloatDecoder default: return decodeTypeError } case reflect.String: switch st.Kind() { case reflect.Bool: return boolAsStringDecoder case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return intAsStringDecoder case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return uintAsStringDecoder case reflect.Float32, reflect.Float64: return floatAsStringDecoder case reflect.String: return stringAsStringDecoder default: return decodeTypeError } case reflect.Interface: if !st.AssignableTo(dt) { return decodeTypeError } return interfaceDecoder case reflect.Ptr: return newPtrDecoder(dt, st) case reflect.Map: if st.AssignableTo(dt) { return interfaceDecoder } switch st.Kind() { case reflect.Map: return newMapAsMapDecoder(dt, st) default: return decodeTypeError } case reflect.Struct: if st.AssignableTo(dt) { return interfaceDecoder } switch st.Kind() { case reflect.Map: if kind := st.Key().Kind(); kind != reflect.String && kind != reflect.Interface { return newDecodeTypeError(fmt.Errorf("map needs string keys")) } return newMapAsStructDecoder(dt, st) default: return decodeTypeError } case reflect.Slice: if st.AssignableTo(dt) { return interfaceDecoder } switch st.Kind() { case reflect.Array, reflect.Slice: return newSliceDecoder(dt, st) default: return decodeTypeError } case reflect.Array: if st.AssignableTo(dt) { return interfaceDecoder } switch st.Kind() { case reflect.Array, reflect.Slice: return newArrayDecoder(dt, st) default: return decodeTypeError } default: return unsupportedTypeDecoder } } func invalidValueDecoder(dv, sv reflect.Value) { dv.Set(reflect.Zero(dv.Type())) } func unsupportedTypeDecoder(dv, sv reflect.Value) { panic(&UnsupportedTypeError{dv.Type()}) } func decodeTypeError(dv, sv reflect.Value) { panic(&DecodeTypeError{ DestType: dv.Type(), SrcType: sv.Type(), }) } func newDecodeTypeError(err error) decoderFunc { return func(dv, sv reflect.Value) { panic(&DecodeTypeError{ DestType: dv.Type(), SrcType: sv.Type(), Reason: err.Error(), }) } } func interfaceDecoder(dv, sv reflect.Value) { dv.Set(sv) } func interfaceAsTypeDecoder(dv, sv reflect.Value) { if !sv.IsNil() { dv = indirect(dv, false) dv.Set(reflect.Zero(dv.Type())) decode(dv, sv.Elem()) } } type ptrDecoder struct { elemDec decoderFunc } func (d *ptrDecoder) decode(dv, sv reflect.Value) { v := reflect.New(dv.Type().Elem()) d.elemDec(v, sv) dv.Set(v) } func newPtrDecoder(dt, st reflect.Type) decoderFunc { dec := &ptrDecoder{typeDecoder(dt.Elem(), st)} return dec.decode } func unmarshalerDecoder(dv, sv reflect.Value) { // modeled off of https://golang.org/src/encoding/json/decode.go?#L325 if dv.Kind() != reflect.Ptr && dv.Type().Name() != "" && dv.CanAddr() { dv = dv.Addr() } if dv.IsNil() { dv.Set(reflect.New(dv.Type().Elem())) } u := dv.Interface().(Unmarshaler) err := u.UnmarshalRQL(sv.Interface()) if err != nil { panic(&DecodeTypeError{dv.Type(), sv.Type(), err.Error()}) } } // Boolean decoders func boolAsBoolDecoder(dv, sv reflect.Value) { dv.SetBool(sv.Bool()) } func boolAsIntDecoder(dv, sv reflect.Value) { if sv.Bool() { dv.SetInt(1) } else { dv.SetInt(0) } } func boolAsUintDecoder(dv, sv reflect.Value) { if sv.Bool() { dv.SetUint(1) } else { dv.SetUint(0) } } func boolAsFloatDecoder(dv, sv reflect.Value) { if sv.Bool() { dv.SetFloat(1) } else { dv.SetFloat(0) } } func boolAsStringDecoder(dv, sv reflect.Value) { if sv.Bool() { dv.SetString("1") } else { dv.SetString("0") } } // Int decoders func intAsBoolDecoder(dv, sv reflect.Value) { dv.SetBool(sv.Int() != 0) } func intAsIntDecoder(dv, sv reflect.Value) { dv.SetInt(sv.Int()) } func intAsUintDecoder(dv, sv reflect.Value) { dv.SetUint(uint64(sv.Int())) } func intAsFloatDecoder(dv, sv reflect.Value) { dv.SetFloat(float64(sv.Int())) } func intAsStringDecoder(dv, sv reflect.Value) { dv.SetString(strconv.FormatInt(sv.Int(), 10)) } // Uint decoders func uintAsBoolDecoder(dv, sv reflect.Value) { dv.SetBool(sv.Uint() != 0) } func uintAsIntDecoder(dv, sv reflect.Value) { dv.SetInt(int64(sv.Uint())) } func uintAsUintDecoder(dv, sv reflect.Value) { dv.SetUint(sv.Uint()) } func uintAsFloatDecoder(dv, sv reflect.Value) { dv.SetFloat(float64(sv.Uint())) } func uintAsStringDecoder(dv, sv reflect.Value) { dv.SetString(strconv.FormatUint(sv.Uint(), 10)) } // Float decoders func floatAsBoolDecoder(dv, sv reflect.Value) { dv.SetBool(sv.Float() != 0) } func floatAsIntDecoder(dv, sv reflect.Value) { dv.SetInt(int64(sv.Float())) } func floatAsUintDecoder(dv, sv reflect.Value) { dv.SetUint(uint64(sv.Float())) } func floatAsFloatDecoder(dv, sv reflect.Value) { dv.SetFloat(float64(sv.Float())) } func floatAsStringDecoder(dv, sv reflect.Value) { dv.SetString(strconv.FormatFloat(sv.Float(), 'f', -1, 64)) } // String decoders func stringAsBoolDecoder(dv, sv reflect.Value) { b, err := strconv.ParseBool(sv.String()) if err == nil { dv.SetBool(b) } else if sv.String() == "" { dv.SetBool(false) } else { panic(&DecodeTypeError{dv.Type(), sv.Type(), err.Error()}) } } func stringAsIntDecoder(dv, sv reflect.Value) { i, err := strconv.ParseInt(sv.String(), 0, dv.Type().Bits()) if err == nil { dv.SetInt(i) } else { panic(&DecodeTypeError{dv.Type(), sv.Type(), err.Error()}) } } func stringAsUintDecoder(dv, sv reflect.Value) { i, err := strconv.ParseUint(sv.String(), 0, dv.Type().Bits()) if err == nil { dv.SetUint(i) } else { panic(&DecodeTypeError{dv.Type(), sv.Type(), err.Error()}) } } func stringAsFloatDecoder(dv, sv reflect.Value) { f, err := strconv.ParseFloat(sv.String(), dv.Type().Bits()) if err == nil { dv.SetFloat(f) } else { panic(&DecodeTypeError{dv.Type(), sv.Type(), err.Error()}) } } func stringAsStringDecoder(dv, sv reflect.Value) { dv.SetString(sv.String()) } // Slice/Array decoder type sliceDecoder struct { arrayDec decoderFunc } func (d *sliceDecoder) decode(dv, sv reflect.Value) { if dv.Kind() == reflect.Slice { dv.Set(reflect.MakeSlice(dv.Type(), dv.Len(), dv.Cap())) } if !sv.IsNil() { d.arrayDec(dv, sv) } } func newSliceDecoder(dt, st reflect.Type) decoderFunc { // Byte slices get special treatment; arrays don't. // if t.Elem().Kind() == reflect.Uint8 { // return decodeByteSlice // } dec := &sliceDecoder{newArrayDecoder(dt, st)} return dec.decode } type arrayDecoder struct { elemDec decoderFunc } func (d *arrayDecoder) decode(dv, sv reflect.Value) { // Iterate through the slice/array and decode each element before adding it // to the dest slice/array i := 0 for i < sv.Len() { if dv.Kind() == reflect.Slice { // Get element of array, growing if necessary. if i >= dv.Cap() { newcap := dv.Cap() + dv.Cap()/2 if newcap < 4 { newcap = 4 } newdv := reflect.MakeSlice(dv.Type(), dv.Len(), newcap) reflect.Copy(newdv, dv) dv.Set(newdv) } if i >= dv.Len() { dv.SetLen(i + 1) } } if i < dv.Len() { // Decode into element. d.elemDec(dv.Index(i), sv.Index(i)) } i++ } // Ensure that the destination is the correct size if i < dv.Len() { if dv.Kind() == reflect.Array { // Array. Zero the rest. z := reflect.Zero(dv.Type().Elem()) for ; i < dv.Len(); i++ { dv.Index(i).Set(z) } } else { dv.SetLen(i) } } } func newArrayDecoder(dt, st reflect.Type) decoderFunc { dec := &arrayDecoder{typeDecoder(dt.Elem(), st.Elem())} return dec.decode } // Map decoder type mapAsMapDecoder struct { keyDec, elemDec decoderFunc } func (d *mapAsMapDecoder) decode(dv, sv reflect.Value) { dt := dv.Type() dv.Set(reflect.MakeMap(reflect.MapOf(dt.Key(), dt.Elem()))) var mapKey reflect.Value var mapElem reflect.Value keyType := dv.Type().Key() elemType := dv.Type().Elem() for _, sElemKey := range sv.MapKeys() { var dElemKey reflect.Value var dElemVal reflect.Value if !mapKey.IsValid() { mapKey = reflect.New(keyType).Elem() } else { mapKey.Set(reflect.Zero(keyType)) } dElemKey = mapKey if !mapElem.IsValid() { mapElem = reflect.New(elemType).Elem() } else { mapElem.Set(reflect.Zero(elemType)) } dElemVal = mapElem d.keyDec(dElemKey, sElemKey) d.elemDec(dElemVal, sv.MapIndex(sElemKey)) dv.SetMapIndex(dElemKey, dElemVal) } } func newMapAsMapDecoder(dt, st reflect.Type) decoderFunc { d := &mapAsMapDecoder{typeDecoder(dt.Key(), st.Key()), typeDecoder(dt.Elem(), st.Elem())} return d.decode } type mapAsStructDecoder struct { fields []field fieldDecs []decoderFunc } func (d *mapAsStructDecoder) decode(dv, sv reflect.Value) { for _, kv := range sv.MapKeys() { var f *field var fieldDec decoderFunc key := []byte(kv.String()) for i := range d.fields { ff := &d.fields[i] ffd := d.fieldDecs[i] if bytes.Equal(ff.nameBytes, key) { f = ff fieldDec = ffd break } if f == nil && ff.equalFold(ff.nameBytes, key) { f = ff fieldDec = ffd } } if f == nil { continue } dElemVal := fieldByIndex(dv, f.index) sElemVal := sv.MapIndex(kv) if !sElemVal.IsValid() || !dElemVal.CanSet() { continue } fieldDec(dElemVal, sElemVal) } } func newMapAsStructDecoder(dt, st reflect.Type) decoderFunc { fields := cachedTypeFields(dt) se := &mapAsStructDecoder{ fields: fields, fieldDecs: make([]decoderFunc, len(fields)), } for i, f := range fields { se.fieldDecs[i] = typeDecoder(typeByIndex(dt, f.index), st.Elem()) } return se.decode } gorethink-2.0.4/encoding/encoder.go000066400000000000000000000036131272042630000172420ustar00rootroot00000000000000// This code is based on encoding/json and gorilla/schema package encoding import ( "errors" "reflect" "runtime" "sync" ) type encoderFunc func(v reflect.Value) interface{} // Encode returns the encoded value of v. // // Encode traverses the value v recursively and looks for structs. If a struct // is found then it is checked for tagged fields and convert to // map[string]interface{} func Encode(v interface{}) (ev interface{}, err error) { defer func() { if r := recover(); r != nil { if _, ok := r.(runtime.Error); ok { panic(r) } if v, ok := r.(string); ok { err = errors.New(v) } else { err = r.(error) } } }() return encode(reflect.ValueOf(v)), nil } func encode(v reflect.Value) interface{} { return valueEncoder(v)(v) } var encoderCache struct { sync.RWMutex m map[reflect.Type]encoderFunc } func valueEncoder(v reflect.Value) encoderFunc { if !v.IsValid() { return invalidValueEncoder } return typeEncoder(v.Type()) } func typeEncoder(t reflect.Type) encoderFunc { encoderCache.RLock() f := encoderCache.m[t] encoderCache.RUnlock() if f != nil { return f } // To deal with recursive types, populate the map with an // indirect func before we build it. This type waits on the // real func (f) to // be ready and then calls it. This indirect // func is only used for recursive types. encoderCache.Lock() var wg sync.WaitGroup wg.Add(1) encoderCache.m[t] = func(v reflect.Value) interface{} { wg.Wait() return f(v) } encoderCache.Unlock() // Compute fields without lock. // Might duplicate effort but won't hold other computations back. f = newTypeEncoder(t, true) wg.Done() encoderCache.Lock() encoderCache.m[t] = f encoderCache.Unlock() return f } // IgnoreType causes the encoder to ignore a type when encoding func IgnoreType(t reflect.Type) { encoderCache.Lock() encoderCache.m[t] = doNothingEncoder encoderCache.Unlock() } gorethink-2.0.4/encoding/encoder_test.go000066400000000000000000000156701272042630000203070ustar00rootroot00000000000000package encoding import ( "image" "reflect" "testing" "time" ) var encodeExpected = map[string]interface{}{ "Level0": int64(1), "Level1b": int64(2), "Level1c": int64(3), "Level1a": int64(5), "LEVEL1B": int64(6), "e": map[string]interface{}{ "Level1a": int64(8), "Level1b": int64(9), "Level1c": int64(10), "Level1d": int64(11), "x": int64(12), }, "Loop1": int64(13), "Loop2": int64(14), "X": int64(15), "Y": int64(16), "Z": int64(17), } func TestEncode(t *testing.T) { // Top is defined in decoder_test.go var in = Top{ Level0: 1, Embed0: Embed0{ Level1b: 2, Level1c: 3, }, Embed0a: &Embed0a{ Level1a: 5, Level1b: 6, }, Embed0b: &Embed0b{ Level1a: 8, Level1b: 9, Level1c: 10, Level1d: 11, Level1e: 12, }, Loop: Loop{ Loop1: 13, Loop2: 14, }, Embed0p: Embed0p{ Point: image.Point{X: 15, Y: 16}, }, Embed0q: Embed0q{ Point: Point{Z: 17}, }, } got, err := Encode(&in) if err != nil { t.Fatal(err) } if !reflect.DeepEqual(got, encodeExpected) { t.Errorf(" got: %v\nwant: %v\n", got, encodeExpected) } } type Optionals struct { Sr string `gorethink:"sr"` So string `gorethink:"so,omitempty"` Sw string `gorethink:"-"` Ir int `gorethink:"omitempty"` // actually named omitempty, not an option Io int `gorethink:"io,omitempty"` Tr time.Time `gorethink:"tr"` To time.Time `gorethink:"to,omitempty"` Slr []string `gorethink:"slr"` Slo []string `gorethink:"slo,omitempty"` Mr map[string]interface{} `gorethink:"mr"` Mo map[string]interface{} `gorethink:",omitempty"` } var optionalsExpected = map[string]interface{}{ "sr": "", "omitempty": int64(0), "tr": map[string]interface{}{"$reql_type$": "TIME", "epoch_time": 0, "timezone": "+00:00"}, "slr": []interface{}{}, "mr": map[string]interface{}{}, } func TestOmitEmpty(t *testing.T) { var o Optionals o.Sw = "something" o.Tr = time.Unix(0, 0).In(time.UTC) o.Mr = map[string]interface{}{} o.Mo = map[string]interface{}{} got, err := Encode(&o) if err != nil { t.Fatal(err) } if !jsonEqual(got, optionalsExpected) { t.Errorf("\ngot: %#v\nwant: %#v\n", got, optionalsExpected) } } type IntType int type MyStruct struct { IntType } func TestAnonymousNonstruct(t *testing.T) { var i IntType = 11 a := MyStruct{i} var want = map[string]interface{}{"IntType": int64(11)} got, err := Encode(a) if err != nil { t.Fatalf("Encode: %v", err) } if !reflect.DeepEqual(got, want) { t.Errorf("got %v, want %v", got, want) } } func TestEncodePointer(t *testing.T) { v := Pointer{PPoint: &Point{Z: 1}, Point: Point{Z: 2}} var want = map[string]interface{}{ "PPoint": map[string]interface{}{"Z": int64(1)}, "Point": map[string]interface{}{"Z": int64(2)}, } got, err := Encode(v) if err != nil { t.Fatalf("Encode: %v", err) } if !reflect.DeepEqual(got, want) { t.Errorf("got %v, want %v", got, want) } } func TestEncodeNilPointer(t *testing.T) { v := Pointer{PPoint: nil, Point: Point{Z: 2}} var want = map[string]interface{}{ "PPoint": nil, "Point": map[string]interface{}{"Z": int64(2)}, } got, err := Encode(v) if err != nil { t.Fatalf("Encode: %v", err) } if !reflect.DeepEqual(got, want) { t.Errorf("got %v, want %v", got, want) } } type BugA struct { S string } type BugB struct { BugA S string } type BugC struct { S string } // Legal Go: We never use the repeated embedded field (S). type BugX struct { A int BugA BugB } // Issue 5245. func TestEmbeddedBug(t *testing.T) { v := BugB{ BugA{"A"}, "B", } got, err := Encode(v) if err != nil { t.Fatal("Encode:", err) } want := map[string]interface{}{"S": "B"} if !reflect.DeepEqual(got, want) { t.Fatalf("Encode: got %v want %v", got, want) } // Now check that the duplicate field, S, does not appear. x := BugX{ A: 23, } got, err = Encode(x) if err != nil { t.Fatal("Encode:", err) } want = map[string]interface{}{"A": int64(23)} if !reflect.DeepEqual(got, want) { t.Fatalf("Encode: got %v want %v", got, want) } } type BugD struct { // Same as BugA after tagging. XXX string `gorethink:"S"` } // BugD's tagged S field should dominate BugA's. type BugY struct { BugA BugD } // Test that a field with a tag dominates untagged fields. func TestTaggedFieldDominates(t *testing.T) { v := BugY{ BugA{"BugA"}, BugD{"BugD"}, } got, err := Encode(v) if err != nil { t.Fatal("Encode:", err) } want := map[string]interface{}{"S": "BugD"} if !reflect.DeepEqual(got, want) { t.Fatalf("Encode: got %v want %v", got, want) } } // There are no tags here, so S should not appear. type BugZ struct { BugA BugC BugY // Contains a tagged S field through BugD; should not dominate. } func TestDuplicatedFieldDisappears(t *testing.T) { v := BugZ{ BugA{"BugA"}, BugC{"BugC"}, BugY{ BugA{"nested BugA"}, BugD{"nested BugD"}, }, } got, err := Encode(v) if err != nil { t.Fatal("Encode:", err) } want := map[string]interface{}{} if !reflect.DeepEqual(got, want) { t.Fatalf("Encode: got %v want %v", got, want) } } func TestEncodeMapIntKeys(t *testing.T) { input := map[int]int{1: 1, 2: 2, 3: 3} want := map[string]int{"1": 1, "2": 2, "3": 3} out, err := Encode(input) if err != nil { t.Errorf("got error %v, expected nil", err) } if !jsonEqual(out, want) { t.Errorf("got %q, want %q", out, want) } } type RefA struct { ID string `gorethink:"id,omitempty"` B *RefB `gorethink:"b_id,reference" gorethink_ref:"id"` } type RefB struct { ID string `gorethink:"id,omitempty"` Name string `gorethink:"name"` } func TestReferenceField(t *testing.T) { input := RefA{"1", &RefB{"2", "Name"}} want := map[string]interface{}{"id": "1", "b_id": "2"} out, err := Encode(input) if err != nil { t.Errorf("got error %v, expected nil", err) } if !jsonEqual(out, want) { t.Errorf("got %q, want %q", out, want) } } type RefC struct { ID string `gorethink:"id,omitempty"` B *RefB `gorethink:"b_id,reference" gorethink_ref:"b_id"` } func TestReferenceFieldMissing(t *testing.T) { input := RefC{"1", &RefB{"2", "Name"}} _, err := Encode(input) if err == nil { t.Errorf("expected non-nil error but got nil") } } type RefD struct { ID string `gorethink:"id,omitempty"` B string `gorethink:"b_id,reference" gorethink_ref:"b_id"` } func TestReferenceFieldInvalid(t *testing.T) { input := RefD{"1", "B"} _, err := Encode(input) if err == nil { t.Errorf("expected non-nil error but got nil") } } type RefE struct { ID string `gorethink:"id,omitempty"` FIDs *[]RefF `gorethink:"f_ids,reference" gorethink_ref:"id"` } type RefF struct { ID string `gorethink:"id,omitempty"` Name string `gorethink:"name"` } func TestReferenceFieldArray(t *testing.T) { input := RefE{"1", &[]RefF{RefF{"2", "Name2"}, RefF{"3", "Name3"}}} want := map[string]interface{}{"id": "1", "f_ids": []string{"2", "3"}} out, err := Encode(input) if err != nil { t.Errorf("got error %v, expected nil", err) } if !jsonEqual(out, want) { t.Errorf("got %q, want %q", out, want) } } gorethink-2.0.4/encoding/encoder_types.go000066400000000000000000000206371272042630000204730ustar00rootroot00000000000000package encoding import ( "encoding/base64" "fmt" "math" "reflect" "time" ) // newTypeEncoder constructs an encoderFunc for a type. // The returned encoder only checks CanAddr when allowAddr is true. func newTypeEncoder(t reflect.Type, allowAddr bool) encoderFunc { if t.Implements(marshalerType) { return marshalerEncoder } if t.Kind() != reflect.Ptr && allowAddr { if reflect.PtrTo(t).Implements(marshalerType) { return newCondAddrEncoder(addrMarshalerEncoder, newTypeEncoder(t, false)) } } // Check for psuedo-types first switch t { case timeType: return timePseudoTypeEncoder } switch t.Kind() { case reflect.Bool: return boolEncoder case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return intEncoder case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return uintEncoder case reflect.Float32, reflect.Float64: return floatEncoder case reflect.String: return stringEncoder case reflect.Interface: return interfaceEncoder case reflect.Struct: return newStructEncoder(t) case reflect.Map: return newMapEncoder(t) case reflect.Slice: return newSliceEncoder(t) case reflect.Array: return newArrayEncoder(t) case reflect.Ptr: return newPtrEncoder(t) case reflect.Func: // functions are a special case as they can be used internally for // optional arguments. Just return the raw function, if somebody tries // to pass a function to the database the JSON marshaller will catch this // anyway. return funcEncoder default: return unsupportedTypeEncoder } } func invalidValueEncoder(v reflect.Value) interface{} { return nil } func doNothingEncoder(v reflect.Value) interface{} { return v.Interface() } func marshalerEncoder(v reflect.Value) interface{} { if v.Kind() == reflect.Ptr && v.IsNil() { return nil } m := v.Interface().(Marshaler) ev, err := m.MarshalRQL() if err != nil { panic(&MarshalerError{v.Type(), err}) } return ev } func addrMarshalerEncoder(v reflect.Value) interface{} { va := v.Addr() if va.IsNil() { return nil } m := va.Interface().(Marshaler) ev, err := m.MarshalRQL() if err != nil { panic(&MarshalerError{v.Type(), err}) } return ev } func boolEncoder(v reflect.Value) interface{} { if v.Bool() { return true } else { return false } } func intEncoder(v reflect.Value) interface{} { return v.Int() } func uintEncoder(v reflect.Value) interface{} { return v.Uint() } func floatEncoder(v reflect.Value) interface{} { return v.Float() } func stringEncoder(v reflect.Value) interface{} { return v.String() } func interfaceEncoder(v reflect.Value) interface{} { if v.IsNil() { return nil } return encode(v.Elem()) } func funcEncoder(v reflect.Value) interface{} { if v.IsNil() { return nil } return v.Interface() } func asStringEncoder(v reflect.Value) interface{} { return fmt.Sprintf("%v", v.Interface()) } func unsupportedTypeEncoder(v reflect.Value) interface{} { panic(&UnsupportedTypeError{v.Type()}) } type structEncoder struct { fields []field fieldEncs []encoderFunc } func (se *structEncoder) encode(v reflect.Value) interface{} { m := make(map[string]interface{}) for i, f := range se.fields { fv := fieldByIndex(v, f.index) if !fv.IsValid() || f.omitEmpty && se.isEmptyValue(fv) { continue } encField := se.fieldEncs[i](fv) // If this field is a referenced field then attempt to extract the value. if f.reference { // Override the encoded field with the referenced field encField = getReferenceField(f, v, encField) } m[f.name] = encField } return m } func getReferenceField(f field, v reflect.Value, encField interface{}) interface{} { refName := f.name if f.refName != "" { refName = f.refName } encFields, isArray := encField.([]interface{}) if isArray { refVals := make([]interface{}, len(encFields)) for i, e := range encFields { refVals[i] = extractValue(e, v, f.name, refName) } return refVals } refVal := extractValue(encField, v, f.name, refName) return refVal } func extractValue(encField interface{}, v reflect.Value, name string, refName string) interface{} { // referenced fields can only handle maps so return an error if the // encoded field is of a different type m, ok := encField.(map[string]interface{}) if !ok { err := fmt.Errorf("Error refing field %s in %s, expected object but got %t", refName, name, encField) panic(&MarshalerError{v.Type(), err}) } refVal, ok := m[refName] if !ok { err := fmt.Errorf("Error refing field %s in %s, could not find referenced field", refName, name) panic(&MarshalerError{v.Type(), err}) } return refVal } func (se *structEncoder) isEmptyValue(v reflect.Value) bool { if v.Type() == timeType { return v.Interface().(time.Time) == time.Time{} } return isEmptyValue(v) } func newStructEncoder(t reflect.Type) encoderFunc { fields := cachedTypeFields(t) se := &structEncoder{ fields: fields, fieldEncs: make([]encoderFunc, len(fields)), } for i, f := range fields { se.fieldEncs[i] = typeEncoder(typeByIndex(t, f.index)) } return se.encode } type mapEncoder struct { keyEnc, elemEnc encoderFunc } func (me *mapEncoder) encode(v reflect.Value) interface{} { if v.IsNil() { return nil } m := make(map[string]interface{}) for _, k := range v.MapKeys() { m[me.keyEnc(k).(string)] = me.elemEnc(v.MapIndex(k)) } return m } func newMapEncoder(t reflect.Type) encoderFunc { var keyEnc encoderFunc switch t.Key().Kind() { case reflect.Bool: keyEnc = asStringEncoder case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: keyEnc = asStringEncoder case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: keyEnc = asStringEncoder case reflect.Float32, reflect.Float64: keyEnc = asStringEncoder case reflect.String: keyEnc = stringEncoder case reflect.Interface: keyEnc = asStringEncoder default: return unsupportedTypeEncoder } me := &mapEncoder{keyEnc, typeEncoder(t.Elem())} return me.encode } // sliceEncoder just wraps an arrayEncoder, checking to make sure the value isn't nil. type sliceEncoder struct { arrayEnc encoderFunc } func (se *sliceEncoder) encode(v reflect.Value) interface{} { if v.IsNil() { return []interface{}{} } return se.arrayEnc(v) } func newSliceEncoder(t reflect.Type) encoderFunc { // Byte slices get special treatment; arrays don't. if t.Elem().Kind() == reflect.Uint8 { return encodeByteSlice } enc := &sliceEncoder{newArrayEncoder(t)} return enc.encode } type arrayEncoder struct { elemEnc encoderFunc } func (ae *arrayEncoder) encode(v reflect.Value) interface{} { n := v.Len() a := make([]interface{}, n) for i := 0; i < n; i++ { a[i] = ae.elemEnc(v.Index(i)) } return a } func newArrayEncoder(t reflect.Type) encoderFunc { enc := &arrayEncoder{typeEncoder(t.Elem())} return enc.encode } type ptrEncoder struct { elemEnc encoderFunc } func (pe *ptrEncoder) encode(v reflect.Value) interface{} { if v.IsNil() { return nil } return pe.elemEnc(v.Elem()) } func newPtrEncoder(t reflect.Type) encoderFunc { enc := &ptrEncoder{typeEncoder(t.Elem())} return enc.encode } type condAddrEncoder struct { canAddrEnc, elseEnc encoderFunc } func (ce *condAddrEncoder) encode(v reflect.Value) interface{} { if v.CanAddr() { return ce.canAddrEnc(v) } else { return ce.elseEnc(v) } } // newCondAddrEncoder returns an encoder that checks whether its value // CanAddr and delegates to canAddrEnc if so, else to elseEnc. func newCondAddrEncoder(canAddrEnc, elseEnc encoderFunc) encoderFunc { enc := &condAddrEncoder{canAddrEnc: canAddrEnc, elseEnc: elseEnc} return enc.encode } // Pseudo-type encoders // Encode a time.Time value to the TIME RQL type func timePseudoTypeEncoder(v reflect.Value) interface{} { t := v.Interface().(time.Time) timeVal := float64(t.UnixNano()) / float64(time.Second) // use seconds-since-epoch precision if time.Time `t` // is before the oldest nanosecond time if t.Before(time.Unix(0, math.MinInt64)) { timeVal = float64(t.Unix()) } return map[string]interface{}{ "$reql_type$": "TIME", "epoch_time": timeVal, "timezone": t.Format("-07:00"), } } // Encode a byte slice to the BINARY RQL type func encodeByteSlice(v reflect.Value) interface{} { var b []byte if !v.IsNil() { b = v.Bytes() } dst := make([]byte, base64.StdEncoding.EncodedLen(len(b))) base64.StdEncoding.Encode(dst, b) return map[string]interface{}{ "$reql_type$": "BINARY", "data": string(dst), } } gorethink-2.0.4/encoding/encoding.go000066400000000000000000000013711272042630000174100ustar00rootroot00000000000000package encoding import ( "reflect" "time" ) var ( // type constants stringType = reflect.TypeOf("") timeType = reflect.TypeOf(new(time.Time)).Elem() marshalerType = reflect.TypeOf(new(Marshaler)).Elem() unmarshalerType = reflect.TypeOf(new(Unmarshaler)).Elem() ) // Marshaler is the interface implemented by objects that // can marshal themselves into a valid RQL psuedo-type. type Marshaler interface { MarshalRQL() (interface{}, error) } // Unmarshaler is the interface implemented by objects // that can unmarshal a psuedo-type object of themselves. type Unmarshaler interface { UnmarshalRQL(interface{}) error } func init() { encoderCache.m = make(map[reflect.Type]encoderFunc) decoderCache.m = make(map[decoderCacheKey]decoderFunc) } gorethink-2.0.4/encoding/encoding_test.go000066400000000000000000000001531272042630000204440ustar00rootroot00000000000000package encoding import ( // Import gocheck so that flag are setup (but unused) _ "gopkg.in/check.v1" ) gorethink-2.0.4/encoding/errors.go000066400000000000000000000047011272042630000171360ustar00rootroot00000000000000package encoding import ( "fmt" "reflect" "strings" ) type MarshalerError struct { Type reflect.Type Err error } func (e *MarshalerError) Error() string { return "gorethink: error calling MarshalRQL for type " + e.Type.String() + ": " + e.Err.Error() } type InvalidUnmarshalError struct { Type reflect.Type } func (e *InvalidUnmarshalError) Error() string { if e.Type == nil { return "gorethink: UnmarshalRQL(nil)" } if e.Type.Kind() != reflect.Ptr { return "gorethink: UnmarshalRQL(non-pointer " + e.Type.String() + ")" } return "gorethink: UnmarshalRQL(nil " + e.Type.String() + ")" } // An InvalidTypeError describes a value that was // not appropriate for a value of a specific Go type. type DecodeTypeError struct { DestType, SrcType reflect.Type Reason string } func (e *DecodeTypeError) Error() string { if e.Reason != "" { return "gorethink: could not decode type " + e.SrcType.String() + " into Go value of type " + e.DestType.String() + ": " + e.Reason } else { return "gorethink: could not decode type " + e.SrcType.String() + " into Go value of type " + e.DestType.String() } } // An UnsupportedTypeError is returned by Marshal when attempting // to encode an unsupported value type. type UnsupportedTypeError struct { Type reflect.Type } func (e *UnsupportedTypeError) Error() string { return "gorethink: unsupported type: " + e.Type.String() } // An UnsupportedTypeError is returned by Marshal when attempting // to encode an unexpected value type. type UnexpectedTypeError struct { DestType, SrcType reflect.Type } func (e *UnexpectedTypeError) Error() string { return "gorethink: expected type: " + e.DestType.String() + ", got " + e.SrcType.String() } type UnsupportedValueError struct { Value reflect.Value Str string } func (e *UnsupportedValueError) Error() string { return "gorethink: unsupported value: " + e.Str } // Error implements the error interface and can represents multiple // errors that occur in the course of a single decode. type Error struct { Errors []string } func (e *Error) Error() string { points := make([]string, len(e.Errors)) for i, err := range e.Errors { points[i] = fmt.Sprintf("* %s", err) } return fmt.Sprintf( "%d error(s) decoding:\n\n%s", len(e.Errors), strings.Join(points, "\n")) } func appendErrors(errors []string, err error) []string { switch e := err.(type) { case *Error: return append(errors, e.Errors...) default: return append(errors, e.Error()) } } gorethink-2.0.4/encoding/fold.go000066400000000000000000000063621272042630000165530ustar00rootroot00000000000000package encoding import ( "bytes" "unicode/utf8" ) const ( caseMask = ^byte(0x20) // Mask to ignore case in ASCII. kelvin = '\u212a' smallLongEss = '\u017f' ) // foldFunc returns one of four different case folding equivalence // functions, from most general (and slow) to fastest: // // 1) bytes.EqualFold, if the key s contains any non-ASCII UTF-8 // 2) equalFoldRight, if s contains special folding ASCII ('k', 'K', 's', 'S') // 3) asciiEqualFold, no special, but includes non-letters (including _) // 4) simpleLetterEqualFold, no specials, no non-letters. // // The letters S and K are special because they map to 3 runes, not just 2: // * S maps to s and to U+017F 'ſ' Latin small letter long s // * k maps to K and to U+212A 'K' Kelvin sign // See http://play.golang.org/p/tTxjOc0OGo // // The returned function is specialized for matching against s and // should only be given s. It's not curried for performance reasons. func foldFunc(s []byte) func(s, t []byte) bool { nonLetter := false special := false // special letter for _, b := range s { if b >= utf8.RuneSelf { return bytes.EqualFold } upper := b & caseMask if upper < 'A' || upper > 'Z' { nonLetter = true } else if upper == 'K' || upper == 'S' { // See above for why these letters are special. special = true } } if special { return equalFoldRight } if nonLetter { return asciiEqualFold } return simpleLetterEqualFold } // equalFoldRight is a specialization of bytes.EqualFold when s is // known to be all ASCII (including punctuation), but contains an 's', // 'S', 'k', or 'K', requiring a Unicode fold on the bytes in t. // See comments on foldFunc. func equalFoldRight(s, t []byte) bool { for _, sb := range s { if len(t) == 0 { return false } tb := t[0] if tb < utf8.RuneSelf { if sb != tb { sbUpper := sb & caseMask if 'A' <= sbUpper && sbUpper <= 'Z' { if sbUpper != tb&caseMask { return false } } else { return false } } t = t[1:] continue } // sb is ASCII and t is not. t must be either kelvin // sign or long s; sb must be s, S, k, or K. tr, size := utf8.DecodeRune(t) switch sb { case 's', 'S': if tr != smallLongEss { return false } case 'k', 'K': if tr != kelvin { return false } default: return false } t = t[size:] } if len(t) > 0 { return false } return true } // asciiEqualFold is a specialization of bytes.EqualFold for use when // s is all ASCII (but may contain non-letters) and contains no // special-folding letters. // See comments on foldFunc. func asciiEqualFold(s, t []byte) bool { if len(s) != len(t) { return false } for i, sb := range s { tb := t[i] if sb == tb { continue } if ('a' <= sb && sb <= 'z') || ('A' <= sb && sb <= 'Z') { if sb&caseMask != tb&caseMask { return false } } else { return false } } return true } // simpleLetterEqualFold is a specialization of bytes.EqualFold for // use when s is all ASCII letters (no underscores, etc) and also // doesn't contain 'k', 'K', 's', or 'S'. // See comments on foldFunc. func simpleLetterEqualFold(s, t []byte) bool { if len(s) != len(t) { return false } for i, b := range s { if b&caseMask != t[i]&caseMask { return false } } return true } gorethink-2.0.4/encoding/tags.go000066400000000000000000000034061272042630000165610ustar00rootroot00000000000000// This code is based on encoding/json and gorilla/schema package encoding import ( "reflect" "strings" "unicode" ) var ( Tags []string ) const ( TagName = "gorethink" JSONTagName = "json" RefTagName = "gorethink_ref" ) // tagOptions is the string following a comma in a struct field's // tag, or the empty string. It does not include the leading comma. type tagOptions string func getTag(sf reflect.StructField) string { if Tags == nil { return sf.Tag.Get(TagName) } for _, tagName := range Tags { if tag := sf.Tag.Get(tagName); tag != "" { return tag } } return "" } func getRefTag(sf reflect.StructField) string { return sf.Tag.Get(RefTagName) } // parseTag splits a struct field's tag into its name and // comma-separated options. func parseTag(tag string) (string, tagOptions) { if idx := strings.Index(tag, ","); idx != -1 { return tag[:idx], tagOptions(tag[idx+1:]) } return tag, tagOptions("") } func isValidTag(s string) bool { if s == "" { return false } for _, c := range s { switch { case strings.ContainsRune("!#$%&()*+-./:<=>?@[]^_{|}~ ", c): // Backslash and quote chars are reserved, but // otherwise any punctuation chars are allowed // in a tag name. default: if !unicode.IsLetter(c) && !unicode.IsDigit(c) { return false } } } return true } // Contains returns whether checks that a comma-separated list of options // contains a particular substr flag. substr must be surrounded by a // string boundary or commas. func (o tagOptions) Contains(optionName string) bool { if len(o) == 0 { return false } s := string(o) for s != "" { var next string i := strings.Index(s, ",") if i >= 0 { s, next = s[:i], s[i+1:] } if s == optionName { return true } s = next } return false } gorethink-2.0.4/encoding/utils.go000066400000000000000000000032341272042630000167620ustar00rootroot00000000000000package encoding import "reflect" func getTypeKind(t reflect.Type) reflect.Kind { kind := t.Kind() switch { case kind >= reflect.Int && kind <= reflect.Int64: return reflect.Int case kind >= reflect.Uint && kind <= reflect.Uint64: return reflect.Uint case kind >= reflect.Float32 && kind <= reflect.Float64: return reflect.Float32 default: return kind } } func isEmptyValue(v reflect.Value) bool { switch v.Kind() { case reflect.Array, reflect.Map, reflect.Slice, reflect.String: return v.Len() == 0 case reflect.Bool: return !v.Bool() case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return v.Int() == 0 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return v.Uint() == 0 case reflect.Float32, reflect.Float64: return v.Float() == 0 case reflect.Interface, reflect.Ptr: return v.IsNil() } return false } func fieldByIndex(v reflect.Value, index []int) reflect.Value { for _, i := range index { if v.Kind() == reflect.Ptr { if v.IsNil() { v.Set(reflect.New(v.Type().Elem())) } v = v.Elem() } v = v.Field(i) } return v } func typeByIndex(t reflect.Type, index []int) reflect.Type { for _, i := range index { if t.Kind() == reflect.Ptr { t = t.Elem() } t = t.Field(i).Type } return t } // valueByString sorts reflect.Value by the string value, this is useful for // sorting the result of MapKeys type valueByString []reflect.Value func (x valueByString) Len() int { return len(x) } func (x valueByString) Swap(i, j int) { x[i], x[j] = x[j], x[i] } func (x valueByString) Less(i, j int) bool { return x[i].String() < x[j].String() } gorethink-2.0.4/errors.go000066400000000000000000000114551272042630000153540ustar00rootroot00000000000000package gorethink import ( "bytes" "encoding/json" "errors" "fmt" "strings" p "gopkg.in/dancannon/gorethink.v2/ql2" ) var ( // ErrNoHosts is returned when no hosts to the Connect method. ErrNoHosts = errors.New("no hosts provided") // ErrNoConnectionsStarted is returned when the driver couldn't to any of // the provided hosts. ErrNoConnectionsStarted = errors.New("no connections were made when creating the session") // ErrInvalidNode is returned when attempting to connect to a node which // returns an invalid response. ErrInvalidNode = errors.New("invalid node") // ErrNoConnections is returned when there are no active connections in the // clusters connection pool. ErrNoConnections = errors.New("gorethink: no connections were available") // ErrConnectionClosed is returned when trying to send a query with a closed // connection. ErrConnectionClosed = errors.New("gorethink: the connection is closed") ) func printCarrots(t Term, frames []*p.Frame) string { var frame *p.Frame if len(frames) > 1 { frame, frames = frames[0], frames[1:] } else if len(frames) == 1 { frame, frames = frames[0], []*p.Frame{} } for i, arg := range t.args { if frame.GetPos() == int64(i) { t.args[i] = Term{ termType: p.Term_DATUM, data: printCarrots(arg, frames), } } } for k, arg := range t.optArgs { if frame.GetOpt() == k { t.optArgs[k] = Term{ termType: p.Term_DATUM, data: printCarrots(arg, frames), } } } b := &bytes.Buffer{} for _, c := range t.String() { if c != '^' { b.WriteString(" ") } else { b.WriteString("^") } } return b.String() } // Error constants var ErrEmptyResult = errors.New("The result does not contain any more rows") // Connection/Response errors // rqlResponseError is the base type for all errors, it formats both // for the response and query if set. type rqlServerError struct { response *Response term *Term } func (e rqlServerError) Error() string { var err = "An error occurred" if e.response != nil { json.Unmarshal(e.response.Responses[0], &err) } if e.term == nil { return fmt.Sprintf("gorethink: %s", err) } return fmt.Sprintf("gorethink: %s in: \n%s", err, e.term.String()) } func (e rqlServerError) String() string { return e.Error() } type rqlError string func (e rqlError) Error() string { return fmt.Sprintf("gorethink: %s", string(e)) } func (e rqlError) String() string { return e.Error() } // Exported Error "Implementations" type RQLClientError struct{ rqlServerError } type RQLCompileError struct{ rqlServerError } type RQLDriverCompileError struct{ RQLCompileError } type RQLServerCompileError struct{ RQLCompileError } type RQLAuthError struct{ RQLDriverError } type RQLRuntimeError struct{ rqlServerError } type RQLQueryLogicError struct{ RQLRuntimeError } type RQLNonExistenceError struct{ RQLQueryLogicError } type RQLResourceLimitError struct{ RQLRuntimeError } type RQLUserError struct{ RQLRuntimeError } type RQLInternalError struct{ RQLRuntimeError } type RQLTimeoutError struct{ rqlServerError } type RQLAvailabilityError struct{ RQLRuntimeError } type RQLOpFailedError struct{ RQLAvailabilityError } type RQLOpIndeterminateError struct{ RQLAvailabilityError } // RQLDriverError represents an unexpected error with the driver, if this error // persists please create an issue. type RQLDriverError struct { rqlError } // RQLConnectionError represents an error when communicating with the database // server. type RQLConnectionError struct { rqlError } func createRuntimeError(errorType p.Response_ErrorType, response *Response, term *Term) error { serverErr := rqlServerError{response, term} switch errorType { case p.Response_QUERY_LOGIC: return RQLQueryLogicError{RQLRuntimeError{serverErr}} case p.Response_NON_EXISTENCE: return RQLNonExistenceError{RQLQueryLogicError{RQLRuntimeError{serverErr}}} case p.Response_RESOURCE_LIMIT: return RQLResourceLimitError{RQLRuntimeError{serverErr}} case p.Response_USER: return RQLUserError{RQLRuntimeError{serverErr}} case p.Response_INTERNAL: return RQLInternalError{RQLRuntimeError{serverErr}} case p.Response_OP_FAILED: return RQLOpFailedError{RQLAvailabilityError{RQLRuntimeError{serverErr}}} case p.Response_OP_INDETERMINATE: return RQLOpIndeterminateError{RQLAvailabilityError{RQLRuntimeError{serverErr}}} default: return RQLRuntimeError{serverErr} } } // Error type helpers // IsConflictErr returns true if the error is non-nil and the query failed // due to a duplicate primary key. func IsConflictErr(err error) bool { if err == nil { return false } return strings.HasPrefix(err.Error(), "Duplicate primary key") } // IsTypeErr returns true if the error is non-nil and the query failed due // to a type error. func IsTypeErr(err error) bool { if err == nil { return false } return strings.HasPrefix(err.Error(), "Expected type") } gorethink-2.0.4/example_query_aggregation_test.go000066400000000000000000000044761272042630000223330ustar00rootroot00000000000000package gorethink import ( "fmt" ) // Group games by player. func ExampleTerm_Group() { cur, err := DB("examples").Table("games").Group("player").Run(session) if err != nil { fmt.Print(err) return } var res []interface{} err = cur.All(&res) if err != nil { fmt.Print(err) return } fmt.Print(res) } // Group games by the index type. func ExampleTerm_GroupByIndex() { cur, err := DB("examples").Table("games").GroupByIndex("type").Run(session) if err != nil { fmt.Print(err) return } var res []interface{} err = cur.All(&res) if err != nil { fmt.Print(err) return } fmt.Print(res) } // Suppose that the table games2 has the following data: // // [ // { id: 1, matches: {'a': [1, 2, 3], 'b': [4, 5, 6]} }, // { id: 2, matches: {'b': [100], 'c': [7, 8, 9]} }, // { id: 3, matches: {'a': [10, 20], 'c': [70, 80]} } // ] // Using MultiGroup we can group data by match A, B or C. func ExampleTerm_MultiGroup() { cur, err := DB("examples").Table("games2").MultiGroup(Row.Field("matches").Keys()).Run(session) if err != nil { fmt.Print(err) return } var res []interface{} err = cur.All(&res) if err != nil { fmt.Print(err) return } fmt.Print(res) } // Ungrouping grouped data. func ExampleTerm_Ungroup() { cur, err := DB("examples").Table("games"). Group("player"). Max("points").Field("points"). Ungroup(). Run(session) if err != nil { fmt.Print(err) return } var res []interface{} err = cur.All(&res) if err != nil { fmt.Print(err) return } fmt.Print(res) } // Return the number of documents in the table posts. func ExampleTerm_Reduce() { cur, err := DB("examples").Table("posts"). Map(func(doc Term) interface{} { return 1 }). Reduce(func(left, right Term) interface{} { return left.Add(right) }). Run(session) if err != nil { fmt.Print(err) return } var res int err = cur.One(&res) if err != nil { fmt.Print(err) return } fmt.Print(res) } // Concatenate words from a list. func ExampleTerm_Fold() { cur, err := Expr([]string{"a", "b", "c"}).Fold("", func(acc, word Term) Term { return acc.Add(Branch(acc.Eq(""), "", ", ")).Add(word) }).Run(session) if err != nil { fmt.Print(err) return } var res string err = cur.One(&res) if err != nil { fmt.Print(err) return } fmt.Print(res) // Output: // a, b, c } gorethink-2.0.4/example_query_control_test.go000066400000000000000000000071761272042630000215240ustar00rootroot00000000000000package gorethink import ( "fmt" ) // Return heroes and superheroes. func ExampleBranch() { cur, err := DB("examples").Table("marvel").OrderBy("name").Map(Branch( Row.Field("victories").Gt(100), Row.Field("name").Add(" is a superhero"), Row.Field("name").Add(" is a hero"), )).Run(session) if err != nil { fmt.Print(err) return } var strs []string err = cur.All(&strs) if err != nil { fmt.Print(err) return } for _, str := range strs { fmt.Println(str) } // Output: // Iron Man is a superhero // Jubilee is a hero } // Return an error func ExampleError() { err := Error("this is a runtime error").Exec(session) fmt.Println(err) } // Suppose we want to retrieve the titles and authors of the table posts. In the // case where the author field is missing or null, we want to retrieve the // string "Anonymous". func ExampleTerm_Default() { cur, err := DB("examples").Table("posts").Map(map[string]interface{}{ "title": Row.Field("title"), "author": Row.Field("author").Default("Anonymous"), }).Run(session) if err != nil { fmt.Print(err) return } var res map[string]interface{} err = cur.One(&res) if err != nil { fmt.Print(err) return } fmt.Print(res) } // Convert a Go integer to a ReQL object func ExampleExpr_int() { cur, err := Expr(1).Run(session) if err != nil { fmt.Print(err) return } var res interface{} err = cur.One(&res) if err != nil { fmt.Print(err) return } jsonPrint(res) // Output: 1 } // Convert a Go slice to a ReQL object func ExampleExpr_slice() { cur, err := Expr([]int{1, 2, 3}).Run(session) if err != nil { fmt.Print(err) return } var res []interface{} err = cur.All(&res) if err != nil { fmt.Print(err) return } jsonPrint(res) // Output: // [ // 1, // 2, // 3 // ] } // Convert a Go slice to a ReQL object func ExampleExpr_map() { cur, err := Expr(map[string]interface{}{ "a": 1, "b": "b", }).Run(session) if err != nil { fmt.Print(err) return } var res interface{} err = cur.One(&res) if err != nil { fmt.Print(err) return } jsonPrint(res) // Output: // { // "a": 1, // "b": "b" // } } // Convert a Go slice to a ReQL object func ExampleExpr_struct() { type ExampleTypeNested struct { N int } type ExampleTypeEmbed struct { C string } type ExampleTypeA struct { ExampleTypeEmbed A int B string Nested ExampleTypeNested } cur, err := Expr(ExampleTypeA{ A: 1, B: "b", ExampleTypeEmbed: ExampleTypeEmbed{ C: "c", }, Nested: ExampleTypeNested{ N: 2, }, }).Run(session) if err != nil { fmt.Print(err) return } var res interface{} err = cur.One(&res) if err != nil { fmt.Print(err) return } jsonPrint(res) // Output: // { // "A": 1, // "B": "b", // "C": "c", // "Nested": { // "N": 2 // } // } } // Convert a Go struct (with gorethink tags) to a ReQL object. The tags allow // the field names to be changed. func ExampleExpr_structTags() { type ExampleType struct { A int `gorethink:"field_a"` B string `gorethink:"field_b"` } cur, err := Expr(ExampleType{ A: 1, B: "b", }).Run(session) if err != nil { fmt.Print(err) return } var res interface{} err = cur.One(&res) if err != nil { fmt.Print(err) return } jsonPrint(res) // Output: // { // "field_a": 1, // "field_b": "b" // } } // Execute a raw JSON query func ExampleRawQuery() { cur, err := RawQuery([]byte(`"hello world"`)).Run(session) if err != nil { fmt.Print(err) return } var res interface{} err = cur.One(&res) if err != nil { fmt.Print(err) return } jsonPrint(res) // Output: "hello world" } gorethink-2.0.4/example_query_db_test.go000066400000000000000000000013661272042630000204240ustar00rootroot00000000000000package gorethink import ( "fmt" ) // Create a database named ’superheroes’. func ExampleDBCreate() { resp, err := DBCreate("superheroes").RunWrite(session) if err != nil { fmt.Print(err) } fmt.Printf("%d DB created", resp.DBsCreated) // Output: // 1 DB created } // Drop a database named ‘superheroes’. func ExampleDBDrop() { // Setup database + tables DBCreate("superheroes").Exec(session) DB("superheroes").TableCreate("superheroes").Exec(session) DB("superheroes").TableCreate("battles").Exec(session) resp, err := DBDrop("superheroes").RunWrite(session) if err != nil { fmt.Print(err) } fmt.Printf("%d DB dropped, %d tables dropped", resp.DBsDropped, resp.TablesDropped) // Output: // 1 DB dropped, 2 tables dropped } gorethink-2.0.4/example_query_manipulation_test.go000066400000000000000000000005061272042630000225320ustar00rootroot00000000000000package gorethink import ( "fmt" ) // Get john's age func ExampleTerm_Field() { cur, err := DB("examples").Table("users").Get("john").Field("age").Run(session) if err != nil { fmt.Print(err) return } var res int err = cur.One(&res) if err != nil { fmt.Print(err) return } fmt.Print(res) // Output: 19 } gorethink-2.0.4/example_query_select_test.go000066400000000000000000000101241272042630000213060ustar00rootroot00000000000000package gorethink import ( "fmt" ) // Find a document by ID. func ExampleTerm_Get() { // Fetch the row from the database res, err := DB("examples").Table("heroes").Get(2).Run(session) if err != nil { fmt.Print(err) return } defer res.Close() if res.IsNil() { fmt.Print("Row not found") return } var hero map[string]interface{} err = res.One(&hero) if err != nil { fmt.Printf("Error scanning database result: %s", err) return } fmt.Print(hero["name"]) // Output: Superman } // Find a document by ID. func ExampleTerm_GetAll() { // Fetch the row from the database res, err := DB("examples").Table("heroes").GetAll(2).Run(session) if err != nil { fmt.Print(err) return } defer res.Close() if res.IsNil() { fmt.Print("Row not found") return } var hero map[string]interface{} err = res.One(&hero) if err != nil { fmt.Printf("Error scanning database result: %s", err) return } fmt.Print(hero["name"]) // Output: Superman } // Find a document by ID. func ExampleTerm_GetAll_multiple() { // Fetch the row from the database res, err := DB("examples").Table("heroes").GetAll(1, 2).Run(session) if err != nil { fmt.Print(err) return } defer res.Close() var heroes []map[string]interface{} err = res.All(&heroes) if err != nil { fmt.Printf("Error scanning database result: %s", err) return } fmt.Print(heroes[0]["name"]) // Output: Superman } // Find all document with an indexed value. func ExampleTerm_GetAllByIndex() { // Fetch the row from the database res, err := DB("examples").Table("heroes").GetAllByIndex("code_name", "man_of_steel").Run(session) if err != nil { fmt.Print(err) return } defer res.Close() if res.IsNil() { fmt.Print("Row not found") return } var hero map[string]interface{} err = res.One(&hero) if err != nil { fmt.Printf("Error scanning database result: %s", err) return } fmt.Print(hero["name"]) // Output: Superman } // Find a document and merge another document with it. func ExampleTerm_Get_merge() { // Fetch the row from the database res, err := DB("examples").Table("heroes").Get(4).Merge(map[string]interface{}{ "powers": []string{"speed"}, }).Run(session) if err != nil { fmt.Print(err) return } defer res.Close() if res.IsNil() { fmt.Print("Row not found") return } var hero map[string]interface{} err = res.One(&hero) if err != nil { fmt.Printf("Error scanning database result: %s", err) return } fmt.Printf("%s: %v", hero["name"], hero["powers"]) // Output: The Flash: [speed] } // Get all users who are 30 years old. func ExampleTerm_Filter() { // Fetch the row from the database res, err := DB("examples").Table("users").Filter(map[string]interface{}{ "age": 30, }).Run(session) if err != nil { fmt.Print(err) return } defer res.Close() // Scan query result into the person variable var users []interface{} err = res.All(&users) if err != nil { fmt.Printf("Error scanning database result: %s", err) return } fmt.Printf("%d users", len(users)) // Output: 2 users } // Get all users who are more than 25 years old. func ExampleTerm_Filter_row() { // Fetch the row from the database res, err := DB("examples").Table("users").Filter(Row.Field("age").Gt(25)).Run(session) if err != nil { fmt.Print(err) return } defer res.Close() // Scan query result into the person variable var users []interface{} err = res.All(&users) if err != nil { fmt.Printf("Error scanning database result: %s", err) return } fmt.Printf("%d users", len(users)) // Output: 3 users } // Retrieve all users who have a gmail account (whose field email ends with @gmail.com). func ExampleTerm_Filter_function() { // Fetch the row from the database res, err := DB("examples").Table("users").Filter(func(user Term) Term { return user.Field("email").Match("@gmail.com$") }).Run(session) if err != nil { fmt.Print(err) return } defer res.Close() // Scan query result into the person variable var users []interface{} err = res.All(&users) if err != nil { fmt.Printf("Error scanning database result: %s", err) return } fmt.Printf("%d users", len(users)) // Output: 1 users } gorethink-2.0.4/example_query_table_test.go000066400000000000000000000026561272042630000211310ustar00rootroot00000000000000package gorethink import ( "fmt" ) // Create a table named "table" with the default settings. func ExampleTerm_TableCreate() { // Setup database DB("examples").TableDrop("table").Run(session) response, err := DB("examples").TableCreate("table").RunWrite(session) if err != nil { Log.Fatalf("Error creating table: %s", err) } fmt.Printf("%d table created", response.TablesCreated) // Output: // 1 table created } // Create a simple index based on the field name. func ExampleTerm_IndexCreate() { // Setup database DB("examples").TableDrop("table").Run(session) DB("examples").TableCreate("table").Run(session) response, err := DB("examples").Table("table").IndexCreate("name").RunWrite(session) if err != nil { Log.Fatalf("Error creating index: %s", err) } fmt.Printf("%d index created", response.Created) // Output: // 1 index created } // Create a compound index based on the fields first_name and last_name. func ExampleTerm_IndexCreate_compound() { // Setup database DB("examples").TableDrop("table").Run(session) DB("examples").TableCreate("table").Run(session) response, err := DB("examples").Table("table").IndexCreateFunc("full_name", func(row Term) interface{} { return []interface{}{row.Field("first_name"), row.Field("last_name")} }).RunWrite(session) if err != nil { Log.Fatalf("Error creating index: %s", err) } fmt.Printf("%d index created", response.Created) // Output: // 1 index created } gorethink-2.0.4/example_query_transformation_test.go000066400000000000000000000056361272042630000231110ustar00rootroot00000000000000package gorethink import ( "fmt" ) // Return the first five squares. func ExampleTerm_Map() { cur, err := Expr([]int{1, 2, 3, 4, 5}).Map(func(val Term) Term { return val.Mul(val) }).Run(session) if err != nil { fmt.Print(err) return } var res []int err = cur.All(&res) if err != nil { fmt.Print(err) return } fmt.Print(res) // Output: // [1 4 9 16 25] } // Sum the elements of three sequences. func ExampleMap_multipleSequences() { var sequence1 = []int{100, 200, 300, 400} var sequence2 = []int{10, 20, 30, 40} var sequence3 = []int{1, 2, 3, 4} cur, err := Map(sequence1, sequence2, sequence3, func(val1, val2, val3 Term) Term { return val1.Add(val2).Add(val3) }).Run(session) if err != nil { fmt.Print(err) return } var res []int err = cur.All(&res) if err != nil { fmt.Print(err) return } fmt.Print(res) // Output: // [111 222 333 444] } // Order all the posts using the index date. func ExampleTerm_OrderBy_index() { cur, err := DB("examples").Table("posts").OrderBy(OrderByOpts{ Index: "date", }).Run(session) if err != nil { fmt.Print(err) return } var res []interface{} err = cur.All(&res) if err != nil { fmt.Print(err) return } fmt.Print(res) } // Order all the posts using the index date in descending order. func ExampleTerm_OrderBy_indexDesc() { cur, err := DB("examples").Table("posts").OrderBy(OrderByOpts{ Index: Desc("date"), }).Run(session) if err != nil { fmt.Print(err) return } var res []interface{} err = cur.All(&res) if err != nil { fmt.Print(err) return } fmt.Print(res) } // You can efficiently order using multiple fields by using a compound index. // For example order by date and title. func ExampleTerm_OrderBy_compound() { cur, err := DB("examples").Table("posts").OrderBy(OrderByOpts{ Index: Desc("dateAndTitle"), }).Run(session) if err != nil { fmt.Print(err) return } var res []interface{} err = cur.All(&res) if err != nil { fmt.Print(err) return } fmt.Print(res) } // If you have a sequence with fewer documents than the arrayLimit, you can order // it by multiple fields without an index. func ExampleTerm_OrderBy_multiple() { cur, err := DB("examples").Table("posts").OrderBy( "title", OrderByOpts{Index: Desc("date")}, ).Run(session) if err != nil { fmt.Print(err) return } var res []interface{} err = cur.All(&res) if err != nil { fmt.Print(err) return } fmt.Print(res) } // Notice that an index ordering always has highest precedence. The following // query orders posts by date, and if multiple posts were published on the same // date, they will be ordered by title. func ExampleTerm_OrderBy_multipleWithIndex() { cur, err := DB("examples").Table("posts").OrderBy( "title", OrderByOpts{Index: Desc("date")}, ).Run(session) if err != nil { fmt.Print(err) return } var res []interface{} err = cur.All(&res) if err != nil { fmt.Print(err) return } fmt.Print(res) } gorethink-2.0.4/example_query_write_test.go000066400000000000000000000115331272042630000211660ustar00rootroot00000000000000package gorethink import ( "fmt" ) // Insert a document into the table posts using a struct. func ExampleTerm_Insert_struct() { type Post struct { ID int `gorethink:"id"` Title string `gorethink:"title"` Content string `gorethink:"content"` } resp, err := DB("examples").Table("posts").Insert(Post{ ID: 1, Title: "Lorem ipsum", Content: "Dolor sit amet", }).RunWrite(session) if err != nil { fmt.Print(err) return } fmt.Printf("%d row inserted", resp.Inserted) // Output: // 1 row inserted } // Insert a document without a defined primary key into the table posts where // the primary key is id. func ExampleTerm_Insert_generatedKey() { type Post struct { Title string `gorethink:"title"` Content string `gorethink:"content"` } resp, err := DB("examples").Table("posts").Insert(map[string]interface{}{ "title": "Lorem ipsum", "content": "Dolor sit amet", }).RunWrite(session) if err != nil { fmt.Print(err) return } fmt.Printf("%d row inserted, %d key generated", resp.Inserted, len(resp.GeneratedKeys)) // Output: // 1 row inserted, 1 key generated } // Insert a document into the table posts using a map. func ExampleTerm_Insert_map() { resp, err := DB("examples").Table("posts").Insert(map[string]interface{}{ "id": 2, "title": "Lorem ipsum", "content": "Dolor sit amet", }).RunWrite(session) if err != nil { fmt.Print(err) return } fmt.Printf("%d row inserted", resp.Inserted) // Output: // 1 row inserted } // Insert multiple documents into the table posts. func ExampleTerm_Insert_multiple() { resp, err := DB("examples").Table("posts").Insert([]interface{}{ map[string]interface{}{ "title": "Lorem ipsum", "content": "Dolor sit amet", }, map[string]interface{}{ "title": "Lorem ipsum", "content": "Dolor sit amet", }, }).RunWrite(session) if err != nil { fmt.Print(err) return } fmt.Printf("%d rows inserted", resp.Inserted) // Output: // 2 rows inserted } // Insert a document into the table posts, replacing the document if it already // exists. func ExampleTerm_Insert_upsert() { resp, err := DB("examples").Table("posts").Insert(map[string]interface{}{ "id": 1, "title": "Lorem ipsum 2", }, InsertOpts{ Conflict: "replace", }).RunWrite(session) if err != nil { fmt.Print(err) return } fmt.Printf("%d row replaced", resp.Replaced) // Output: // 1 row replaced } // Update the status of the post with id of 1 to published. func ExampleTerm_Update() { resp, err := DB("examples").Table("posts").Get(2).Update(map[string]interface{}{ "status": "published", }).RunWrite(session) if err != nil { fmt.Print(err) return } fmt.Printf("%d row replaced", resp.Replaced) // Output: // 1 row replaced } // Update bob's cell phone number. func ExampleTerm_Update_nested() { resp, err := DB("examples").Table("users").Get("bob").Update(map[string]interface{}{ "contact": map[string]interface{}{ "phone": "408-555-4242", }, }).RunWrite(session) if err != nil { fmt.Print(err) return } fmt.Printf("%d row replaced", resp.Replaced) // Output: // 1 row replaced } // Update the status of all posts to published. func ExampleTerm_Update_all() { resp, err := DB("examples").Table("posts").Update(map[string]interface{}{ "status": "published", }).RunWrite(session) if err != nil { fmt.Print(err) return } fmt.Printf("%d row replaced", resp.Replaced) // Output: // 4 row replaced } // Increment the field view of the post with id of 1. If the field views does not // exist, it will be set to 0. func ExampleTerm_Update_increment() { resp, err := DB("examples").Table("posts").Get(1).Update(map[string]interface{}{ "views": Row.Field("views").Add(1).Default(0), }).RunWrite(session) if err != nil { fmt.Print(err) return } fmt.Printf("%d row replaced", resp.Replaced) // Output: // 1 row replaced } // Update the status of the post with id of 1 using soft durability. func ExampleTerm_Update_softDurability() { resp, err := DB("examples").Table("posts").Get(2).Update(map[string]interface{}{ "status": "draft", }, UpdateOpts{ Durability: "soft", }).RunWrite(session) if err != nil { fmt.Print(err) return } fmt.Printf("%d row replaced", resp.Replaced) // Output: // 1 row replaced } // Delete a single document from the table posts. func ExampleTerm_Delete() { resp, err := DB("examples").Table("posts").Get(2).Delete().RunWrite(session) if err != nil { fmt.Print(err) return } fmt.Printf("%d row deleted", resp.Deleted) // Output: // 1 row deleted } // Delete all comments where the field status is published func ExampleTerm_Delete_many() { resp, err := DB("examples").Table("posts").Filter(map[string]interface{}{ "status": "published", }).Delete().RunWrite(session) if err != nil { fmt.Print(err) return } fmt.Printf("%d rows deleted", resp.Deleted) // Output: // 4 rows deleted } gorethink-2.0.4/example_test.go000066400000000000000000000006241272042630000165260ustar00rootroot00000000000000package gorethink import ( "fmt" ) func Example() { session, err := Connect(ConnectOpts{ Address: url, }) if err != nil { Log.Fatalln(err.Error()) } res, err := Expr("Hello World").Run(session) if err != nil { Log.Fatalln(err.Error()) } var response string err = res.One(&response) if err != nil { Log.Fatalln(err.Error()) } fmt.Println(response) // Output: // Hello World } gorethink-2.0.4/gorethink.go000066400000000000000000000025531272042630000160310ustar00rootroot00000000000000package gorethink import ( "reflect" "github.com/Sirupsen/logrus" "gopkg.in/dancannon/gorethink.v2/encoding" ) var ( Log *logrus.Logger ) const ( SystemDatabase = "rethinkdb" TableConfigSystemTable = "table_config" ServerConfigSystemTable = "server_config" DBConfigSystemTable = "db_config" ClusterConfigSystemTable = "cluster_config" TableStatusSystemTable = "table_status" ServerStatusSystemTable = "server_status" CurrentIssuesSystemTable = "current_issues" UsersSystemTable = "users" PermissionsSystemTable = "permissions" JobsSystemTable = "jobs" StatsSystemTable = "stats" LogsSystemTable = "logs" ) func init() { // Set encoding package encoding.IgnoreType(reflect.TypeOf(Term{})) Log = logrus.New() } // SetVerbose allows the driver logging level to be set. If true is passed then // the log level is set to Debug otherwise it defaults to Info. func SetVerbose(verbose bool) { if verbose { Log.Level = logrus.DebugLevel return } Log.Level = logrus.InfoLevel } // SetTags allows you to override the tags used when decoding or encoding // structs. The driver will check for the tags in the same order that they were // passed into this function. If no parameters are passed then the driver will // default to checking for the gorethink tag. func SetTags(tags ...string) { encoding.Tags = tags } gorethink-2.0.4/gorethink_test.go000066400000000000000000000224671272042630000170760ustar00rootroot00000000000000package gorethink import ( "encoding/json" "flag" "fmt" "math/rand" "os" "runtime" "sync" "testing" "time" test "gopkg.in/check.v1" ) var session *Session var debug = flag.Bool("gorethink.debug", false, "print query trees") var testdata = flag.Bool("gorethink.testdata", true, "create test data") var url, url1, url2, url3, db, authKey string func init() { flag.Parse() SetVerbose(true) // If the test is being run by wercker look for the rethink url url = os.Getenv("RETHINKDB_URL") if url == "" { url = "localhost:28015" } url1 = os.Getenv("RETHINKDB_URL_1") if url1 == "" { url1 = "localhost:28016" } url2 = os.Getenv("RETHINKDB_URL_2") if url2 == "" { url2 = "localhost:28017" } url3 = os.Getenv("RETHINKDB_URL_3") if url3 == "" { url3 = "localhost:28018" } db = os.Getenv("RETHINKDB_DB") if db == "" { db = "test" } } // // Begin TestMain(), Setup, Teardown // func testSetup(m *testing.M) { var err error session, err = Connect(ConnectOpts{ Address: url, }) if err != nil { Log.Fatalln(err.Error()) } setupTestData() } func testTeardown(m *testing.M) { session.Close() } func testBenchmarkSetup() { DBDrop("benchmarks").Exec(session) DBCreate("benchmarks").Exec(session) DB("benchmarks").TableDrop("benchmarks").Run(session) DB("benchmarks").TableCreate("benchmarks").Run(session) } func testBenchmarkTeardown() { DBDrop("benchmarks").Run(session) } func TestMain(m *testing.M) { // seed randomness for use with tests rand.Seed(time.Now().UTC().UnixNano()) testSetup(m) testBenchmarkSetup() res := m.Run() testBenchmarkTeardown() testTeardown(m) os.Exit(res) } // // End TestMain(), Setup, Teardown // // Hook up gocheck into the gotest runner. func Test(t *testing.T) { test.TestingT(t) } type RethinkSuite struct{} var _ = test.Suite(&RethinkSuite{}) // Expressions used in tests var now = time.Now() var arr = []interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9} var darr = []interface{}{1, 1, 2, 2, 3, 3, 5, 5, 6} var narr = []interface{}{ 1, 2, 3, 4, 5, 6, []interface{}{ 7.1, 7.2, 7.3, }, } var obj = map[string]interface{}{"a": 1, "b": 2, "c": 3} var nobj = map[string]interface{}{ "A": 1, "B": 2, "C": map[string]interface{}{ "1": 3, "2": 4, }, } var noDupNumObjList = []interface{}{ map[string]interface{}{"id": 1, "g1": 1, "g2": 1, "num": 0}, map[string]interface{}{"id": 2, "g1": 2, "g2": 2, "num": 5}, map[string]interface{}{"id": 3, "g1": 3, "g2": 2, "num": 10}, map[string]interface{}{"id": 5, "g1": 2, "g2": 3, "num": 100}, map[string]interface{}{"id": 6, "g1": 1, "g2": 1, "num": 15}, map[string]interface{}{"id": 8, "g1": 4, "g2": 2, "num": 50}, map[string]interface{}{"id": 9, "g1": 2, "g2": 3, "num": 25}, } var objList = []interface{}{ map[string]interface{}{"id": 1, "g1": 1, "g2": 1, "num": 0}, map[string]interface{}{"id": 2, "g1": 2, "g2": 2, "num": 5}, map[string]interface{}{"id": 3, "g1": 3, "g2": 2, "num": 10}, map[string]interface{}{"id": 4, "g1": 2, "g2": 3, "num": 0}, map[string]interface{}{"id": 5, "g1": 2, "g2": 3, "num": 100}, map[string]interface{}{"id": 6, "g1": 1, "g2": 1, "num": 15}, map[string]interface{}{"id": 7, "g1": 1, "g2": 2, "num": 0}, map[string]interface{}{"id": 8, "g1": 4, "g2": 2, "num": 50}, map[string]interface{}{"id": 9, "g1": 2, "g2": 3, "num": 25}, } var nameList = []interface{}{ map[string]interface{}{"id": 1, "first_name": "John", "last_name": "Smith", "gender": "M"}, map[string]interface{}{"id": 2, "first_name": "Jane", "last_name": "Smith", "gender": "F"}, } var defaultObjList = []interface{}{ map[string]interface{}{"a": 1}, map[string]interface{}{}, } var joinTable1 = []interface{}{ map[string]interface{}{"id": 0, "name": "bob"}, map[string]interface{}{"id": 1, "name": "tom"}, map[string]interface{}{"id": 2, "name": "joe"}, } var joinTable2 = []interface{}{ map[string]interface{}{"id": 0, "title": "goof"}, map[string]interface{}{"id": 2, "title": "lmoe"}, } var joinTable3 = []interface{}{ map[string]interface{}{"it": 0, "title": "goof"}, map[string]interface{}{"it": 2, "title": "lmoe"}, } type TStr string type TMap map[string]interface{} type T struct { A string `gorethink:"id, omitempty"` B int C int `gorethink:"-"` D map[string]interface{} E []interface{} F X } type SimpleT struct { A string B int } type X struct { XA int XB string XC []string XD Y XE TStr XF []TStr } type Y struct { YA int YB map[string]interface{} YC map[string]string YD TMap } type PseudoTypes struct { T time.Time Z time.Time B []byte } var str = T{ A: "A", B: 1, C: 1, D: map[string]interface{}{ "D1": 1, "D2": "2", }, E: []interface{}{ "E1", "E2", "E3", 4, }, F: X{ XA: 2, XB: "B", XC: []string{"XC1", "XC2"}, XD: Y{ YA: 3, YB: map[string]interface{}{ "1": "1", "2": "2", "3": 3, }, YC: map[string]string{ "YC1": "YC1", }, YD: TMap{ "YD1": "YD1", }, }, XE: "XE", XF: []TStr{ "XE1", "XE2", }, }, } type Author struct { ID string `gorethink:"id,omitempty"` Name string `gorethink:"name"` } type Book struct { ID string `gorethink:"id,omitempty"` Title string `gorethink:"title"` Author Author `gorethink:"author_id,reference" gorethink_ref:"id"` } type TagsTest struct { A string `gorethink:"a"` B string `json:"b"` C string `gorethink:"c1" json:"c2"` } func (s *RethinkSuite) BenchmarkExpr(c *test.C) { for i := 0; i < c.N; i++ { // Test query query := Expr(true) err := query.Exec(session) c.Assert(err, test.IsNil) } } func (s *RethinkSuite) BenchmarkNoReplyExpr(c *test.C) { for i := 0; i < c.N; i++ { // Test query query := Expr(true) err := query.Exec(session, ExecOpts{NoReply: true}) c.Assert(err, test.IsNil) } } func (s *RethinkSuite) BenchmarkGet(c *test.C) { // Ensure table + database exist DBCreate("test").RunWrite(session) DB("test").TableCreate("TestMany").RunWrite(session) DB("test").Table("TestMany").Delete().RunWrite(session) // Insert rows data := []interface{}{} for i := 0; i < 100; i++ { data = append(data, map[string]interface{}{ "id": i, }) } DB("test").Table("TestMany").Insert(data).Run(session) for i := 0; i < c.N; i++ { n := rand.Intn(100) // Test query var response interface{} query := DB("test").Table("TestMany").Get(n) res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, map[string]interface{}{"id": n}) } } func (s *RethinkSuite) BenchmarkGetStruct(c *test.C) { // Ensure table + database exist DBCreate("test").RunWrite(session) DB("test").TableCreate("TestMany").RunWrite(session) DB("test").Table("TestMany").Delete().RunWrite(session) // Insert rows data := []interface{}{} for i := 0; i < 100; i++ { data = append(data, map[string]interface{}{ "id": i, "name": "Object 1", "Attrs": []interface{}{map[string]interface{}{ "Name": "attr 1", "Value": "value 1", }}, }) } DB("test").Table("TestMany").Insert(data).Run(session) for i := 0; i < c.N; i++ { n := rand.Intn(100) // Test query var resObj object query := DB("test").Table("TestMany").Get(n) res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.One(&resObj) c.Assert(err, test.IsNil) } } func (s *RethinkSuite) BenchmarkSelectMany(c *test.C) { // Ensure table + database exist DBCreate("test").RunWrite(session) DB("test").TableCreate("TestMany").RunWrite(session) DB("test").Table("TestMany").Delete().RunWrite(session) // Insert rows data := []interface{}{} for i := 0; i < 100; i++ { data = append(data, map[string]interface{}{ "id": i, }) } DB("test").Table("TestMany").Insert(data).Run(session) for i := 0; i < c.N; i++ { // Test query res, err := DB("test").Table("TestMany").Run(session) c.Assert(err, test.IsNil) var response []map[string]interface{} err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, test.HasLen, 100) } } func (s *RethinkSuite) BenchmarkSelectManyStruct(c *test.C) { // Ensure table + database exist DBCreate("test").RunWrite(session) DB("test").TableCreate("TestMany").RunWrite(session) DB("test").Table("TestMany").Delete().RunWrite(session) // Insert rows data := []interface{}{} for i := 0; i < 100; i++ { data = append(data, map[string]interface{}{ "id": i, "name": "Object 1", "Attrs": []interface{}{map[string]interface{}{ "Name": "attr 1", "Value": "value 1", }}, }) } DB("test").Table("TestMany").Insert(data).Run(session) for i := 0; i < c.N; i++ { // Test query res, err := DB("test").Table("TestMany").Run(session) c.Assert(err, test.IsNil) var response []object err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, test.HasLen, 100) } } func doConcurrentTest(c *test.C, ct func()) { maxProcs, numReqs := 1, 150 if testing.Short() { maxProcs, numReqs = 4, 50 } defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(maxProcs)) var wg sync.WaitGroup wg.Add(numReqs) reqs := make(chan bool) defer close(reqs) for i := 0; i < maxProcs*2; i++ { go func() { for _ = range reqs { ct() if c.Failed() { wg.Done() continue } wg.Done() } }() } for i := 0; i < numReqs; i++ { reqs <- true } wg.Wait() } // Test utils // Print variable as JSON func jsonPrint(v interface{}) { b, err := json.MarshalIndent(v, "", " ") if err != nil { fmt.Println(err) return } fmt.Println(string(b)) } gorethink-2.0.4/host.go000066400000000000000000000005361272042630000150130ustar00rootroot00000000000000package gorethink import ( "fmt" ) // Host name and port of server type Host struct { Name string Port int } // NewHost create a new Host func NewHost(name string, port int) Host { return Host{ Name: name, Port: port, } } // Returns host address (name:port) func (h Host) String() string { return fmt.Sprintf("%s:%d", h.Name, h.Port) } gorethink-2.0.4/node.go000066400000000000000000000051251272042630000147620ustar00rootroot00000000000000package gorethink import ( "sync" p "gopkg.in/dancannon/gorethink.v2/ql2" ) // Node represents a database server in the cluster type Node struct { ID string Host Host aliases []Host cluster *Cluster pool *Pool mu sync.RWMutex closed bool } func newNode(id string, aliases []Host, cluster *Cluster, pool *Pool) *Node { node := &Node{ ID: id, Host: aliases[0], aliases: aliases, cluster: cluster, pool: pool, } return node } // Closed returns true if the node is closed func (n *Node) Closed() bool { n.mu.RLock() defer n.mu.RUnlock() return n.closed } // Close closes the session func (n *Node) Close(optArgs ...CloseOpts) error { n.mu.Lock() defer n.mu.Unlock() if n.closed { return nil } if len(optArgs) >= 1 { if optArgs[0].NoReplyWait { n.NoReplyWait() } } if n.pool != nil { n.pool.Close() } n.pool = nil n.closed = true return nil } // SetMaxIdleConns sets the maximum number of connections in the idle // connection pool. func (n *Node) SetMaxIdleConns(idleConns int) { n.pool.SetMaxIdleConns(idleConns) } // SetMaxOpenConns sets the maximum number of open connections to the database. func (n *Node) SetMaxOpenConns(openConns int) { n.pool.SetMaxOpenConns(openConns) } // NoReplyWait ensures that previous queries with the noreply flag have been // processed by the server. Note that this guarantee only applies to queries // run on the given connection func (n *Node) NoReplyWait() error { return n.pool.Exec(Query{ Type: p.Query_NOREPLY_WAIT, }) } // Query executes a ReQL query using this nodes connection pool. func (n *Node) Query(q Query) (cursor *Cursor, err error) { if n.Closed() { return nil, ErrInvalidNode } return n.pool.Query(q) } // Exec executes a ReQL query using this nodes connection pool. func (n *Node) Exec(q Query) (err error) { if n.Closed() { return ErrInvalidNode } return n.pool.Exec(q) } // Server returns the server name and server UUID being used by a connection. func (n *Node) Server() (ServerResponse, error) { var response ServerResponse if n.Closed() { return response, ErrInvalidNode } return n.pool.Server() } type nodeStatus struct { ID string `gorethink:"id"` Name string `gorethink:"name"` Status string `gorethink:"status"` Network struct { Hostname string `gorethink:"hostname"` ClusterPort int64 `gorethink:"cluster_port"` ReqlPort int64 `gorethink:"reql_port"` CanonicalAddresses []struct { Host string `gorethink:"host"` Port int64 `gorethink:"port"` } `gorethink:"canonical_addresses"` } `gorethink:"network"` } gorethink-2.0.4/pool.go000066400000000000000000000103531272042630000150050ustar00rootroot00000000000000package gorethink import ( "errors" "fmt" "net" "sync" "gopkg.in/fatih/pool.v2" ) const maxBadConnRetries = 3 var ( errPoolClosed = errors.New("gorethink: pool is closed") ) // A Pool is used to store a pool of connections to a single RethinkDB server type Pool struct { host Host opts *ConnectOpts pool pool.Pool mu sync.RWMutex // protects following fields closed bool maxIdle int maxOpen int } // NewPool creates a new connection pool for the given host func NewPool(host Host, opts *ConnectOpts) (*Pool, error) { maxOpen := opts.MaxOpen if maxOpen == 0 { maxOpen = 2 } p, err := pool.NewChannelPool(opts.MaxIdle, maxOpen, func() (net.Conn, error) { conn, err := NewConnection(host.String(), opts) if err != nil { return nil, err } return conn, err }) if err != nil { return nil, err } return &Pool{ pool: p, host: host, opts: opts, }, nil } // Ping verifies a connection to the database is still alive, // establishing a connection if necessary. func (p *Pool) Ping() error { _, pc, err := p.conn() if err != nil { return err } return pc.Close() } // Close closes the database, releasing any open resources. // // It is rare to Close a Pool, as the Pool handle is meant to be // long-lived and shared between many goroutines. func (p *Pool) Close() error { p.mu.RLock() defer p.mu.RUnlock() if p.closed { return nil } p.pool.Close() return nil } func (p *Pool) conn() (*Connection, *pool.PoolConn, error) { p.mu.RLock() defer p.mu.RUnlock() if p.closed { return nil, nil, errPoolClosed } nc, err := p.pool.Get() if err != nil { return nil, nil, err } pc, ok := nc.(*pool.PoolConn) if !ok { // This should never happen! return nil, nil, fmt.Errorf("Invalid connection in pool") } conn, ok := pc.Conn.(*Connection) if !ok { // This should never happen! return nil, nil, fmt.Errorf("Invalid connection in pool") } return conn, pc, nil } // SetMaxIdleConns sets the maximum number of connections in the idle // connection pool. // // If MaxOpenConns is greater than 0 but less than the new MaxIdleConns // then the new MaxIdleConns will be reduced to match the MaxOpenConns limit // // If n <= 0, no idle connections are retained. // // Deprecated: This value should only be set when connecting func (p *Pool) SetMaxIdleConns(n int) { return } // SetMaxOpenConns sets the maximum number of open connections to the database. // // If MaxIdleConns is greater than 0 and the new MaxOpenConns is less than // MaxIdleConns, then MaxIdleConns will be reduced to match the new // MaxOpenConns limit // // If n <= 0, then there is no limit on the number of open connections. // The default is 0 (unlimited). // // Deprecated: This value should only be set when connecting func (p *Pool) SetMaxOpenConns(n int) { return } // Query execution functions // Exec executes a query without waiting for any response. func (p *Pool) Exec(q Query) error { var err error for i := 0; i < maxBadConnRetries; i++ { var c *Connection var pc *pool.PoolConn c, pc, err = p.conn() if err != nil { continue } defer pc.Close() _, _, err = c.Query(q) if c.isBad() { pc.MarkUnusable() } else { break } } return err } // Query executes a query and waits for the response func (p *Pool) Query(q Query) (*Cursor, error) { var cursor *Cursor var err error for i := 0; i < maxBadConnRetries; i++ { var c *Connection var pc *pool.PoolConn c, pc, err = p.conn() if err != nil { continue } _, cursor, err = c.Query(q) if err == nil { cursor.releaseConn = releaseConn(c, pc) } else if c.isBad() { pc.MarkUnusable() continue } break } return cursor, err } // Server returns the server name and server UUID being used by a connection. func (p *Pool) Server() (ServerResponse, error) { var response ServerResponse var err error for i := 0; i < maxBadConnRetries; i++ { var c *Connection var pc *pool.PoolConn c, pc, err = p.conn() if err != nil { continue } defer pc.Close() response, err = c.Server() if c.isBad() { pc.MarkUnusable() } else { break } } return response, err } func releaseConn(c *Connection, pc *pool.PoolConn) func() error { return func() error { if c.isBad() { pc.MarkUnusable() } return pc.Close() } } gorethink-2.0.4/pseudotypes.go000066400000000000000000000135041272042630000164210ustar00rootroot00000000000000package gorethink import ( "encoding/base64" "math" "strconv" "time" "gopkg.in/dancannon/gorethink.v2/types" "fmt" ) func convertPseudotype(obj map[string]interface{}, opts map[string]interface{}) (interface{}, error) { if reqlType, ok := obj["$reql_type$"]; ok { if reqlType == "TIME" { // load timeFormat, set to native if the option was not set timeFormat := "native" if opt, ok := opts["time_format"]; ok { if sopt, ok := opt.(string); ok { timeFormat = sopt } else { return nil, fmt.Errorf("Invalid time_format run option \"%s\".", opt) } } if timeFormat == "native" { return reqlTimeToNativeTime(obj["epoch_time"].(float64), obj["timezone"].(string)) } else if timeFormat == "raw" { return obj, nil } else { return nil, fmt.Errorf("Unknown time_format run option \"%s\".", reqlType) } } else if reqlType == "GROUPED_DATA" { // load groupFormat, set to native if the option was not set groupFormat := "native" if opt, ok := opts["group_format"]; ok { if sopt, ok := opt.(string); ok { groupFormat = sopt } else { return nil, fmt.Errorf("Invalid group_format run option \"%s\".", opt) } } if groupFormat == "native" { return reqlGroupedDataToObj(obj) } else if groupFormat == "raw" { return obj, nil } else { return nil, fmt.Errorf("Unknown group_format run option \"%s\".", reqlType) } } else if reqlType == "BINARY" { binaryFormat := "native" if opt, ok := opts["binary_format"]; ok { if sopt, ok := opt.(string); ok { binaryFormat = sopt } else { return nil, fmt.Errorf("Invalid binary_format run option \"%s\".", opt) } } if binaryFormat == "native" { return reqlBinaryToNativeBytes(obj) } else if binaryFormat == "raw" { return obj, nil } else { return nil, fmt.Errorf("Unknown binary_format run option \"%s\".", reqlType) } } else if reqlType == "GEOMETRY" { geometryFormat := "native" if opt, ok := opts["geometry_format"]; ok { if sopt, ok := opt.(string); ok { geometryFormat = sopt } else { return nil, fmt.Errorf("Invalid geometry_format run option \"%s\".", opt) } } if geometryFormat == "native" { return reqlGeometryToNativeGeometry(obj) } else if geometryFormat == "raw" { return obj, nil } else { return nil, fmt.Errorf("Unknown geometry_format run option \"%s\".", reqlType) } } else { return obj, nil } } return obj, nil } func recursivelyConvertPseudotype(obj interface{}, opts map[string]interface{}) (interface{}, error) { var err error switch obj := obj.(type) { case []interface{}: for key, val := range obj { obj[key], err = recursivelyConvertPseudotype(val, opts) if err != nil { return nil, err } } case map[string]interface{}: for key, val := range obj { obj[key], err = recursivelyConvertPseudotype(val, opts) if err != nil { return nil, err } } pobj, err := convertPseudotype(obj, opts) if err != nil { return nil, err } return pobj, nil } return obj, nil } // Pseudo-type helper functions func reqlTimeToNativeTime(timestamp float64, timezone string) (time.Time, error) { sec, ms := math.Modf(timestamp) // Convert to native time rounding to milliseconds t := time.Unix(int64(sec), int64(math.Floor(ms*1000+0.5))*1000*1000) // Caclulate the timezone if timezone != "" { hours, err := strconv.Atoi(timezone[1:3]) if err != nil { return time.Time{}, err } minutes, err := strconv.Atoi(timezone[4:6]) if err != nil { return time.Time{}, err } tzOffset := ((hours * 60) + minutes) * 60 if timezone[:1] == "-" { tzOffset = 0 - tzOffset } t = t.In(time.FixedZone(timezone, tzOffset)) } return t, nil } func reqlGroupedDataToObj(obj map[string]interface{}) (interface{}, error) { if data, ok := obj["data"]; ok { ret := []interface{}{} for _, v := range data.([]interface{}) { v := v.([]interface{}) ret = append(ret, map[string]interface{}{ "group": v[0], "reduction": v[1], }) } return ret, nil } return nil, fmt.Errorf("pseudo-type GROUPED_DATA object %v does not have the expected field \"data\"", obj) } func reqlBinaryToNativeBytes(obj map[string]interface{}) (interface{}, error) { if data, ok := obj["data"]; ok { if data, ok := data.(string); ok { b, err := base64.StdEncoding.DecodeString(data) if err != nil { return nil, fmt.Errorf("error decoding pseudo-type BINARY object %v", obj) } return b, nil } return nil, fmt.Errorf("pseudo-type BINARY object %v field \"data\" is not valid", obj) } return nil, fmt.Errorf("pseudo-type BINARY object %v does not have the expected field \"data\"", obj) } func reqlGeometryToNativeGeometry(obj map[string]interface{}) (interface{}, error) { if typ, ok := obj["type"]; !ok { return nil, fmt.Errorf("pseudo-type GEOMETRY object %v does not have the expected field \"type\"", obj) } else if typ, ok := typ.(string); !ok { return nil, fmt.Errorf("pseudo-type GEOMETRY object %v field \"type\" is not valid", obj) } else if coords, ok := obj["coordinates"]; !ok { return nil, fmt.Errorf("pseudo-type GEOMETRY object %v does not have the expected field \"coordinates\"", obj) } else if typ == "Point" { point, err := types.UnmarshalPoint(coords) if err != nil { return nil, err } return types.Geometry{ Type: "Point", Point: point, }, nil } else if typ == "LineString" { line, err := types.UnmarshalLineString(coords) if err != nil { return nil, err } return types.Geometry{ Type: "LineString", Line: line, }, nil } else if typ == "Polygon" { lines, err := types.UnmarshalPolygon(coords) if err != nil { return nil, err } return types.Geometry{ Type: "Polygon", Lines: lines, }, nil } else { return nil, fmt.Errorf("pseudo-type GEOMETRY object %v field has unknown type %s", obj, typ) } } gorethink-2.0.4/ql2/000077500000000000000000000000001272042630000142015ustar00rootroot00000000000000gorethink-2.0.4/ql2/generate.go000066400000000000000000000000671272042630000163250ustar00rootroot00000000000000//go:generate protoc --go_out=. ql2.proto package ql2 gorethink-2.0.4/ql2/ql2.pb.go000066400000000000000000001510371272042630000156350ustar00rootroot00000000000000// Code generated by protoc-gen-go. // source: ql2.proto // DO NOT EDIT! /* Package ql2 is a generated protocol buffer package. It is generated from these files: ql2.proto It has these top-level messages: VersionDummy Query Frame Backtrace Response Datum Term */ package ql2 import proto "github.com/golang/protobuf/proto" import json "encoding/json" import math "math" // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = &json.SyntaxError{} var _ = math.Inf // non-conforming protobuf libraries // This enum contains the magic numbers for your version. See **THE HIGH-LEVEL // VIEW** for what to do with it. type VersionDummy_Version int32 const ( VersionDummy_V0_1 VersionDummy_Version = 1063369270 VersionDummy_V0_2 VersionDummy_Version = 1915781601 VersionDummy_V0_3 VersionDummy_Version = 1601562686 VersionDummy_V0_4 VersionDummy_Version = 1074539808 VersionDummy_V1_0 VersionDummy_Version = 885177795 ) var VersionDummy_Version_name = map[int32]string{ 1063369270: "V0_1", 1915781601: "V0_2", 1601562686: "V0_3", 1074539808: "V0_4", 885177795: "V1_0", } var VersionDummy_Version_value = map[string]int32{ "V0_1": 1063369270, "V0_2": 1915781601, "V0_3": 1601562686, "V0_4": 1074539808, "V1_0": 885177795, } func (x VersionDummy_Version) Enum() *VersionDummy_Version { p := new(VersionDummy_Version) *p = x return p } func (x VersionDummy_Version) String() string { return proto.EnumName(VersionDummy_Version_name, int32(x)) } func (x VersionDummy_Version) MarshalJSON() ([]byte, error) { return json.Marshal(x.String()) } func (x *VersionDummy_Version) UnmarshalJSON(data []byte) error { value, err := proto.UnmarshalJSONEnum(VersionDummy_Version_value, data, "VersionDummy_Version") if err != nil { return err } *x = VersionDummy_Version(value) return nil } // The protocol to use after the handshake, specified in V0_3 type VersionDummy_Protocol int32 const ( VersionDummy_PROTOBUF VersionDummy_Protocol = 656407617 VersionDummy_JSON VersionDummy_Protocol = 2120839367 ) var VersionDummy_Protocol_name = map[int32]string{ 656407617: "PROTOBUF", 2120839367: "JSON", } var VersionDummy_Protocol_value = map[string]int32{ "PROTOBUF": 656407617, "JSON": 2120839367, } func (x VersionDummy_Protocol) Enum() *VersionDummy_Protocol { p := new(VersionDummy_Protocol) *p = x return p } func (x VersionDummy_Protocol) String() string { return proto.EnumName(VersionDummy_Protocol_name, int32(x)) } func (x VersionDummy_Protocol) MarshalJSON() ([]byte, error) { return json.Marshal(x.String()) } func (x *VersionDummy_Protocol) UnmarshalJSON(data []byte) error { value, err := proto.UnmarshalJSONEnum(VersionDummy_Protocol_value, data, "VersionDummy_Protocol") if err != nil { return err } *x = VersionDummy_Protocol(value) return nil } type Query_QueryType int32 const ( Query_START Query_QueryType = 1 Query_CONTINUE Query_QueryType = 2 // (see [Response]). Query_STOP Query_QueryType = 3 Query_NOREPLY_WAIT Query_QueryType = 4 Query_SERVER_INFO Query_QueryType = 5 ) var Query_QueryType_name = map[int32]string{ 1: "START", 2: "CONTINUE", 3: "STOP", 4: "NOREPLY_WAIT", 5: "SERVER_INFO", } var Query_QueryType_value = map[string]int32{ "START": 1, "CONTINUE": 2, "STOP": 3, "NOREPLY_WAIT": 4, "SERVER_INFO": 5, } func (x Query_QueryType) Enum() *Query_QueryType { p := new(Query_QueryType) *p = x return p } func (x Query_QueryType) String() string { return proto.EnumName(Query_QueryType_name, int32(x)) } func (x Query_QueryType) MarshalJSON() ([]byte, error) { return json.Marshal(x.String()) } func (x *Query_QueryType) UnmarshalJSON(data []byte) error { value, err := proto.UnmarshalJSONEnum(Query_QueryType_value, data, "Query_QueryType") if err != nil { return err } *x = Query_QueryType(value) return nil } type Frame_FrameType int32 const ( Frame_POS Frame_FrameType = 1 Frame_OPT Frame_FrameType = 2 ) var Frame_FrameType_name = map[int32]string{ 1: "POS", 2: "OPT", } var Frame_FrameType_value = map[string]int32{ "POS": 1, "OPT": 2, } func (x Frame_FrameType) Enum() *Frame_FrameType { p := new(Frame_FrameType) *p = x return p } func (x Frame_FrameType) String() string { return proto.EnumName(Frame_FrameType_name, int32(x)) } func (x Frame_FrameType) MarshalJSON() ([]byte, error) { return json.Marshal(x.String()) } func (x *Frame_FrameType) UnmarshalJSON(data []byte) error { value, err := proto.UnmarshalJSONEnum(Frame_FrameType_value, data, "Frame_FrameType") if err != nil { return err } *x = Frame_FrameType(value) return nil } type Response_ResponseType int32 const ( // These response types indicate success. Response_SUCCESS_ATOM Response_ResponseType = 1 Response_SUCCESS_SEQUENCE Response_ResponseType = 2 Response_SUCCESS_PARTIAL Response_ResponseType = 3 // datatypes. If you send a [CONTINUE] query with // the same token as this response, you will get // more of the sequence. Keep sending [CONTINUE] // queries until you get back [SUCCESS_SEQUENCE]. Response_WAIT_COMPLETE Response_ResponseType = 4 Response_SERVER_INFO Response_ResponseType = 5 // These response types indicate failure. Response_CLIENT_ERROR Response_ResponseType = 16 // client sends a malformed protobuf, or tries to // send [CONTINUE] for an unknown token. Response_COMPILE_ERROR Response_ResponseType = 17 // checking. For example, if you pass too many // arguments to a function. Response_RUNTIME_ERROR Response_ResponseType = 18 ) var Response_ResponseType_name = map[int32]string{ 1: "SUCCESS_ATOM", 2: "SUCCESS_SEQUENCE", 3: "SUCCESS_PARTIAL", 4: "WAIT_COMPLETE", 5: "SERVER_INFO", 16: "CLIENT_ERROR", 17: "COMPILE_ERROR", 18: "RUNTIME_ERROR", } var Response_ResponseType_value = map[string]int32{ "SUCCESS_ATOM": 1, "SUCCESS_SEQUENCE": 2, "SUCCESS_PARTIAL": 3, "WAIT_COMPLETE": 4, "SERVER_INFO": 5, "CLIENT_ERROR": 16, "COMPILE_ERROR": 17, "RUNTIME_ERROR": 18, } func (x Response_ResponseType) Enum() *Response_ResponseType { p := new(Response_ResponseType) *p = x return p } func (x Response_ResponseType) String() string { return proto.EnumName(Response_ResponseType_name, int32(x)) } func (x Response_ResponseType) MarshalJSON() ([]byte, error) { return json.Marshal(x.String()) } func (x *Response_ResponseType) UnmarshalJSON(data []byte) error { value, err := proto.UnmarshalJSONEnum(Response_ResponseType_value, data, "Response_ResponseType") if err != nil { return err } *x = Response_ResponseType(value) return nil } // If `ResponseType` is `RUNTIME_ERROR`, this may be filled in with more // information about the error. type Response_ErrorType int32 const ( Response_INTERNAL Response_ErrorType = 1000000 Response_RESOURCE_LIMIT Response_ErrorType = 2000000 Response_QUERY_LOGIC Response_ErrorType = 3000000 Response_NON_EXISTENCE Response_ErrorType = 3100000 Response_OP_FAILED Response_ErrorType = 4100000 Response_OP_INDETERMINATE Response_ErrorType = 4200000 Response_USER Response_ErrorType = 5000000 Response_PERMISSION_ERROR Response_ErrorType = 6000000 ) var Response_ErrorType_name = map[int32]string{ 1000000: "INTERNAL", 2000000: "RESOURCE_LIMIT", 3000000: "QUERY_LOGIC", 3100000: "NON_EXISTENCE", 4100000: "OP_FAILED", 4200000: "OP_INDETERMINATE", 5000000: "USER", 6000000: "PERMISSION_ERROR", } var Response_ErrorType_value = map[string]int32{ "INTERNAL": 1000000, "RESOURCE_LIMIT": 2000000, "QUERY_LOGIC": 3000000, "NON_EXISTENCE": 3100000, "OP_FAILED": 4100000, "OP_INDETERMINATE": 4200000, "USER": 5000000, "PERMISSION_ERROR": 6000000, } func (x Response_ErrorType) Enum() *Response_ErrorType { p := new(Response_ErrorType) *p = x return p } func (x Response_ErrorType) String() string { return proto.EnumName(Response_ErrorType_name, int32(x)) } func (x Response_ErrorType) MarshalJSON() ([]byte, error) { return json.Marshal(x.String()) } func (x *Response_ErrorType) UnmarshalJSON(data []byte) error { value, err := proto.UnmarshalJSONEnum(Response_ErrorType_value, data, "Response_ErrorType") if err != nil { return err } *x = Response_ErrorType(value) return nil } // ResponseNotes are used to provide information about the query // response that may be useful for people writing drivers or ORMs. // Currently all the notes we send indicate that a stream has certain // special properties. type Response_ResponseNote int32 const ( // The stream is a changefeed stream (e.g. `r.table('test').changes()`). Response_SEQUENCE_FEED Response_ResponseNote = 1 // The stream is a point changefeed stream // (e.g. `r.table('test').get(0).changes()`). Response_ATOM_FEED Response_ResponseNote = 2 // The stream is an order_by_limit changefeed stream // (e.g. `r.table('test').order_by(index: 'id').limit(5).changes()`). Response_ORDER_BY_LIMIT_FEED Response_ResponseNote = 3 // The stream is a union of multiple changefeed types that can't be // collapsed to a single type // (e.g. `r.table('test').changes().union(r.table('test').get(0).changes())`). Response_UNIONED_FEED Response_ResponseNote = 4 // The stream is a changefeed stream and includes notes on what state // the changefeed stream is in (e.g. objects of the form `{state: // 'initializing'}`). Response_INCLUDES_STATES Response_ResponseNote = 5 ) var Response_ResponseNote_name = map[int32]string{ 1: "SEQUENCE_FEED", 2: "ATOM_FEED", 3: "ORDER_BY_LIMIT_FEED", 4: "UNIONED_FEED", 5: "INCLUDES_STATES", } var Response_ResponseNote_value = map[string]int32{ "SEQUENCE_FEED": 1, "ATOM_FEED": 2, "ORDER_BY_LIMIT_FEED": 3, "UNIONED_FEED": 4, "INCLUDES_STATES": 5, } func (x Response_ResponseNote) Enum() *Response_ResponseNote { p := new(Response_ResponseNote) *p = x return p } func (x Response_ResponseNote) String() string { return proto.EnumName(Response_ResponseNote_name, int32(x)) } func (x Response_ResponseNote) MarshalJSON() ([]byte, error) { return json.Marshal(x.String()) } func (x *Response_ResponseNote) UnmarshalJSON(data []byte) error { value, err := proto.UnmarshalJSONEnum(Response_ResponseNote_value, data, "Response_ResponseNote") if err != nil { return err } *x = Response_ResponseNote(value) return nil } type Datum_DatumType int32 const ( Datum_R_NULL Datum_DatumType = 1 Datum_R_BOOL Datum_DatumType = 2 Datum_R_NUM Datum_DatumType = 3 Datum_R_STR Datum_DatumType = 4 Datum_R_ARRAY Datum_DatumType = 5 Datum_R_OBJECT Datum_DatumType = 6 // This [DatumType] will only be used if [accepts_r_json] is // set to [true] in [Query]. [r_str] will be filled with a // JSON encoding of the [Datum]. Datum_R_JSON Datum_DatumType = 7 ) var Datum_DatumType_name = map[int32]string{ 1: "R_NULL", 2: "R_BOOL", 3: "R_NUM", 4: "R_STR", 5: "R_ARRAY", 6: "R_OBJECT", 7: "R_JSON", } var Datum_DatumType_value = map[string]int32{ "R_NULL": 1, "R_BOOL": 2, "R_NUM": 3, "R_STR": 4, "R_ARRAY": 5, "R_OBJECT": 6, "R_JSON": 7, } func (x Datum_DatumType) Enum() *Datum_DatumType { p := new(Datum_DatumType) *p = x return p } func (x Datum_DatumType) String() string { return proto.EnumName(Datum_DatumType_name, int32(x)) } func (x Datum_DatumType) MarshalJSON() ([]byte, error) { return json.Marshal(x.String()) } func (x *Datum_DatumType) UnmarshalJSON(data []byte) error { value, err := proto.UnmarshalJSONEnum(Datum_DatumType_value, data, "Datum_DatumType") if err != nil { return err } *x = Datum_DatumType(value) return nil } type Term_TermType int32 const ( // A RQL datum, stored in `datum` below. Term_DATUM Term_TermType = 1 Term_MAKE_ARRAY Term_TermType = 2 // Evaluate the terms in [optargs] and make an object Term_MAKE_OBJ Term_TermType = 3 // Takes an integer representing a variable and returns the value stored // in that variable. It's the responsibility of the client to translate // from their local representation of a variable to a unique _non-negative_ // integer for that variable. (We do it this way instead of letting // clients provide variable names as strings to discourage // variable-capturing client libraries, and because it's more efficient // on the wire.) Term_VAR Term_TermType = 10 // Takes some javascript code and executes it. Term_JAVASCRIPT Term_TermType = 11 // STRING {timeout: !NUMBER} -> Function(*) Term_UUID Term_TermType = 169 // Takes an HTTP URL and gets it. If the get succeeds and // returns valid JSON, it is converted into a DATUM Term_HTTP Term_TermType = 153 // Takes a string and throws an error with that message. // Inside of a `default` block, you can omit the first // argument to rethrow whatever error you catch (this is most // useful as an argument to the `default` filter optarg). Term_ERROR Term_TermType = 12 // Takes nothing and returns a reference to the implicit variable. Term_IMPLICIT_VAR Term_TermType = 13 // * Data Operators // Returns a reference to a database. Term_DB Term_TermType = 14 // Returns a reference to a table. Term_TABLE Term_TermType = 15 // STRING, {read_mode:STRING, identifier_format:STRING} -> Table // Gets a single element from a table by its primary or a secondary key. Term_GET Term_TermType = 16 // Table, STRING -> NULL | Table, NUMBER -> NULL | Term_GET_ALL Term_TermType = 78 // Simple DATUM Ops Term_EQ Term_TermType = 17 Term_NE Term_TermType = 18 Term_LT Term_TermType = 19 Term_LE Term_TermType = 20 Term_GT Term_TermType = 21 Term_GE Term_TermType = 22 Term_NOT Term_TermType = 23 // ADD can either add two numbers or concatenate two arrays. Term_ADD Term_TermType = 24 Term_SUB Term_TermType = 25 Term_MUL Term_TermType = 26 Term_DIV Term_TermType = 27 Term_MOD Term_TermType = 28 Term_FLOOR Term_TermType = 183 Term_CEIL Term_TermType = 184 Term_ROUND Term_TermType = 185 // DATUM Array Ops // Append a single element to the end of an array (like `snoc`). Term_APPEND Term_TermType = 29 // Prepend a single element to the end of an array (like `cons`). Term_PREPEND Term_TermType = 80 // Remove the elements of one array from another array. Term_DIFFERENCE Term_TermType = 95 // DATUM Set Ops // Set ops work on arrays. They don't use actual sets and thus have // performance characteristics you would expect from arrays rather than // from sets. All set operations have the post condition that they // array they return contains no duplicate values. Term_SET_INSERT Term_TermType = 88 Term_SET_INTERSECTION Term_TermType = 89 Term_SET_UNION Term_TermType = 90 Term_SET_DIFFERENCE Term_TermType = 91 Term_SLICE Term_TermType = 30 Term_SKIP Term_TermType = 70 Term_LIMIT Term_TermType = 71 Term_OFFSETS_OF Term_TermType = 87 Term_CONTAINS Term_TermType = 93 // Stream/Object Ops // Get a particular field from an object, or map that over a // sequence. Term_GET_FIELD Term_TermType = 31 // | Sequence, STRING -> Sequence // Return an array containing the keys of the object. Term_KEYS Term_TermType = 94 // Return an array containing the values of the object. Term_VALUES Term_TermType = 186 // Creates an object Term_OBJECT Term_TermType = 143 // Check whether an object contains all the specified fields, // or filters a sequence so that all objects inside of it // contain all the specified fields. Term_HAS_FIELDS Term_TermType = 32 // x.with_fields(...) <=> x.has_fields(...).pluck(...) Term_WITH_FIELDS Term_TermType = 96 // Get a subset of an object by selecting some attributes to preserve, // or map that over a sequence. (Both pick and pluck, polymorphic.) Term_PLUCK Term_TermType = 33 // Get a subset of an object by selecting some attributes to discard, or // map that over a sequence. (Both unpick and without, polymorphic.) Term_WITHOUT Term_TermType = 34 // Merge objects (right-preferential) Term_MERGE Term_TermType = 35 // Sequence Ops // Get all elements of a sequence between two values. // Half-open by default, but the openness of either side can be // changed by passing 'closed' or 'open for `right_bound` or // `left_bound`. Term_BETWEEN_DEPRECATED Term_TermType = 36 // With the newer version, clients should use `r.minval` and `r.maxval` for unboundedness Term_BETWEEN Term_TermType = 182 Term_REDUCE Term_TermType = 37 Term_MAP Term_TermType = 38 Term_FOLD Term_TermType = 187 // Filter a sequence with either a function or a shortcut // object (see API docs for details). The body of FILTER is // wrapped in an implicit `.default(false)`, and you can // change the default value by specifying the `default` // optarg. If you make the default `r.error`, all errors // caught by `default` will be rethrown as if the `default` // did not exist. Term_FILTER Term_TermType = 39 // Sequence, OBJECT, {default:DATUM} -> Sequence // Map a function over a sequence and then concatenate the results together. Term_CONCAT_MAP Term_TermType = 40 // Order a sequence based on one or more attributes. Term_ORDER_BY Term_TermType = 41 // Get all distinct elements of a sequence (like `uniq`). Term_DISTINCT Term_TermType = 42 // Count the number of elements in a sequence, or only the elements that match // a given filter. Term_COUNT Term_TermType = 43 Term_IS_EMPTY Term_TermType = 86 // Take the union of multiple sequences (preserves duplicate elements! (use distinct)). Term_UNION Term_TermType = 44 // Get the Nth element of a sequence. Term_NTH Term_TermType = 45 // do NTH or GET_FIELD depending on target object Term_BRACKET Term_TermType = 170 Term_INNER_JOIN Term_TermType = 48 Term_OUTER_JOIN Term_TermType = 49 // An inner-join that does an equality comparison on two attributes. Term_EQ_JOIN Term_TermType = 50 Term_ZIP Term_TermType = 72 Term_RANGE Term_TermType = 173 // Array Ops // Insert an element in to an array at a given index. Term_INSERT_AT Term_TermType = 82 // Remove an element at a given index from an array. Term_DELETE_AT Term_TermType = 83 // ARRAY, NUMBER, NUMBER -> ARRAY // Change the element at a given index of an array. Term_CHANGE_AT Term_TermType = 84 // Splice one array in to another array. Term_SPLICE_AT Term_TermType = 85 // * Type Ops // Coerces a datum to a named type (e.g. "bool"). // If you previously used `stream_to_array`, you should use this instead // with the type "array". Term_COERCE_TO Term_TermType = 51 // Returns the named type of a datum (e.g. TYPE_OF(true) = "BOOL") Term_TYPE_OF Term_TermType = 52 // * Write Ops (the OBJECTs contain data about number of errors etc.) // Updates all the rows in a selection. Calls its Function with the row // to be updated, and then merges the result of that call. Term_UPDATE Term_TermType = 53 // SingleSelection, Function(1), {non_atomic:BOOL, durability:STRING, return_changes:BOOL} -> OBJECT | // StreamSelection, OBJECT, {non_atomic:BOOL, durability:STRING, return_changes:BOOL} -> OBJECT | // SingleSelection, OBJECT, {non_atomic:BOOL, durability:STRING, return_changes:BOOL} -> OBJECT // Deletes all the rows in a selection. Term_DELETE Term_TermType = 54 // Replaces all the rows in a selection. Calls its Function with the row // to be replaced, and then discards it and stores the result of that // call. Term_REPLACE Term_TermType = 55 // Inserts into a table. If `conflict` is replace, overwrites // entries with the same primary key. If `conflict` is // update, does an update on the entry. If `conflict` is // error, or is omitted, conflicts will trigger an error. Term_INSERT Term_TermType = 56 // * Administrative OPs // Creates a database with a particular name. Term_DB_CREATE Term_TermType = 57 // Drops a database with a particular name. Term_DB_DROP Term_TermType = 58 // Lists all the databases by name. (Takes no arguments) Term_DB_LIST Term_TermType = 59 // Creates a table with a particular name in a particular // database. (You may omit the first argument to use the // default database.) Term_TABLE_CREATE Term_TermType = 60 // Database, STRING, {primary_key:STRING, shards:NUMBER, replicas:OBJECT, primary_replica_tag:STRING} -> OBJECT // STRING, {primary_key:STRING, shards:NUMBER, replicas:NUMBER, primary_replica_tag:STRING} -> OBJECT // STRING, {primary_key:STRING, shards:NUMBER, replicas:OBJECT, primary_replica_tag:STRING} -> OBJECT // Drops a table with a particular name from a particular // database. (You may omit the first argument to use the // default database.) Term_TABLE_DROP Term_TermType = 61 // STRING -> OBJECT // Lists all the tables in a particular database. (You may // omit the first argument to use the default database.) Term_TABLE_LIST Term_TermType = 62 // -> ARRAY // Returns the row in the `rethinkdb.table_config` or `rethinkdb.db_config` table // that corresponds to the given database or table. Term_CONFIG Term_TermType = 174 // Table -> SingleSelection // Returns the row in the `rethinkdb.table_status` table that corresponds to the // given table. Term_STATUS Term_TermType = 175 // Called on a table, waits for that table to be ready for read/write operations. // Called on a database, waits for all of the tables in the database to be ready. // Returns the corresponding row or rows from the `rethinkdb.table_status` table. Term_WAIT Term_TermType = 177 // Database -> OBJECT // Generates a new config for the given table, or all tables in the given database // The `shards` and `replicas` arguments are required. If `emergency_repair` is // specified, it will enter a completely different mode of repairing a table // which has lost half or more of its replicas. Term_RECONFIGURE Term_TermType = 176 // dry_run:BOOLEAN] // } -> OBJECT // Database|Table, {shards:NUMBER, replicas:OBJECT [, // primary_replica_tag:STRING, // nonvoting_replica_tags:ARRAY, // dry_run:BOOLEAN] // } -> OBJECT // Table, {emergency_repair:STRING, dry_run:BOOLEAN} -> OBJECT // Balances the table's shards but leaves everything else the same. Can also be // applied to an entire database at once. Term_REBALANCE Term_TermType = 179 // Ensures that previously issued soft-durability writes are complete and // written to disk. Term_SYNC Term_TermType = 138 // Set global, database, or table-specific permissions Term_GRANT Term_TermType = 188 // * Secondary indexes OPs // Creates a new secondary index with a particular name and definition. Term_INDEX_CREATE Term_TermType = 75 // Drops a secondary index with a particular name from the specified table. Term_INDEX_DROP Term_TermType = 76 // Lists all secondary indexes on a particular table. Term_INDEX_LIST Term_TermType = 77 // Gets information about whether or not a set of indexes are ready to // be accessed. Returns a list of objects that look like this: // {index:STRING, ready:BOOL[, progress:NUMBER]} Term_INDEX_STATUS Term_TermType = 139 // Blocks until a set of indexes are ready to be accessed. Returns the // same values INDEX_STATUS. Term_INDEX_WAIT Term_TermType = 140 // Renames the given index to a new name Term_INDEX_RENAME Term_TermType = 156 // * Control Operators // Calls a function on data Term_FUNCALL Term_TermType = 64 // Executes its first argument, and returns its second argument if it // got [true] or its third argument if it got [false] (like an `if` // statement). Term_BRANCH Term_TermType = 65 // Returns true if any of its arguments returns true (short-circuits). Term_OR Term_TermType = 66 // Returns true if all of its arguments return true (short-circuits). Term_AND Term_TermType = 67 // Calls its Function with each entry in the sequence // and executes the array of terms that Function returns. Term_FOR_EACH Term_TermType = 68 // An anonymous function. Takes an array of numbers representing // variables (see [VAR] above), and a [Term] to execute with those in // scope. Returns a function that may be passed an array of arguments, // then executes the Term with those bound to the variable names. The // user will never construct this directly. We use it internally for // things like `map` which take a function. The "arity" of a [Function] is // the number of arguments it takes. // For example, here's what `_X_.map{|x| x+2}` turns into: // Term { // type = MAP; // args = [_X_, // Term { // type = Function; // args = [Term { // type = DATUM; // datum = Datum { // type = R_ARRAY; // r_array = [Datum { type = R_NUM; r_num = 1; }]; // }; // }, // Term { // type = ADD; // args = [Term { // type = VAR; // args = [Term { // type = DATUM; // datum = Datum { type = R_NUM; // r_num = 1}; // }]; // }, // Term { // type = DATUM; // datum = Datum { type = R_NUM; r_num = 2; }; // }]; // }]; // }]; Term_FUNC Term_TermType = 69 // Indicates to ORDER_BY that this attribute is to be sorted in ascending order. Term_ASC Term_TermType = 73 // Indicates to ORDER_BY that this attribute is to be sorted in descending order. Term_DESC Term_TermType = 74 // Gets info about anything. INFO is most commonly called on tables. Term_INFO Term_TermType = 79 // `a.match(b)` returns a match object if the string `a` // matches the regular expression `b`. Term_MATCH Term_TermType = 97 // Change the case of a string. Term_UPCASE Term_TermType = 141 Term_DOWNCASE Term_TermType = 142 // Select a number of elements from sequence with uniform distribution. Term_SAMPLE Term_TermType = 81 // Evaluates its first argument. If that argument returns // NULL or throws an error related to the absence of an // expected value (for instance, accessing a non-existent // field or adding NULL to an integer), DEFAULT will either // return its second argument or execute it if it's a // function. If the second argument is a function, it will be // passed either the text of the error or NULL as its // argument. Term_DEFAULT Term_TermType = 92 // Parses its first argument as a json string and returns it as a // datum. Term_JSON Term_TermType = 98 // Returns the datum as a JSON string. // N.B.: we would really prefer this be named TO_JSON and that exists as // an alias in Python and JavaScript drivers; however it conflicts with the // standard `to_json` method defined by Ruby's standard json library. Term_TO_JSON_STRING Term_TermType = 172 // Parses its first arguments as an ISO 8601 time and returns it as a // datum. Term_ISO8601 Term_TermType = 99 // Prints a time as an ISO 8601 time. Term_TO_ISO8601 Term_TermType = 100 // Returns a time given seconds since epoch in UTC. Term_EPOCH_TIME Term_TermType = 101 // Returns seconds since epoch in UTC given a time. Term_TO_EPOCH_TIME Term_TermType = 102 // The time the query was received by the server. Term_NOW Term_TermType = 103 // Puts a time into an ISO 8601 timezone. Term_IN_TIMEZONE Term_TermType = 104 // a.during(b, c) returns whether a is in the range [b, c) Term_DURING Term_TermType = 105 // Retrieves the date portion of a time. Term_DATE Term_TermType = 106 // x.time_of_day == x.date - x Term_TIME_OF_DAY Term_TermType = 126 // Returns the timezone of a time. Term_TIMEZONE Term_TermType = 127 // These access the various components of a time. Term_YEAR Term_TermType = 128 Term_MONTH Term_TermType = 129 Term_DAY Term_TermType = 130 Term_DAY_OF_WEEK Term_TermType = 131 Term_DAY_OF_YEAR Term_TermType = 132 Term_HOURS Term_TermType = 133 Term_MINUTES Term_TermType = 134 Term_SECONDS Term_TermType = 135 // Construct a time from a date and optional timezone or a // date+time and optional timezone. Term_TIME Term_TermType = 136 // Constants for ISO 8601 days of the week. Term_MONDAY Term_TermType = 107 Term_TUESDAY Term_TermType = 108 Term_WEDNESDAY Term_TermType = 109 Term_THURSDAY Term_TermType = 110 Term_FRIDAY Term_TermType = 111 Term_SATURDAY Term_TermType = 112 Term_SUNDAY Term_TermType = 113 // Constants for ISO 8601 months. Term_JANUARY Term_TermType = 114 Term_FEBRUARY Term_TermType = 115 Term_MARCH Term_TermType = 116 Term_APRIL Term_TermType = 117 Term_MAY Term_TermType = 118 Term_JUNE Term_TermType = 119 Term_JULY Term_TermType = 120 Term_AUGUST Term_TermType = 121 Term_SEPTEMBER Term_TermType = 122 Term_OCTOBER Term_TermType = 123 Term_NOVEMBER Term_TermType = 124 Term_DECEMBER Term_TermType = 125 // Indicates to MERGE to replace, or remove in case of an empty literal, the // other object rather than merge it. Term_LITERAL Term_TermType = 137 // SEQUENCE, STRING -> GROUPED_SEQUENCE | SEQUENCE, FUNCTION -> GROUPED_SEQUENCE Term_GROUP Term_TermType = 144 Term_SUM Term_TermType = 145 Term_AVG Term_TermType = 146 Term_MIN Term_TermType = 147 Term_MAX Term_TermType = 148 // `str.split()` splits on whitespace // `str.split(" ")` splits on spaces only // `str.split(" ", 5)` splits on spaces with at most 5 results // `str.split(nil, 5)` splits on whitespace with at most 5 results Term_SPLIT Term_TermType = 149 Term_UNGROUP Term_TermType = 150 // Takes a range of numbers and returns a random number within the range Term_RANDOM Term_TermType = 151 Term_CHANGES Term_TermType = 152 Term_ARGS Term_TermType = 154 // BINARY is client-only at the moment, it is not supported on the server Term_BINARY Term_TermType = 155 Term_GEOJSON Term_TermType = 157 Term_TO_GEOJSON Term_TermType = 158 Term_POINT Term_TermType = 159 Term_LINE Term_TermType = 160 Term_POLYGON Term_TermType = 161 Term_DISTANCE Term_TermType = 162 Term_INTERSECTS Term_TermType = 163 Term_INCLUDES Term_TermType = 164 Term_CIRCLE Term_TermType = 165 Term_GET_INTERSECTING Term_TermType = 166 Term_FILL Term_TermType = 167 Term_GET_NEAREST Term_TermType = 168 Term_POLYGON_SUB Term_TermType = 171 // Constants for specifying key ranges Term_MINVAL Term_TermType = 180 Term_MAXVAL Term_TermType = 181 ) var Term_TermType_name = map[int32]string{ 1: "DATUM", 2: "MAKE_ARRAY", 3: "MAKE_OBJ", 10: "VAR", 11: "JAVASCRIPT", 169: "UUID", 153: "HTTP", 12: "ERROR", 13: "IMPLICIT_VAR", 14: "DB", 15: "TABLE", 16: "GET", 78: "GET_ALL", 17: "EQ", 18: "NE", 19: "LT", 20: "LE", 21: "GT", 22: "GE", 23: "NOT", 24: "ADD", 25: "SUB", 26: "MUL", 27: "DIV", 28: "MOD", 183: "FLOOR", 184: "CEIL", 185: "ROUND", 29: "APPEND", 80: "PREPEND", 95: "DIFFERENCE", 88: "SET_INSERT", 89: "SET_INTERSECTION", 90: "SET_UNION", 91: "SET_DIFFERENCE", 30: "SLICE", 70: "SKIP", 71: "LIMIT", 87: "OFFSETS_OF", 93: "CONTAINS", 31: "GET_FIELD", 94: "KEYS", 186: "VALUES", 143: "OBJECT", 32: "HAS_FIELDS", 96: "WITH_FIELDS", 33: "PLUCK", 34: "WITHOUT", 35: "MERGE", 36: "BETWEEN_DEPRECATED", 182: "BETWEEN", 37: "REDUCE", 38: "MAP", 187: "FOLD", 39: "FILTER", 40: "CONCAT_MAP", 41: "ORDER_BY", 42: "DISTINCT", 43: "COUNT", 86: "IS_EMPTY", 44: "UNION", 45: "NTH", 170: "BRACKET", 48: "INNER_JOIN", 49: "OUTER_JOIN", 50: "EQ_JOIN", 72: "ZIP", 173: "RANGE", 82: "INSERT_AT", 83: "DELETE_AT", 84: "CHANGE_AT", 85: "SPLICE_AT", 51: "COERCE_TO", 52: "TYPE_OF", 53: "UPDATE", 54: "DELETE", 55: "REPLACE", 56: "INSERT", 57: "DB_CREATE", 58: "DB_DROP", 59: "DB_LIST", 60: "TABLE_CREATE", 61: "TABLE_DROP", 62: "TABLE_LIST", 174: "CONFIG", 175: "STATUS", 177: "WAIT", 176: "RECONFIGURE", 179: "REBALANCE", 138: "SYNC", 188: "GRANT", 75: "INDEX_CREATE", 76: "INDEX_DROP", 77: "INDEX_LIST", 139: "INDEX_STATUS", 140: "INDEX_WAIT", 156: "INDEX_RENAME", 64: "FUNCALL", 65: "BRANCH", 66: "OR", 67: "AND", 68: "FOR_EACH", 69: "FUNC", 73: "ASC", 74: "DESC", 79: "INFO", 97: "MATCH", 141: "UPCASE", 142: "DOWNCASE", 81: "SAMPLE", 92: "DEFAULT", 98: "JSON", 172: "TO_JSON_STRING", 99: "ISO8601", 100: "TO_ISO8601", 101: "EPOCH_TIME", 102: "TO_EPOCH_TIME", 103: "NOW", 104: "IN_TIMEZONE", 105: "DURING", 106: "DATE", 126: "TIME_OF_DAY", 127: "TIMEZONE", 128: "YEAR", 129: "MONTH", 130: "DAY", 131: "DAY_OF_WEEK", 132: "DAY_OF_YEAR", 133: "HOURS", 134: "MINUTES", 135: "SECONDS", 136: "TIME", 107: "MONDAY", 108: "TUESDAY", 109: "WEDNESDAY", 110: "THURSDAY", 111: "FRIDAY", 112: "SATURDAY", 113: "SUNDAY", 114: "JANUARY", 115: "FEBRUARY", 116: "MARCH", 117: "APRIL", 118: "MAY", 119: "JUNE", 120: "JULY", 121: "AUGUST", 122: "SEPTEMBER", 123: "OCTOBER", 124: "NOVEMBER", 125: "DECEMBER", 137: "LITERAL", 144: "GROUP", 145: "SUM", 146: "AVG", 147: "MIN", 148: "MAX", 149: "SPLIT", 150: "UNGROUP", 151: "RANDOM", 152: "CHANGES", 154: "ARGS", 155: "BINARY", 157: "GEOJSON", 158: "TO_GEOJSON", 159: "POINT", 160: "LINE", 161: "POLYGON", 162: "DISTANCE", 163: "INTERSECTS", 164: "INCLUDES", 165: "CIRCLE", 166: "GET_INTERSECTING", 167: "FILL", 168: "GET_NEAREST", 171: "POLYGON_SUB", 180: "MINVAL", 181: "MAXVAL", } var Term_TermType_value = map[string]int32{ "DATUM": 1, "MAKE_ARRAY": 2, "MAKE_OBJ": 3, "VAR": 10, "JAVASCRIPT": 11, "UUID": 169, "HTTP": 153, "ERROR": 12, "IMPLICIT_VAR": 13, "DB": 14, "TABLE": 15, "GET": 16, "GET_ALL": 78, "EQ": 17, "NE": 18, "LT": 19, "LE": 20, "GT": 21, "GE": 22, "NOT": 23, "ADD": 24, "SUB": 25, "MUL": 26, "DIV": 27, "MOD": 28, "FLOOR": 183, "CEIL": 184, "ROUND": 185, "APPEND": 29, "PREPEND": 80, "DIFFERENCE": 95, "SET_INSERT": 88, "SET_INTERSECTION": 89, "SET_UNION": 90, "SET_DIFFERENCE": 91, "SLICE": 30, "SKIP": 70, "LIMIT": 71, "OFFSETS_OF": 87, "CONTAINS": 93, "GET_FIELD": 31, "KEYS": 94, "VALUES": 186, "OBJECT": 143, "HAS_FIELDS": 32, "WITH_FIELDS": 96, "PLUCK": 33, "WITHOUT": 34, "MERGE": 35, "BETWEEN_DEPRECATED": 36, "BETWEEN": 182, "REDUCE": 37, "MAP": 38, "FOLD": 187, "FILTER": 39, "CONCAT_MAP": 40, "ORDER_BY": 41, "DISTINCT": 42, "COUNT": 43, "IS_EMPTY": 86, "UNION": 44, "NTH": 45, "BRACKET": 170, "INNER_JOIN": 48, "OUTER_JOIN": 49, "EQ_JOIN": 50, "ZIP": 72, "RANGE": 173, "INSERT_AT": 82, "DELETE_AT": 83, "CHANGE_AT": 84, "SPLICE_AT": 85, "COERCE_TO": 51, "TYPE_OF": 52, "UPDATE": 53, "DELETE": 54, "REPLACE": 55, "INSERT": 56, "DB_CREATE": 57, "DB_DROP": 58, "DB_LIST": 59, "TABLE_CREATE": 60, "TABLE_DROP": 61, "TABLE_LIST": 62, "CONFIG": 174, "STATUS": 175, "WAIT": 177, "RECONFIGURE": 176, "REBALANCE": 179, "SYNC": 138, "GRANT": 188, "INDEX_CREATE": 75, "INDEX_DROP": 76, "INDEX_LIST": 77, "INDEX_STATUS": 139, "INDEX_WAIT": 140, "INDEX_RENAME": 156, "FUNCALL": 64, "BRANCH": 65, "OR": 66, "AND": 67, "FOR_EACH": 68, "FUNC": 69, "ASC": 73, "DESC": 74, "INFO": 79, "MATCH": 97, "UPCASE": 141, "DOWNCASE": 142, "SAMPLE": 81, "DEFAULT": 92, "JSON": 98, "TO_JSON_STRING": 172, "ISO8601": 99, "TO_ISO8601": 100, "EPOCH_TIME": 101, "TO_EPOCH_TIME": 102, "NOW": 103, "IN_TIMEZONE": 104, "DURING": 105, "DATE": 106, "TIME_OF_DAY": 126, "TIMEZONE": 127, "YEAR": 128, "MONTH": 129, "DAY": 130, "DAY_OF_WEEK": 131, "DAY_OF_YEAR": 132, "HOURS": 133, "MINUTES": 134, "SECONDS": 135, "TIME": 136, "MONDAY": 107, "TUESDAY": 108, "WEDNESDAY": 109, "THURSDAY": 110, "FRIDAY": 111, "SATURDAY": 112, "SUNDAY": 113, "JANUARY": 114, "FEBRUARY": 115, "MARCH": 116, "APRIL": 117, "MAY": 118, "JUNE": 119, "JULY": 120, "AUGUST": 121, "SEPTEMBER": 122, "OCTOBER": 123, "NOVEMBER": 124, "DECEMBER": 125, "LITERAL": 137, "GROUP": 144, "SUM": 145, "AVG": 146, "MIN": 147, "MAX": 148, "SPLIT": 149, "UNGROUP": 150, "RANDOM": 151, "CHANGES": 152, "ARGS": 154, "BINARY": 155, "GEOJSON": 157, "TO_GEOJSON": 158, "POINT": 159, "LINE": 160, "POLYGON": 161, "DISTANCE": 162, "INTERSECTS": 163, "INCLUDES": 164, "CIRCLE": 165, "GET_INTERSECTING": 166, "FILL": 167, "GET_NEAREST": 168, "POLYGON_SUB": 171, "MINVAL": 180, "MAXVAL": 181, } func (x Term_TermType) Enum() *Term_TermType { p := new(Term_TermType) *p = x return p } func (x Term_TermType) String() string { return proto.EnumName(Term_TermType_name, int32(x)) } func (x Term_TermType) MarshalJSON() ([]byte, error) { return json.Marshal(x.String()) } func (x *Term_TermType) UnmarshalJSON(data []byte) error { value, err := proto.UnmarshalJSONEnum(Term_TermType_value, data, "Term_TermType") if err != nil { return err } *x = Term_TermType(value) return nil } type VersionDummy struct { XXX_unrecognized []byte `json:"-"` } func (m *VersionDummy) Reset() { *m = VersionDummy{} } func (m *VersionDummy) String() string { return proto.CompactTextString(m) } func (*VersionDummy) ProtoMessage() {} // You send one of: // * A [START] query with a [Term] to evaluate and a unique-per-connection token. // * A [CONTINUE] query with the same token as a [START] query that returned // [SUCCESS_PARTIAL] in its [Response]. // * A [STOP] query with the same token as a [START] query that you want to stop. // * A [NOREPLY_WAIT] query with a unique per-connection token. The server answers // with a [WAIT_COMPLETE] [Response]. // * A [SERVER_INFO] query. The server answers with a [SERVER_INFO] [Response]. type Query struct { Type *Query_QueryType `protobuf:"varint,1,opt,name=type,enum=Query_QueryType" json:"type,omitempty"` // A [Term] is how we represent the operations we want a query to perform. Query *Term `protobuf:"bytes,2,opt,name=query" json:"query,omitempty"` Token *int64 `protobuf:"varint,3,opt,name=token" json:"token,omitempty"` // This flag is ignored on the server. `noreply` should be added // to `global_optargs` instead (the key "noreply" should map to // either true or false). OBSOLETENoreply *bool `protobuf:"varint,4,opt,name=OBSOLETE_noreply,def=0" json:"OBSOLETE_noreply,omitempty"` // If this is set to [true], then [Datum] values will sometimes be // of [DatumType] [R_JSON] (see below). This can provide enormous // speedups in languages with poor protobuf libraries. AcceptsRJson *bool `protobuf:"varint,5,opt,name=accepts_r_json,def=0" json:"accepts_r_json,omitempty"` GlobalOptargs []*Query_AssocPair `protobuf:"bytes,6,rep,name=global_optargs" json:"global_optargs,omitempty"` XXX_unrecognized []byte `json:"-"` } func (m *Query) Reset() { *m = Query{} } func (m *Query) String() string { return proto.CompactTextString(m) } func (*Query) ProtoMessage() {} const Default_Query_OBSOLETENoreply bool = false const Default_Query_AcceptsRJson bool = false func (m *Query) GetType() Query_QueryType { if m != nil && m.Type != nil { return *m.Type } return Query_START } func (m *Query) GetQuery() *Term { if m != nil { return m.Query } return nil } func (m *Query) GetToken() int64 { if m != nil && m.Token != nil { return *m.Token } return 0 } func (m *Query) GetOBSOLETENoreply() bool { if m != nil && m.OBSOLETENoreply != nil { return *m.OBSOLETENoreply } return Default_Query_OBSOLETENoreply } func (m *Query) GetAcceptsRJson() bool { if m != nil && m.AcceptsRJson != nil { return *m.AcceptsRJson } return Default_Query_AcceptsRJson } func (m *Query) GetGlobalOptargs() []*Query_AssocPair { if m != nil { return m.GlobalOptargs } return nil } type Query_AssocPair struct { Key *string `protobuf:"bytes,1,opt,name=key" json:"key,omitempty"` Val *Term `protobuf:"bytes,2,opt,name=val" json:"val,omitempty"` XXX_unrecognized []byte `json:"-"` } func (m *Query_AssocPair) Reset() { *m = Query_AssocPair{} } func (m *Query_AssocPair) String() string { return proto.CompactTextString(m) } func (*Query_AssocPair) ProtoMessage() {} func (m *Query_AssocPair) GetKey() string { if m != nil && m.Key != nil { return *m.Key } return "" } func (m *Query_AssocPair) GetVal() *Term { if m != nil { return m.Val } return nil } // A backtrace frame (see `backtrace` in Response below) type Frame struct { Type *Frame_FrameType `protobuf:"varint,1,opt,name=type,enum=Frame_FrameType" json:"type,omitempty"` Pos *int64 `protobuf:"varint,2,opt,name=pos" json:"pos,omitempty"` Opt *string `protobuf:"bytes,3,opt,name=opt" json:"opt,omitempty"` XXX_unrecognized []byte `json:"-"` } func (m *Frame) Reset() { *m = Frame{} } func (m *Frame) String() string { return proto.CompactTextString(m) } func (*Frame) ProtoMessage() {} func (m *Frame) GetType() Frame_FrameType { if m != nil && m.Type != nil { return *m.Type } return Frame_POS } func (m *Frame) GetPos() int64 { if m != nil && m.Pos != nil { return *m.Pos } return 0 } func (m *Frame) GetOpt() string { if m != nil && m.Opt != nil { return *m.Opt } return "" } type Backtrace struct { Frames []*Frame `protobuf:"bytes,1,rep,name=frames" json:"frames,omitempty"` XXX_unrecognized []byte `json:"-"` } func (m *Backtrace) Reset() { *m = Backtrace{} } func (m *Backtrace) String() string { return proto.CompactTextString(m) } func (*Backtrace) ProtoMessage() {} func (m *Backtrace) GetFrames() []*Frame { if m != nil { return m.Frames } return nil } // You get back a response with the same [token] as your query. type Response struct { Type *Response_ResponseType `protobuf:"varint,1,opt,name=type,enum=Response_ResponseType" json:"type,omitempty"` ErrorType *Response_ErrorType `protobuf:"varint,7,opt,name=error_type,enum=Response_ErrorType" json:"error_type,omitempty"` Notes []Response_ResponseNote `protobuf:"varint,6,rep,name=notes,enum=Response_ResponseNote" json:"notes,omitempty"` Token *int64 `protobuf:"varint,2,opt,name=token" json:"token,omitempty"` // [response] contains 1 RQL datum if [type] is [SUCCESS_ATOM] or // [SERVER_INFO]. [response] contains many RQL data if [type] is // [SUCCESS_SEQUENCE] or [SUCCESS_PARTIAL]. [response] contains 1 // error message (of type [R_STR]) in all other cases. Response []*Datum `protobuf:"bytes,3,rep,name=response" json:"response,omitempty"` // If [type] is [CLIENT_ERROR], [TYPE_ERROR], or [RUNTIME_ERROR], then a // backtrace will be provided. The backtrace says where in the query the // error occurred. Ideally this information will be presented to the user as // a pretty-printed version of their query with the erroneous section // underlined. A backtrace is a series of 0 or more [Frame]s, each of which // specifies either the index of a positional argument or the name of an // optional argument. (Those words will make more sense if you look at the // [Term] message below.) Backtrace *Backtrace `protobuf:"bytes,4,opt,name=backtrace" json:"backtrace,omitempty"` // If the [global_optargs] in the [Query] that this [Response] is a // response to contains a key "profile" which maps to a static value of // true then [profile] will contain a [Datum] which provides profiling // information about the execution of the query. This field should be // returned to the user along with the result that would normally be // returned (a datum or a cursor). In official drivers this is accomplished // by putting them inside of an object with "value" mapping to the return // value and "profile" mapping to the profile object. Profile *Datum `protobuf:"bytes,5,opt,name=profile" json:"profile,omitempty"` XXX_unrecognized []byte `json:"-"` } func (m *Response) Reset() { *m = Response{} } func (m *Response) String() string { return proto.CompactTextString(m) } func (*Response) ProtoMessage() {} func (m *Response) GetType() Response_ResponseType { if m != nil && m.Type != nil { return *m.Type } return Response_SUCCESS_ATOM } func (m *Response) GetErrorType() Response_ErrorType { if m != nil && m.ErrorType != nil { return *m.ErrorType } return Response_INTERNAL } func (m *Response) GetNotes() []Response_ResponseNote { if m != nil { return m.Notes } return nil } func (m *Response) GetToken() int64 { if m != nil && m.Token != nil { return *m.Token } return 0 } func (m *Response) GetResponse() []*Datum { if m != nil { return m.Response } return nil } func (m *Response) GetBacktrace() *Backtrace { if m != nil { return m.Backtrace } return nil } func (m *Response) GetProfile() *Datum { if m != nil { return m.Profile } return nil } // A [Datum] is a chunk of data that can be serialized to disk or returned to // the user in a Response. Currently we only support JSON types, but we may // support other types in the future (e.g., a date type or an integer type). type Datum struct { Type *Datum_DatumType `protobuf:"varint,1,opt,name=type,enum=Datum_DatumType" json:"type,omitempty"` RBool *bool `protobuf:"varint,2,opt,name=r_bool" json:"r_bool,omitempty"` RNum *float64 `protobuf:"fixed64,3,opt,name=r_num" json:"r_num,omitempty"` RStr *string `protobuf:"bytes,4,opt,name=r_str" json:"r_str,omitempty"` RArray []*Datum `protobuf:"bytes,5,rep,name=r_array" json:"r_array,omitempty"` RObject []*Datum_AssocPair `protobuf:"bytes,6,rep,name=r_object" json:"r_object,omitempty"` XXX_unrecognized []byte `json:"-"` } func (m *Datum) Reset() { *m = Datum{} } func (m *Datum) String() string { return proto.CompactTextString(m) } func (*Datum) ProtoMessage() {} func (m *Datum) GetType() Datum_DatumType { if m != nil && m.Type != nil { return *m.Type } return Datum_R_NULL } func (m *Datum) GetRBool() bool { if m != nil && m.RBool != nil { return *m.RBool } return false } func (m *Datum) GetRNum() float64 { if m != nil && m.RNum != nil { return *m.RNum } return 0 } func (m *Datum) GetRStr() string { if m != nil && m.RStr != nil { return *m.RStr } return "" } func (m *Datum) GetRArray() []*Datum { if m != nil { return m.RArray } return nil } func (m *Datum) GetRObject() []*Datum_AssocPair { if m != nil { return m.RObject } return nil } type Datum_AssocPair struct { Key *string `protobuf:"bytes,1,opt,name=key" json:"key,omitempty"` Val *Datum `protobuf:"bytes,2,opt,name=val" json:"val,omitempty"` XXX_unrecognized []byte `json:"-"` } func (m *Datum_AssocPair) Reset() { *m = Datum_AssocPair{} } func (m *Datum_AssocPair) String() string { return proto.CompactTextString(m) } func (*Datum_AssocPair) ProtoMessage() {} func (m *Datum_AssocPair) GetKey() string { if m != nil && m.Key != nil { return *m.Key } return "" } func (m *Datum_AssocPair) GetVal() *Datum { if m != nil { return m.Val } return nil } // A [Term] is either a piece of data (see **Datum** above), or an operator and // its operands. If you have a [Datum], it's stored in the member [datum]. If // you have an operator, its positional arguments are stored in [args] and its // optional arguments are stored in [optargs]. // // A note about type signatures: // We use the following notation to denote types: // arg1_type, arg2_type, argrest_type... -> result_type // So, for example, if we have a function `avg` that takes any number of // arguments and averages them, we might write: // NUMBER... -> NUMBER // Or if we had a function that took one number modulo another: // NUMBER, NUMBER -> NUMBER // Or a function that takes a table and a primary key of any Datum type, then // retrieves the entry with that primary key: // Table, DATUM -> OBJECT // Some arguments must be provided as literal values (and not the results of sub // terms). These are marked with a `!`. // Optional arguments are specified within curly braces as argname `:` value // type (e.x `{noreply:BOOL}`) // Many RQL operations are polymorphic. For these, alterantive type signatures // are separated by `|`. // // The RQL type hierarchy is as follows: // Top // DATUM // NULL // BOOL // NUMBER // STRING // OBJECT // SingleSelection // ARRAY // Sequence // ARRAY // Stream // StreamSelection // Table // Database // Function // Ordering - used only by ORDER_BY // Pathspec -- an object, string, or array that specifies a path // Error type Term struct { Type *Term_TermType `protobuf:"varint,1,opt,name=type,enum=Term_TermType" json:"type,omitempty"` // This is only used when type is DATUM. Datum *Datum `protobuf:"bytes,2,opt,name=datum" json:"datum,omitempty"` Args []*Term `protobuf:"bytes,3,rep,name=args" json:"args,omitempty"` Optargs []*Term_AssocPair `protobuf:"bytes,4,rep,name=optargs" json:"optargs,omitempty"` XXX_unrecognized []byte `json:"-"` } func (m *Term) Reset() { *m = Term{} } func (m *Term) String() string { return proto.CompactTextString(m) } func (*Term) ProtoMessage() {} func (m *Term) GetType() Term_TermType { if m != nil && m.Type != nil { return *m.Type } return Term_DATUM } func (m *Term) GetDatum() *Datum { if m != nil { return m.Datum } return nil } func (m *Term) GetArgs() []*Term { if m != nil { return m.Args } return nil } func (m *Term) GetOptargs() []*Term_AssocPair { if m != nil { return m.Optargs } return nil } type Term_AssocPair struct { Key *string `protobuf:"bytes,1,opt,name=key" json:"key,omitempty"` Val *Term `protobuf:"bytes,2,opt,name=val" json:"val,omitempty"` XXX_unrecognized []byte `json:"-"` } func (m *Term_AssocPair) Reset() { *m = Term_AssocPair{} } func (m *Term_AssocPair) String() string { return proto.CompactTextString(m) } func (*Term_AssocPair) ProtoMessage() {} func (m *Term_AssocPair) GetKey() string { if m != nil && m.Key != nil { return *m.Key } return "" } func (m *Term_AssocPair) GetVal() *Term { if m != nil { return m.Val } return nil } func init() { proto.RegisterEnum("VersionDummy_Version", VersionDummy_Version_name, VersionDummy_Version_value) proto.RegisterEnum("VersionDummy_Protocol", VersionDummy_Protocol_name, VersionDummy_Protocol_value) proto.RegisterEnum("Query_QueryType", Query_QueryType_name, Query_QueryType_value) proto.RegisterEnum("Frame_FrameType", Frame_FrameType_name, Frame_FrameType_value) proto.RegisterEnum("Response_ResponseType", Response_ResponseType_name, Response_ResponseType_value) proto.RegisterEnum("Response_ErrorType", Response_ErrorType_name, Response_ErrorType_value) proto.RegisterEnum("Response_ResponseNote", Response_ResponseNote_name, Response_ResponseNote_value) proto.RegisterEnum("Datum_DatumType", Datum_DatumType_name, Datum_DatumType_value) proto.RegisterEnum("Term_TermType", Term_TermType_name, Term_TermType_value) } gorethink-2.0.4/ql2/ql2.proto000066400000000000000000001224231272042630000157700ustar00rootroot00000000000000//////////////////////////////////////////////////////////////////////////////// // THE HIGH-LEVEL VIEW // //////////////////////////////////////////////////////////////////////////////// // Process: When you first open a connection, send the magic number // for the version of the protobuf you're targeting (in the [Version] // enum). This should **NOT** be sent as a protobuf; just send the // little-endian 32-bit integer over the wire raw. This number should // only be sent once per connection. // The magic number shall be followed by an authorization key. The // first 4 bytes are the length of the key to be sent as a little-endian // 32-bit integer, followed by the key string. Even if there is no key, // an empty string should be sent (length 0 and no data). // Following the authorization key, the client shall send a magic number // for the communication protocol they want to use (in the [Protocol] // enum). This shall be a little-endian 32-bit integer. // The server will then respond with a NULL-terminated string response. // "SUCCESS" indicates that the connection has been accepted. Any other // response indicates an error, and the response string should describe // the error. // Next, for each query you want to send, construct a [Query] protobuf // and serialize it to a binary blob. Send the blob's size to the // server encoded as a little-endian 32-bit integer, followed by the // blob itself. You will recieve a [Response] protobuf back preceded // by its own size, once again encoded as a little-endian 32-bit // integer. You can see an example exchange below in **EXAMPLE**. // A query consists of a [Term] to evaluate and a unique-per-connection // [token]. // Tokens are used for two things: // * Keeping track of which responses correspond to which queries. // * Batched queries. Some queries return lots of results, so we send back // batches of <1000, and you need to send a [CONTINUE] query with the same // token to get more results from the original query. //////////////////////////////////////////////////////////////////////////////// message VersionDummy { // We need to wrap it like this for some // non-conforming protobuf libraries // This enum contains the magic numbers for your version. See **THE HIGH-LEVEL // VIEW** for what to do with it. enum Version { V0_1 = 0x3f61ba36; V0_2 = 0x723081e1; // Authorization key during handshake V0_3 = 0x5f75e83e; // Authorization key and protocol during handshake V0_4 = 0x400c2d20; // Queries execute in parallel V1_0 = 0x34c2bdc3; // Users and permissions } // The protocol to use after the handshake, specified in V0_3 enum Protocol { PROTOBUF = 0x271ffc41; JSON = 0x7e6970c7; } } // You send one of: // * A [START] query with a [Term] to evaluate and a unique-per-connection token. // * A [CONTINUE] query with the same token as a [START] query that returned // [SUCCESS_PARTIAL] in its [Response]. // * A [STOP] query with the same token as a [START] query that you want to stop. // * A [NOREPLY_WAIT] query with a unique per-connection token. The server answers // with a [WAIT_COMPLETE] [Response]. // * A [SERVER_INFO] query. The server answers with a [SERVER_INFO] [Response]. message Query { enum QueryType { START = 1; // Start a new query. CONTINUE = 2; // Continue a query that returned [SUCCESS_PARTIAL] // (see [Response]). STOP = 3; // Stop a query partway through executing. NOREPLY_WAIT = 4; // Wait for noreply operations to finish. SERVER_INFO = 5; // Get server information. } optional QueryType type = 1; // A [Term] is how we represent the operations we want a query to perform. optional Term query = 2; // only present when [type] = [START] optional int64 token = 3; // This flag is ignored on the server. `noreply` should be added // to `global_optargs` instead (the key "noreply" should map to // either true or false). optional bool OBSOLETE_noreply = 4 [default = false]; // If this is set to [true], then [Datum] values will sometimes be // of [DatumType] [R_JSON] (see below). This can provide enormous // speedups in languages with poor protobuf libraries. optional bool accepts_r_json = 5 [default = false]; message AssocPair { optional string key = 1; optional Term val = 2; } repeated AssocPair global_optargs = 6; } // A backtrace frame (see `backtrace` in Response below) message Frame { enum FrameType { POS = 1; // Error occurred in a positional argument. OPT = 2; // Error occurred in an optional argument. } optional FrameType type = 1; optional int64 pos = 2; // The index of the positional argument. optional string opt = 3; // The name of the optional argument. } message Backtrace { repeated Frame frames = 1; } // You get back a response with the same [token] as your query. message Response { enum ResponseType { // These response types indicate success. SUCCESS_ATOM = 1; // Query returned a single RQL datatype. SUCCESS_SEQUENCE = 2; // Query returned a sequence of RQL datatypes. SUCCESS_PARTIAL = 3; // Query returned a partial sequence of RQL // datatypes. If you send a [CONTINUE] query with // the same token as this response, you will get // more of the sequence. Keep sending [CONTINUE] // queries until you get back [SUCCESS_SEQUENCE]. WAIT_COMPLETE = 4; // A [NOREPLY_WAIT] query completed. SERVER_INFO = 5; // The data for a [SERVER_INFO] request. This is // the same as `SUCCESS_ATOM` except that there will // never be profiling data. // These response types indicate failure. CLIENT_ERROR = 16; // Means the client is buggy. An example is if the // client sends a malformed protobuf, or tries to // send [CONTINUE] for an unknown token. COMPILE_ERROR = 17; // Means the query failed during parsing or type // checking. For example, if you pass too many // arguments to a function. RUNTIME_ERROR = 18; // Means the query failed at runtime. An example is // if you add together two values from a table, but // they turn out at runtime to be booleans rather // than numbers. } optional ResponseType type = 1; // If `ResponseType` is `RUNTIME_ERROR`, this may be filled in with more // information about the error. enum ErrorType { INTERNAL = 1000000; RESOURCE_LIMIT = 2000000; QUERY_LOGIC = 3000000; NON_EXISTENCE = 3100000; OP_FAILED = 4100000; OP_INDETERMINATE = 4200000; USER = 5000000; PERMISSION_ERROR = 6000000; } optional ErrorType error_type = 7; // ResponseNotes are used to provide information about the query // response that may be useful for people writing drivers or ORMs. // Currently all the notes we send indicate that a stream has certain // special properties. enum ResponseNote { // The stream is a changefeed stream (e.g. `r.table('test').changes()`). SEQUENCE_FEED = 1; // The stream is a point changefeed stream // (e.g. `r.table('test').get(0).changes()`). ATOM_FEED = 2; // The stream is an order_by_limit changefeed stream // (e.g. `r.table('test').order_by(index: 'id').limit(5).changes()`). ORDER_BY_LIMIT_FEED = 3; // The stream is a union of multiple changefeed types that can't be // collapsed to a single type // (e.g. `r.table('test').changes().union(r.table('test').get(0).changes())`). UNIONED_FEED = 4; // The stream is a changefeed stream and includes notes on what state // the changefeed stream is in (e.g. objects of the form `{state: // 'initializing'}`). INCLUDES_STATES = 5; } repeated ResponseNote notes = 6; optional int64 token = 2; // Indicates what [Query] this response corresponds to. // [response] contains 1 RQL datum if [type] is [SUCCESS_ATOM] or // [SERVER_INFO]. [response] contains many RQL data if [type] is // [SUCCESS_SEQUENCE] or [SUCCESS_PARTIAL]. [response] contains 1 // error message (of type [R_STR]) in all other cases. repeated Datum response = 3; // If [type] is [CLIENT_ERROR], [TYPE_ERROR], or [RUNTIME_ERROR], then a // backtrace will be provided. The backtrace says where in the query the // error occurred. Ideally this information will be presented to the user as // a pretty-printed version of their query with the erroneous section // underlined. A backtrace is a series of 0 or more [Frame]s, each of which // specifies either the index of a positional argument or the name of an // optional argument. (Those words will make more sense if you look at the // [Term] message below.) optional Backtrace backtrace = 4; // Contains n [Frame]s when you get back an error. // If the [global_optargs] in the [Query] that this [Response] is a // response to contains a key "profile" which maps to a static value of // true then [profile] will contain a [Datum] which provides profiling // information about the execution of the query. This field should be // returned to the user along with the result that would normally be // returned (a datum or a cursor). In official drivers this is accomplished // by putting them inside of an object with "value" mapping to the return // value and "profile" mapping to the profile object. optional Datum profile = 5; } // A [Datum] is a chunk of data that can be serialized to disk or returned to // the user in a Response. Currently we only support JSON types, but we may // support other types in the future (e.g., a date type or an integer type). message Datum { enum DatumType { R_NULL = 1; R_BOOL = 2; R_NUM = 3; // a double R_STR = 4; R_ARRAY = 5; R_OBJECT = 6; // This [DatumType] will only be used if [accepts_r_json] is // set to [true] in [Query]. [r_str] will be filled with a // JSON encoding of the [Datum]. R_JSON = 7; // uses r_str } optional DatumType type = 1; optional bool r_bool = 2; optional double r_num = 3; optional string r_str = 4; repeated Datum r_array = 5; message AssocPair { optional string key = 1; optional Datum val = 2; } repeated AssocPair r_object = 6; } // A [Term] is either a piece of data (see **Datum** above), or an operator and // its operands. If you have a [Datum], it's stored in the member [datum]. If // you have an operator, its positional arguments are stored in [args] and its // optional arguments are stored in [optargs]. // // A note about type signatures: // We use the following notation to denote types: // arg1_type, arg2_type, argrest_type... -> result_type // So, for example, if we have a function `avg` that takes any number of // arguments and averages them, we might write: // NUMBER... -> NUMBER // Or if we had a function that took one number modulo another: // NUMBER, NUMBER -> NUMBER // Or a function that takes a table and a primary key of any Datum type, then // retrieves the entry with that primary key: // Table, DATUM -> OBJECT // Some arguments must be provided as literal values (and not the results of sub // terms). These are marked with a `!`. // Optional arguments are specified within curly braces as argname `:` value // type (e.x `{noreply:BOOL}`) // Many RQL operations are polymorphic. For these, alterantive type signatures // are separated by `|`. // // The RQL type hierarchy is as follows: // Top // DATUM // NULL // BOOL // NUMBER // STRING // OBJECT // SingleSelection // ARRAY // Sequence // ARRAY // Stream // StreamSelection // Table // Database // Function // Ordering - used only by ORDER_BY // Pathspec -- an object, string, or array that specifies a path // Error message Term { enum TermType { // A RQL datum, stored in `datum` below. DATUM = 1; MAKE_ARRAY = 2; // DATUM... -> ARRAY // Evaluate the terms in [optargs] and make an object MAKE_OBJ = 3; // {...} -> OBJECT // * Compound types // Takes an integer representing a variable and returns the value stored // in that variable. It's the responsibility of the client to translate // from their local representation of a variable to a unique _non-negative_ // integer for that variable. (We do it this way instead of letting // clients provide variable names as strings to discourage // variable-capturing client libraries, and because it's more efficient // on the wire.) VAR = 10; // !NUMBER -> DATUM // Takes some javascript code and executes it. JAVASCRIPT = 11; // STRING {timeout: !NUMBER} -> DATUM | // STRING {timeout: !NUMBER} -> Function(*) UUID = 169; // () -> DATUM // Takes an HTTP URL and gets it. If the get succeeds and // returns valid JSON, it is converted into a DATUM HTTP = 153; // STRING {data: OBJECT | STRING, // timeout: !NUMBER, // method: STRING, // params: OBJECT, // header: OBJECT | ARRAY, // attempts: NUMBER, // redirects: NUMBER, // verify: BOOL, // page: FUNC | STRING, // page_limit: NUMBER, // auth: OBJECT, // result_format: STRING, // } -> STRING | STREAM // Takes a string and throws an error with that message. // Inside of a `default` block, you can omit the first // argument to rethrow whatever error you catch (this is most // useful as an argument to the `default` filter optarg). ERROR = 12; // STRING -> Error | -> Error // Takes nothing and returns a reference to the implicit variable. IMPLICIT_VAR = 13; // -> DATUM // * Data Operators // Returns a reference to a database. DB = 14; // STRING -> Database // Returns a reference to a table. TABLE = 15; // Database, STRING, {read_mode:STRING, identifier_format:STRING} -> Table // STRING, {read_mode:STRING, identifier_format:STRING} -> Table // Gets a single element from a table by its primary or a secondary key. GET = 16; // Table, STRING -> SingleSelection | Table, NUMBER -> SingleSelection | // Table, STRING -> NULL | Table, NUMBER -> NULL | GET_ALL = 78; // Table, DATUM..., {index:!STRING} => ARRAY // Simple DATUM Ops EQ = 17; // DATUM... -> BOOL NE = 18; // DATUM... -> BOOL LT = 19; // DATUM... -> BOOL LE = 20; // DATUM... -> BOOL GT = 21; // DATUM... -> BOOL GE = 22; // DATUM... -> BOOL NOT = 23; // BOOL -> BOOL // ADD can either add two numbers or concatenate two arrays. ADD = 24; // NUMBER... -> NUMBER | STRING... -> STRING SUB = 25; // NUMBER... -> NUMBER MUL = 26; // NUMBER... -> NUMBER DIV = 27; // NUMBER... -> NUMBER MOD = 28; // NUMBER, NUMBER -> NUMBER FLOOR = 183; // NUMBER -> NUMBER CEIL = 184; // NUMBER -> NUMBER ROUND = 185; // NUMBER -> NUMBER // DATUM Array Ops // Append a single element to the end of an array (like `snoc`). APPEND = 29; // ARRAY, DATUM -> ARRAY // Prepend a single element to the end of an array (like `cons`). PREPEND = 80; // ARRAY, DATUM -> ARRAY //Remove the elements of one array from another array. DIFFERENCE = 95; // ARRAY, ARRAY -> ARRAY // DATUM Set Ops // Set ops work on arrays. They don't use actual sets and thus have // performance characteristics you would expect from arrays rather than // from sets. All set operations have the post condition that they // array they return contains no duplicate values. SET_INSERT = 88; // ARRAY, DATUM -> ARRAY SET_INTERSECTION = 89; // ARRAY, ARRAY -> ARRAY SET_UNION = 90; // ARRAY, ARRAY -> ARRAY SET_DIFFERENCE = 91; // ARRAY, ARRAY -> ARRAY SLICE = 30; // Sequence, NUMBER, NUMBER -> Sequence SKIP = 70; // Sequence, NUMBER -> Sequence LIMIT = 71; // Sequence, NUMBER -> Sequence OFFSETS_OF = 87; // Sequence, DATUM -> Sequence | Sequence, Function(1) -> Sequence CONTAINS = 93; // Sequence, (DATUM | Function(1))... -> BOOL // Stream/Object Ops // Get a particular field from an object, or map that over a // sequence. GET_FIELD = 31; // OBJECT, STRING -> DATUM // | Sequence, STRING -> Sequence // Return an array containing the keys of the object. KEYS = 94; // OBJECT -> ARRAY // Return an array containing the values of the object. VALUES = 186; // OBJECT -> ARRAY // Creates an object OBJECT = 143; // STRING, DATUM, ... -> OBJECT // Check whether an object contains all the specified fields, // or filters a sequence so that all objects inside of it // contain all the specified fields. HAS_FIELDS = 32; // OBJECT, Pathspec... -> BOOL // x.with_fields(...) <=> x.has_fields(...).pluck(...) WITH_FIELDS = 96; // Sequence, Pathspec... -> Sequence // Get a subset of an object by selecting some attributes to preserve, // or map that over a sequence. (Both pick and pluck, polymorphic.) PLUCK = 33; // Sequence, Pathspec... -> Sequence | OBJECT, Pathspec... -> OBJECT // Get a subset of an object by selecting some attributes to discard, or // map that over a sequence. (Both unpick and without, polymorphic.) WITHOUT = 34; // Sequence, Pathspec... -> Sequence | OBJECT, Pathspec... -> OBJECT // Merge objects (right-preferential) MERGE = 35; // OBJECT... -> OBJECT | Sequence -> Sequence // Sequence Ops // Get all elements of a sequence between two values. // Half-open by default, but the openness of either side can be // changed by passing 'closed' or 'open for `right_bound` or // `left_bound`. BETWEEN_DEPRECATED = 36; // Deprecated version of between, which allows `null` to specify unboundedness // With the newer version, clients should use `r.minval` and `r.maxval` for unboundedness BETWEEN = 182; // StreamSelection, DATUM, DATUM, {index:!STRING, right_bound:STRING, left_bound:STRING} -> StreamSelection REDUCE = 37; // Sequence, Function(2) -> DATUM MAP = 38; // Sequence, Function(1) -> Sequence // The arity of the function should be // Sequence..., Function(sizeof...(Sequence)) -> Sequence FOLD = 187; // Sequence, Datum, Function(2), {Function(3), Function(1) // Filter a sequence with either a function or a shortcut // object (see API docs for details). The body of FILTER is // wrapped in an implicit `.default(false)`, and you can // change the default value by specifying the `default` // optarg. If you make the default `r.error`, all errors // caught by `default` will be rethrown as if the `default` // did not exist. FILTER = 39; // Sequence, Function(1), {default:DATUM} -> Sequence | // Sequence, OBJECT, {default:DATUM} -> Sequence // Map a function over a sequence and then concatenate the results together. CONCAT_MAP = 40; // Sequence, Function(1) -> Sequence // Order a sequence based on one or more attributes. ORDER_BY = 41; // Sequence, (!STRING | Ordering)..., {index: (!STRING | Ordering)} -> Sequence // Get all distinct elements of a sequence (like `uniq`). DISTINCT = 42; // Sequence -> Sequence // Count the number of elements in a sequence, or only the elements that match // a given filter. COUNT = 43; // Sequence -> NUMBER | Sequence, DATUM -> NUMBER | Sequence, Function(1) -> NUMBER IS_EMPTY = 86; // Sequence -> BOOL // Take the union of multiple sequences (preserves duplicate elements! (use distinct)). UNION = 44; // Sequence... -> Sequence // Get the Nth element of a sequence. NTH = 45; // Sequence, NUMBER -> DATUM // do NTH or GET_FIELD depending on target object BRACKET = 170; // Sequence | OBJECT, NUMBER | STRING -> DATUM // OBSOLETE_GROUPED_MAPREDUCE = 46; // OBSOLETE_GROUPBY = 47; INNER_JOIN = 48; // Sequence, Sequence, Function(2) -> Sequence OUTER_JOIN = 49; // Sequence, Sequence, Function(2) -> Sequence // An inner-join that does an equality comparison on two attributes. EQ_JOIN = 50; // Sequence, !STRING, Sequence, {index:!STRING} -> Sequence ZIP = 72; // Sequence -> Sequence RANGE = 173; // -> Sequence [0, +inf) // NUMBER -> Sequence [0, a) // NUMBER, NUMBER -> Sequence [a, b) // Array Ops // Insert an element in to an array at a given index. INSERT_AT = 82; // ARRAY, NUMBER, DATUM -> ARRAY // Remove an element at a given index from an array. DELETE_AT = 83; // ARRAY, NUMBER -> ARRAY | // ARRAY, NUMBER, NUMBER -> ARRAY // Change the element at a given index of an array. CHANGE_AT = 84; // ARRAY, NUMBER, DATUM -> ARRAY // Splice one array in to another array. SPLICE_AT = 85; // ARRAY, NUMBER, ARRAY -> ARRAY // * Type Ops // Coerces a datum to a named type (e.g. "bool"). // If you previously used `stream_to_array`, you should use this instead // with the type "array". COERCE_TO = 51; // Top, STRING -> Top // Returns the named type of a datum (e.g. TYPE_OF(true) = "BOOL") TYPE_OF = 52; // Top -> STRING // * Write Ops (the OBJECTs contain data about number of errors etc.) // Updates all the rows in a selection. Calls its Function with the row // to be updated, and then merges the result of that call. UPDATE = 53; // StreamSelection, Function(1), {non_atomic:BOOL, durability:STRING, return_changes:BOOL} -> OBJECT | // SingleSelection, Function(1), {non_atomic:BOOL, durability:STRING, return_changes:BOOL} -> OBJECT | // StreamSelection, OBJECT, {non_atomic:BOOL, durability:STRING, return_changes:BOOL} -> OBJECT | // SingleSelection, OBJECT, {non_atomic:BOOL, durability:STRING, return_changes:BOOL} -> OBJECT // Deletes all the rows in a selection. DELETE = 54; // StreamSelection, {durability:STRING, return_changes:BOOL} -> OBJECT | SingleSelection -> OBJECT // Replaces all the rows in a selection. Calls its Function with the row // to be replaced, and then discards it and stores the result of that // call. REPLACE = 55; // StreamSelection, Function(1), {non_atomic:BOOL, durability:STRING, return_changes:BOOL} -> OBJECT | SingleSelection, Function(1), {non_atomic:BOOL, durability:STRING, return_changes:BOOL} -> OBJECT // Inserts into a table. If `conflict` is replace, overwrites // entries with the same primary key. If `conflict` is // update, does an update on the entry. If `conflict` is // error, or is omitted, conflicts will trigger an error. INSERT = 56; // Table, OBJECT, {conflict:STRING, durability:STRING, return_changes:BOOL} -> OBJECT | Table, Sequence, {conflict:STRING, durability:STRING, return_changes:BOOL} -> OBJECT // * Administrative OPs // Creates a database with a particular name. DB_CREATE = 57; // STRING -> OBJECT // Drops a database with a particular name. DB_DROP = 58; // STRING -> OBJECT // Lists all the databases by name. (Takes no arguments) DB_LIST = 59; // -> ARRAY // Creates a table with a particular name in a particular // database. (You may omit the first argument to use the // default database.) TABLE_CREATE = 60; // Database, STRING, {primary_key:STRING, shards:NUMBER, replicas:NUMBER, primary_replica_tag:STRING} -> OBJECT // Database, STRING, {primary_key:STRING, shards:NUMBER, replicas:OBJECT, primary_replica_tag:STRING} -> OBJECT // STRING, {primary_key:STRING, shards:NUMBER, replicas:NUMBER, primary_replica_tag:STRING} -> OBJECT // STRING, {primary_key:STRING, shards:NUMBER, replicas:OBJECT, primary_replica_tag:STRING} -> OBJECT // Drops a table with a particular name from a particular // database. (You may omit the first argument to use the // default database.) TABLE_DROP = 61; // Database, STRING -> OBJECT // STRING -> OBJECT // Lists all the tables in a particular database. (You may // omit the first argument to use the default database.) TABLE_LIST = 62; // Database -> ARRAY // -> ARRAY // Returns the row in the `rethinkdb.table_config` or `rethinkdb.db_config` table // that corresponds to the given database or table. CONFIG = 174; // Database -> SingleSelection // Table -> SingleSelection // Returns the row in the `rethinkdb.table_status` table that corresponds to the // given table. STATUS = 175; // Table -> SingleSelection // Called on a table, waits for that table to be ready for read/write operations. // Called on a database, waits for all of the tables in the database to be ready. // Returns the corresponding row or rows from the `rethinkdb.table_status` table. WAIT = 177; // Table -> OBJECT // Database -> OBJECT // Generates a new config for the given table, or all tables in the given database // The `shards` and `replicas` arguments are required. If `emergency_repair` is // specified, it will enter a completely different mode of repairing a table // which has lost half or more of its replicas. RECONFIGURE = 176; // Database|Table, {shards:NUMBER, replicas:NUMBER [, // dry_run:BOOLEAN] // } -> OBJECT // Database|Table, {shards:NUMBER, replicas:OBJECT [, // primary_replica_tag:STRING, // nonvoting_replica_tags:ARRAY, // dry_run:BOOLEAN] // } -> OBJECT // Table, {emergency_repair:STRING, dry_run:BOOLEAN} -> OBJECT // Balances the table's shards but leaves everything else the same. Can also be // applied to an entire database at once. REBALANCE = 179; // Table -> OBJECT // Database -> OBJECT // Ensures that previously issued soft-durability writes are complete and // written to disk. SYNC = 138; // Table -> OBJECT // Set global, database, or table-specific permissions GRANT = 188; // -> OBJECT // Database -> OBJECT // Table -> OBJECT // * Secondary indexes OPs // Creates a new secondary index with a particular name and definition. INDEX_CREATE = 75; // Table, STRING, Function(1), {multi:BOOL} -> OBJECT // Drops a secondary index with a particular name from the specified table. INDEX_DROP = 76; // Table, STRING -> OBJECT // Lists all secondary indexes on a particular table. INDEX_LIST = 77; // Table -> ARRAY // Gets information about whether or not a set of indexes are ready to // be accessed. Returns a list of objects that look like this: // {index:STRING, ready:BOOL[, progress:NUMBER]} INDEX_STATUS = 139; // Table, STRING... -> ARRAY // Blocks until a set of indexes are ready to be accessed. Returns the // same values INDEX_STATUS. INDEX_WAIT = 140; // Table, STRING... -> ARRAY // Renames the given index to a new name INDEX_RENAME = 156; // Table, STRING, STRING, {overwrite:BOOL} -> OBJECT // * Control Operators // Calls a function on data FUNCALL = 64; // Function(*), DATUM... -> DATUM // Executes its first argument, and returns its second argument if it // got [true] or its third argument if it got [false] (like an `if` // statement). BRANCH = 65; // BOOL, Top, Top -> Top // Returns true if any of its arguments returns true (short-circuits). OR = 66; // BOOL... -> BOOL // Returns true if all of its arguments return true (short-circuits). AND = 67; // BOOL... -> BOOL // Calls its Function with each entry in the sequence // and executes the array of terms that Function returns. FOR_EACH = 68; // Sequence, Function(1) -> OBJECT //////////////////////////////////////////////////////////////////////////////// ////////// Special Terms //////////////////////////////////////////////////////////////////////////////// // An anonymous function. Takes an array of numbers representing // variables (see [VAR] above), and a [Term] to execute with those in // scope. Returns a function that may be passed an array of arguments, // then executes the Term with those bound to the variable names. The // user will never construct this directly. We use it internally for // things like `map` which take a function. The "arity" of a [Function] is // the number of arguments it takes. // For example, here's what `_X_.map{|x| x+2}` turns into: // Term { // type = MAP; // args = [_X_, // Term { // type = Function; // args = [Term { // type = DATUM; // datum = Datum { // type = R_ARRAY; // r_array = [Datum { type = R_NUM; r_num = 1; }]; // }; // }, // Term { // type = ADD; // args = [Term { // type = VAR; // args = [Term { // type = DATUM; // datum = Datum { type = R_NUM; // r_num = 1}; // }]; // }, // Term { // type = DATUM; // datum = Datum { type = R_NUM; r_num = 2; }; // }]; // }]; // }]; FUNC = 69; // ARRAY, Top -> ARRAY -> Top // Indicates to ORDER_BY that this attribute is to be sorted in ascending order. ASC = 73; // !STRING -> Ordering // Indicates to ORDER_BY that this attribute is to be sorted in descending order. DESC = 74; // !STRING -> Ordering // Gets info about anything. INFO is most commonly called on tables. INFO = 79; // Top -> OBJECT // `a.match(b)` returns a match object if the string `a` // matches the regular expression `b`. MATCH = 97; // STRING, STRING -> DATUM // Change the case of a string. UPCASE = 141; // STRING -> STRING DOWNCASE = 142; // STRING -> STRING // Select a number of elements from sequence with uniform distribution. SAMPLE = 81; // Sequence, NUMBER -> Sequence // Evaluates its first argument. If that argument returns // NULL or throws an error related to the absence of an // expected value (for instance, accessing a non-existent // field or adding NULL to an integer), DEFAULT will either // return its second argument or execute it if it's a // function. If the second argument is a function, it will be // passed either the text of the error or NULL as its // argument. DEFAULT = 92; // Top, Top -> Top // Parses its first argument as a json string and returns it as a // datum. JSON = 98; // STRING -> DATUM // Returns the datum as a JSON string. // N.B.: we would really prefer this be named TO_JSON and that exists as // an alias in Python and JavaScript drivers; however it conflicts with the // standard `to_json` method defined by Ruby's standard json library. TO_JSON_STRING = 172; // DATUM -> STRING // Parses its first arguments as an ISO 8601 time and returns it as a // datum. ISO8601 = 99; // STRING -> PSEUDOTYPE(TIME) // Prints a time as an ISO 8601 time. TO_ISO8601 = 100; // PSEUDOTYPE(TIME) -> STRING // Returns a time given seconds since epoch in UTC. EPOCH_TIME = 101; // NUMBER -> PSEUDOTYPE(TIME) // Returns seconds since epoch in UTC given a time. TO_EPOCH_TIME = 102; // PSEUDOTYPE(TIME) -> NUMBER // The time the query was received by the server. NOW = 103; // -> PSEUDOTYPE(TIME) // Puts a time into an ISO 8601 timezone. IN_TIMEZONE = 104; // PSEUDOTYPE(TIME), STRING -> PSEUDOTYPE(TIME) // a.during(b, c) returns whether a is in the range [b, c) DURING = 105; // PSEUDOTYPE(TIME), PSEUDOTYPE(TIME), PSEUDOTYPE(TIME) -> BOOL // Retrieves the date portion of a time. DATE = 106; // PSEUDOTYPE(TIME) -> PSEUDOTYPE(TIME) // x.time_of_day == x.date - x TIME_OF_DAY = 126; // PSEUDOTYPE(TIME) -> NUMBER // Returns the timezone of a time. TIMEZONE = 127; // PSEUDOTYPE(TIME) -> STRING // These access the various components of a time. YEAR = 128; // PSEUDOTYPE(TIME) -> NUMBER MONTH = 129; // PSEUDOTYPE(TIME) -> NUMBER DAY = 130; // PSEUDOTYPE(TIME) -> NUMBER DAY_OF_WEEK = 131; // PSEUDOTYPE(TIME) -> NUMBER DAY_OF_YEAR = 132; // PSEUDOTYPE(TIME) -> NUMBER HOURS = 133; // PSEUDOTYPE(TIME) -> NUMBER MINUTES = 134; // PSEUDOTYPE(TIME) -> NUMBER SECONDS = 135; // PSEUDOTYPE(TIME) -> NUMBER // Construct a time from a date and optional timezone or a // date+time and optional timezone. TIME = 136; // NUMBER, NUMBER, NUMBER, STRING -> PSEUDOTYPE(TIME) | // NUMBER, NUMBER, NUMBER, NUMBER, NUMBER, NUMBER, STRING -> PSEUDOTYPE(TIME) | // Constants for ISO 8601 days of the week. MONDAY = 107; // -> 1 TUESDAY = 108; // -> 2 WEDNESDAY = 109; // -> 3 THURSDAY = 110; // -> 4 FRIDAY = 111; // -> 5 SATURDAY = 112; // -> 6 SUNDAY = 113; // -> 7 // Constants for ISO 8601 months. JANUARY = 114; // -> 1 FEBRUARY = 115; // -> 2 MARCH = 116; // -> 3 APRIL = 117; // -> 4 MAY = 118; // -> 5 JUNE = 119; // -> 6 JULY = 120; // -> 7 AUGUST = 121; // -> 8 SEPTEMBER = 122; // -> 9 OCTOBER = 123; // -> 10 NOVEMBER = 124; // -> 11 DECEMBER = 125; // -> 12 // Indicates to MERGE to replace, or remove in case of an empty literal, the // other object rather than merge it. LITERAL = 137; // -> Merging // JSON -> Merging // SEQUENCE, STRING -> GROUPED_SEQUENCE | SEQUENCE, FUNCTION -> GROUPED_SEQUENCE GROUP = 144; SUM = 145; AVG = 146; MIN = 147; MAX = 148; // `str.split()` splits on whitespace // `str.split(" ")` splits on spaces only // `str.split(" ", 5)` splits on spaces with at most 5 results // `str.split(nil, 5)` splits on whitespace with at most 5 results SPLIT = 149; // STRING -> ARRAY | STRING, STRING -> ARRAY | STRING, STRING, NUMBER -> ARRAY | STRING, NULL, NUMBER -> ARRAY UNGROUP = 150; // GROUPED_DATA -> ARRAY // Takes a range of numbers and returns a random number within the range RANDOM = 151; // NUMBER, NUMBER {float:BOOL} -> DATUM CHANGES = 152; // TABLE -> STREAM ARGS = 154; // ARRAY -> SPECIAL (used to splice arguments) // BINARY is client-only at the moment, it is not supported on the server BINARY = 155; // STRING -> PSEUDOTYPE(BINARY) GEOJSON = 157; // OBJECT -> PSEUDOTYPE(GEOMETRY) TO_GEOJSON = 158; // PSEUDOTYPE(GEOMETRY) -> OBJECT POINT = 159; // NUMBER, NUMBER -> PSEUDOTYPE(GEOMETRY) LINE = 160; // (ARRAY | PSEUDOTYPE(GEOMETRY))... -> PSEUDOTYPE(GEOMETRY) POLYGON = 161; // (ARRAY | PSEUDOTYPE(GEOMETRY))... -> PSEUDOTYPE(GEOMETRY) DISTANCE = 162; // PSEUDOTYPE(GEOMETRY), PSEUDOTYPE(GEOMETRY) {geo_system:STRING, unit:STRING} -> NUMBER INTERSECTS = 163; // PSEUDOTYPE(GEOMETRY), PSEUDOTYPE(GEOMETRY) -> BOOL INCLUDES = 164; // PSEUDOTYPE(GEOMETRY), PSEUDOTYPE(GEOMETRY) -> BOOL CIRCLE = 165; // PSEUDOTYPE(GEOMETRY), NUMBER {num_vertices:NUMBER, geo_system:STRING, unit:STRING, fill:BOOL} -> PSEUDOTYPE(GEOMETRY) GET_INTERSECTING = 166; // TABLE, PSEUDOTYPE(GEOMETRY) {index:!STRING} -> StreamSelection FILL = 167; // PSEUDOTYPE(GEOMETRY) -> PSEUDOTYPE(GEOMETRY) GET_NEAREST = 168; // TABLE, PSEUDOTYPE(GEOMETRY) {index:!STRING, max_results:NUM, max_dist:NUM, geo_system:STRING, unit:STRING} -> ARRAY POLYGON_SUB = 171; // PSEUDOTYPE(GEOMETRY), PSEUDOTYPE(GEOMETRY) -> PSEUDOTYPE(GEOMETRY) // Constants for specifying key ranges MINVAL = 180; MAXVAL = 181; } optional TermType type = 1; // This is only used when type is DATUM. optional Datum datum = 2; repeated Term args = 3; // Holds the positional arguments of the query. message AssocPair { optional string key = 1; optional Term val = 2; } repeated AssocPair optargs = 4; // Holds the optional arguments of the query. // (Note that the order of the optional arguments doesn't matter; think of a // Hash.) } //////////////////////////////////////////////////////////////////////////////// // EXAMPLE // //////////////////////////////////////////////////////////////////////////////// // ```ruby // r.table('tbl', {:read_mode => 'outdated'}).insert([{:id => 0}, {:id => 1}]) // ``` // Would turn into: // Term { // type = INSERT; // args = [Term { // type = TABLE; // args = [Term { // type = DATUM; // datum = Datum { type = R_STR; r_str = "tbl"; }; // }]; // optargs = [["read_mode", // Term { // type = DATUM; // datum = Datum { type = R_STR; r_bool = "outdated"; }; // }]]; // }, // Term { // type = MAKE_ARRAY; // args = [Term { // type = DATUM; // datum = Datum { type = R_OBJECT; r_object = [["id", 0]]; }; // }, // Term { // type = DATUM; // datum = Datum { type = R_OBJECT; r_object = [["id", 1]]; }; // }]; // }] // } // And the server would reply: // Response { // type = SUCCESS_ATOM; // token = 1; // response = [Datum { type = R_OBJECT; r_object = [["inserted", 2]]; }]; // } // Or, if there were an error: // Response { // type = RUNTIME_ERROR; // token = 1; // response = [Datum { type = R_STR; r_str = "The table `tbl` doesn't exist!"; }]; // backtrace = [Frame { type = POS; pos = 0; }, Frame { type = POS; pos = 0; }]; // } gorethink-2.0.4/query.go000066400000000000000000000250101272042630000151750ustar00rootroot00000000000000package gorethink import ( "fmt" "strconv" "strings" p "gopkg.in/dancannon/gorethink.v2/ql2" ) // A Query represents a query ready to be sent to the database, A Query differs // from a Term as it contains both a query type and token. These values are used // by the database to determine if the query is continuing a previous request // and also allows the driver to identify the response as they can come out of // order. type Query struct { Type p.Query_QueryType Token int64 Term *Term Opts map[string]interface{} builtTerm interface{} } func (q *Query) build() []interface{} { res := []interface{}{int(q.Type)} if q.Term != nil { res = append(res, q.builtTerm) } if len(q.Opts) > 0 { res = append(res, q.Opts) } return res } type termsList []Term type termsObj map[string]Term // A Term represents a query that is being built. Terms consist of a an array of // "sub-terms" and a term type. When a Term is a sub-term the first element of // the terms data is its parent Term. // // When built the term becomes a JSON array, for more information on the format // see http://rethinkdb.com/docs/writing-drivers/. type Term struct { name string rawQuery bool rootTerm bool termType p.Term_TermType data interface{} args []Term optArgs map[string]Term lastErr error } // build takes the query tree and prepares it to be sent as a JSON // expression func (t Term) build() (interface{}, error) { var err error if t.lastErr != nil { return nil, t.lastErr } if t.rawQuery { return t.data, nil } switch t.termType { case p.Term_DATUM: return t.data, nil case p.Term_MAKE_OBJ: res := map[string]interface{}{} for k, v := range t.optArgs { res[k], err = v.build() if err != nil { return nil, err } } return res, nil case p.Term_BINARY: if len(t.args) == 0 { return map[string]interface{}{ "$reql_type$": "BINARY", "data": t.data, }, nil } } args := make([]interface{}, len(t.args)) optArgs := make(map[string]interface{}, len(t.optArgs)) for i, v := range t.args { arg, err := v.build() if err != nil { return nil, err } args[i] = arg } for k, v := range t.optArgs { optArgs[k], err = v.build() if err != nil { return nil, err } } ret := []interface{}{int(t.termType)} if len(args) > 0 { ret = append(ret, args) } if len(optArgs) > 0 { ret = append(ret, optArgs) } return ret, nil } // String returns a string representation of the query tree func (t Term) String() string { switch t.termType { case p.Term_MAKE_ARRAY: return fmt.Sprintf("[%s]", strings.Join(argsToStringSlice(t.args), ", ")) case p.Term_MAKE_OBJ: return fmt.Sprintf("{%s}", strings.Join(optArgsToStringSlice(t.optArgs), ", ")) case p.Term_FUNC: // Get string representation of each argument args := []string{} for _, v := range t.args[0].args { args = append(args, fmt.Sprintf("var_%d", v.data)) } return fmt.Sprintf("func(%s r.Term) r.Term { return %s }", strings.Join(args, ", "), t.args[1].String(), ) case p.Term_VAR: return fmt.Sprintf("var_%s", t.args[0]) case p.Term_IMPLICIT_VAR: return "r.Row" case p.Term_DATUM: switch v := t.data.(type) { case string: return strconv.Quote(v) default: return fmt.Sprintf("%v", v) } case p.Term_BINARY: if len(t.args) == 0 { return fmt.Sprintf("r.binary()") } } if t.rootTerm { return fmt.Sprintf("r.%s(%s)", t.name, strings.Join(allArgsToStringSlice(t.args, t.optArgs), ", ")) } if t.args == nil { return "r" } return fmt.Sprintf("%s.%s(%s)", t.args[0].String(), t.name, strings.Join(allArgsToStringSlice(t.args[1:], t.optArgs), ", ")) } // OptArgs is an interface used to represent a terms optional arguments. All // optional argument types have a toMap function, the returned map can be encoded // and sent as part of the query. type OptArgs interface { toMap() map[string]interface{} } // WriteResponse is a helper type used when dealing with the response of a // write query. It is also returned by the RunWrite function. type WriteResponse struct { Errors int `gorethink:"errors"` Inserted int `gorethink:"inserted"` Updated int `gorethink:"updated"` Unchanged int `gorethink:"unchanged"` Replaced int `gorethink:"replaced"` Renamed int `gorethink:"renamed"` Skipped int `gorethink:"skipped"` Deleted int `gorethink:"deleted"` Created int `gorethink:"created"` DBsCreated int `gorethink:"dbs_created"` TablesCreated int `gorethink:"tables_created"` Dropped int `gorethink:"dropped"` DBsDropped int `gorethink:"dbs_dropped"` TablesDropped int `gorethink:"tables_dropped"` GeneratedKeys []string `gorethink:"generated_keys"` FirstError string `gorethink:"first_error"` // populated if Errors > 0 ConfigChanges []ChangeResponse `gorethink:"config_changes"` Changes []ChangeResponse } // ChangeResponse is a helper type used when dealing with changefeeds. The type // contains both the value before the query and the new value. type ChangeResponse struct { NewValue interface{} `gorethink:"new_val"` OldValue interface{} `gorethink:"old_val"` } // RunOpts contains the optional arguments for the Run function. type RunOpts struct { DB interface{} `gorethink:"db,omitempty"` Db interface{} `gorethink:"db,omitempty"` // Deprecated Profile interface{} `gorethink:"profile,omitempty"` Durability interface{} `gorethink:"durability,omitempty"` UseOutdated interface{} `gorethink:"use_outdated,omitempty"` // Deprecated ArrayLimit interface{} `gorethink:"array_limit,omitempty"` TimeFormat interface{} `gorethink:"time_format,omitempty"` GroupFormat interface{} `gorethink:"group_format,omitempty"` BinaryFormat interface{} `gorethink:"binary_format,omitempty"` GeometryFormat interface{} `gorethink:"geometry_format,omitempty"` MinBatchRows interface{} `gorethink:"min_batch_rows,omitempty"` MaxBatchRows interface{} `gorethink:"max_batch_rows,omitempty"` MaxBatchBytes interface{} `gorethink:"max_batch_bytes,omitempty"` MaxBatchSeconds interface{} `gorethink:"max_batch_seconds,omitempty"` FirstBatchScaledownFactor interface{} `gorethink:"first_batch_scaledown_factor,omitempty"` } func (o *RunOpts) toMap() map[string]interface{} { return optArgsToMap(o) } // Run runs a query using the given connection. // // rows, err := query.Run(sess) // if err != nil { // // error // } // // var doc MyDocumentType // for rows.Next(&doc) { // // Do something with document // } func (t Term) Run(s *Session, optArgs ...RunOpts) (*Cursor, error) { opts := map[string]interface{}{} if len(optArgs) >= 1 { opts = optArgs[0].toMap() } if s == nil || !s.IsConnected() { return nil, ErrConnectionClosed } q, err := s.newQuery(t, opts) if err != nil { return nil, err } return s.Query(q) } // RunWrite runs a query using the given connection but unlike Run automatically // scans the result into a variable of type WriteResponse. This function should be used // if you are running a write query (such as Insert, Update, TableCreate, etc...). // // If an error occurs when running the write query the first error is returned. // // res, err := r.DB("database").Table("table").Insert(doc).RunWrite(sess) func (t Term) RunWrite(s *Session, optArgs ...RunOpts) (WriteResponse, error) { var response WriteResponse res, err := t.Run(s, optArgs...) if err != nil { return response, err } defer res.Close() if err = res.One(&response); err != nil { return response, err } if response.Errors > 0 { return response, fmt.Errorf("%s", response.FirstError) } return response, nil } // ReadOne is a shortcut method that runs the query on the given connection // and reads one response from the cursor before closing it. // // It returns any errors encountered from running the query or reading the response func (t Term) ReadOne(dest interface{}, s *Session, optArgs ...RunOpts) error { res, err := t.Run(s, optArgs...) if err != nil { return err } return res.One(dest) } // ReadAll is a shortcut method that runs the query on the given connection // and reads all of the responses from the cursor before closing it. // // It returns any errors encountered from running the query or reading the responses func (t Term) ReadAll(dest interface{}, s *Session, optArgs ...RunOpts) error { res, err := t.Run(s, optArgs...) if err != nil { return err } return res.All(dest) } // ExecOpts contains the optional arguments for the Exec function and inherits // its options from RunOpts, the only difference is the addition of the NoReply // field. // // When NoReply is true it causes the driver not to wait to receive the result // and return immediately. type ExecOpts struct { DB interface{} `gorethink:"db,omitempty"` Db interface{} `gorethink:"db,omitempty"` // Deprecated Profile interface{} `gorethink:"profile,omitempty"` Durability interface{} `gorethink:"durability,omitempty"` UseOutdated interface{} `gorethink:"use_outdated,omitempty"` // Deprecated ArrayLimit interface{} `gorethink:"array_limit,omitempty"` TimeFormat interface{} `gorethink:"time_format,omitempty"` GroupFormat interface{} `gorethink:"group_format,omitempty"` BinaryFormat interface{} `gorethink:"binary_format,omitempty"` GeometryFormat interface{} `gorethink:"geometry_format,omitempty"` MinBatchRows interface{} `gorethink:"min_batch_rows,omitempty"` MaxBatchRows interface{} `gorethink:"max_batch_rows,omitempty"` MaxBatchBytes interface{} `gorethink:"max_batch_bytes,omitempty"` MaxBatchSeconds interface{} `gorethink:"max_batch_seconds,omitempty"` FirstBatchScaledownFactor interface{} `gorethink:"first_batch_scaledown_factor,omitempty"` NoReply interface{} `gorethink:"noreply,omitempty"` } func (o *ExecOpts) toMap() map[string]interface{} { return optArgsToMap(o) } // Exec runs the query but does not return the result. Exec will still wait for // the response to be received unless the NoReply field is true. // // err := r.DB("database").Table("table").Insert(doc).Exec(sess, r.ExecOpts{ // NoReply: true, // }) func (t Term) Exec(s *Session, optArgs ...ExecOpts) error { opts := map[string]interface{}{} if len(optArgs) >= 1 { opts = optArgs[0].toMap() } q, err := s.newQuery(t, opts) if err != nil { return err } return s.Exec(q) } gorethink-2.0.4/query_admin.go000066400000000000000000000057731272042630000163630ustar00rootroot00000000000000package gorethink import ( p "gopkg.in/dancannon/gorethink.v2/ql2" ) // Config can be used to read and/or update the configurations for individual // tables or databases. func (t Term) Config() Term { return constructMethodTerm(t, "Config", p.Term_CONFIG, []interface{}{}, map[string]interface{}{}) } // Rebalance rebalances the shards of a table. When called on a database, all // the tables in that database will be rebalanced. func (t Term) Rebalance() Term { return constructMethodTerm(t, "Rebalance", p.Term_REBALANCE, []interface{}{}, map[string]interface{}{}) } // ReconfigureOpts contains the optional arguments for the Reconfigure term. type ReconfigureOpts struct { Shards interface{} `gorethink:"shards,omitempty"` Replicas interface{} `gorethink:"replicas,omitempty"` PrimaryTag interface{} `gorethink:"primary_replica_tag,omitempty"` DryRun interface{} `gorethink:"dry_run,omitempty"` EmergencyRepair interface{} `gorethink:"emergency_repair,omitempty"` NonVotingReplicaTags interface{} `gorethink:"nonvoting_replica_tags,omitempty"` } func (o *ReconfigureOpts) toMap() map[string]interface{} { return optArgsToMap(o) } // Reconfigure a table's sharding and replication. func (t Term) Reconfigure(opts ReconfigureOpts) Term { return constructMethodTerm(t, "Reconfigure", p.Term_RECONFIGURE, []interface{}{}, opts.toMap()) } // Status return the status of a table func (t Term) Status() Term { return constructMethodTerm(t, "Status", p.Term_STATUS, []interface{}{}, map[string]interface{}{}) } // WaitOpts contains the optional arguments for the Wait term. type WaitOpts struct { WaitFor interface{} `gorethink:"wait_for,omitempty"` Timeout interface{} `gorethink:"timeout,omitempty"` } func (o *WaitOpts) toMap() map[string]interface{} { return optArgsToMap(o) } // Wait for a table or all the tables in a database to be ready. A table may be // temporarily unavailable after creation, rebalancing or reconfiguring. The // wait command blocks until the given table (or database) is fully up to date. // // Deprecated: This function is not supported by RethinkDB 2.3 and above. func Wait(optArgs ...WaitOpts) Term { opts := map[string]interface{}{} if len(optArgs) >= 1 { opts = optArgs[0].toMap() } return constructRootTerm("Wait", p.Term_WAIT, []interface{}{}, opts) } // Wait for a table or all the tables in a database to be ready. A table may be // temporarily unavailable after creation, rebalancing or reconfiguring. The // wait command blocks until the given table (or database) is fully up to date. func (t Term) Wait(optArgs ...WaitOpts) Term { opts := map[string]interface{}{} if len(optArgs) >= 1 { opts = optArgs[0].toMap() } return constructMethodTerm(t, "Wait", p.Term_WAIT, []interface{}{}, opts) } // Grant modifies access permissions for a user account, globally or on a // per-database or per-table basis. func (t Term) Grant(args ...interface{}) Term { return constructMethodTerm(t, "Grant", p.Term_GRANT, args, map[string]interface{}{}) } gorethink-2.0.4/query_admin_test.go000066400000000000000000000051661272042630000174160ustar00rootroot00000000000000package gorethink import ( test "gopkg.in/check.v1" ) func (s *RethinkSuite) TestAdminDbConfig(c *test.C) { DB("test").TableDrop("test").Exec(session) DB("test").TableCreate("test").Exec(session) // Test index rename query := DB("test").Table("test").Config() res, err := query.Run(session) c.Assert(err, test.IsNil) var response map[string]interface{} err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response["name"], test.Equals, "test") } func (s *RethinkSuite) TestAdminTableConfig(c *test.C) { DB("test").TableDrop("test").Exec(session) DB("test").TableCreate("test").Exec(session) // Test index rename query := DB("test").Config() res, err := query.Run(session) c.Assert(err, test.IsNil) var response map[string]interface{} err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response["name"], test.Equals, "test") } func (s *RethinkSuite) TestAdminTableStatus(c *test.C) { DB("test").TableDrop("test").Exec(session) DB("test").TableCreate("test").Exec(session) // Test index rename query := DB("test").Table("test").Status() res, err := query.Run(session) c.Assert(err, test.IsNil) var response map[string]interface{} err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response["name"], test.Equals, "test") c.Assert(response["status"], test.NotNil) } func (s *RethinkSuite) TestAdminWaitOpts(c *test.C) { DB("test").TableDrop("test").Exec(session) DB("test").TableCreate("test").Exec(session) query := DB("test").Table("test").Wait(WaitOpts{ WaitFor: "all_replicas_ready", Timeout: 10, }) res, err := query.Run(session) c.Assert(err, test.IsNil) var response map[string]interface{} err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response["ready"].(float64) > 0, test.Equals, true) } func (s *RethinkSuite) TestAdminStatus(c *test.C) { DB("test").TableDrop("test").Exec(session) DB("test").TableCreate("test").Exec(session) // Test index rename query := DB("test").Table("test").Wait() res, err := query.Run(session) c.Assert(err, test.IsNil) var response map[string]interface{} err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response["ready"], test.Equals, float64(1)) } func (s *RethinkSuite) TestAdminGrantDatabase(c *test.C) { DB("rethinkdb").Table("users").Insert(map[string]string{ "id": "test_user", "password": "password", }).Exec(session) DB("test").TableDrop("test_grant").Exec(session) DB("test").TableCreate("test_grant").Exec(session) err := DB("test").Table("test_grant").Grant("test_user", map[string]bool{ "read": true, "write": true, "config": true, }).Exec(session) c.Assert(err, test.IsNil) } gorethink-2.0.4/query_aggregation.go000066400000000000000000000231641272042630000175540ustar00rootroot00000000000000package gorethink import p "gopkg.in/dancannon/gorethink.v2/ql2" // Aggregation // These commands are used to compute smaller values from large sequences. // Reduce produces a single value from a sequence through repeated application // of a reduction function // // It takes one argument of type `func (r.Term, r.Term) interface{}`, for // example this query sums all elements in an array: // // r.Expr([]int{1,3,6}).Reduce(func (left, right r.Term) interface{} { // return left.Add(right) // }) func (t Term) Reduce(args ...interface{}) Term { return constructMethodTerm(t, "Reduce", p.Term_REDUCE, funcWrapArgs(args), map[string]interface{}{}) } // DistinctOpts contains the optional arguments for the Distinct term type DistinctOpts struct { Index interface{} `gorethink:"index,omitempty"` } func (o *DistinctOpts) toMap() map[string]interface{} { return optArgsToMap(o) } // Distinct removes duplicate elements from the sequence. func (t Term) Distinct(optArgs ...DistinctOpts) Term { opts := map[string]interface{}{} if len(optArgs) >= 1 { opts = optArgs[0].toMap() } return constructMethodTerm(t, "Distinct", p.Term_DISTINCT, []interface{}{}, opts) } // Group takes a stream and partitions it into multiple groups based on the // fields or functions provided. Commands chained after group will be // called on each of these grouped sub-streams, producing grouped data. func (t Term) Group(fieldOrFunctions ...interface{}) Term { return constructMethodTerm(t, "Group", p.Term_GROUP, funcWrapArgs(fieldOrFunctions), map[string]interface{}{}) } // MultiGroup takes a stream and partitions it into multiple groups based on the // fields or functions provided. Commands chained after group will be // called on each of these grouped sub-streams, producing grouped data. // // Unlike Group single documents can be assigned to multiple groups, similar // to the behavior of multi-indexes. When the grouping value is an array, documents // will be placed in each group that corresponds to the elements of the array. If // the array is empty the row will be ignored. func (t Term) MultiGroup(fieldOrFunctions ...interface{}) Term { return constructMethodTerm(t, "Group", p.Term_GROUP, funcWrapArgs(fieldOrFunctions), map[string]interface{}{ "multi": true, }) } // GroupByIndex takes a stream and partitions it into multiple groups based on the // fields or functions provided. Commands chained after group will be // called on each of these grouped sub-streams, producing grouped data. func (t Term) GroupByIndex(index interface{}, fieldOrFunctions ...interface{}) Term { return constructMethodTerm(t, "Group", p.Term_GROUP, funcWrapArgs(fieldOrFunctions), map[string]interface{}{ "index": index, }) } // MultiGroupByIndex takes a stream and partitions it into multiple groups based on the // fields or functions provided. Commands chained after group will be // called on each of these grouped sub-streams, producing grouped data. // // Unlike Group single documents can be assigned to multiple groups, similar // to the behavior of multi-indexes. When the grouping value is an array, documents // will be placed in each group that corresponds to the elements of the array. If // the array is empty the row will be ignored. func (t Term) MultiGroupByIndex(index interface{}, fieldOrFunctions ...interface{}) Term { return constructMethodTerm(t, "Group", p.Term_GROUP, funcWrapArgs(fieldOrFunctions), map[string]interface{}{ "index": index, "mutli": true, }) } // Ungroup takes a grouped stream or grouped data and turns it into an array of // objects representing the groups. Any commands chained after Ungroup will // operate on this array, rather than operating on each group individually. // This is useful if you want to e.g. order the groups by the value of their // reduction. func (t Term) Ungroup(args ...interface{}) Term { return constructMethodTerm(t, "Ungroup", p.Term_UNGROUP, args, map[string]interface{}{}) } // Contains returns whether or not a sequence contains all the specified values, // or if functions are provided instead, returns whether or not a sequence // contains values matching all the specified functions. func (t Term) Contains(args ...interface{}) Term { return constructMethodTerm(t, "Contains", p.Term_CONTAINS, args, map[string]interface{}{}) } // Aggregators // These standard aggregator objects are to be used in conjunction with Group. // Count the number of elements in the sequence. With a single argument, // count the number of elements equal to it. If the argument is a function, // it is equivalent to calling filter before count. func (t Term) Count(args ...interface{}) Term { return constructMethodTerm(t, "Count", p.Term_COUNT, funcWrapArgs(args), map[string]interface{}{}) } // Sum returns the sum of all the elements of a sequence. If called with a field // name, sums all the values of that field in the sequence, skipping elements of // the sequence that lack that field. If called with a function, calls that // function on every element of the sequence and sums the results, skipping // elements of the sequence where that function returns null or a non-existence // error. func (t Term) Sum(args ...interface{}) Term { return constructMethodTerm(t, "Sum", p.Term_SUM, funcWrapArgs(args), map[string]interface{}{}) } // Avg returns the average of all the elements of a sequence. If called with a field // name, averages all the values of that field in the sequence, skipping elements of // the sequence that lack that field. If called with a function, calls that function // on every element of the sequence and averages the results, skipping elements of the // sequence where that function returns null or a non-existence error. func (t Term) Avg(args ...interface{}) Term { return constructMethodTerm(t, "Avg", p.Term_AVG, funcWrapArgs(args), map[string]interface{}{}) } // Min finds the minimum of a sequence. If called with a field name, finds the element // of that sequence with the smallest value in that field. If called with a function, // calls that function on every element of the sequence and returns the element // which produced the smallest value, ignoring any elements where the function // returns null or produces a non-existence error. func (t Term) Min(args ...interface{}) Term { return constructMethodTerm(t, "Min", p.Term_MIN, funcWrapArgs(args), map[string]interface{}{}) } // MinIndex finds the minimum of a sequence. If called with a field name, finds the element // of that sequence with the smallest value in that field. If called with a function, // calls that function on every element of the sequence and returns the element // which produced the smallest value, ignoring any elements where the function // returns null or produces a non-existence error. func (t Term) MinIndex(index interface{}, args ...interface{}) Term { return constructMethodTerm(t, "Min", p.Term_MIN, funcWrapArgs(args), map[string]interface{}{ "index": index, }) } // Max finds the maximum of a sequence. If called with a field name, finds the element // of that sequence with the largest value in that field. If called with a function, // calls that function on every element of the sequence and returns the element // which produced the largest value, ignoring any elements where the function // returns null or produces a non-existence error. func (t Term) Max(args ...interface{}) Term { return constructMethodTerm(t, "Max", p.Term_MAX, funcWrapArgs(args), map[string]interface{}{}) } // MaxIndex finds the maximum of a sequence. If called with a field name, finds the element // of that sequence with the largest value in that field. If called with a function, // calls that function on every element of the sequence and returns the element // which produced the largest value, ignoring any elements where the function // returns null or produces a non-existence error. func (t Term) MaxIndex(index interface{}, args ...interface{}) Term { return constructMethodTerm(t, "Max", p.Term_MAX, funcWrapArgs(args), map[string]interface{}{ "index": index, }) } // FoldOpts contains the optional arguments for the Fold term type FoldOpts struct { Emit interface{} `gorethink:"emit,omitempty"` FinalEmit interface{} `gorethink:"finalEmit,omitempty"` } func (o *FoldOpts) toMap() map[string]interface{} { return optArgsToMap(o) } // Fold applies a function to a sequence in order, maintaining state via an // accumulator. The Fold command returns either a single value or a new sequence. // // In its first form, Fold operates like Reduce, returning a value by applying a // combining function to each element in a sequence, passing the current element // and the previous reduction result to the function. However, Fold has the // following differences from Reduce: // - it is guaranteed to proceed through the sequence from first element to last. // - it passes an initial base value to the function with the first element in // place of the previous reduction result. // // In its second form, Fold operates like ConcatMap, returning a new sequence // rather than a single value. When an emit function is provided, Fold will: // - proceed through the sequence in order and take an initial base value, as above. // - for each element in the sequence, call both the combining function and a // separate emitting function with the current element and previous reduction result. // - optionally pass the result of the combining function to the emitting function. // // If provided, the emitting function must return a list. func (t Term) Fold(base, fn interface{}, optArgs ...FoldOpts) Term { opts := map[string]interface{}{} if len(optArgs) >= 1 { opts = optArgs[0].toMap() } args := []interface{}{base, funcWrap(fn)} return constructMethodTerm(t, "Fold", p.Term_FOLD, args, opts) } gorethink-2.0.4/query_aggregation_test.go000066400000000000000000000271041272042630000206110ustar00rootroot00000000000000package gorethink import test "gopkg.in/check.v1" func (s *RethinkSuite) TestAggregationReduce(c *test.C) { var response int query := Expr(arr).Reduce(func(acc, val Term) Term { return acc.Add(val) }) res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, 45) } func (s *RethinkSuite) TestAggregationExprCount(c *test.C) { var response int query := Expr(arr).Count() res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, 9) } func (s *RethinkSuite) TestAggregationDistinct(c *test.C) { var response []int query := Expr(darr).Distinct() res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, test.HasLen, 5) } func (s *RethinkSuite) TestAggregationGroupMapReduce(c *test.C) { var response []interface{} query := Expr(objList).Group(func(row Term) Term { return row.Field("id").Mod(2).Eq(0) }).Map(func(row Term) Term { return row.Field("num") }).Reduce(func(acc, num Term) Term { return acc.Add(num) }) res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{ map[string]interface{}{"reduction": 135, "group": false}, map[string]interface{}{"reduction": 70, "group": true}, }) } func (s *RethinkSuite) TestAggregationGroupMapReduceUngroup(c *test.C) { var response []interface{} query := Expr(objList).Group(func(row Term) Term { return row.Field("id").Mod(2).Eq(0) }).Map(func(row Term) Term { return row.Field("num") }).Reduce(func(acc, num Term) Term { return acc.Add(num) }).Ungroup().OrderBy("reduction") res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{ map[string]interface{}{"reduction": 70, "group": true}, map[string]interface{}{"reduction": 135, "group": false}, }) } func (s *RethinkSuite) TestAggregationGroupMapReduceTable(c *test.C) { // Ensure table + database exist DBCreate("test").Exec(session) DB("test").TableCreate("TestAggregationGroupedMapReduceTable").Exec(session) // Insert rows err := DB("test").Table("TestAggregationGroupedMapReduceTable").Insert(objList).Exec(session) c.Assert(err, test.IsNil) var response []interface{} query := DB("test").Table("TestAggregationGroupedMapReduceTable").Group(func(row Term) Term { return row.Field("id").Mod(2).Eq(0) }).Map(func(row Term) Term { return row.Field("num") }).Reduce(func(acc, num Term) Term { return acc.Add(num) }) res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{ map[string]interface{}{"reduction": 135, "group": false}, map[string]interface{}{"reduction": 70, "group": true}, }) } func (s *RethinkSuite) TestAggregationGroupCount(c *test.C) { var response []interface{} query := Expr(objList).Group("g1") res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{ map[string]interface{}{"group": 1, "reduction": []interface{}{ map[string]interface{}{"id": 1, "num": 0, "g1": 1, "g2": 1}, map[string]interface{}{"num": 15, "g1": 1, "g2": 1, "id": 6}, map[string]interface{}{"id": 7, "num": 0, "g1": 1, "g2": 2}, }}, map[string]interface{}{"group": 2, "reduction": []interface{}{ map[string]interface{}{"g1": 2, "g2": 2, "id": 2, "num": 5}, map[string]interface{}{"num": 0, "g1": 2, "g2": 3, "id": 4}, map[string]interface{}{"num": 100, "g1": 2, "g2": 3, "id": 5}, map[string]interface{}{"g2": 3, "id": 9, "num": 25, "g1": 2}, }}, map[string]interface{}{"group": 3, "reduction": []interface{}{ map[string]interface{}{"num": 10, "g1": 3, "g2": 2, "id": 3}, }}, map[string]interface{}{"group": 4, "reduction": []interface{}{ map[string]interface{}{"id": 8, "num": 50, "g1": 4, "g2": 2}, }}, }) } func (s *RethinkSuite) TestAggregationGroupSum(c *test.C) { var response []interface{} query := Expr(objList).Group("g1").Sum("num") res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{ map[string]interface{}{"group": 1, "reduction": 15}, map[string]interface{}{"reduction": 130, "group": 2}, map[string]interface{}{"reduction": 10, "group": 3}, map[string]interface{}{"group": 4, "reduction": 50}, }) } func (s *RethinkSuite) TestAggregationGroupAvg(c *test.C) { var response []interface{} query := Expr(objList).Group("g1").Avg("num") res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{ map[string]interface{}{"group": 1, "reduction": 5}, map[string]interface{}{"group": 2, "reduction": 32.5}, map[string]interface{}{"group": 3, "reduction": 10}, map[string]interface{}{"group": 4, "reduction": 50}, }) } func (s *RethinkSuite) TestAggregationGroupMin(c *test.C) { var response []interface{} query := Expr(objList).Group("g1").Min("num") res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{ map[string]interface{}{"group": 1, "reduction": map[string]interface{}{"id": 1, "num": 0, "g1": 1, "g2": 1}}, map[string]interface{}{"reduction": map[string]interface{}{"num": 0, "g1": 2, "g2": 3, "id": 4}, "group": 2}, map[string]interface{}{"group": 3, "reduction": map[string]interface{}{"num": 10, "g1": 3, "g2": 2, "id": 3}}, map[string]interface{}{"group": 4, "reduction": map[string]interface{}{"g2": 2, "id": 8, "num": 50, "g1": 4}}, }) } func (s *RethinkSuite) TestAggregationGroupMax(c *test.C) { var response []interface{} query := Expr(objList).Group("g1").Max("num") res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{ map[string]interface{}{"reduction": map[string]interface{}{"num": 15, "g1": 1, "g2": 1, "id": 6}, "group": 1}, map[string]interface{}{"group": 2, "reduction": map[string]interface{}{"num": 100, "g1": 2, "g2": 3, "id": 5}}, map[string]interface{}{"group": 3, "reduction": map[string]interface{}{"num": 10, "g1": 3, "g2": 2, "id": 3}}, map[string]interface{}{"group": 4, "reduction": map[string]interface{}{"g2": 2, "id": 8, "num": 50, "g1": 4}}, }) } func (s *RethinkSuite) TestAggregationMin(c *test.C) { // Ensure table + database exist DBCreate("test").Exec(session) DB("test").TableCreate("Table2").Exec(session) DB("test").Table("Table2").IndexCreate("num").Exec(session) DB("test").Table("Table2").IndexWait().Exec(session) // Insert rows DB("test").Table("Table2").Insert(objList).Exec(session) // Test query var response interface{} query := DB("test").Table("Table2").MinIndex("num") res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, map[string]interface{}{"id": 1, "g1": 1, "g2": 1, "num": 0}) } func (s *RethinkSuite) TestAggregationMaxIndex(c *test.C) { // Ensure table + database exist DBCreate("test").Exec(session) DB("test").TableCreate("Table2").Exec(session) DB("test").Table("Table2").IndexCreate("num").Exec(session) DB("test").Table("Table2").IndexWait().Exec(session) // Insert rows DB("test").Table("Table2").Insert(objList).Exec(session) // Test query var response interface{} query := DB("test").Table("Table2").MaxIndex("num") res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, map[string]interface{}{"id": 5, "g1": 2, "g2": 3, "num": 100}) } func (s *RethinkSuite) TestAggregationMultipleGroupSum(c *test.C) { var response []interface{} query := Expr(objList).Group("g1", "g2").Sum("num") res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{ map[string]interface{}{"group": []interface{}{1, 1}, "reduction": 15}, map[string]interface{}{"reduction": 0, "group": []interface{}{1, 2}}, map[string]interface{}{"group": []interface{}{2, 2}, "reduction": 5}, map[string]interface{}{"reduction": 125, "group": []interface{}{2, 3}}, map[string]interface{}{"group": []interface{}{3, 2}, "reduction": 10}, map[string]interface{}{"group": []interface{}{4, 2}, "reduction": 50}, }) } func (s *RethinkSuite) TestAggregationGroupChained(c *test.C) { var response []interface{} query := Expr(objList).Group("g1").Max("num").Field("g2") res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{ map[string]interface{}{"group": 1, "reduction": 1}, map[string]interface{}{"group": 2, "reduction": 3}, map[string]interface{}{"group": 3, "reduction": 2}, map[string]interface{}{"group": 4, "reduction": 2}, }) } func (s *RethinkSuite) TestAggregationGroupUngroup(c *test.C) { var response []interface{} query := Expr(objList).Group("g1", "g2").Max("num").Ungroup() res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{ map[string]interface{}{"group": []interface{}{1, 1}, "reduction": map[string]interface{}{"g1": 1, "g2": 1, "id": 6, "num": 15}}, map[string]interface{}{"group": []interface{}{1, 2}, "reduction": map[string]interface{}{"g1": 1, "g2": 2, "id": 7, "num": 0}}, map[string]interface{}{"group": []interface{}{2, 2}, "reduction": map[string]interface{}{"g1": 2, "g2": 2, "id": 2, "num": 5}}, map[string]interface{}{"group": []interface{}{2, 3}, "reduction": map[string]interface{}{"g1": 2, "g2": 3, "id": 5, "num": 100}}, map[string]interface{}{"group": []interface{}{3, 2}, "reduction": map[string]interface{}{"g2": 2, "id": 3, "num": 10, "g1": 3}}, map[string]interface{}{"reduction": map[string]interface{}{"num": 50, "g1": 4, "g2": 2, "id": 8}, "group": []interface{}{4, 2}}, }) } func (s *RethinkSuite) TestAggregationContains(c *test.C) { var response interface{} query := Expr(arr).Contains(2) res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, true) } func (s *RethinkSuite) TestAggregationFold(c *test.C) { var response int query := Expr(arr).Reduce(func(acc, val Term) Term { return acc.Add(val) }) res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, 45) } func (s *RethinkSuite) TestAggregationFoldEmit(c *test.C) { var response []interface{} query := Expr(objList).Fold(0, func(acc, row Term) Term { return acc.Add(1) }, FoldOpts{ Emit: func(acc, row, cur Term) Term { return Branch(acc.Mod(2).Eq(0), []interface{}{row}, []interface{}{}) }, }) res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{ map[string]interface{}{"id": 1, "g1": 1, "g2": 1, "num": 0}, map[string]interface{}{"id": 3, "g1": 3, "g2": 2, "num": 10}, map[string]interface{}{"id": 5, "g1": 2, "g2": 3, "num": 100}, map[string]interface{}{"id": 7, "g1": 1, "g2": 2, "num": 0}, map[string]interface{}{"id": 9, "g1": 2, "g2": 3, "num": 25}, }) } gorethink-2.0.4/query_control.go000066400000000000000000000262211272042630000167420ustar00rootroot00000000000000package gorethink import ( "encoding/base64" "encoding/json" "reflect" p "gopkg.in/dancannon/gorethink.v2/ql2" ) // Expr converts any value to an expression and is also used by many other terms // such as Insert and Update. This function can convert the following basic Go // types (bool, int, uint, string, float) and even pointers, maps and structs. // // When evaluating structs they are encoded into a map before being sent to the // server. Each exported field is added to the map unless // // - the field's tag is "-", or // - the field is empty and its tag specifies the "omitempty" option. // // Each fields default name in the map is the field name but can be specified // in the struct field's tag value. The "gorethink" key in the struct field's // tag value is the key name, followed by an optional comma and options. Examples: // // // Field is ignored by this package. // Field int `gorethink:"-"` // // Field appears as key "myName". // Field int `gorethink:"myName"` // // Field appears as key "myName" and // // the field is omitted from the object if its value is empty, // // as defined above. // Field int `gorethink:"myName,omitempty"` // // Field appears as key "Field" (the default), but // // the field is skipped if empty. // // Note the leading comma. // Field int `gorethink:",omitempty"` func Expr(val interface{}) Term { if val == nil { return Term{ termType: p.Term_DATUM, data: nil, } } switch val := val.(type) { case Term: return val case []interface{}: vals := make([]Term, len(val)) for i, v := range val { vals[i] = Expr(v) } return makeArray(vals) case map[string]interface{}: vals := make(map[string]Term, len(val)) for k, v := range val { vals[k] = Expr(v) } return makeObject(vals) case bool, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64, uintptr, string, *bool, *int, *int8, *int16, *int32, *int64, *uint, *uint8, *uint16, *uint32, *uint64, *float32, *float64, *uintptr, *string: return Term{ termType: p.Term_DATUM, data: val, } default: // Use reflection to check for other types valType := reflect.TypeOf(val) valValue := reflect.ValueOf(val) switch valType.Kind() { case reflect.Func: return makeFunc(val) case reflect.Struct, reflect.Map, reflect.Ptr: data, err := encode(val) if err != nil || data == nil { return Term{ termType: p.Term_DATUM, data: nil, lastErr: err, } } return Expr(data) case reflect.Slice, reflect.Array: // Check if slice is a byte slice if valType.Elem().Kind() == reflect.Uint8 { data, err := encode(val) if err != nil || data == nil { return Term{ termType: p.Term_DATUM, data: nil, lastErr: err, } } return Expr(data) } vals := make([]Term, valValue.Len()) for i := 0; i < valValue.Len(); i++ { vals[i] = Expr(valValue.Index(i).Interface()) } return makeArray(vals) default: data, err := encode(val) if err != nil || data == nil { return Term{ termType: p.Term_DATUM, data: nil, lastErr: err, } } return Term{ termType: p.Term_DATUM, data: data, } } } } // JS creates a JavaScript expression which is evaluated by the database when // running the query. func JS(jssrc interface{}) Term { return constructRootTerm("Js", p.Term_JAVASCRIPT, []interface{}{jssrc}, map[string]interface{}{}) } // HTTPOpts contains the optional arguments for the HTTP term type HTTPOpts struct { // General Options Timeout interface{} `gorethink:"timeout,omitempty"` Reattempts interface{} `gorethink:"reattempts,omitempty"` Redirects interface{} `gorethink:"redirect,omitempty"` Verify interface{} `gorethink:"verify,omitempty"` ResultFormat interface{} `gorethink:"resul_format,omitempty"` // Request Options Method interface{} `gorethink:"method,omitempty"` Auth interface{} `gorethink:"auth,omitempty"` Params interface{} `gorethink:"params,omitempty"` Header interface{} `gorethink:"header,omitempty"` Data interface{} `gorethink:"data,omitempty"` // Pagination Page interface{} `gorethink:"page,omitempty"` PageLimit interface{} `gorethink:"page_limit,omitempty"` } func (o *HTTPOpts) toMap() map[string]interface{} { return optArgsToMap(o) } // HTTP retrieves data from the specified URL over HTTP. The return type depends // on the resultFormat option, which checks the Content-Type of the response by // default. func HTTP(url interface{}, optArgs ...HTTPOpts) Term { opts := map[string]interface{}{} if len(optArgs) >= 1 { opts = optArgs[0].toMap() } return constructRootTerm("Http", p.Term_HTTP, []interface{}{url}, opts) } // JSON parses a JSON string on the server. func JSON(args ...interface{}) Term { return constructRootTerm("Json", p.Term_JSON, args, map[string]interface{}{}) } // Error throws a runtime error. If called with no arguments inside the second argument // to `default`, re-throw the current error. func Error(args ...interface{}) Term { return constructRootTerm("Error", p.Term_ERROR, args, map[string]interface{}{}) } // Args is a special term usd to splice an array of arguments into another term. // This is useful when you want to call a varadic term such as GetAll with a set // of arguments provided at runtime. func Args(args ...interface{}) Term { return constructRootTerm("Args", p.Term_ARGS, args, map[string]interface{}{}) } // Binary encapsulates binary data within a query. // // The type of data binary accepts depends on the client language. In Go, it // expects either a byte array/slice or a bytes.Buffer. // // Only a limited subset of ReQL commands may be chained after binary: // - coerceTo can coerce binary objects to string types // - count will return the number of bytes in the object // - slice will treat bytes like array indexes (i.e., slice(10,20) will return bytes 10–19) // - typeOf returns PTYPE // - info will return information on a binary object. func Binary(data interface{}) Term { var b []byte switch data := data.(type) { case Term: return constructRootTerm("Binary", p.Term_BINARY, []interface{}{data}, map[string]interface{}{}) case []byte: b = data default: typ := reflect.TypeOf(data) if (typ.Kind() == reflect.Slice || typ.Kind() == reflect.Array) && typ.Elem().Kind() == reflect.Uint8 { return Binary(reflect.ValueOf(data).Bytes()) } panic("Unsupported binary type") } return binaryTerm(base64.StdEncoding.EncodeToString(b)) } func binaryTerm(data string) Term { t := constructRootTerm("Binary", p.Term_BINARY, []interface{}{}, map[string]interface{}{}) t.data = data return t } // Do evaluates the expr in the context of one or more value bindings. The type of // the result is the type of the value returned from expr. func (t Term) Do(args ...interface{}) Term { newArgs := []interface{}{} newArgs = append(newArgs, funcWrap(args[len(args)-1])) newArgs = append(newArgs, t) newArgs = append(newArgs, args[:len(args)-1]...) return constructRootTerm("Do", p.Term_FUNCALL, newArgs, map[string]interface{}{}) } // Do evaluates the expr in the context of one or more value bindings. The type of // the result is the type of the value returned from expr. func Do(args ...interface{}) Term { newArgs := []interface{}{} newArgs = append(newArgs, funcWrap(args[len(args)-1])) newArgs = append(newArgs, args[:len(args)-1]...) return constructRootTerm("Do", p.Term_FUNCALL, newArgs, map[string]interface{}{}) } // Branch evaluates one of two control paths based on the value of an expression. // branch is effectively an if renamed due to language constraints. // // The type of the result is determined by the type of the branch that gets executed. func Branch(args ...interface{}) Term { return constructRootTerm("Branch", p.Term_BRANCH, args, map[string]interface{}{}) } // Branch evaluates one of two control paths based on the value of an expression. // branch is effectively an if renamed due to language constraints. // // The type of the result is determined by the type of the branch that gets executed. func (t Term) Branch(args ...interface{}) Term { return constructMethodTerm(t, "Branch", p.Term_BRANCH, args, map[string]interface{}{}) } // ForEach loops over a sequence, evaluating the given write query for each element. // // It takes one argument of type `func (r.Term) interface{}`, for // example clones a table: // // r.Table("table").ForEach(func (row r.Term) interface{} { // return r.Table("new_table").Insert(row) // }) func (t Term) ForEach(args ...interface{}) Term { return constructMethodTerm(t, "Foreach", p.Term_FOR_EACH, funcWrapArgs(args), map[string]interface{}{}) } // Range generates a stream of sequential integers in a specified range. It // accepts 0, 1, or 2 arguments, all of which should be numbers. func Range(args ...interface{}) Term { return constructRootTerm("Range", p.Term_RANGE, args, map[string]interface{}{}) } // Default handles non-existence errors. Tries to evaluate and return its first argument. // If an error related to the absence of a value is thrown in the process, or if // its first argument returns null, returns its second argument. (Alternatively, // the second argument may be a function which will be called with either the // text of the non-existence error or null.) func (t Term) Default(args ...interface{}) Term { return constructMethodTerm(t, "Default", p.Term_DEFAULT, args, map[string]interface{}{}) } // CoerceTo converts a value of one type into another. // // You can convert: a selection, sequence, or object into an ARRAY, an array of // pairs into an OBJECT, and any DATUM into a STRING. func (t Term) CoerceTo(args ...interface{}) Term { return constructMethodTerm(t, "CoerceTo", p.Term_COERCE_TO, args, map[string]interface{}{}) } // TypeOf gets the type of a value. func (t Term) TypeOf(args ...interface{}) Term { return constructMethodTerm(t, "TypeOf", p.Term_TYPE_OF, args, map[string]interface{}{}) } // ToJSON converts a ReQL value or object to a JSON string. func (t Term) ToJSON() Term { return constructMethodTerm(t, "ToJSON", p.Term_TO_JSON_STRING, []interface{}{}, map[string]interface{}{}) } // Info gets information about a RQL value. func (t Term) Info(args ...interface{}) Term { return constructMethodTerm(t, "Info", p.Term_INFO, args, map[string]interface{}{}) } // Return a UUID (universally unique identifier), a string that can be used as a // unique ID. If a string is passed to uuid as an argument, the UUID will be // deterministic, derived from the string’s SHA-1 hash. func UUID(args ...interface{}) Term { return constructRootTerm("UUID", p.Term_UUID, args, map[string]interface{}{}) } // RawQuery creates a new query from a JSON string, this bypasses any encoding // done by GoRethink. The query should not contain the query type or any options // as this should be handled using the normal driver API. // // THis query will only work if this is the only term in the query. func RawQuery(q []byte) Term { data := json.RawMessage(q) return Term{ name: "RawQuery", rootTerm: true, rawQuery: true, data: &data, args: []Term{ Term{ termType: p.Term_DATUM, data: string(q), }, }, } } gorethink-2.0.4/query_control_test.go000066400000000000000000000300731272042630000200010ustar00rootroot00000000000000package gorethink import ( "bytes" "testing" "time" test "gopkg.in/check.v1" ) func (s *RethinkSuite) TestControlExprNil(c *test.C) { var response interface{} query := Expr(nil) res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.Equals, ErrEmptyResult) c.Assert(response, test.Equals, nil) } func (s *RethinkSuite) TestControlExprSimple(c *test.C) { var response int query := Expr(1) res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, 1) } func (s *RethinkSuite) TestControlExprList(c *test.C) { var response []interface{} query := Expr(narr) res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{ 1, 2, 3, 4, 5, 6, []interface{}{ 7.1, 7.2, 7.3, }, }) } func (s *RethinkSuite) TestControlExprObj(c *test.C) { var response map[string]interface{} query := Expr(nobj) res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, map[string]interface{}{ "A": 1, "B": 2, "C": map[string]interface{}{ "1": 3, "2": 4, }, }) } func (s *RethinkSuite) TestControlStruct(c *test.C) { var response map[string]interface{} query := Expr(str) res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, map[string]interface{}{ "id": "A", "B": 1, "D": map[string]interface{}{"D2": "2", "D1": 1}, "E": []interface{}{"E1", "E2", "E3", 4}, "F": map[string]interface{}{ "XA": 2, "XB": "B", "XC": []interface{}{"XC1", "XC2"}, "XD": map[string]interface{}{ "YA": 3, "YB": map[string]interface{}{ "1": "1", "2": "2", "3": 3, }, "YC": map[string]interface{}{ "YC1": "YC1", }, "YD": map[string]interface{}{ "YD1": "YD1", }, }, "XE": "XE", "XF": []interface{}{"XE1", "XE2"}, }, }) } func (s *RethinkSuite) TestControlStructTags(c *test.C) { SetTags("gorethink", "json") defer SetTags() var response map[string]interface{} query := Expr(TagsTest{"1", "2", "3"}) res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, map[string]interface{}{ "a": "1", "b": "2", "c1": "3", }) } func (s *RethinkSuite) TestControlMapTypeAlias(c *test.C) { var response TMap query := Expr(TMap{"A": 1, "B": 2}) res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, TMap{"A": 1, "B": 2}) } func (s *RethinkSuite) TestControlStringTypeAlias(c *test.C) { var response TStr query := Expr(TStr("Hello")) res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, TStr("Hello")) } func (s *RethinkSuite) TestControlExprTypes(c *test.C) { var response []interface{} query := Expr([]interface{}{int64(1), uint64(1), float64(1.0), int32(1), uint32(1), float32(1), "1", true, false}) res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{int64(1), uint64(1), float64(1.0), int32(1), uint32(1), float32(1), "1", true, false}) } func (s *RethinkSuite) TestControlJs(c *test.C) { var response int query := JS("1;") res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, 1) } func (s *RethinkSuite) TestControlHttp(c *test.C) { if testing.Short() { c.Skip("-short set") } var response map[string]interface{} query := HTTP("httpbin.org/get?data=1") res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response["args"], jsonEquals, map[string]interface{}{ "data": "1", }) } func (s *RethinkSuite) TestControlJson(c *test.C) { var response []int query := JSON("[1,2,3]") res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{1, 2, 3}) } func (s *RethinkSuite) TestControlError(c *test.C) { query := Error("An error occurred") err := query.Exec(session) c.Assert(err, test.NotNil) c.Assert(err, test.NotNil) c.Assert(err, test.FitsTypeOf, RQLUserError{}) c.Assert(err.Error(), test.Equals, "gorethink: An error occurred in: \nr.Error(\"An error occurred\")") } func (s *RethinkSuite) TestControlDoNothing(c *test.C) { var response []interface{} query := Do([]interface{}{map[string]interface{}{"a": 1}, map[string]interface{}{"a": 2}, map[string]interface{}{"a": 3}}) res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{map[string]interface{}{"a": 1}, map[string]interface{}{"a": 2}, map[string]interface{}{"a": 3}}) } func (s *RethinkSuite) TestControlArgs(c *test.C) { var response time.Time query := Time(Args(Expr([]interface{}{2014, 7, 12, "Z"}))) res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response.Unix(), test.Equals, int64(1405123200)) } func (s *RethinkSuite) TestControlBinaryByteArray(c *test.C) { var response []byte query := Binary([]byte("Hello World")) res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(bytes.Equal(response, []byte("Hello World")), test.Equals, true) } type byteArray []byte func (s *RethinkSuite) TestControlBinaryByteArrayAlias(c *test.C) { var response []byte query := Binary(byteArray("Hello World")) res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(bytes.Equal(response, []byte("Hello World")), test.Equals, true) } func (s *RethinkSuite) TestControlBinaryExpr(c *test.C) { var response []byte query := Expr([]byte("Hello World")) res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(bytes.Equal(response, []byte("Hello World")), test.Equals, true) } func (s *RethinkSuite) TestControlBinaryExprAlias(c *test.C) { var response []byte query := Expr(byteArray("Hello World")) res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(bytes.Equal(response, []byte("Hello World")), test.Equals, true) } func (s *RethinkSuite) TestControlBinaryTerm(c *test.C) { var response []byte query := Binary(Expr([]byte("Hello World"))) res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(bytes.Equal(response, []byte("Hello World")), test.Equals, true) } func (s *RethinkSuite) TestControlBinaryElemTerm(c *test.C) { var response map[string]interface{} query := Expr(map[string]interface{}{ "bytes": []byte("Hello World"), }) res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(bytes.Equal(response["bytes"].([]byte), []byte("Hello World")), test.Equals, true) } func (s *RethinkSuite) TestControlDo(c *test.C) { var response []interface{} query := Do([]interface{}{ map[string]interface{}{"a": 1}, map[string]interface{}{"a": 2}, map[string]interface{}{"a": 3}, }, func(row Term) Term { return row.Field("a") }) res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{1, 2, 3}) } func (s *RethinkSuite) TestControlDoWithExpr(c *test.C) { var response []interface{} query := Expr([]interface{}{ map[string]interface{}{"a": 1}, map[string]interface{}{"a": 2}, map[string]interface{}{"a": 3}, }).Do(func(row Term) Term { return row.Field("a") }) res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{1, 2, 3}) } func (s *RethinkSuite) TestControlBranchSimple(c *test.C) { var response int query := Branch( true, 1, 2, ) res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, 1) } func (s *RethinkSuite) TestControlBranchWithMapExpr(c *test.C) { var response []interface{} query := Expr([]interface{}{1, 2, 3}).Map(Branch( Row.Eq(2), Row.Sub(1), Row.Add(1), )) res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{2, 1, 4}) } func (s *RethinkSuite) TestControlDefault(c *test.C) { var response []interface{} query := Expr(defaultObjList).Map(func(row Term) Term { return row.Field("a").Default(1) }) res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{1, 1}) } func (s *RethinkSuite) TestControlCoerceTo(c *test.C) { var response string query := Expr(1).CoerceTo("STRING") res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, "1") } func (s *RethinkSuite) TestControlTypeOf(c *test.C) { var response string query := Expr(1).TypeOf() res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, "NUMBER") } func (s *RethinkSuite) TestControlRangeNoArgs(c *test.C) { var response []int query := Range().Limit(100) res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(len(response), test.Equals, 100) } func (s *RethinkSuite) TestControlRangeSingleArgs(c *test.C) { var response []int query := Range(4) res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, test.DeepEquals, []int{0, 1, 2, 3}) } func (s *RethinkSuite) TestControlRangeTwoArgs(c *test.C) { var response []int query := Range(4, 6) res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, test.DeepEquals, []int{4, 5}) } func (s *RethinkSuite) TestControlToJSON(c *test.C) { var response string query := Expr([]int{4, 5}).ToJSON() res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, "[4,5]") } func (s *RethinkSuite) TestControlUUID(c *test.C) { var response string query := UUID() res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(len(response) == 36, test.Equals, true) } func (s *RethinkSuite) TestControlUUIDString(c *test.C) { var response string query := UUID("rethinkdb") res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(len(response) == 36, test.Equals, true) } func (s *RethinkSuite) TestControlInvalidType(c *test.C) { query := Expr(map[struct{ string }]string{}) _, err := query.Run(session) c.Assert(err, test.NotNil) } func (s *RethinkSuite) TestControlRawQuery(c *test.C) { var response int query := RawQuery([]byte(`1`)) res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, 1) } func (s *RethinkSuite) TestControlRawQuery_advanced(c *test.C) { var response []int // r.expr([1,2,3]).map(function(v) { return v.add(1)}) query := RawQuery([]byte(`[38,[[2,[1,2,3]],[69,[[2,[25]],[24,[[10,[25]],1]]]]]]`)) res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []int{2, 3, 4}) } gorethink-2.0.4/query_db.go000066400000000000000000000014711272042630000156470ustar00rootroot00000000000000package gorethink import ( p "gopkg.in/dancannon/gorethink.v2/ql2" ) // DBCreate creates a database. A RethinkDB database is a collection of tables, // similar to relational databases. // // Note: that you can only use alphanumeric characters and underscores for the // database name. func DBCreate(args ...interface{}) Term { return constructRootTerm("DBCreate", p.Term_DB_CREATE, args, map[string]interface{}{}) } // DBDrop drops a database. The database, all its tables, and corresponding data // will be deleted. func DBDrop(args ...interface{}) Term { return constructRootTerm("DBDrop", p.Term_DB_DROP, args, map[string]interface{}{}) } // DBList lists all database names in the system. func DBList(args ...interface{}) Term { return constructRootTerm("DBList", p.Term_DB_LIST, args, map[string]interface{}{}) } gorethink-2.0.4/query_db_test.go000066400000000000000000000023231272042630000167030ustar00rootroot00000000000000package gorethink import ( test "gopkg.in/check.v1" ) func (s *RethinkSuite) TestDbCreate(c *test.C) { // Delete the test2 database if it already exists DBDrop("test").Exec(session) // Test database creation query := DBCreate("test") response, err := query.RunWrite(session) c.Assert(err, test.IsNil) c.Assert(response.DBsCreated, jsonEquals, 1) } func (s *RethinkSuite) TestDbList(c *test.C) { var response []interface{} // create database DBCreate("test").Exec(session) // Try and find it in the list success := false res, err := DBList().Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, test.FitsTypeOf, []interface{}{}) for _, db := range response { if db == "test" { success = true } } c.Assert(success, test.Equals, true) } func (s *RethinkSuite) TestDbDelete(c *test.C) { // Delete the test2 database if it already exists DBCreate("test").Exec(session) // Test database creation query := DBDrop("test") response, err := query.RunWrite(session) c.Assert(err, test.IsNil) c.Assert(response.DBsDropped, jsonEquals, 1) // Ensure that there is still a test DB after the test has finished DBCreate("test").Exec(session) } gorethink-2.0.4/query_geospatial.go000066400000000000000000000150721272042630000174140ustar00rootroot00000000000000package gorethink import ( p "gopkg.in/dancannon/gorethink.v2/ql2" ) // CircleOpts contains the optional arguments for the Circle term. type CircleOpts struct { NumVertices interface{} `gorethink:"num_vertices,omitempty"` GeoSystem interface{} `gorethink:"geo_system,omitempty"` Unit interface{} `gorethink:"unit,omitempty"` Fill interface{} `gorethink:"fill,omitempty"` } func (o *CircleOpts) toMap() map[string]interface{} { return optArgsToMap(o) } // Circle constructs a circular line or polygon. A circle in RethinkDB is // a polygon or line approximating a circle of a given radius around a given // center, consisting of a specified number of vertices (default 32). func Circle(point, radius interface{}, optArgs ...CircleOpts) Term { opts := map[string]interface{}{} if len(optArgs) >= 1 { opts = optArgs[0].toMap() } return constructRootTerm("Circle", p.Term_CIRCLE, []interface{}{point, radius}, opts) } // DistanceOpts contains the optional arguments for the Distance term. type DistanceOpts struct { GeoSystem interface{} `gorethink:"geo_system,omitempty"` Unit interface{} `gorethink:"unit,omitempty"` } func (o *DistanceOpts) toMap() map[string]interface{} { return optArgsToMap(o) } // Distance calculates the Haversine distance between two points. At least one // of the geometry objects specified must be a point. func (t Term) Distance(point interface{}, optArgs ...DistanceOpts) Term { opts := map[string]interface{}{} if len(optArgs) >= 1 { opts = optArgs[0].toMap() } return constructMethodTerm(t, "Distance", p.Term_DISTANCE, []interface{}{point}, opts) } // Distance calculates the Haversine distance between two points. At least one // of the geometry objects specified must be a point. func Distance(point1, point2 interface{}, optArgs ...DistanceOpts) Term { opts := map[string]interface{}{} if len(optArgs) >= 1 { opts = optArgs[0].toMap() } return constructRootTerm("Distance", p.Term_DISTANCE, []interface{}{point1, point2}, opts) } // Fill converts a Line object into a Polygon object. If the last point does not // specify the same coordinates as the first point, polygon will close the // polygon by connecting them func (t Term) Fill() Term { return constructMethodTerm(t, "Fill", p.Term_FILL, []interface{}{}, map[string]interface{}{}) } // GeoJSON converts a GeoJSON object to a ReQL geometry object. func GeoJSON(args ...interface{}) Term { return constructRootTerm("GeoJSON", p.Term_GEOJSON, args, map[string]interface{}{}) } // ToGeoJSON converts a ReQL geometry object to a GeoJSON object. func (t Term) ToGeoJSON(args ...interface{}) Term { return constructMethodTerm(t, "ToGeoJSON", p.Term_TO_GEOJSON, args, map[string]interface{}{}) } // GetIntersectingOpts contains the optional arguments for the GetIntersecting term. type GetIntersectingOpts struct { Index interface{} `gorethink:"index,omitempty"` } func (o *GetIntersectingOpts) toMap() map[string]interface{} { return optArgsToMap(o) } // GetIntersecting gets all documents where the given geometry object intersects // the geometry object of the requested geospatial index. func (t Term) GetIntersecting(args interface{}, optArgs ...GetIntersectingOpts) Term { opts := map[string]interface{}{} if len(optArgs) >= 1 { opts = optArgs[0].toMap() } return constructMethodTerm(t, "GetIntersecting", p.Term_GET_INTERSECTING, []interface{}{args}, opts) } // GetNearestOpts contains the optional arguments for the GetNearest term. type GetNearestOpts struct { Index interface{} `gorethink:"index,omitempty"` MaxResults interface{} `gorethink:"max_results,omitempty"` MaxDist interface{} `gorethink:"max_dist,omitempty"` Unit interface{} `gorethink:"unit,omitempty"` GeoSystem interface{} `gorethink:"geo_system,omitempty"` } func (o *GetNearestOpts) toMap() map[string]interface{} { return optArgsToMap(o) } // GetNearest gets all documents where the specified geospatial index is within a // certain distance of the specified point (default 100 kilometers). func (t Term) GetNearest(point interface{}, optArgs ...GetNearestOpts) Term { opts := map[string]interface{}{} if len(optArgs) >= 1 { opts = optArgs[0].toMap() } return constructMethodTerm(t, "GetNearest", p.Term_GET_NEAREST, []interface{}{point}, opts) } // Includes tests whether a geometry object is completely contained within another. // When applied to a sequence of geometry objects, includes acts as a filter, // returning a sequence of objects from the sequence that include the argument. func (t Term) Includes(args ...interface{}) Term { return constructMethodTerm(t, "Includes", p.Term_INCLUDES, args, map[string]interface{}{}) } // Intersects tests whether two geometry objects intersect with one another. // When applied to a sequence of geometry objects, intersects acts as a filter, // returning a sequence of objects from the sequence that intersect with the // argument. func (t Term) Intersects(args ...interface{}) Term { return constructMethodTerm(t, "Intersects", p.Term_INTERSECTS, args, map[string]interface{}{}) } // Line constructs a geometry object of type Line. The line can be specified in // one of two ways: // - Two or more two-item arrays, specifying longitude and latitude numbers of // the line's vertices; // - Two or more Point objects specifying the line's vertices. func Line(args ...interface{}) Term { return constructRootTerm("Line", p.Term_LINE, args, map[string]interface{}{}) } // Point constructs a geometry object of type Point. The point is specified by // two floating point numbers, the longitude (−180 to 180) and latitude // (−90 to 90) of the point on a perfect sphere. func Point(lon, lat interface{}) Term { return constructRootTerm("Point", p.Term_POINT, []interface{}{lon, lat}, map[string]interface{}{}) } // Polygon constructs a geometry object of type Polygon. The Polygon can be // specified in one of two ways: // - Three or more two-item arrays, specifying longitude and latitude numbers of the polygon's vertices; // - Three or more Point objects specifying the polygon's vertices. func Polygon(args ...interface{}) Term { return constructRootTerm("Polygon", p.Term_POLYGON, args, map[string]interface{}{}) } // PolygonSub "punches a hole" out of the parent polygon using the polygon passed // to the function. // polygon1.PolygonSub(polygon2) -> polygon // In the example above polygon2 must be completely contained within polygon1 // and must have no holes itself (it must not be the output of polygon_sub itself). func (t Term) PolygonSub(args ...interface{}) Term { return constructMethodTerm(t, "PolygonSub", p.Term_POLYGON_SUB, args, map[string]interface{}{}) } gorethink-2.0.4/query_geospatial_test.go000066400000000000000000000365201272042630000204540ustar00rootroot00000000000000package gorethink import ( test "gopkg.in/check.v1" "gopkg.in/dancannon/gorethink.v2/types" ) func (s *RethinkSuite) TestGeospatialDecodeGeometryPseudoType(c *test.C) { var response types.Geometry // setup coordinates coords := [][][]float64{ { {-122.423246, 37.779388}, {-122.423246, 37.329898}, {-121.88642, 37.329898}, {-121.88642, 37.329898}, {-122.423246, 37.779388}, }, } gt := "Polygon" res, err := Expr(map[string]interface{}{ "$reql_type$": "GEOMETRY", "type": "Polygon", "coordinates": coords, }).Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) // test shape if response.Type != gt { c.Errorf("expected [%v], instead [%v]", gt, response.Type) } // assert points are within threshold c.Assert(response, geometryEquals, "Polygon", coords) } func (s *RethinkSuite) TestGeospatialEncodeGeometryPseudoType(c *test.C) { encoded, err := encode(types.Geometry{ Type: "Polygon", Lines: types.Lines{ types.Line{ types.Point{Lon: -122.423246, Lat: 37.779388}, types.Point{Lon: -122.423246, Lat: 37.329898}, types.Point{Lon: -121.88642, Lat: 37.329898}, types.Point{Lon: -121.88642, Lat: 37.779388}, types.Point{Lon: -122.423246, Lat: 37.779388}, }, }, }) c.Assert(err, test.IsNil) c.Assert(encoded, jsonEquals, map[string]interface{}{ "$reql_type$": "GEOMETRY", "type": "Polygon", "coordinates": []interface{}{ []interface{}{ []interface{}{-122.423246, 37.779388}, []interface{}{-122.423246, 37.329898}, []interface{}{-121.88642, 37.329898}, []interface{}{-121.88642, 37.779388}, []interface{}{-122.423246, 37.779388}, }, }, }) } func (s *RethinkSuite) TestGeospatialCircle(c *test.C) { var response types.Geometry res, err := Circle([]float64{-122.423246, 37.779388}, 10).Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, geometryEquals, "Polygon", [][][]float64{ { {-122.423246, 37.77929790366427}, {-122.42326814543915, 37.77929963483801}, {-122.4232894398445, 37.779304761831504}, {-122.42330906488651, 37.77931308761787}, {-122.42332626638755, 37.77932429224285}, {-122.42334038330416, 37.77933794512014}, {-122.42335087313059, 37.77935352157849}, {-122.42335733274696, 37.77937042302436}, {-122.4233595139113, 37.77938799994533}, {-122.42335733279968, 37.7794055768704}, {-122.42335087322802, 37.779422478327966}, {-122.42334038343147, 37.77943805480385}, {-122.42332626652532, 37.779451707701796}, {-122.42330906501378, 37.77946291234741}, {-122.42328943994191, 37.77947123815131}, {-122.42326814549187, 37.77947636515649}, {-122.423246, 37.779478096334365}, {-122.42322385450814, 37.77947636515649}, {-122.4232025600581, 37.77947123815131}, {-122.42318293498623, 37.77946291234741}, {-122.42316573347469, 37.779451707701796}, {-122.42315161656855, 37.77943805480385}, {-122.423141126772, 37.779422478327966}, {-122.42313466720033, 37.7794055768704}, {-122.42313248608872, 37.77938799994533}, {-122.42313466725305, 37.77937042302436}, {-122.42314112686942, 37.77935352157849}, {-122.42315161669585, 37.77933794512014}, {-122.42316573361246, 37.77932429224285}, {-122.4231829351135, 37.77931308761787}, {-122.42320256015552, 37.779304761831504}, {-122.42322385456086, 37.77929963483801}, {-122.423246, 37.77929790366427}, }, }) } func (s *RethinkSuite) TestGeospatialCirclePoint(c *test.C) { var response types.Geometry res, err := Circle(Point(-122.423246, 37.779388), 10).Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, geometryEquals, "Polygon", [][][]float64{ { {-122.423246, 37.77929790366427}, {-122.42326814543915, 37.77929963483801}, {-122.4232894398445, 37.779304761831504}, {-122.42330906488651, 37.77931308761787}, {-122.42332626638755, 37.77932429224285}, {-122.42334038330416, 37.77933794512014}, {-122.42335087313059, 37.77935352157849}, {-122.42335733274696, 37.77937042302436}, {-122.4233595139113, 37.77938799994533}, {-122.42335733279968, 37.7794055768704}, {-122.42335087322802, 37.779422478327966}, {-122.42334038343147, 37.77943805480385}, {-122.42332626652532, 37.779451707701796}, {-122.42330906501378, 37.77946291234741}, {-122.42328943994191, 37.77947123815131}, {-122.42326814549187, 37.77947636515649}, {-122.423246, 37.779478096334365}, {-122.42322385450814, 37.77947636515649}, {-122.4232025600581, 37.77947123815131}, {-122.42318293498623, 37.77946291234741}, {-122.42316573347469, 37.779451707701796}, {-122.42315161656855, 37.77943805480385}, {-122.423141126772, 37.779422478327966}, {-122.42313466720033, 37.7794055768704}, {-122.42313248608872, 37.77938799994533}, {-122.42313466725305, 37.77937042302436}, {-122.42314112686942, 37.77935352157849}, {-122.42315161669585, 37.77933794512014}, {-122.42316573361246, 37.77932429224285}, {-122.4231829351135, 37.77931308761787}, {-122.42320256015552, 37.779304761831504}, {-122.42322385456086, 37.77929963483801}, {-122.423246, 37.77929790366427}, }, }) } func (s *RethinkSuite) TestGeospatialCirclePointFill(c *test.C) { var response types.Geometry res, err := Circle(Point(-122.423246, 37.779388), 10, CircleOpts{Fill: true}).Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, geometryEquals, "Polygon", [][][]float64{ { {-122.423246, 37.77929790366427}, {-122.42326814543915, 37.77929963483801}, {-122.4232894398445, 37.779304761831504}, {-122.42330906488651, 37.77931308761787}, {-122.42332626638755, 37.77932429224285}, {-122.42334038330416, 37.77933794512014}, {-122.42335087313059, 37.77935352157849}, {-122.42335733274696, 37.77937042302436}, {-122.4233595139113, 37.77938799994533}, {-122.42335733279968, 37.7794055768704}, {-122.42335087322802, 37.779422478327966}, {-122.42334038343147, 37.77943805480385}, {-122.42332626652532, 37.779451707701796}, {-122.42330906501378, 37.77946291234741}, {-122.42328943994191, 37.77947123815131}, {-122.42326814549187, 37.77947636515649}, {-122.423246, 37.779478096334365}, {-122.42322385450814, 37.77947636515649}, {-122.4232025600581, 37.77947123815131}, {-122.42318293498623, 37.77946291234741}, {-122.42316573347469, 37.779451707701796}, {-122.42315161656855, 37.77943805480385}, {-122.423141126772, 37.779422478327966}, {-122.42313466720033, 37.7794055768704}, {-122.42313248608872, 37.77938799994533}, {-122.42313466725305, 37.77937042302436}, {-122.42314112686942, 37.77935352157849}, {-122.42315161669585, 37.77933794512014}, {-122.42316573361246, 37.77932429224285}, {-122.4231829351135, 37.77931308761787}, {-122.42320256015552, 37.779304761831504}, {-122.42322385456086, 37.77929963483801}, {-122.423246, 37.77929790366427}, }, }) } func (s *RethinkSuite) TestGeospatialPointDistanceMethod(c *test.C) { var response float64 f := 734125.249602186 res, err := Point(-122.423246, 37.779388).Distance(Point(-117.220406, 32.719464)).Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) if !kindaclose(response, f) { c.Errorf("the deviation between the compared floats is too great [%v:%v]", response, f) } } func (s *RethinkSuite) TestGeospatialPointDistanceRoot(c *test.C) { var response float64 f := 734125.249602186 res, err := Distance(Point(-122.423246, 37.779388), Point(-117.220406, 32.719464)).Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) if !kindaclose(response, f) { c.Errorf("the deviation between the compared floats is too great [%v:%v]", response, f) } } func (s *RethinkSuite) TestGeospatialPointDistanceRootKm(c *test.C) { var response float64 f := 734.125249602186 res, err := Distance(Point(-122.423246, 37.779388), Point(-117.220406, 32.719464), DistanceOpts{Unit: "km"}).Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) if !kindaclose(response, f) { c.Errorf("the deviation between the compared floats is too great [%v:%v]", response, f) } } func (s *RethinkSuite) TestGeospatialFill(c *test.C) { var response types.Geometry res, err := Line( []float64{-122.423246, 37.779388}, []float64{-122.423246, 37.329898}, []float64{-121.886420, 37.329898}, []float64{-121.886420, 37.779388}, ).Fill().Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, geometryEquals, "Polygon", [][][]float64{ { {-122.423246, 37.779388}, {-122.423246, 37.329898}, {-121.88642, 37.329898}, {-121.88642, 37.779388}, {-122.423246, 37.779388}, }, }) } func (s *RethinkSuite) TestGeospatialGeoJSON(c *test.C) { var response types.Geometry res, err := GeoJSON(map[string]interface{}{ "type": "Point", "coordinates": []interface{}{-122.423246, 37.779388}, }).Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, geometryEquals, "Point", []float64{-122.423246, 37.779388}) } func (s *RethinkSuite) TestGeospatialToGeoJSON(c *test.C) { var response map[string]interface{} res, err := Point(-122.423246, 37.779388).ToGeoJSON().Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, map[string]interface{}{ "type": "Point", "coordinates": []interface{}{-122.423246, 37.779388}, }) } func (s *RethinkSuite) TestGeospatialGetIntersecting(c *test.C) { // Setup table DB("test").TableDrop("geospatial").Exec(session) DB("test").TableCreate("geospatial").Exec(session) DB("test").Table("geospatial").IndexCreate("area", IndexCreateOpts{ Geo: true, }).Exec(session) DB("test").Table("geospatial").IndexWait().Exec(session) DB("test").Table("geospatial").Insert([]interface{}{ map[string]interface{}{"area": Circle(Point(-117.220406, 32.719464), 100000)}, map[string]interface{}{"area": Circle(Point(-100.220406, 20.719464), 100000)}, map[string]interface{}{"area": Circle(Point(-117.200406, 32.723464), 100000)}, }).Exec(session) var response []interface{} res, err := DB("test").Table("geospatial").GetIntersecting( Circle(Point(-117.220406, 32.719464), 100000), GetIntersectingOpts{ Index: "area", }, ).Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, test.HasLen, 2) } func (s *RethinkSuite) TestGeospatialGetNearest(c *test.C) { // Setup table DB("test").TableDrop("geospatial").Exec(session) DB("test").TableCreate("geospatial").Exec(session) DB("test").Table("geospatial").IndexCreate("area", IndexCreateOpts{ Geo: true, }).Exec(session) DB("test").Table("geospatial").IndexWait().Exec(session) DB("test").Table("geospatial").Insert([]interface{}{ map[string]interface{}{"area": Circle(Point(-117.220406, 32.719464), 100000)}, map[string]interface{}{"area": Circle(Point(-100.220406, 20.719464), 100000)}, map[string]interface{}{"area": Circle(Point(-115.210306, 32.733364), 100000)}, }).Exec(session) var response []interface{} res, err := DB("test").Table("geospatial").GetNearest( Point(-117.220406, 32.719464), GetNearestOpts{ Index: "area", MaxDist: 1, }, ).Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, test.HasLen, 1) } func (s *RethinkSuite) TestGeospatialIncludesTrue(c *test.C) { var response bool res, err := Polygon( Point(-122.4, 37.7), Point(-122.4, 37.3), Point(-121.8, 37.3), Point(-121.8, 37.7), ).Includes(Point(-122.3, 37.4)).Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, true) } func (s *RethinkSuite) TestGeospatialIncludesFalse(c *test.C) { var response bool res, err := Polygon( Point(-122.4, 37.7), Point(-122.4, 37.3), Point(-121.8, 37.3), Point(-121.8, 37.7), ).Includes(Point(100.3, 37.4)).Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, false) } func (s *RethinkSuite) TestGeospatialIntersectsTrue(c *test.C) { var response bool res, err := Polygon( Point(-122.4, 37.7), Point(-122.4, 37.3), Point(-121.8, 37.3), Point(-121.8, 37.7), ).Intersects(Polygon( Point(-122.3, 37.4), Point(-122.4, 37.3), Point(-121.8, 37.3), Point(-121.8, 37.4), )).Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, true) } func (s *RethinkSuite) TestGeospatialIntersectsFalse(c *test.C) { var response bool res, err := Polygon( Point(-122.4, 37.7), Point(-122.4, 37.3), Point(-121.8, 37.3), Point(-121.8, 37.7), ).Intersects(Polygon( Point(-102.4, 37.7), Point(-102.4, 37.3), Point(-101.8, 37.3), Point(-101.8, 37.7), )).Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, false) } func (s *RethinkSuite) TestGeospatialLineLatLon(c *test.C) { var response types.Geometry res, err := Line([]float64{-122.423246, 37.779388}, []float64{-121.886420, 37.329898}).Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, geometryEquals, "LineString", [][]float64{ {-122.423246, 37.779388}, {-121.886420, 37.329898}, }) } func (s *RethinkSuite) TestGeospatialLinePoint(c *test.C) { var response types.Geometry res, err := Line(Point(-122.423246, 37.779388), Point(-121.886420, 37.329898)).Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, geometryEquals, "LineString", [][]float64{ {-122.423246, 37.779388}, {-121.886420, 37.329898}, }) } func (s *RethinkSuite) TestGeospatialPoint(c *test.C) { var response types.Geometry res, err := Point(-122.423246, 37.779388).Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, types.Geometry{ Type: "Point", Point: types.Point{Lon: -122.423246, Lat: 37.779388}, }) c.Assert(response, geometryEquals, "Point", []float64{-122.423246, 37.779388}) } func (s *RethinkSuite) TestGeospatialPolygon(c *test.C) { var response types.Geometry res, err := Polygon(Point(-122.423246, 37.779388), Point(-122.423246, 37.329898), Point(-121.886420, 37.329898)).Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, geometryEquals, "Polygon", [][][]float64{ { {-122.423246, 37.779388}, {-122.423246, 37.329898}, {-121.88642, 37.329898}, {-122.423246, 37.779388}, }, }) } func (s *RethinkSuite) TestGeospatialPolygonSub(c *test.C) { var response types.Geometry res, err := Polygon( Point(-122.4, 37.7), Point(-122.4, 37.3), Point(-121.8, 37.3), Point(-121.8, 37.7), ).PolygonSub(Polygon( Point(-122.3, 37.4), Point(-122.3, 37.6), Point(-122.0, 37.6), Point(-122.0, 37.4), )).Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, geometryEquals, "Polygon", [][][]float64{ { {-122.4, 37.7}, {-122.4, 37.3}, {-121.8, 37.3}, {-121.8, 37.7}, {-122.4, 37.7}, }, { {-122.3, 37.4}, {-122.3, 37.6}, {-122, 37.6}, {-122, 37.4}, {-122.3, 37.4}, }, }) } gorethink-2.0.4/query_join.go000066400000000000000000000035421272042630000162220ustar00rootroot00000000000000package gorethink import ( p "gopkg.in/dancannon/gorethink.v2/ql2" ) // InnerJoin returns the inner product of two sequences (e.g. a table, a filter result) // filtered by the predicate. The query compares each row of the left sequence // with each row of the right sequence to find all pairs of rows which satisfy // the predicate. When the predicate is satisfied, each matched pair of rows // of both sequences are combined into a result row. func (t Term) InnerJoin(args ...interface{}) Term { return constructMethodTerm(t, "InnerJoin", p.Term_INNER_JOIN, args, map[string]interface{}{}) } // OuterJoin computes a left outer join by retaining each row in the left table even // if no match was found in the right table. func (t Term) OuterJoin(args ...interface{}) Term { return constructMethodTerm(t, "OuterJoin", p.Term_OUTER_JOIN, args, map[string]interface{}{}) } // EqJoinOpts contains the optional arguments for the EqJoin term. type EqJoinOpts struct { Index interface{} `gorethink:"index,omitempty"` Ordered interface{} `gorethink:"ordered,omitempty"` } func (o *EqJoinOpts) toMap() map[string]interface{} { return optArgsToMap(o) } // EqJoin is an efficient join that looks up elements in the right table by primary key. // // Optional arguments: "index" (string - name of the index to use in right table instead of the primary key) func (t Term) EqJoin(left, right interface{}, optArgs ...EqJoinOpts) Term { opts := map[string]interface{}{} if len(optArgs) >= 1 { opts = optArgs[0].toMap() } return constructMethodTerm(t, "EqJoin", p.Term_EQ_JOIN, []interface{}{funcWrap(left), right}, opts) } // Zip is used to 'zip' up the result of a join by merging the 'right' fields into 'left' // fields of each member of the sequence. func (t Term) Zip(args ...interface{}) Term { return constructMethodTerm(t, "Zip", p.Term_ZIP, args, map[string]interface{}{}) } gorethink-2.0.4/query_join_test.go000066400000000000000000000130771272042630000172650ustar00rootroot00000000000000package gorethink import ( test "gopkg.in/check.v1" ) func (s *RethinkSuite) TestJoinInnerJoin(c *test.C) { // Ensure table + database exist DBCreate("test").Exec(session) DB("test").TableCreate("Join1").Exec(session) DB("test").TableCreate("Join2").Exec(session) // Insert rows DB("test").Table("Join1").Insert(joinTable1).Exec(session) DB("test").Table("Join2").Insert(joinTable2).Exec(session) // Test query var response []interface{} query := DB("test").Table("Join1").InnerJoin(DB("test").Table("Join2"), func(a, b Term) Term { return a.Field("id").Eq(b.Field("id")) }) res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(response, jsonEquals, []interface{}{ map[string]interface{}{ "right": map[string]interface{}{"title": "goof", "id": 0}, "left": map[string]interface{}{"name": "bob", "id": 0}, }, map[string]interface{}{ "right": map[string]interface{}{"title": "lmoe", "id": 2}, "left": map[string]interface{}{"name": "joe", "id": 2}, }, }) } func (s *RethinkSuite) TestJoinInnerJoinZip(c *test.C) { // Ensure table + database exist DBCreate("test").Exec(session) DB("test").TableCreate("Join1").Exec(session) DB("test").TableCreate("Join2").Exec(session) // Insert rows DB("test").Table("Join1").Insert(joinTable1).Exec(session) DB("test").Table("Join2").Insert(joinTable2).Exec(session) // Test query var response []interface{} query := DB("test").Table("Join1").InnerJoin(DB("test").Table("Join2"), func(a, b Term) Term { return a.Field("id").Eq(b.Field("id")) }).Zip() res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{ map[string]interface{}{"title": "goof", "name": "bob", "id": 0}, map[string]interface{}{"title": "lmoe", "name": "joe", "id": 2}, }) } func (s *RethinkSuite) TestJoinOuterJoinZip(c *test.C) { // Ensure table + database exist DBCreate("test").Exec(session) DB("test").TableCreate("Join1").Exec(session) DB("test").TableCreate("Join2").Exec(session) // Insert rows DB("test").Table("Join1").Insert(joinTable1).Exec(session) DB("test").Table("Join2").Insert(joinTable2).Exec(session) // Test query var response []interface{} query := DB("test").Table("Join1").OuterJoin(DB("test").Table("Join2"), func(a, b Term) Term { return a.Field("id").Eq(b.Field("id")) }).Zip().OrderBy("id") res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{ map[string]interface{}{"title": "goof", "name": "bob", "id": 0}, map[string]interface{}{"name": "tom", "id": 1}, map[string]interface{}{"title": "lmoe", "name": "joe", "id": 2}, }) } func (s *RethinkSuite) TestJoinEqJoinZip(c *test.C) { // Ensure table + database exist DBCreate("test").Exec(session) DB("test").TableCreate("Join1").Exec(session) DB("test").TableCreate("Join2").Exec(session) // Insert rows DB("test").Table("Join1").Insert(joinTable1).Exec(session) DB("test").Table("Join2").Insert(joinTable2).Exec(session) // Test query var response []interface{} query := DB("test").Table("Join1").EqJoin("id", DB("test").Table("Join2"), EqJoinOpts{ Ordered: true, }).Zip() res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(response, jsonEquals, []interface{}{ map[string]interface{}{"title": "goof", "name": "bob", "id": 0}, map[string]interface{}{"title": "lmoe", "name": "joe", "id": 2}, }) } func (s *RethinkSuite) TestJoinEqJoinDiffIdsZip(c *test.C) { // Ensure table + database exist DBCreate("test").Exec(session) DB("test").TableCreate("Join1").Exec(session) err := DB("test").TableCreate("Join3", TableCreateOpts{ PrimaryKey: "it", }).Exec(session) c.Assert(err, test.IsNil) DB("test").Table("Join3").IndexCreate("it").Exec(session) DB("test").Table("Join3").IndexWait().Exec(session) // Insert rows DB("test").Table("Join1").Delete().Exec(session) DB("test").Table("Join3").Delete().Exec(session) DB("test").Table("Join1").Insert(joinTable1).Exec(session) DB("test").Table("Join3").Insert(joinTable3).Exec(session) // Test query var response []interface{} query := DB("test").Table("Join1").EqJoin("id", DB("test").Table("Join3"), EqJoinOpts{ Index: "it", Ordered: true, }).Zip() res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(response, jsonEquals, []interface{}{ map[string]interface{}{"title": "goof", "name": "bob", "id": 0, "it": 0}, map[string]interface{}{"title": "lmoe", "name": "joe", "id": 2, "it": 2}, }) } func (s *RethinkSuite) TestOrderByJoinEq(c *test.C) { type Map map[string]interface{} var err error // Ensure table + database exist DBCreate("test").Exec(session) DB("test").TableCreate("test").Exec(session) DB("test").TableCreate("test2").Exec(session) tab := DB("test").Table("test") tab2 := DB("test").Table("test2") // insert rows err = tab.Insert(Map{"S": "s1", "T": 2}).Exec(session) err = tab.Insert(Map{"S": "s1", "T": 1}).Exec(session) err = tab.Insert(Map{"S": "s1", "T": 3}).Exec(session) err = tab.Insert(Map{"S": "s2", "T": 3}).Exec(session) c.Assert(err, test.IsNil) err = tab2.Insert(Map{"id": "s1", "N": "Rob"}).Exec(session) err = tab2.Insert(Map{"id": "s2", "N": "Zar"}).Exec(session) c.Assert(err, test.IsNil) // Test query var response []Map res, err := tab.OrderBy("T").EqJoin("S", tab2).Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Check(len(response), test.Equals, 4, test.Commentf("%v", response)) } gorethink-2.0.4/query_manipulation.go000066400000000000000000000125631272042630000177660ustar00rootroot00000000000000package gorethink import ( p "gopkg.in/dancannon/gorethink.v2/ql2" ) // Row returns the currently visited document. Note that Row does not work within // subqueries to access nested documents; you should use anonymous functions to // access those documents instead. Also note that unlike in other drivers to // access a rows fields you should call Field. For example: // r.row("fieldname") should instead be r.Row.Field("fieldname") var Row = constructRootTerm("Doc", p.Term_IMPLICIT_VAR, []interface{}{}, map[string]interface{}{}) // Literal replaces an object in a field instead of merging it with an existing // object in a merge or update operation. func Literal(args ...interface{}) Term { return constructRootTerm("Literal", p.Term_LITERAL, args, map[string]interface{}{}) } // Field gets a single field from an object. If called on a sequence, gets that field // from every object in the sequence, skipping objects that lack it. func (t Term) Field(args ...interface{}) Term { return constructMethodTerm(t, "Field", p.Term_GET_FIELD, args, map[string]interface{}{}) } // HasFields tests if an object has all of the specified fields. An object has a field if // it has the specified key and that key maps to a non-null value. For instance, // the object `{'a':1,'b':2,'c':null}` has the fields `a` and `b`. func (t Term) HasFields(args ...interface{}) Term { return constructMethodTerm(t, "HasFields", p.Term_HAS_FIELDS, args, map[string]interface{}{}) } // Pluck plucks out one or more attributes from either an object or a sequence of // objects (projection). func (t Term) Pluck(args ...interface{}) Term { return constructMethodTerm(t, "Pluck", p.Term_PLUCK, args, map[string]interface{}{}) } // Without is the opposite of pluck; takes an object or a sequence of objects, and returns // them with the specified paths removed. func (t Term) Without(args ...interface{}) Term { return constructMethodTerm(t, "Without", p.Term_WITHOUT, args, map[string]interface{}{}) } // Merge merges two objects together to construct a new object with properties from both. // Gives preference to attributes from other when there is a conflict. func (t Term) Merge(args ...interface{}) Term { return constructMethodTerm(t, "Merge", p.Term_MERGE, funcWrapArgs(args), map[string]interface{}{}) } // Append appends a value to an array. func (t Term) Append(args ...interface{}) Term { return constructMethodTerm(t, "Append", p.Term_APPEND, args, map[string]interface{}{}) } // Prepend prepends a value to an array. func (t Term) Prepend(args ...interface{}) Term { return constructMethodTerm(t, "Prepend", p.Term_PREPEND, args, map[string]interface{}{}) } // Difference removes the elements of one array from another array. func (t Term) Difference(args ...interface{}) Term { return constructMethodTerm(t, "Difference", p.Term_DIFFERENCE, args, map[string]interface{}{}) } // SetInsert adds a value to an array and return it as a set (an array with distinct values). func (t Term) SetInsert(args ...interface{}) Term { return constructMethodTerm(t, "SetInsert", p.Term_SET_INSERT, args, map[string]interface{}{}) } // SetUnion adds several values to an array and return it as a set (an array with // distinct values). func (t Term) SetUnion(args ...interface{}) Term { return constructMethodTerm(t, "SetUnion", p.Term_SET_UNION, args, map[string]interface{}{}) } // SetIntersection calculates the intersection of two arrays returning values that // occur in both of them as a set (an array with distinct values). func (t Term) SetIntersection(args ...interface{}) Term { return constructMethodTerm(t, "SetIntersection", p.Term_SET_INTERSECTION, args, map[string]interface{}{}) } // SetDifference removes the elements of one array from another and return them as a set (an // array with distinct values). func (t Term) SetDifference(args ...interface{}) Term { return constructMethodTerm(t, "SetDifference", p.Term_SET_DIFFERENCE, args, map[string]interface{}{}) } // InsertAt inserts a value in to an array at a given index. Returns the modified array. func (t Term) InsertAt(args ...interface{}) Term { return constructMethodTerm(t, "InsertAt", p.Term_INSERT_AT, args, map[string]interface{}{}) } // SpliceAt inserts several values in to an array at a given index. Returns the modified array. func (t Term) SpliceAt(args ...interface{}) Term { return constructMethodTerm(t, "SpliceAt", p.Term_SPLICE_AT, args, map[string]interface{}{}) } // DeleteAt removes an element from an array at a given index. Returns the modified array. func (t Term) DeleteAt(args ...interface{}) Term { return constructMethodTerm(t, "DeleteAt", p.Term_DELETE_AT, args, map[string]interface{}{}) } // ChangeAt changes a value in an array at a given index. Returns the modified array. func (t Term) ChangeAt(args ...interface{}) Term { return constructMethodTerm(t, "ChangeAt", p.Term_CHANGE_AT, args, map[string]interface{}{}) } // Keys returns an array containing all of the object's keys. func (t Term) Keys(args ...interface{}) Term { return constructMethodTerm(t, "Keys", p.Term_KEYS, args, map[string]interface{}{}) } func (t Term) Values(args ...interface{}) Term { return constructMethodTerm(t, "Values", p.Term_VALUES, args, map[string]interface{}{}) } // Object creates an object from a list of key-value pairs, where the keys must be strings. func Object(args ...interface{}) Term { return constructRootTerm("Object", p.Term_OBJECT, args, map[string]interface{}{}) } gorethink-2.0.4/query_manipulation_test.go000066400000000000000000000204061272042630000210200ustar00rootroot00000000000000package gorethink import ( test "gopkg.in/check.v1" ) func (s *RethinkSuite) TestManipulationDocField(c *test.C) { query := Expr(map[string]interface{}{"a": 1}).Do(Row.Field("a")) var response int res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, 1) } func (s *RethinkSuite) TestManipulationPluck(c *test.C) { query := Expr(map[string]interface{}{"a": 1, "b": 2, "c": 3}).Pluck("a", "c") var response map[string]interface{} res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, map[string]interface{}{"a": 1, "c": 3}) } func (s *RethinkSuite) TestManipulationWithout(c *test.C) { query := Expr(map[string]interface{}{"a": 1, "b": 2, "c": 3}).Pluck("a", "c") var response map[string]interface{} res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, map[string]interface{}{"a": 1, "c": 3}) } func (s *RethinkSuite) TestManipulationMerge(c *test.C) { query := Expr(map[string]interface{}{"a": 1, "c": 3}).Merge(map[string]interface{}{"b": 2}) var response map[string]interface{} res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, map[string]interface{}{"a": 1, "b": 2, "c": 3}) } func (s *RethinkSuite) TestManipulationMergeLiteral(c *test.C) { query := Expr(map[string]interface{}{ "a": map[string]interface{}{ "aa": map[string]interface{}{ "aaa": 1, "aab": 2, }, "ab": map[string]interface{}{ "aba": 3, "abb": 4, }, }, }).Merge(map[string]interface{}{"a": map[string]interface{}{"ab": Literal()}}) var response map[string]interface{} res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, map[string]interface{}{"a": map[string]interface{}{"aa": map[string]interface{}{"aab": 2, "aaa": 1}}}) } func (s *RethinkSuite) TestManipulationAppend(c *test.C) { query := Expr([]interface{}{1, 2, 3}).Append(4).Append(5) var response []interface{} res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{1, 2, 3, 4, 5}) } func (s *RethinkSuite) TestManipulationPrepend(c *test.C) { query := Expr([]interface{}{3, 4, 5}).Prepend(2).Prepend(1) var response []interface{} res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{1, 2, 3, 4, 5}) } func (s *RethinkSuite) TestManipulationDifference(c *test.C) { query := Expr([]interface{}{3, 4, 5}).Difference([]interface{}{3, 4}) var response []interface{} res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{5}) } func (s *RethinkSuite) TestManipulationSetInsert(c *test.C) { query := Expr([]interface{}{1, 2, 3}).SetInsert(3).SetInsert(4) var response []interface{} res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{1, 2, 3, 4}) } func (s *RethinkSuite) TestManipulationSetUnion(c *test.C) { query := Expr([]interface{}{1, 2, 3}).SetUnion([]interface{}{3, 4}) var response []interface{} res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{1, 2, 3, 4}) } func (s *RethinkSuite) TestManipulationSetIntersection(c *test.C) { query := Expr([]interface{}{1, 2, 3}).SetIntersection([]interface{}{2, 3, 3, 4}) var response []interface{} res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{2, 3}) } func (s *RethinkSuite) TestManipulationSetDifference(c *test.C) { query := Expr([]interface{}{1, 2, 3}).SetDifference([]interface{}{2, 3, 4, 4}) var response []interface{} res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{1}) } func (s *RethinkSuite) TestManipulationHasFieldsTrue(c *test.C) { query := Expr(map[string]interface{}{"a": 1}).HasFields("a") var response bool r, err := query.Run(session) c.Assert(err, test.IsNil) err = r.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, true) } func (s *RethinkSuite) TestManipulationHasFieldsNested(c *test.C) { query := Expr(map[string]interface{}{"a": map[string]interface{}{"b": 1}}).HasFields(map[string]interface{}{"a": map[string]interface{}{"b": true}}) var response bool res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, true) } func (s *RethinkSuite) TestManipulationHasFieldsNestedShort(c *test.C) { query := Expr(map[string]interface{}{"a": map[string]interface{}{"b": 1}}).HasFields(map[string]interface{}{"a": "b"}) var response bool res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, true) } func (s *RethinkSuite) TestManipulationHasFieldsFalse(c *test.C) { query := Expr(map[string]interface{}{"a": 1}).HasFields("b") var response bool res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, false) } func (s *RethinkSuite) TestManipulationInsertAt(c *test.C) { query := Expr([]interface{}{1, 2, 3}).InsertAt(1, 1.5) var response []interface{} res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{1, 1.5, 2, 3}) } func (s *RethinkSuite) TestManipulationSpliceAt(c *test.C) { query := Expr([]interface{}{1, 2, 3}).SpliceAt(1, []interface{}{1.25, 1.5, 1.75}) var response []interface{} res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{1, 1.25, 1.5, 1.75, 2, 3}) } func (s *RethinkSuite) TestManipulationDeleteAt(c *test.C) { query := Expr([]interface{}{1, 2, 3}).DeleteAt(1) var response []interface{} res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{1, 3}) } func (s *RethinkSuite) TestManipulationDeleteAtRange(c *test.C) { query := Expr([]interface{}{1, 2, 3, 4}).DeleteAt(1, 3) var response []interface{} res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{1, 4}) } func (s *RethinkSuite) TestManipulationChangeAt(c *test.C) { query := Expr([]interface{}{1, 5, 3, 4}).ChangeAt(1, 2) var response []interface{} res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{1, 2, 3, 4}) } func (s *RethinkSuite) TestManipulationKeys(c *test.C) { query := Expr(map[string]interface{}{"a": 1, "b": 2, "c": 3}).Keys() var response []interface{} res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{"a", "b", "c"}) } func (s *RethinkSuite) TestManipulationValues(c *test.C) { query := Expr(map[string]interface{}{"a": 1, "b": 2, "c": 3}).Values() var response []interface{} res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{1, 2, 3}) } func (s *RethinkSuite) TestManipulationObject(c *test.C) { query := Object("a", 1, "b", 2) var response interface{} res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, map[string]interface{}{ "a": 1, "b": 2, }) } gorethink-2.0.4/query_math.go000066400000000000000000000174001272042630000162120ustar00rootroot00000000000000package gorethink import ( p "gopkg.in/dancannon/gorethink.v2/ql2" ) var ( // MinVal represents the smallest possible value RethinkDB can store MinVal = constructRootTerm("MinVal", p.Term_MINVAL, []interface{}{}, map[string]interface{}{}) // MaxVal represents the largest possible value RethinkDB can store MaxVal = constructRootTerm("MaxVal", p.Term_MAXVAL, []interface{}{}, map[string]interface{}{}) ) // Add sums two numbers or concatenates two arrays. func (t Term) Add(args ...interface{}) Term { return constructMethodTerm(t, "Add", p.Term_ADD, args, map[string]interface{}{}) } // Add sums two numbers or concatenates two arrays. func Add(args ...interface{}) Term { return constructRootTerm("Add", p.Term_ADD, args, map[string]interface{}{}) } // Sub subtracts two numbers. func (t Term) Sub(args ...interface{}) Term { return constructMethodTerm(t, "Sub", p.Term_SUB, args, map[string]interface{}{}) } // Sub subtracts two numbers. func Sub(args ...interface{}) Term { return constructRootTerm("Sub", p.Term_SUB, args, map[string]interface{}{}) } // Mul multiplies two numbers. func (t Term) Mul(args ...interface{}) Term { return constructMethodTerm(t, "Mul", p.Term_MUL, args, map[string]interface{}{}) } // Mul multiplies two numbers. func Mul(args ...interface{}) Term { return constructRootTerm("Mul", p.Term_MUL, args, map[string]interface{}{}) } // Div divides two numbers. func (t Term) Div(args ...interface{}) Term { return constructMethodTerm(t, "Div", p.Term_DIV, args, map[string]interface{}{}) } // Div divides two numbers. func Div(args ...interface{}) Term { return constructRootTerm("Div", p.Term_DIV, args, map[string]interface{}{}) } // Mod divides two numbers and returns the remainder. func (t Term) Mod(args ...interface{}) Term { return constructMethodTerm(t, "Mod", p.Term_MOD, args, map[string]interface{}{}) } // Mod divides two numbers and returns the remainder. func Mod(args ...interface{}) Term { return constructRootTerm("Mod", p.Term_MOD, args, map[string]interface{}{}) } // And performs a logical and on two values. func (t Term) And(args ...interface{}) Term { return constructMethodTerm(t, "And", p.Term_AND, args, map[string]interface{}{}) } // And performs a logical and on two values. func And(args ...interface{}) Term { return constructRootTerm("And", p.Term_AND, args, map[string]interface{}{}) } // Or performs a logical or on two values. func (t Term) Or(args ...interface{}) Term { return constructMethodTerm(t, "Or", p.Term_OR, args, map[string]interface{}{}) } // Or performs a logical or on two values. func Or(args ...interface{}) Term { return constructRootTerm("Or", p.Term_OR, args, map[string]interface{}{}) } // Eq returns true if two values are equal. func (t Term) Eq(args ...interface{}) Term { return constructMethodTerm(t, "Eq", p.Term_EQ, args, map[string]interface{}{}) } // Eq returns true if two values are equal. func Eq(args ...interface{}) Term { return constructRootTerm("Eq", p.Term_EQ, args, map[string]interface{}{}) } // Ne returns true if two values are not equal. func (t Term) Ne(args ...interface{}) Term { return constructMethodTerm(t, "Ne", p.Term_NE, args, map[string]interface{}{}) } // Ne returns true if two values are not equal. func Ne(args ...interface{}) Term { return constructRootTerm("Ne", p.Term_NE, args, map[string]interface{}{}) } // Gt returns true if the first value is greater than the second. func (t Term) Gt(args ...interface{}) Term { return constructMethodTerm(t, "Gt", p.Term_GT, args, map[string]interface{}{}) } // Gt returns true if the first value is greater than the second. func Gt(args ...interface{}) Term { return constructRootTerm("Gt", p.Term_GT, args, map[string]interface{}{}) } // Ge returns true if the first value is greater than or equal to the second. func (t Term) Ge(args ...interface{}) Term { return constructMethodTerm(t, "Ge", p.Term_GE, args, map[string]interface{}{}) } // Ge returns true if the first value is greater than or equal to the second. func Ge(args ...interface{}) Term { return constructRootTerm("Ge", p.Term_GE, args, map[string]interface{}{}) } // Lt returns true if the first value is less than the second. func (t Term) Lt(args ...interface{}) Term { return constructMethodTerm(t, "Lt", p.Term_LT, args, map[string]interface{}{}) } // Lt returns true if the first value is less than the second. func Lt(args ...interface{}) Term { return constructRootTerm("Lt", p.Term_LT, args, map[string]interface{}{}) } // Le returns true if the first value is less than or equal to the second. func (t Term) Le(args ...interface{}) Term { return constructMethodTerm(t, "Le", p.Term_LE, args, map[string]interface{}{}) } // Le returns true if the first value is less than or equal to the second. func Le(args ...interface{}) Term { return constructRootTerm("Le", p.Term_LE, args, map[string]interface{}{}) } // Not performs a logical not on a value. func (t Term) Not(args ...interface{}) Term { return constructMethodTerm(t, "Not", p.Term_NOT, args, map[string]interface{}{}) } // Not performs a logical not on a value. func Not(args ...interface{}) Term { return constructRootTerm("Not", p.Term_NOT, args, map[string]interface{}{}) } // RandomOpts contains the optional arguments for the Random term. type RandomOpts struct { Float interface{} `gorethink:"float,omitempty"` } func (o *RandomOpts) toMap() map[string]interface{} { return optArgsToMap(o) } // Random generates a random number between the given bounds. If no arguments // are given, the result will be a floating-point number in the range [0,1). // // When passing a single argument, r.random(x), the result will be in the // range [0,x), and when passing two arguments, r.random(x,y), the range is // [x,y). If x and y are equal, an error will occur, unless generating a // floating-point number, for which x will be returned. // // Note: The last argument given will always be the 'open' side of the range, // but when generating a floating-point number, the 'open' side may be less // than the 'closed' side. func (t Term) Random(args ...interface{}) Term { var opts = map[string]interface{}{} // Look for options map if len(args) > 0 { if possibleOpts, ok := args[len(args)-1].(RandomOpts); ok { opts = possibleOpts.toMap() args = args[:len(args)-1] } } return constructMethodTerm(t, "Random", p.Term_RANDOM, args, opts) } // Round causes the input number to be rounded the given value to the nearest whole integer. func (t Term) Round(args ...interface{}) Term { return constructMethodTerm(t, "Round", p.Term_ROUND, args, map[string]interface{}{}) } // Round causes the input number to be rounded the given value to the nearest whole integer. func Round(args ...interface{}) Term { return constructRootTerm("Round", p.Term_ROUND, args, map[string]interface{}{}) } // Ceil rounds the given value up, returning the smallest integer value greater // than or equal to the given value (the value’s ceiling). func (t Term) Ceil(args ...interface{}) Term { return constructMethodTerm(t, "Ceil", p.Term_CEIL, args, map[string]interface{}{}) } // Ceil rounds the given value up, returning the smallest integer value greater // than or equal to the given value (the value’s ceiling). func Ceil(args ...interface{}) Term { return constructRootTerm("Ceil", p.Term_CEIL, args, map[string]interface{}{}) } // Floor rounds the given value down, returning the largest integer value less // than or equal to the given value (the value’s floor). func (t Term) Floor(args ...interface{}) Term { return constructMethodTerm(t, "Floor", p.Term_FLOOR, args, map[string]interface{}{}) } // Floor rounds the given value down, returning the largest integer value less // than or equal to the given value (the value’s floor). func Floor(args ...interface{}) Term { return constructRootTerm("Floor", p.Term_FLOOR, args, map[string]interface{}{}) } gorethink-2.0.4/query_math_test.go000066400000000000000000000142331272042630000172520ustar00rootroot00000000000000package gorethink import ( test "gopkg.in/check.v1" ) func (s *RethinkSuite) TestMathAdd(c *test.C) { query := Expr(1).Add(2) var response int r, err := query.Run(session) c.Assert(err, test.IsNil) err = r.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, 3) } func (s *RethinkSuite) TestMathSub(c *test.C) { query := Expr(2).Sub(1) var response int r, err := query.Run(session) c.Assert(err, test.IsNil) err = r.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, 1) } func (s *RethinkSuite) TestMathSubNegative(c *test.C) { query := Expr(1).Sub(2) var response int r, err := query.Run(session) c.Assert(err, test.IsNil) err = r.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, -1) } func (s *RethinkSuite) TestMathMul(c *test.C) { query := Expr(5).Mul(4) var response int r, err := query.Run(session) c.Assert(err, test.IsNil) err = r.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, 20) } func (s *RethinkSuite) TestMathDiv(c *test.C) { query := Expr(8).Div(4) var response int r, err := query.Run(session) c.Assert(err, test.IsNil) err = r.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, 2) } func (s *RethinkSuite) TestMathMod(c *test.C) { query := Expr(7).Mod(2) var response int r, err := query.Run(session) c.Assert(err, test.IsNil) err = r.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, 1) } func (s *RethinkSuite) TestMathEqTrue(c *test.C) { query := Expr(1).Eq(1) var response bool r, err := query.Run(session) c.Assert(err, test.IsNil) err = r.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, true) } func (s *RethinkSuite) TestMathEqFalse(c *test.C) { query := Expr(1).Eq(2) var response bool r, err := query.Run(session) c.Assert(err, test.IsNil) err = r.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, false) } func (s *RethinkSuite) TestMathEqStringTrue(c *test.C) { query := Expr("test").Eq("test") var response bool r, err := query.Run(session) c.Assert(err, test.IsNil) err = r.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, true) } func (s *RethinkSuite) TestCompareLt(c *test.C) { query := Expr(2).Lt(1) var response bool r, err := query.Run(session) c.Assert(err, test.IsNil) err = r.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, false) } func (s *RethinkSuite) TestCompareLe(c *test.C) { query := Expr(2).Le(1) var response bool r, err := query.Run(session) c.Assert(err, test.IsNil) err = r.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, false) } func (s *RethinkSuite) TestCompareLeEqual(c *test.C) { query := Expr(2).Le(2) var response bool r, err := query.Run(session) c.Assert(err, test.IsNil) err = r.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, true) } func (s *RethinkSuite) TestCompareGt(c *test.C) { query := Expr(2).Gt(1) var response bool r, err := query.Run(session) c.Assert(err, test.IsNil) err = r.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, true) } func (s *RethinkSuite) TestCompareGe(c *test.C) { query := Expr(2).Ge(1) var response bool r, err := query.Run(session) c.Assert(err, test.IsNil) err = r.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, true) } func (s *RethinkSuite) TestCompareGeEqual(c *test.C) { query := Expr(2).Le(2) var response bool r, err := query.Run(session) c.Assert(err, test.IsNil) err = r.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, true) } func (s *RethinkSuite) TestBoolNotTrue(c *test.C) { query := Expr(true).Not() var response bool r, err := query.Run(session) c.Assert(err, test.IsNil) err = r.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, false) } func (s *RethinkSuite) TestBoolAnd(c *test.C) { query := Expr(true).And(true) var response bool r, err := query.Run(session) c.Assert(err, test.IsNil) err = r.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, true) } func (s *RethinkSuite) TestBoolOr(c *test.C) { query := Expr(true).Or(false) var response bool r, err := query.Run(session) c.Assert(err, test.IsNil) err = r.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, true) } func (s *RethinkSuite) TestBoolDeMorgan(c *test.C) { query := Expr(true).And(false).Eq(Expr(true).Not().Or(Expr(false).Not()).Not()) var response bool r, err := query.Run(session) c.Assert(err, test.IsNil) err = r.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, true) } func (s *RethinkSuite) TestMathRootRound(c *test.C) { query := Round(2.1) var response int r, err := query.Run(session) c.Assert(err, test.IsNil) err = r.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, 2) } func (s *RethinkSuite) TestMathMethodRound(c *test.C) { query := Expr(2.1).Round() var response int r, err := query.Run(session) c.Assert(err, test.IsNil) err = r.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, 2) } func (s *RethinkSuite) TestMathRootCeil(c *test.C) { query := Ceil(2.1) var response int r, err := query.Run(session) c.Assert(err, test.IsNil) err = r.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, 3) } func (s *RethinkSuite) TestMathMethodCeil(c *test.C) { query := Expr(2.1).Ceil() var response int r, err := query.Run(session) c.Assert(err, test.IsNil) err = r.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, 3) } func (s *RethinkSuite) TestMathRootFloor(c *test.C) { query := Floor(2.1) var response int r, err := query.Run(session) c.Assert(err, test.IsNil) err = r.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, 2) } func (s *RethinkSuite) TestMathMethodFloor(c *test.C) { query := Expr(2.1).Floor() var response int r, err := query.Run(session) c.Assert(err, test.IsNil) err = r.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, 2) } gorethink-2.0.4/query_select.go000066400000000000000000000132671272042630000165470ustar00rootroot00000000000000package gorethink import ( p "gopkg.in/dancannon/gorethink.v2/ql2" ) // DB references a database. func DB(args ...interface{}) Term { return constructRootTerm("DB", p.Term_DB, args, map[string]interface{}{}) } // TableOpts contains the optional arguments for the Table term type TableOpts struct { ReadMode interface{} `gorethink:"read_mode,omitempty"` UseOutdated interface{} `gorethink:"use_outdated,omitempty"` // Deprecated IdentifierFormat interface{} `gorethink:"identifier_format,omitempty"` } func (o *TableOpts) toMap() map[string]interface{} { return optArgsToMap(o) } // Table selects all documents in a table. This command can be chained with // other commands to do further processing on the data. // // There are two optional arguments. // - useOutdated: if true, this allows potentially out-of-date data to be // returned, with potentially faster reads. It also allows you to perform reads // from a secondary replica if a primary has failed. Default false. // - identifierFormat: possible values are name and uuid, with a default of name. // If set to uuid, then system tables will refer to servers, databases and tables // by UUID rather than name. (This only has an effect when used with system tables.) func Table(name interface{}, optArgs ...TableOpts) Term { opts := map[string]interface{}{} if len(optArgs) >= 1 { opts = optArgs[0].toMap() } return constructRootTerm("Table", p.Term_TABLE, []interface{}{name}, opts) } // Table selects all documents in a table. This command can be chained with // other commands to do further processing on the data. // // There are two optional arguments. // - useOutdated: if true, this allows potentially out-of-date data to be // returned, with potentially faster reads. It also allows you to perform reads // from a secondary replica if a primary has failed. Default false. // - identifierFormat: possible values are name and uuid, with a default of name. // If set to uuid, then system tables will refer to servers, databases and tables // by UUID rather than name. (This only has an effect when used with system tables.) func (t Term) Table(name interface{}, optArgs ...TableOpts) Term { opts := map[string]interface{}{} if len(optArgs) >= 1 { opts = optArgs[0].toMap() } return constructMethodTerm(t, "Table", p.Term_TABLE, []interface{}{name}, opts) } // Get gets a document by primary key. If nothing was found, RethinkDB will return a nil value. func (t Term) Get(args ...interface{}) Term { return constructMethodTerm(t, "Get", p.Term_GET, args, map[string]interface{}{}) } // GetAll gets all documents where the given value matches the value of the primary // index. Multiple values can be passed this function if you want to select multiple // documents. If the documents you are fetching have composite keys then each // argument should be a slice. For more information see the examples. func (t Term) GetAll(keys ...interface{}) Term { return constructMethodTerm(t, "GetAll", p.Term_GET_ALL, keys, map[string]interface{}{}) } // GetAllByIndex gets all documents where the given value matches the value of // the requested index. func (t Term) GetAllByIndex(index interface{}, keys ...interface{}) Term { return constructMethodTerm(t, "GetAll", p.Term_GET_ALL, keys, map[string]interface{}{"index": index}) } // BetweenOpts contains the optional arguments for the Between term type BetweenOpts struct { Index interface{} `gorethink:"index,omitempty"` LeftBound interface{} `gorethink:"left_bound,omitempty"` RightBound interface{} `gorethink:"right_bound,omitempty"` } func (o *BetweenOpts) toMap() map[string]interface{} { return optArgsToMap(o) } // Between gets all documents between two keys. Accepts three optional arguments: // index, leftBound, and rightBound. If index is set to the name of a secondary // index, between will return all documents where that index’s value is in the // specified range (it uses the primary key by default). leftBound or rightBound // may be set to open or closed to indicate whether or not to include that endpoint // of the range (by default, leftBound is closed and rightBound is open). // // You may also use the special constants r.minval and r.maxval for boundaries, // which represent “less than any index key” and “more than any index key” // respectively. For instance, if you use r.minval as the lower key, then between // will return all documents whose primary keys (or indexes) are less than the // specified upper key. func (t Term) Between(lowerKey, upperKey interface{}, optArgs ...BetweenOpts) Term { opts := map[string]interface{}{} if len(optArgs) >= 1 { opts = optArgs[0].toMap() } return constructMethodTerm(t, "Between", p.Term_BETWEEN, []interface{}{lowerKey, upperKey}, opts) } // FilterOpts contains the optional arguments for the Filter term type FilterOpts struct { Default interface{} `gorethink:"default,omitempty"` } func (o *FilterOpts) toMap() map[string]interface{} { return optArgsToMap(o) } // Filter gets all the documents for which the given predicate is true. // // Filter can be called on a sequence, selection, or a field containing an array // of elements. The return type is the same as the type on which the function was // called on. The body of every filter is wrapped in an implicit `.default(false)`, // and the default value can be changed by passing the optional argument `default`. // Setting this optional argument to `r.error()` will cause any non-existence // errors to abort the filter. func (t Term) Filter(f interface{}, optArgs ...FilterOpts) Term { opts := map[string]interface{}{} if len(optArgs) >= 1 { opts = optArgs[0].toMap() } return constructMethodTerm(t, "Filter", p.Term_FILTER, []interface{}{funcWrap(f)}, opts) } gorethink-2.0.4/query_select_test.go000066400000000000000000000323751272042630000176070ustar00rootroot00000000000000package gorethink import ( "encoding/json" "fmt" "math/rand" "testing" "time" test "gopkg.in/check.v1" ) func (s *RethinkSuite) TestSelectGet(c *test.C) { // Ensure table + database exist DBCreate("test").Exec(session) DB("test").TableCreate("Table1").Exec(session) // Insert rows DB("test").Table("Table1").Insert(objList).Exec(session) // Test query var response interface{} query := DB("test").Table("Table1").Get(6) res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, map[string]interface{}{"id": 6, "g1": 1, "g2": 1, "num": 15}) res.Close() } func (s *RethinkSuite) TestSelectJSONNumbers(c *test.C) { session, err := Connect(ConnectOpts{ Address: url, UseJSONNumber: true, }) c.Assert(err, test.IsNil) defer session.Close() // Ensure table + database exist DBCreate("test").Exec(session) DB("test").TableCreate("Table1").Exec(session) // Insert rows DB("test").Table("Table1").Insert(objList).Exec(session) // Test query var response interface{} query := DB("test").Table("Table1").Get(6) res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, map[string]interface{}{"id": json.Number("6"), "g1": json.Number("1"), "g2": json.Number("1"), "num": json.Number("15")}) res.Close() } func (s *RethinkSuite) TestSelectGetAll(c *test.C) { // Ensure table + database exist DBCreate("test").Exec(session) DB("test").TableCreate("Table1").Exec(session) DB("test").Table("Table1").IndexCreate("num").Exec(session) DB("test").Table("Table1").IndexWait().Exec(session) // Insert rows DB("test").Table("Table1").Insert(objList).Exec(session) // Test query var response []interface{} query := DB("test").Table("Table1").GetAll(6).OrderBy("id") res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{ map[string]interface{}{"num": 15, "id": 6, "g2": 1, "g1": 1}, }) res.Close() } func (s *RethinkSuite) TestSelectGetAllMultiple(c *test.C) { // Ensure table + database exist DBCreate("test").Exec(session) DB("test").TableCreate("Table1").Exec(session) DB("test").Table("Table1").IndexCreate("num").Exec(session) DB("test").Table("Table1").IndexWait().Exec(session) // Insert rows DB("test").Table("Table1").Insert(objList).Exec(session) // Test query var response []interface{} query := DB("test").Table("Table1").GetAll(1, 2, 3).OrderBy("id") res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{ map[string]interface{}{"num": 0, "id": 1, "g2": 1, "g1": 1}, map[string]interface{}{"num": 5, "id": 2, "g2": 2, "g1": 2}, map[string]interface{}{"num": 10, "id": 3, "g2": 2, "g1": 3}, }) res.Close() } func (s *RethinkSuite) TestSelectGetAllByIndex(c *test.C) { // Ensure table + database exist DBCreate("test").Exec(session) DB("test").TableCreate("Table1").Exec(session) DB("test").Table("Table1").IndexCreate("num").Exec(session) DB("test").Table("Table1").IndexWait().Exec(session) // Insert rows DB("test").Table("Table1").Insert(objList).Exec(session) // Test query var response interface{} query := DB("test").Table("Table1").GetAllByIndex("num", 15).OrderBy("id") res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, map[string]interface{}{"id": 6, "g1": 1, "g2": 1, "num": 15}) res.Close() } func (s *RethinkSuite) TestSelectGetAllMultipleByIndex(c *test.C) { // Ensure table + database exist DBCreate("test").Exec(session) DB("test").TableCreate("Table2").Exec(session) DB("test").Table("Table2").IndexCreate("num").Exec(session) DB("test").Table("Table2").IndexWait().Exec(session) // Insert rows DB("test").Table("Table2").Insert(objList).Exec(session) // Test query var response interface{} query := DB("test").Table("Table2").GetAllByIndex("num", 15).OrderBy("id") res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, map[string]interface{}{"id": 6, "g1": 1, "g2": 1, "num": 15}) res.Close() } func (s *RethinkSuite) TestSelectGetAllCompoundIndex(c *test.C) { // Ensure table + database exist DBCreate("test").Exec(session) DB("test").TableDrop("TableCompound").Exec(session) DB("test").TableCreate("TableCompound").Exec(session) write, err := DB("test").Table("TableCompound").IndexCreateFunc("full_name", func(row Term) interface{} { return []interface{}{row.Field("first_name"), row.Field("last_name")} }).RunWrite(session) DB("test").Table("TableCompound").IndexWait().Exec(session) c.Assert(err, test.IsNil) c.Assert(write.Created, test.Equals, 1) // Insert rows DB("test").Table("TableCompound").Insert(nameList).Exec(session) // Test query var response interface{} query := DB("test").Table("TableCompound").GetAllByIndex("full_name", []interface{}{"John", "Smith"}) res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, map[string]interface{}{"id": 1, "first_name": "John", "last_name": "Smith", "gender": "M"}) res.Close() } func (s *RethinkSuite) TestSelectBetween(c *test.C) { // Ensure table + database exist DBCreate("test").Exec(session) DB("test").TableCreate("Table1").Exec(session) // Insert rows DB("test").Table("Table1").Insert(objList).Exec(session) // Test query var response []interface{} query := DB("test").Table("Table1").Between(1, 3).OrderBy("id") res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{ map[string]interface{}{"num": 0, "id": 1, "g2": 1, "g1": 1}, map[string]interface{}{"num": 5, "id": 2, "g2": 2, "g1": 2}, }) res.Close() } func (s *RethinkSuite) TestSelectBetweenWithIndex(c *test.C) { // Ensure table + database exist DBCreate("test").Exec(session) DB("test").TableCreate("Table2").Exec(session) DB("test").Table("Table2").IndexCreate("num").Exec(session) DB("test").Table("Table2").IndexWait().Exec(session) // Insert rows DB("test").Table("Table2").Insert(objList).Exec(session) // Test query var response []interface{} query := DB("test").Table("Table2").Between(10, 50, BetweenOpts{ Index: "num", }).OrderBy("id") res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{ map[string]interface{}{"num": 10, "id": 3, "g2": 2, "g1": 3}, map[string]interface{}{"num": 15, "id": 6, "g2": 1, "g1": 1}, map[string]interface{}{"num": 25, "id": 9, "g2": 3, "g1": 2}, }) res.Close() } func (s *RethinkSuite) TestSelectBetweenWithOptions(c *test.C) { // Ensure table + database exist DBCreate("test").Exec(session) DB("test").TableCreate("Table2").Exec(session) DB("test").Table("Table2").IndexCreate("num").Exec(session) DB("test").Table("Table2").IndexWait().Exec(session) // Insert rows DB("test").Table("Table2").Insert(objList).Exec(session) // Test query var response []interface{} query := DB("test").Table("Table2").Between(10, 50, BetweenOpts{ Index: "num", RightBound: "closed", }).OrderBy("id") res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{ map[string]interface{}{"num": 10, "id": 3, "g2": 2, "g1": 3}, map[string]interface{}{"num": 15, "id": 6, "g2": 1, "g1": 1}, map[string]interface{}{"num": 50, "id": 8, "g2": 2, "g1": 4}, map[string]interface{}{"num": 25, "id": 9, "g2": 3, "g1": 2}, }) res.Close() } func (s *RethinkSuite) TestSelectFilterImplicit(c *test.C) { // Ensure table + database exist DBCreate("test").Exec(session) DB("test").TableCreate("Table1").Exec(session) // Insert rows DB("test").Table("Table1").Insert(objList).Exec(session) // Test query var response []interface{} query := DB("test").Table("Table1").Filter(Row.Field("num").Ge(50)).OrderBy("id") res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{ map[string]interface{}{"num": 100, "id": 5, "g2": 3, "g1": 2}, map[string]interface{}{"num": 50, "id": 8, "g2": 2, "g1": 4}, }) res.Close() } func (s *RethinkSuite) TestSelectFilterFunc(c *test.C) { // Ensure table + database exist DBCreate("test").Exec(session) DB("test").TableCreate("Table1").Exec(session) // Insert rows DB("test").Table("Table1").Insert(objList).Exec(session) // Test query var response []interface{} query := DB("test").Table("Table1").Filter(func(row Term) Term { return row.Field("num").Ge(50) }).OrderBy("id") res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{ map[string]interface{}{"num": 100, "id": 5, "g2": 3, "g1": 2}, map[string]interface{}{"num": 50, "id": 8, "g2": 2, "g1": 4}, }) res.Close() } func (s *RethinkSuite) TestSelectManyRows(c *test.C) { // Ensure table + database exist DBCreate("test").RunWrite(session) DB("test").TableCreate("TestMany").RunWrite(session) DB("test").Table("TestMany").Delete().RunWrite(session) // Insert rows for i := 0; i < 100; i++ { data := []interface{}{} for j := 0; j < 100; j++ { data = append(data, map[string]interface{}{ "i": i, "j": j, }) } DB("test").Table("TestMany").Insert(data).RunWrite(session) } // Test query res, err := DB("test").Table("TestMany").Run(session, RunOpts{ MaxBatchRows: 1, }) c.Assert(err, test.IsNil) var n int var response map[string]interface{} for res.Next(&response) { n++ } c.Assert(res.Err(), test.IsNil) c.Assert(n, test.Equals, 10000) res.Close() } func (s *RethinkSuite) TestConcurrentSelectManyWorkers(c *test.C) { if testing.Short() { c.Skip("Skipping long test") } rand.Seed(time.Now().UnixNano()) sess, _ := Connect(ConnectOpts{ Address: url, MaxOpen: 200, MaxIdle: 200, }) // Ensure table + database exist DBCreate("test").RunWrite(sess) DB("test").TableDrop("TestConcurrent").RunWrite(sess) DB("test").TableCreate("TestConcurrent").RunWrite(sess) DB("test").TableDrop("TestConcurrent2").RunWrite(sess) DB("test").TableCreate("TestConcurrent2").RunWrite(sess) // Insert rows for j := 0; j < 200; j++ { DB("test").Table("TestConcurrent").Insert(map[string]interface{}{ "id": j, "i": j, }).Exec(sess) DB("test").Table("TestConcurrent2").Insert(map[string]interface{}{ "j": j, "k": j * 2, }).Exec(sess) } // Test queries concurrently numQueries := 1000 numWorkers := 10 queryChan := make(chan int) doneChan := make(chan error) // Start workers for i := 0; i < numWorkers; i++ { go func() { for _ = range queryChan { res, err := DB("test").Table("TestConcurrent2").EqJoin("j", DB("test").Table("TestConcurrent")).Zip().Run(sess) if err != nil { doneChan <- err return } var response []map[string]interface{} err = res.All(&response) if err != nil { doneChan <- err return } if err := res.Close(); err != nil { doneChan <- err return } if len(response) != 200 { doneChan <- fmt.Errorf("expected response length 200, received %d", len(response)) return } res, err = DB("test").Table("TestConcurrent").Get(response[rand.Intn(len(response))]["id"]).Run(sess) if err != nil { doneChan <- err return } err = res.All(&response) if err != nil { doneChan <- err return } if err := res.Close(); err != nil { doneChan <- err return } if len(response) != 1 { doneChan <- fmt.Errorf("expected response length 1, received %d", len(response)) return } doneChan <- nil } }() } go func() { for i := 0; i < numQueries; i++ { queryChan <- i } }() for i := 0; i < numQueries; i++ { ret := <-doneChan if ret != nil { c.Fatalf("non-nil error returned (%s)", ret) } } } func (s *RethinkSuite) TestConcurrentSelectManyRows(c *test.C) { if testing.Short() { c.Skip("Skipping long test") } // Ensure table + database exist DBCreate("test").RunWrite(session) DB("test").TableCreate("TestMany").RunWrite(session) DB("test").Table("TestMany").Delete().RunWrite(session) // Insert rows for i := 0; i < 100; i++ { DB("test").Table("TestMany").Insert(map[string]interface{}{ "i": i, }).Exec(session) } // Test queries concurrently attempts := 10 waitChannel := make(chan error, attempts) for i := 0; i < attempts; i++ { go func(i int, ch chan error) { res, err := DB("test").Table("TestMany").Run(session) if err != nil { ch <- err return } var response []map[string]interface{} err = res.All(&response) if err != nil { ch <- err return } if len(response) != 100 { ch <- fmt.Errorf("expected response length 100, received %d", len(response)) return } res.Close() ch <- nil }(i, waitChannel) } for i := 0; i < attempts; i++ { ret := <-waitChannel if ret != nil { c.Fatalf("non-nil error returned (%s)", ret) } } } gorethink-2.0.4/query_string.go000066400000000000000000000035131272042630000165670ustar00rootroot00000000000000package gorethink import ( p "gopkg.in/dancannon/gorethink.v2/ql2" ) // Match matches against a regular expression. If no match is found, returns // null. If there is a match then an object with the following fields is // returned: // str: The matched string // start: The matched string’s start // end: The matched string’s end // groups: The capture groups defined with parentheses // // Accepts RE2 syntax (https://code.google.com/p/re2/wiki/Syntax). You can // enable case-insensitive matching by prefixing the regular expression with // (?i). See the linked RE2 documentation for more flags. // // The match command does not support backreferences. func (t Term) Match(args ...interface{}) Term { return constructMethodTerm(t, "Match", p.Term_MATCH, args, map[string]interface{}{}) } // Split splits a string into substrings. Splits on whitespace when called with no arguments. // When called with a separator, splits on that separator. When called with a separator // and a maximum number of splits, splits on that separator at most max_splits times. // (Can be called with null as the separator if you want to split on whitespace while still // specifying max_splits.) // // Mimics the behavior of Python's string.split in edge cases, except for splitting on the // empty string, which instead produces an array of single-character strings. func (t Term) Split(args ...interface{}) Term { return constructMethodTerm(t, "Split", p.Term_SPLIT, funcWrapArgs(args), map[string]interface{}{}) } // Upcase upper-cases a string. func (t Term) Upcase(args ...interface{}) Term { return constructMethodTerm(t, "Upcase", p.Term_UPCASE, args, map[string]interface{}{}) } // Downcase lower-cases a string. func (t Term) Downcase(args ...interface{}) Term { return constructMethodTerm(t, "Downcase", p.Term_DOWNCASE, args, map[string]interface{}{}) } gorethink-2.0.4/query_string_test.go000066400000000000000000000037601272042630000176320ustar00rootroot00000000000000package gorethink import ( test "gopkg.in/check.v1" ) func (s *RethinkSuite) TestStringMatchSuccess(c *test.C) { query := Expr("id:0,name:mlucy,foo:bar").Match("name:(\\w+)").Field("groups").Nth(0).Field("str") var response string res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, "mlucy") } func (s *RethinkSuite) TestStringMatchFail(c *test.C) { query := Expr("id:0,foo:bar").Match("name:(\\w+)") res, err := query.Run(session) c.Assert(err, test.IsNil) c.Assert(res.IsNil(), test.Equals, true) } func (s *RethinkSuite) TestStringSplit(c *test.C) { query := Expr("a,b,c").Split(",") var response []string res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, test.DeepEquals, []string{"a", "b", "c"}) } func (s *RethinkSuite) TestStringSplitMax(c *test.C) { query := Expr("a,b,c").Split(",", 1) var response []string res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, test.DeepEquals, []string{"a", "b,c"}) } func (s *RethinkSuite) TestStringSplitWhitespace(c *test.C) { query := Expr("a b c").Split() var response []string res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, test.DeepEquals, []string{"a", "b", "c"}) } func (s *RethinkSuite) TestStringMatchUpcase(c *test.C) { query := Expr("tESt").Upcase() var response string res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, "TEST") } func (s *RethinkSuite) TestStringMatchDowncase(c *test.C) { query := Expr("tESt").Downcase() var response string res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, "test") } gorethink-2.0.4/query_table.go000066400000000000000000000155451272042630000163600ustar00rootroot00000000000000package gorethink import ( p "gopkg.in/dancannon/gorethink.v2/ql2" ) // TableCreateOpts contains the optional arguments for the TableCreate term type TableCreateOpts struct { PrimaryKey interface{} `gorethink:"primary_key,omitempty"` Durability interface{} `gorethink:"durability,omitempty"` Shards interface{} `gorethink:"shards,omitempty"` DataCenter interface{} `gorethink:"replicas,omitempty"` PrimaryReplicaTag interface{} `gorethink:"primary_replica_tag,omitempty"` } func (o *TableCreateOpts) toMap() map[string]interface{} { return optArgsToMap(o) } // TableCreate creates a table. A RethinkDB table is a collection of JSON // documents. // // Note: Only alphanumeric characters and underscores are valid for the table name. func TableCreate(name interface{}, optArgs ...TableCreateOpts) Term { opts := map[string]interface{}{} if len(optArgs) >= 1 { opts = optArgs[0].toMap() } return constructRootTerm("TableCreate", p.Term_TABLE_CREATE, []interface{}{name}, opts) } // TableCreate creates a table. A RethinkDB table is a collection of JSON // documents. // // Note: Only alphanumeric characters and underscores are valid for the table name. func (t Term) TableCreate(name interface{}, optArgs ...TableCreateOpts) Term { opts := map[string]interface{}{} if len(optArgs) >= 1 { opts = optArgs[0].toMap() } return constructMethodTerm(t, "TableCreate", p.Term_TABLE_CREATE, []interface{}{name}, opts) } // TableDrop deletes a table. The table and all its data will be deleted. func TableDrop(args ...interface{}) Term { return constructRootTerm("TableDrop", p.Term_TABLE_DROP, args, map[string]interface{}{}) } // TableDrop deletes a table. The table and all its data will be deleted. func (t Term) TableDrop(args ...interface{}) Term { return constructMethodTerm(t, "TableDrop", p.Term_TABLE_DROP, args, map[string]interface{}{}) } // TableList lists all table names in a database. func TableList(args ...interface{}) Term { return constructRootTerm("TableList", p.Term_TABLE_LIST, args, map[string]interface{}{}) } // TableList lists all table names in a database. func (t Term) TableList(args ...interface{}) Term { return constructMethodTerm(t, "TableList", p.Term_TABLE_LIST, args, map[string]interface{}{}) } // IndexCreateOpts contains the optional arguments for the IndexCreate term type IndexCreateOpts struct { Multi interface{} `gorethink:"multi,omitempty"` Geo interface{} `gorethink:"geo,omitempty"` } func (o *IndexCreateOpts) toMap() map[string]interface{} { return optArgsToMap(o) } // IndexCreate creates a new secondary index on a table. Secondary indexes // improve the speed of many read queries at the slight cost of increased // storage space and decreased write performance. // // IndexCreate supports the creation of the following types of indexes, to create // indexes using arbitrary expressions use IndexCreateFunc. // - Simple indexes based on the value of a single field. // - Geospatial indexes based on indexes of geometry objects, created when the // geo optional argument is true. func (t Term) IndexCreate(name interface{}, optArgs ...IndexCreateOpts) Term { opts := map[string]interface{}{} if len(optArgs) >= 1 { opts = optArgs[0].toMap() } return constructMethodTerm(t, "IndexCreate", p.Term_INDEX_CREATE, []interface{}{name}, opts) } // IndexCreateFunc creates a new secondary index on a table. Secondary indexes // improve the speed of many read queries at the slight cost of increased // storage space and decreased write performance. The function takes a index // name and RQL term as the index value , the term can be an anonymous function // or a binary representation obtained from the function field of indexStatus. // // It supports the creation of the following types of indexes. // - Simple indexes based on the value of a single field where the index has a // different name to the field. // - Compound indexes based on multiple fields. // - Multi indexes based on arrays of values, created when the multi optional argument is true. func (t Term) IndexCreateFunc(name, indexFunction interface{}, optArgs ...IndexCreateOpts) Term { opts := map[string]interface{}{} if len(optArgs) >= 1 { opts = optArgs[0].toMap() } return constructMethodTerm(t, "IndexCreate", p.Term_INDEX_CREATE, []interface{}{name, funcWrap(indexFunction)}, opts) } // IndexDrop deletes a previously created secondary index of a table. func (t Term) IndexDrop(args ...interface{}) Term { return constructMethodTerm(t, "IndexDrop", p.Term_INDEX_DROP, args, map[string]interface{}{}) } // IndexList lists all the secondary indexes of a table. func (t Term) IndexList(args ...interface{}) Term { return constructMethodTerm(t, "IndexList", p.Term_INDEX_LIST, args, map[string]interface{}{}) } // IndexRenameOpts contains the optional arguments for the IndexRename term type IndexRenameOpts struct { Overwrite interface{} `gorethink:"overwrite,omitempty"` } func (o *IndexRenameOpts) toMap() map[string]interface{} { return optArgsToMap(o) } // IndexRename renames an existing secondary index on a table. func (t Term) IndexRename(oldName, newName interface{}, optArgs ...IndexRenameOpts) Term { opts := map[string]interface{}{} if len(optArgs) >= 1 { opts = optArgs[0].toMap() } return constructMethodTerm(t, "IndexRename", p.Term_INDEX_RENAME, []interface{}{oldName, newName}, opts) } // IndexStatus gets the status of the specified indexes on this table, or the // status of all indexes on this table if no indexes are specified. func (t Term) IndexStatus(args ...interface{}) Term { return constructMethodTerm(t, "IndexStatus", p.Term_INDEX_STATUS, args, map[string]interface{}{}) } // IndexWait waits for the specified indexes on this table to be ready, or for // all indexes on this table to be ready if no indexes are specified. func (t Term) IndexWait(args ...interface{}) Term { return constructMethodTerm(t, "IndexWait", p.Term_INDEX_WAIT, args, map[string]interface{}{}) } // ChangesOpts contains the optional arguments for the Changes term type ChangesOpts struct { Squash interface{} `gorethink:"squash,omitempty"` IncludeInitial interface{} `gorethink:"include_initial,omitempty"` IncludeStates interface{} `gorethink:"include_states,omitempty"` IncludeOffsets interface{} `gorethink:"include_offsets,omitempty"` IncludeTypes interface{} `gorethink:"include_types,omitempty"` ChangefeedQueueSize interface{} `gorethink:"changefeed_queue_size,omitempty"` } // ChangesOpts contains the optional arguments for the Changes term func (o *ChangesOpts) toMap() map[string]interface{} { return optArgsToMap(o) } // Changes returns an infinite stream of objects representing changes to a query. func (t Term) Changes(optArgs ...ChangesOpts) Term { opts := map[string]interface{}{} if len(optArgs) >= 1 { opts = optArgs[0].toMap() } return constructMethodTerm(t, "Changes", p.Term_CHANGES, []interface{}{}, opts) } gorethink-2.0.4/query_table_test.go000066400000000000000000000224201272042630000174050ustar00rootroot00000000000000package gorethink import ( "sync" "time" test "gopkg.in/check.v1" ) func (s *RethinkSuite) TestTableCreate(c *test.C) { DB("test").TableDrop("test").Exec(session) // Test database creation query := DB("test").TableCreate("test") response, err := query.RunWrite(session) c.Assert(err, test.IsNil) c.Assert(response.TablesCreated, jsonEquals, 1) } func (s *RethinkSuite) TestTableCreateSessionDatabase(c *test.C) { session, err := Connect(ConnectOpts{ Address: url, }) c.Assert(err, test.IsNil) TableDrop("test").Exec(session) // Test database creation response, err := TableCreate("test").RunWrite(session) c.Assert(err, test.IsNil) c.Assert(response.TablesCreated, jsonEquals, 1) } func (s *RethinkSuite) TestTableCreatePrimaryKey(c *test.C) { DB("test").TableDrop("testOpts").Exec(session) // Test database creation query := DB("test").TableCreate("testOpts", TableCreateOpts{ PrimaryKey: "it", }) response, err := query.RunWrite(session) c.Assert(err, test.IsNil) c.Assert(response.TablesCreated, jsonEquals, 1) } func (s *RethinkSuite) TestTableCreateSoftDurability(c *test.C) { DB("test").TableDrop("testOpts").Exec(session) // Test database creation query := DB("test").TableCreate("testOpts", TableCreateOpts{ Durability: "soft", }) response, err := query.RunWrite(session) c.Assert(err, test.IsNil) c.Assert(response.TablesCreated, jsonEquals, 1) } func (s *RethinkSuite) TestTableCreateSoftMultipleOpts(c *test.C) { DB("test").TableDrop("testOpts").Exec(session) // Test database creation query := DB("test").TableCreate("testOpts", TableCreateOpts{ PrimaryKey: "it", Durability: "soft", }) response, err := query.RunWrite(session) c.Assert(err, test.IsNil) c.Assert(response.TablesCreated, jsonEquals, 1) DB("test").TableDrop("test").Exec(session) } func (s *RethinkSuite) TestTableList(c *test.C) { var response []interface{} DB("test").TableCreate("test").Exec(session) // Try and find it in the list success := false res, err := DB("test").TableList().Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, test.FitsTypeOf, []interface{}{}) for _, db := range response { if db == "test" { success = true } } c.Assert(success, test.Equals, true) } func (s *RethinkSuite) TestTableDelete(c *test.C) { DB("test").TableCreate("test").Exec(session) // Test database creation query := DB("test").TableDrop("test") response, err := query.RunWrite(session) c.Assert(err, test.IsNil) c.Assert(response.TablesDropped, jsonEquals, 1) } func (s *RethinkSuite) TestTableIndexCreate(c *test.C) { DB("test").TableCreate("test").Exec(session) DB("test").Table("test").IndexDrop("test").Exec(session) // Test database creation query := DB("test").Table("test").IndexCreate("test", IndexCreateOpts{ Multi: true, }) response, err := query.RunWrite(session) c.Assert(err, test.IsNil) c.Assert(response.Created, jsonEquals, 1) } func (s *RethinkSuite) TestTableCompoundIndexCreate(c *test.C) { DBCreate("test").Exec(session) DB("test").TableDrop("TableCompound").Exec(session) DB("test").TableCreate("TableCompound").Exec(session) response, err := DB("test").Table("TableCompound").IndexCreateFunc("full_name", func(row Term) interface{} { return []interface{}{row.Field("first_name"), row.Field("last_name")} }).RunWrite(session) c.Assert(err, test.IsNil) c.Assert(response.Created, test.Equals, 1) } func (s *RethinkSuite) TestTableIndexList(c *test.C) { var response []interface{} DB("test").TableCreate("test").Exec(session) DB("test").Table("test").IndexCreate("test").Exec(session) DB("test").Table("test").IndexWait().Exec(session) // Try and find it in the list success := false res, err := DB("test").Table("test").IndexList().Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, test.FitsTypeOf, []interface{}{}) for _, db := range response { if db == "test" { success = true } } c.Assert(success, test.Equals, true) } func (s *RethinkSuite) TestTableIndexDelete(c *test.C) { DB("test").TableCreate("test").Exec(session) DB("test").Table("test").IndexCreate("test").Exec(session) DB("test").Table("test").IndexWait().Exec(session) // Test database creation query := DB("test").Table("test").IndexDrop("test") response, err := query.RunWrite(session) c.Assert(err, test.IsNil) c.Assert(response.Dropped, jsonEquals, 1) } func (s *RethinkSuite) TestTableIndexRename(c *test.C) { DB("test").TableDrop("test").Exec(session) DB("test").TableCreate("test").Exec(session) DB("test").Table("test").IndexCreate("test").Exec(session) DB("test").Table("test").IndexWait().Exec(session) // Test index rename query := DB("test").Table("test").IndexRename("test", "test2") response, err := query.RunWrite(session) c.Assert(err, test.IsNil) c.Assert(response.Renamed, jsonEquals, 1) } func (s *RethinkSuite) TestTableChanges(c *test.C) { DB("test").TableDrop("changes").Exec(session) DB("test").TableCreate("changes").Exec(session) var n int res, err := DB("test").Table("changes").Changes().Run(session) if err != nil { c.Fatal(err.Error()) } c.Assert(res.Type(), test.Equals, "Feed") wg := &sync.WaitGroup{} wg.Add(1) // Use goroutine to wait for changes. Prints the first 10 results go func() { var response interface{} for n < 10 && res.Next(&response) { n++ } if res.Err() != nil { c.Fatal(res.Err()) } wg.Done() }() DB("test").Table("changes").Insert(map[string]interface{}{"n": 1}).Exec(session) DB("test").Table("changes").Insert(map[string]interface{}{"n": 2}).Exec(session) DB("test").Table("changes").Insert(map[string]interface{}{"n": 3}).Exec(session) DB("test").Table("changes").Insert(map[string]interface{}{"n": 4}).Exec(session) DB("test").Table("changes").Insert(map[string]interface{}{"n": 5}).Exec(session) DB("test").Table("changes").Insert(map[string]interface{}{"n": 6}).Exec(session) DB("test").Table("changes").Insert(map[string]interface{}{"n": 7}).Exec(session) DB("test").Table("changes").Insert(map[string]interface{}{"n": 8}).Exec(session) DB("test").Table("changes").Insert(map[string]interface{}{"n": 9}).Exec(session) DB("test").Table("changes").Insert(map[string]interface{}{"n": 10}).Exec(session) wg.Wait() c.Assert(n, test.Equals, 10) } func (s *RethinkSuite) TestTableChangesExit(c *test.C) { DB("test").TableDrop("changes").Exec(session) DB("test").TableCreate("changes").Exec(session) var n int res, err := DB("test").Table("changes").Changes().Run(session) if err != nil { c.Fatal(err.Error()) } c.Assert(res.Type(), test.Equals, "Feed") change := make(chan ChangeResponse) // Close cursor after one second go func() { <-time.After(time.Second) res.Close() }() // Insert 5 docs DB("test").Table("changes").Insert(map[string]interface{}{"n": 1}).Exec(session) DB("test").Table("changes").Insert(map[string]interface{}{"n": 2}).Exec(session) DB("test").Table("changes").Insert(map[string]interface{}{"n": 3}).Exec(session) DB("test").Table("changes").Insert(map[string]interface{}{"n": 4}).Exec(session) DB("test").Table("changes").Insert(map[string]interface{}{"n": 5}).Exec(session) // Listen for changes res.Listen(change) for _ = range change { n++ } c.Assert(n, test.Equals, 5) } func (s *RethinkSuite) TestTableChangesExitNoResults(c *test.C) { DB("test").TableDrop("changes").Exec(session) DB("test").TableCreate("changes").Exec(session) var n int res, err := DB("test").Table("changes").Changes().Run(session) if err != nil { c.Fatal(err.Error()) } c.Assert(res.Type(), test.Equals, "Feed") change := make(chan ChangeResponse) // Close cursor after one second go func() { <-time.After(time.Second) res.Close() }() // Listen for changes res.Listen(change) for _ = range change { n++ } c.Assert(n, test.Equals, 0) } func (s *RethinkSuite) TestTableChangesIncludeInitial(c *test.C) { DB("test").TableDrop("changes").Exec(session) DB("test").TableCreate("changes").Exec(session) // Insert 5 documents to table initially DB("test").Table("changes").Insert(map[string]interface{}{"n": 1}).Exec(session) DB("test").Table("changes").Insert(map[string]interface{}{"n": 2}).Exec(session) DB("test").Table("changes").Insert(map[string]interface{}{"n": 3}).Exec(session) DB("test").Table("changes").Insert(map[string]interface{}{"n": 4}).Exec(session) DB("test").Table("changes").Insert(map[string]interface{}{"n": 5}).Exec(session) var n int res, err := DB("test").Table("changes").Changes(ChangesOpts{IncludeInitial: true}).Run(session) if err != nil { c.Fatal(err.Error()) } c.Assert(res.Type(), test.Equals, "Feed") wg := &sync.WaitGroup{} wg.Add(1) // Use goroutine to wait for changes. Prints the first 10 results go func() { var response interface{} for n < 10 && res.Next(&response) { n++ } if res.Err() != nil { c.Fatal(res.Err()) } wg.Done() }() DB("test").Table("changes").Insert(map[string]interface{}{"n": 6}).Exec(session) DB("test").Table("changes").Insert(map[string]interface{}{"n": 7}).Exec(session) DB("test").Table("changes").Insert(map[string]interface{}{"n": 8}).Exec(session) DB("test").Table("changes").Insert(map[string]interface{}{"n": 9}).Exec(session) DB("test").Table("changes").Insert(map[string]interface{}{"n": 10}).Exec(session) wg.Wait() c.Assert(n, test.Equals, 10) } gorethink-2.0.4/query_test.go000066400000000000000000000035631272042630000162450ustar00rootroot00000000000000package gorethink import test "gopkg.in/check.v1" func (s *RethinkSuite) TestQueryRun(c *test.C) { var response string res, err := Expr("Test").Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, "Test") } func (s *RethinkSuite) TestQueryReadOne(c *test.C) { var response string err := Expr("Test").ReadOne(&response, session) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, "Test") } func (s *RethinkSuite) TestQueryReadAll(c *test.C) { var response []int err := Expr([]int{1, 2, 3}).ReadAll(&response, session) c.Assert(err, test.IsNil) c.Assert(response, test.HasLen, 3) c.Assert(response, test.DeepEquals, []int{1, 2, 3}) } func (s *RethinkSuite) TestQueryExec(c *test.C) { err := Expr("Test").Exec(session) c.Assert(err, test.IsNil) } func (s *RethinkSuite) TestQueryProfile(c *test.C) { var response string res, err := Expr("Test").Run(session, RunOpts{ Profile: true, }) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(res.Profile(), test.NotNil) c.Assert(response, test.Equals, "Test") } func (s *RethinkSuite) TestQueryRunRawTime(c *test.C) { var response map[string]interface{} res, err := Now().Run(session, RunOpts{ TimeFormat: "raw", }) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response["$reql_type$"], test.NotNil) c.Assert(response["$reql_type$"], test.Equals, "TIME") } func (s *RethinkSuite) TestQueryRunNil(c *test.C) { res, err := Expr("Test").Run(nil) c.Assert(res, test.IsNil) c.Assert(err, test.NotNil) c.Assert(err, test.Equals, ErrConnectionClosed) } func (s *RethinkSuite) TestQueryRunNotConnected(c *test.C) { res, err := Expr("Test").Run(&Session{}) c.Assert(res, test.IsNil) c.Assert(err, test.NotNil) c.Assert(err, test.Equals, ErrConnectionClosed) }gorethink-2.0.4/query_time.go000066400000000000000000000204401272042630000162150ustar00rootroot00000000000000package gorethink import ( p "gopkg.in/dancannon/gorethink.v2/ql2" ) // Now returns a time object representing the current time in UTC func Now(args ...interface{}) Term { return constructRootTerm("Now", p.Term_NOW, args, map[string]interface{}{}) } // Time creates a time object for a specific time func Time(args ...interface{}) Term { return constructRootTerm("Time", p.Term_TIME, args, map[string]interface{}{}) } // EpochTime returns a time object based on seconds since epoch func EpochTime(args ...interface{}) Term { return constructRootTerm("EpochTime", p.Term_EPOCH_TIME, args, map[string]interface{}{}) } // ISO8601Opts contains the optional arguments for the ISO8601 term type ISO8601Opts struct { DefaultTimezone interface{} `gorethink:"default_timezone,omitempty"` } func (o *ISO8601Opts) toMap() map[string]interface{} { return optArgsToMap(o) } // ISO8601 returns a time object based on an ISO8601 formatted date-time string func ISO8601(date interface{}, optArgs ...ISO8601Opts) Term { opts := map[string]interface{}{} if len(optArgs) >= 1 { opts = optArgs[0].toMap() } return constructRootTerm("ISO8601", p.Term_ISO8601, []interface{}{date}, opts) } // InTimezone returns a new time object with a different time zone. While the // time stays the same, the results returned by methods such as hours() will // change since they take the timezone into account. The timezone argument // has to be of the ISO 8601 format. func (t Term) InTimezone(args ...interface{}) Term { return constructMethodTerm(t, "InTimezone", p.Term_IN_TIMEZONE, args, map[string]interface{}{}) } // Timezone returns the timezone of the time object func (t Term) Timezone(args ...interface{}) Term { return constructMethodTerm(t, "Timezone", p.Term_TIMEZONE, args, map[string]interface{}{}) } // DuringOpts contains the optional arguments for the During term type DuringOpts struct { LeftBound interface{} `gorethink:"left_bound,omitempty"` RightBound interface{} `gorethink:"right_bound,omitempty"` } func (o *DuringOpts) toMap() map[string]interface{} { return optArgsToMap(o) } // During returns true if a time is between two other times // (by default, inclusive for the start, exclusive for the end). func (t Term) During(startTime, endTime interface{}, optArgs ...DuringOpts) Term { opts := map[string]interface{}{} if len(optArgs) >= 1 { opts = optArgs[0].toMap() } return constructMethodTerm(t, "During", p.Term_DURING, []interface{}{startTime, endTime}, opts) } // Date returns a new time object only based on the day, month and year // (ie. the same day at 00:00). func (t Term) Date(args ...interface{}) Term { return constructMethodTerm(t, "Date", p.Term_DATE, args, map[string]interface{}{}) } // TimeOfDay returns the number of seconds elapsed since the beginning of the // day stored in the time object. func (t Term) TimeOfDay(args ...interface{}) Term { return constructMethodTerm(t, "TimeOfDay", p.Term_TIME_OF_DAY, args, map[string]interface{}{}) } // Year returns the year of a time object. func (t Term) Year(args ...interface{}) Term { return constructMethodTerm(t, "Year", p.Term_YEAR, args, map[string]interface{}{}) } // Month returns the month of a time object as a number between 1 and 12. // For your convenience, the terms r.January(), r.February() etc. are // defined and map to the appropriate integer. func (t Term) Month(args ...interface{}) Term { return constructMethodTerm(t, "Month", p.Term_MONTH, args, map[string]interface{}{}) } // Day return the day of a time object as a number between 1 and 31. func (t Term) Day(args ...interface{}) Term { return constructMethodTerm(t, "Day", p.Term_DAY, args, map[string]interface{}{}) } // DayOfWeek returns the day of week of a time object as a number between // 1 and 7 (following ISO 8601 standard). For your convenience, // the terms r.Monday(), r.Tuesday() etc. are defined and map to // the appropriate integer. func (t Term) DayOfWeek(args ...interface{}) Term { return constructMethodTerm(t, "DayOfWeek", p.Term_DAY_OF_WEEK, args, map[string]interface{}{}) } // DayOfYear returns the day of the year of a time object as a number between // 1 and 366 (following ISO 8601 standard). func (t Term) DayOfYear(args ...interface{}) Term { return constructMethodTerm(t, "DayOfYear", p.Term_DAY_OF_YEAR, args, map[string]interface{}{}) } // Hours returns the hour in a time object as a number between 0 and 23. func (t Term) Hours(args ...interface{}) Term { return constructMethodTerm(t, "Hours", p.Term_HOURS, args, map[string]interface{}{}) } // Minutes returns the minute in a time object as a number between 0 and 59. func (t Term) Minutes(args ...interface{}) Term { return constructMethodTerm(t, "Minutes", p.Term_MINUTES, args, map[string]interface{}{}) } // Seconds returns the seconds in a time object as a number between 0 and // 59.999 (double precision). func (t Term) Seconds(args ...interface{}) Term { return constructMethodTerm(t, "Seconds", p.Term_SECONDS, args, map[string]interface{}{}) } // ToISO8601 converts a time object to its iso 8601 format. func (t Term) ToISO8601(args ...interface{}) Term { return constructMethodTerm(t, "ToISO8601", p.Term_TO_ISO8601, args, map[string]interface{}{}) } // ToEpochTime converts a time object to its epoch time. func (t Term) ToEpochTime(args ...interface{}) Term { return constructMethodTerm(t, "ToEpochTime", p.Term_TO_EPOCH_TIME, args, map[string]interface{}{}) } var ( // Days // Monday is a constant representing the day of the week Monday Monday = constructRootTerm("Monday", p.Term_MONDAY, []interface{}{}, map[string]interface{}{}) // Tuesday is a constant representing the day of the week Tuesday Tuesday = constructRootTerm("Tuesday", p.Term_TUESDAY, []interface{}{}, map[string]interface{}{}) // Wednesday is a constant representing the day of the week Wednesday Wednesday = constructRootTerm("Wednesday", p.Term_WEDNESDAY, []interface{}{}, map[string]interface{}{}) // Thursday is a constant representing the day of the week Thursday Thursday = constructRootTerm("Thursday", p.Term_THURSDAY, []interface{}{}, map[string]interface{}{}) // Friday is a constant representing the day of the week Friday Friday = constructRootTerm("Friday", p.Term_FRIDAY, []interface{}{}, map[string]interface{}{}) // Saturday is a constant representing the day of the week Saturday Saturday = constructRootTerm("Saturday", p.Term_SATURDAY, []interface{}{}, map[string]interface{}{}) // Sunday is a constant representing the day of the week Sunday Sunday = constructRootTerm("Sunday", p.Term_SUNDAY, []interface{}{}, map[string]interface{}{}) // Months // January is a constant representing the month January January = constructRootTerm("January", p.Term_JANUARY, []interface{}{}, map[string]interface{}{}) // February is a constant representing the month February February = constructRootTerm("February", p.Term_FEBRUARY, []interface{}{}, map[string]interface{}{}) // March is a constant representing the month March March = constructRootTerm("March", p.Term_MARCH, []interface{}{}, map[string]interface{}{}) // April is a constant representing the month April April = constructRootTerm("April", p.Term_APRIL, []interface{}{}, map[string]interface{}{}) // May is a constant representing the month May May = constructRootTerm("May", p.Term_MAY, []interface{}{}, map[string]interface{}{}) // June is a constant representing the month June June = constructRootTerm("June", p.Term_JUNE, []interface{}{}, map[string]interface{}{}) // July is a constant representing the month July July = constructRootTerm("July", p.Term_JULY, []interface{}{}, map[string]interface{}{}) // August is a constant representing the month August August = constructRootTerm("August", p.Term_AUGUST, []interface{}{}, map[string]interface{}{}) // September is a constant representing the month September September = constructRootTerm("September", p.Term_SEPTEMBER, []interface{}{}, map[string]interface{}{}) // October is a constant representing the month October October = constructRootTerm("October", p.Term_OCTOBER, []interface{}{}, map[string]interface{}{}) // November is a constant representing the month November November = constructRootTerm("November", p.Term_NOVEMBER, []interface{}{}, map[string]interface{}{}) // December is a constant representing the month December December = constructRootTerm("December", p.Term_DECEMBER, []interface{}{}, map[string]interface{}{}) ) gorethink-2.0.4/query_time_test.go000066400000000000000000000070561272042630000172640ustar00rootroot00000000000000package gorethink import ( "time" test "gopkg.in/check.v1" ) func (s *RethinkSuite) TestTimeTime(c *test.C) { var response time.Time res, err := Time(1986, 11, 3, 12, 30, 15, "Z").Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response.Equal(time.Date(1986, 11, 3, 12, 30, 15, 0, time.UTC)), test.Equals, true) } func (s *RethinkSuite) TestTimeTimeMillisecond(c *test.C) { var response time.Time res, err := Time(1986, 11, 3, 12, 30, 15.6790123, "Z").Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response.Equal(time.Date(1986, 11, 3, 12, 30, 15, 679*1000*1000, time.UTC)), test.Equals, true) } func (s *RethinkSuite) TestTimeEpochTime(c *test.C) { var response time.Time res, err := EpochTime(531360000).Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response.Equal(time.Date(1986, 11, 3, 0, 0, 0, 0, time.UTC)), test.Equals, true) } func (s *RethinkSuite) TestTimeExpr(c *test.C) { var response time.Time t := time.Unix(531360000, 0) res, err := Expr(Expr(t)).Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) } func (s *RethinkSuite) TestTimeExprMillisecond(c *test.C) { var response time.Time t := time.Unix(531360000, 679000000) res, err := Expr(t).Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(float64(response.UnixNano()), test.Equals, float64(t.UnixNano())) } func (s *RethinkSuite) TestTimeISO8601(c *test.C) { var t1, t2 time.Time t2, _ = time.Parse("2006-01-02T15:04:05-07:00", "1986-11-03T08:30:00-07:00") res, err := ISO8601("1986-11-03T08:30:00-07:00").Run(session) c.Assert(err, test.IsNil) err = res.One(&t1) c.Assert(err, test.IsNil) c.Assert(t1.Equal(t2), test.Equals, true) } func (s *RethinkSuite) TestTimeInTimezone(c *test.C) { var response []time.Time res, err := Expr([]interface{}{Now(), Now().InTimezone("-07:00")}).Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response[1].Equal(response[0]), test.Equals, true) } func (s *RethinkSuite) TestTimeBetween(c *test.C) { var response interface{} times := Expr([]interface{}{ Time(1986, 9, 3, 12, 30, 15, "Z"), Time(1986, 10, 3, 12, 30, 15, "Z"), Time(1986, 11, 3, 12, 30, 15, "Z"), Time(1986, 12, 3, 12, 30, 15, "Z"), }) res, err := times.Filter(func(row Term) Term { return row.During(Time(1986, 9, 3, 12, 30, 15, "Z"), Time(1986, 11, 3, 12, 30, 15, "Z")) }).Count().Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(int(response.(float64)), test.Equals, 2) } func (s *RethinkSuite) TestTimeYear(c *test.C) { var response interface{} res, err := Time(1986, 12, 3, 12, 30, 15, "Z").Year().Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(int(response.(float64)), test.Equals, 1986) } func (s *RethinkSuite) TestTimeMonth(c *test.C) { var response interface{} res, err := Time(1986, 12, 3, 12, 30, 15, "Z").Month().Eq(December).Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response.(bool), test.Equals, true) } func (s *RethinkSuite) TestTimeDay(c *test.C) { var response interface{} res, err := Time(1986, 12, 3, 12, 30, 15, "Z").Day().Eq(Wednesday).Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response.(bool), test.Equals, true) } gorethink-2.0.4/query_transformation.go000066400000000000000000000154211272042630000203300ustar00rootroot00000000000000package gorethink import p "gopkg.in/dancannon/gorethink.v2/ql2" // Map transform each element of the sequence by applying the given mapping // function. It takes two arguments, a sequence and a function of type // `func (r.Term) interface{}`. // // For example this query doubles each element in an array: // // r.Map([]int{1,3,6}, func (row r.Term) interface{} { // return row.Mul(2) // }) func Map(args ...interface{}) Term { if len(args) > 0 { args = append(args[:len(args)-1], funcWrapArgs(args[len(args)-1:])...) } return constructRootTerm("Map", p.Term_MAP, funcWrapArgs(args), map[string]interface{}{}) } // Map transforms each element of the sequence by applying the given mapping // function. It takes one argument of type `func (r.Term) interface{}`. // // For example this query doubles each element in an array: // // r.Expr([]int{1,3,6}).Map(func (row r.Term) interface{} { // return row.Mul(2) // }) func (t Term) Map(args ...interface{}) Term { return constructMethodTerm(t, "Map", p.Term_MAP, funcWrapArgs(args), map[string]interface{}{}) } // WithFields takes a sequence of objects and a list of fields. If any objects in the // sequence don't have all of the specified fields, they're dropped from the // sequence. The remaining objects have the specified fields plucked out. // (This is identical to `HasFields` followed by `Pluck` on a sequence.) func (t Term) WithFields(args ...interface{}) Term { return constructMethodTerm(t, "WithFields", p.Term_WITH_FIELDS, args, map[string]interface{}{}) } // ConcatMap concatenates one or more elements into a single sequence using a // mapping function. ConcatMap works in a similar fashion to Map, applying the // given function to each element in a sequence, but it will always return a // single sequence. func (t Term) ConcatMap(args ...interface{}) Term { return constructMethodTerm(t, "ConcatMap", p.Term_CONCAT_MAP, funcWrapArgs(args), map[string]interface{}{}) } // OrderByOpts contains the optional arguments for the OrderBy term type OrderByOpts struct { Index interface{} `gorethink:"index,omitempty"` } func (o *OrderByOpts) toMap() map[string]interface{} { return optArgsToMap(o) } // OrderBy sorts the sequence by document values of the given key(s). To specify // the ordering, wrap the attribute with either r.Asc or r.Desc (defaults to // ascending). // // Sorting without an index requires the server to hold the sequence in memory, // and is limited to 100,000 documents (or the setting of the ArrayLimit option // for run). Sorting with an index can be done on arbitrarily large tables, or // after a between command using the same index. func (t Term) OrderBy(args ...interface{}) Term { var opts = map[string]interface{}{} // Look for options map if len(args) > 0 { if possibleOpts, ok := args[len(args)-1].(OrderByOpts); ok { opts = possibleOpts.toMap() args = args[:len(args)-1] } } for k, arg := range args { if t, ok := arg.(Term); !(ok && (t.termType == p.Term_DESC || t.termType == p.Term_ASC)) { args[k] = funcWrap(arg) } } return constructMethodTerm(t, "OrderBy", p.Term_ORDER_BY, args, opts) } // Desc is used by the OrderBy term to specify the ordering to be descending. func Desc(args ...interface{}) Term { return constructRootTerm("Desc", p.Term_DESC, funcWrapArgs(args), map[string]interface{}{}) } // Asc is used by the OrderBy term to specify that the ordering be ascending (the // default). func Asc(args ...interface{}) Term { return constructRootTerm("Asc", p.Term_ASC, funcWrapArgs(args), map[string]interface{}{}) } // Skip skips a number of elements from the head of the sequence. func (t Term) Skip(args ...interface{}) Term { return constructMethodTerm(t, "Skip", p.Term_SKIP, args, map[string]interface{}{}) } // Limit ends the sequence after the given number of elements. func (t Term) Limit(args ...interface{}) Term { return constructMethodTerm(t, "Limit", p.Term_LIMIT, args, map[string]interface{}{}) } // SliceOpts contains the optional arguments for the Slice term type SliceOpts struct { LeftBound interface{} `gorethink:"left_bound,omitempty"` RightBound interface{} `gorethink:"right_bound,omitempty"` } func (o *SliceOpts) toMap() map[string]interface{} { return optArgsToMap(o) } // Slice trims the sequence to within the bounds provided. func (t Term) Slice(args ...interface{}) Term { var opts = map[string]interface{}{} // Look for options map if len(args) > 0 { if possibleOpts, ok := args[len(args)-1].(SliceOpts); ok { opts = possibleOpts.toMap() args = args[:len(args)-1] } } return constructMethodTerm(t, "Slice", p.Term_SLICE, args, opts) } // AtIndex gets a single field from an object or the nth element from a sequence. func (t Term) AtIndex(args ...interface{}) Term { return constructMethodTerm(t, "AtIndex", p.Term_BRACKET, args, map[string]interface{}{}) } // Nth gets the nth element from a sequence. func (t Term) Nth(args ...interface{}) Term { return constructMethodTerm(t, "Nth", p.Term_NTH, args, map[string]interface{}{}) } // OffsetsOf gets the indexes of an element in a sequence. If the argument is a // predicate, get the indexes of all elements matching it. func (t Term) OffsetsOf(args ...interface{}) Term { return constructMethodTerm(t, "OffsetsOf", p.Term_OFFSETS_OF, funcWrapArgs(args), map[string]interface{}{}) } // IsEmpty tests if a sequence is empty. func (t Term) IsEmpty(args ...interface{}) Term { return constructMethodTerm(t, "IsEmpty", p.Term_IS_EMPTY, args, map[string]interface{}{}) } // UnionOpts contains the optional arguments for the Slice term type UnionOpts struct { Interleave interface{} `gorethink:"interleave,omitempty"` } func (o *UnionOpts) toMap() map[string]interface{} { return optArgsToMap(o) } // Union concatenates two sequences. func Union(args ...interface{}) Term { return constructRootTerm("Union", p.Term_UNION, args, map[string]interface{}{}) } // Union concatenates two sequences. func (t Term) Union(args ...interface{}) Term { return constructMethodTerm(t, "Union", p.Term_UNION, args, map[string]interface{}{}) } // UnionWithOpts like Union concatenates two sequences however allows for optional // arguments to be passed. func UnionWithOpts(optArgs UnionOpts, args ...interface{}) Term { return constructRootTerm("Union", p.Term_UNION, args, optArgs.toMap()) } // UnionWithOpts like Union concatenates two sequences however allows for optional // arguments to be passed. func (t Term) UnionWithOpts(optArgs UnionOpts, args ...interface{}) Term { return constructMethodTerm(t, "Union", p.Term_UNION, args, optArgs.toMap()) } // Sample selects a given number of elements from a sequence with uniform random // distribution. Selection is done without replacement. func (t Term) Sample(args ...interface{}) Term { return constructMethodTerm(t, "Sample", p.Term_SAMPLE, args, map[string]interface{}{}) } gorethink-2.0.4/query_transformation_test.go000066400000000000000000000303211272042630000213630ustar00rootroot00000000000000package gorethink import ( test "gopkg.in/check.v1" ) func (s *RethinkSuite) TestTransformationMapImplicit(c *test.C) { query := Expr(arr).Map(Row.Add(1)) var response []interface{} res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{2, 3, 4, 5, 6, 7, 8, 9, 10}) } func (s *RethinkSuite) TestTransformationMapFunc(c *test.C) { query := Expr(arr).Map(func(row Term) Term { return row.Add(1) }) var response []interface{} res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{2, 3, 4, 5, 6, 7, 8, 9, 10}) } func (s *RethinkSuite) TestTransformationWithFields(c *test.C) { query := Expr(objList).WithFields("id", "num").OrderBy("id") var response []interface{} res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{ map[string]interface{}{"num": 0, "id": 1}, map[string]interface{}{"num": 5, "id": 2}, map[string]interface{}{"num": 10, "id": 3}, map[string]interface{}{"num": 0, "id": 4}, map[string]interface{}{"num": 100, "id": 5}, map[string]interface{}{"num": 15, "id": 6}, map[string]interface{}{"num": 0, "id": 7}, map[string]interface{}{"num": 50, "id": 8}, map[string]interface{}{"num": 25, "id": 9}, }) } func (s *RethinkSuite) TestTransformationConcatMap(c *test.C) { query := Expr(objList).ConcatMap(func(row Term) Term { return Expr([]interface{}{row.Field("num")}) }) var response []interface{} res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{0, 5, 10, 0, 100, 15, 0, 50, 25}) } func (s *RethinkSuite) TestTransformationVariadicMap(c *test.C) { query := Range(5).Map(Range(5), func(a, b Term) interface{} { return []interface{}{a, b} }) var response []interface{} res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, [][]int{ {0, 0}, {1, 1}, {2, 2}, {3, 3}, {4, 4}, }) } func (s *RethinkSuite) TestTransformationVariadicRootMap(c *test.C) { query := Map(Range(5), Range(5), func(a, b Term) interface{} { return []interface{}{a, b} }) var response []interface{} res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, [][]int{ {0, 0}, {1, 1}, {2, 2}, {3, 3}, {4, 4}, }) } func (s *RethinkSuite) TestTransformationOrderByDesc(c *test.C) { query := Expr(noDupNumObjList).OrderBy(Desc("num")) var response []interface{} res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{ map[string]interface{}{"num": 100, "id": 5, "g2": 3, "g1": 2}, map[string]interface{}{"num": 50, "id": 8, "g2": 2, "g1": 4}, map[string]interface{}{"num": 25, "id": 9, "g2": 3, "g1": 2}, map[string]interface{}{"num": 15, "id": 6, "g2": 1, "g1": 1}, map[string]interface{}{"num": 10, "id": 3, "g2": 2, "g1": 3}, map[string]interface{}{"num": 5, "id": 2, "g2": 2, "g1": 2}, map[string]interface{}{"num": 0, "id": 1, "g2": 1, "g1": 1}, }) } func (s *RethinkSuite) TestTransformationOrderByAsc(c *test.C) { query := Expr(noDupNumObjList).OrderBy(Asc("num")) var response []interface{} res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{ map[string]interface{}{"num": 0, "id": 1, "g2": 1, "g1": 1}, map[string]interface{}{"num": 5, "id": 2, "g2": 2, "g1": 2}, map[string]interface{}{"num": 10, "id": 3, "g2": 2, "g1": 3}, map[string]interface{}{"num": 15, "id": 6, "g2": 1, "g1": 1}, map[string]interface{}{"num": 25, "id": 9, "g2": 3, "g1": 2}, map[string]interface{}{"num": 50, "id": 8, "g2": 2, "g1": 4}, map[string]interface{}{"num": 100, "id": 5, "g2": 3, "g1": 2}, }) } func (s *RethinkSuite) TestTransformationOrderByIndex(c *test.C) { DB("test").TableCreate("OrderByIndex").Exec(session) DB("test").Table("test").IndexDrop("OrderByIndex").Exec(session) // Test database creation DB("test").Table("OrderByIndex").IndexCreateFunc("test", Row.Field("num")).Exec(session) DB("test").Table("OrderByIndex").IndexWait().Exec(session) DB("test").Table("OrderByIndex").Insert(noDupNumObjList).Exec(session) query := DB("test").Table("OrderByIndex").OrderBy(OrderByOpts{ Index: "test", }) var response []interface{} res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{ map[string]interface{}{"num": 0, "id": 1, "g2": 1, "g1": 1}, map[string]interface{}{"num": 5, "id": 2, "g2": 2, "g1": 2}, map[string]interface{}{"num": 10, "id": 3, "g2": 2, "g1": 3}, map[string]interface{}{"num": 15, "id": 6, "g2": 1, "g1": 1}, map[string]interface{}{"num": 25, "id": 9, "g2": 3, "g1": 2}, map[string]interface{}{"num": 50, "id": 8, "g2": 2, "g1": 4}, map[string]interface{}{"num": 100, "id": 5, "g2": 3, "g1": 2}, }) } func (s *RethinkSuite) TestTransformationOrderByIndexAsc(c *test.C) { DB("test").TableCreate("OrderByIndex").Exec(session) DB("test").Table("test").IndexDrop("OrderByIndex").Exec(session) // Test database creation DB("test").Table("OrderByIndex").IndexCreateFunc("test", Row.Field("num")).Exec(session) DB("test").Table("OrderByIndex").IndexWait().Exec(session) DB("test").Table("OrderByIndex").Insert(noDupNumObjList).Exec(session) query := DB("test").Table("OrderByIndex").OrderBy(OrderByOpts{ Index: Asc("test"), }) var response []interface{} res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{ map[string]interface{}{"num": 0, "id": 1, "g2": 1, "g1": 1}, map[string]interface{}{"num": 5, "id": 2, "g2": 2, "g1": 2}, map[string]interface{}{"num": 10, "id": 3, "g2": 2, "g1": 3}, map[string]interface{}{"num": 15, "id": 6, "g2": 1, "g1": 1}, map[string]interface{}{"num": 25, "id": 9, "g2": 3, "g1": 2}, map[string]interface{}{"num": 50, "id": 8, "g2": 2, "g1": 4}, map[string]interface{}{"num": 100, "id": 5, "g2": 3, "g1": 2}, }) } func (s *RethinkSuite) TestTransformationOrderByMultiple(c *test.C) { query := Expr(objList).OrderBy(Desc("num"), Asc("id")) var response []interface{} res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{ map[string]interface{}{"num": 100, "id": 5, "g2": 3, "g1": 2}, map[string]interface{}{"num": 50, "id": 8, "g2": 2, "g1": 4}, map[string]interface{}{"num": 25, "id": 9, "g2": 3, "g1": 2}, map[string]interface{}{"num": 15, "id": 6, "g2": 1, "g1": 1}, map[string]interface{}{"num": 10, "id": 3, "g2": 2, "g1": 3}, map[string]interface{}{"num": 5, "id": 2, "g2": 2, "g1": 2}, map[string]interface{}{"num": 0, "id": 1, "g2": 1, "g1": 1}, map[string]interface{}{"num": 0, "id": 4, "g2": 3, "g1": 2}, map[string]interface{}{"num": 0, "id": 7, "g2": 2, "g1": 1}, }) } func (s *RethinkSuite) TestTransformationOrderByFunc(c *test.C) { query := Expr(objList).OrderBy(func(row Term) Term { return row.Field("num").Add(row.Field("id")) }) var response []interface{} res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{ map[string]interface{}{"num": 0, "id": 1, "g2": 1, "g1": 1}, map[string]interface{}{"num": 0, "id": 4, "g2": 3, "g1": 2}, map[string]interface{}{"num": 5, "id": 2, "g2": 2, "g1": 2}, map[string]interface{}{"num": 0, "id": 7, "g2": 2, "g1": 1}, map[string]interface{}{"num": 10, "id": 3, "g2": 2, "g1": 3}, map[string]interface{}{"num": 15, "id": 6, "g2": 1, "g1": 1}, map[string]interface{}{"num": 25, "id": 9, "g2": 3, "g1": 2}, map[string]interface{}{"num": 50, "id": 8, "g2": 2, "g1": 4}, map[string]interface{}{"num": 100, "id": 5, "g2": 3, "g1": 2}, }) } func (s *RethinkSuite) TestTransformationSkip(c *test.C) { query := Expr(arr).Skip(7) var response []interface{} res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{8, 9}) } func (s *RethinkSuite) TestTransformationLimit(c *test.C) { query := Expr(arr).Limit(2) var response []interface{} res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{1, 2}) } func (s *RethinkSuite) TestTransformationSlice(c *test.C) { query := Expr(arr).Slice(4) var response []interface{} res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{5, 6, 7, 8, 9}) } func (s *RethinkSuite) TestTransformationSliceRight(c *test.C) { query := Expr(arr).Slice(5, 6) var response []interface{} res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{6}) } func (s *RethinkSuite) TestTransformationSliceOpts(c *test.C) { query := Expr(arr).Slice(4, SliceOpts{LeftBound: "open"}) var response []interface{} res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{6, 7, 8, 9}) } func (s *RethinkSuite) TestTransformationSliceRightOpts(c *test.C) { query := Expr(arr).Slice(5, 6, SliceOpts{RightBound: "closed"}) var response []interface{} res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{6, 7}) } func (s *RethinkSuite) TestTransformationNth(c *test.C) { query := Expr(arr).Nth(2) var response interface{} r, err := query.Run(session) c.Assert(err, test.IsNil) err = r.One(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, 3) } func (s *RethinkSuite) TestTransformationAtIndexNth(c *test.C) { query := Expr([]interface{}{1}).AtIndex(Expr(0)) var response interface{} r, err := query.Run(session) c.Assert(err, test.IsNil) err = r.One(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, 1) } func (s *RethinkSuite) TestTransformationAtIndexField(c *test.C) { query := Expr(map[string]interface{}{"foo": 1}).AtIndex(Expr("foo")) var response interface{} r, err := query.Run(session) c.Assert(err, test.IsNil) err = r.One(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, 1) } func (s *RethinkSuite) TestTransformationAtIndexArrayField(c *test.C) { query := Expr([]interface{}{1}).AtIndex(Expr("foo")) _, err := query.Run(session) c.Assert(err, test.NotNil) } func (s *RethinkSuite) TestTransformationOffsetsOf(c *test.C) { query := Expr(arr).OffsetsOf(2) var response []interface{} res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{1}) } func (s *RethinkSuite) TestTransformationIsEmpty(c *test.C) { query := Expr([]interface{}{}).IsEmpty() var response bool r, err := query.Run(session) c.Assert(err, test.IsNil) err = r.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, true) } func (s *RethinkSuite) TestTransformationUnion(c *test.C) { query := Expr(arr).Union(arr) var response []interface{} res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9}) } func (s *RethinkSuite) TestTransformationUnionRoot(c *test.C) { query := Union(arr, arr) var response []interface{} res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.All(&response) c.Assert(err, test.IsNil) c.Assert(response, jsonEquals, []interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9}) } gorethink-2.0.4/query_write.go000066400000000000000000000066711272042630000164230ustar00rootroot00000000000000package gorethink import ( p "gopkg.in/dancannon/gorethink.v2/ql2" ) // InsertOpts contains the optional arguments for the Insert term type InsertOpts struct { Durability interface{} `gorethink:"durability,omitempty"` ReturnChanges interface{} `gorethink:"return_changes,omitempty"` Conflict interface{} `gorethink:"conflict,omitempty"` } func (o *InsertOpts) toMap() map[string]interface{} { return optArgsToMap(o) } // Insert documents into a table. Accepts a single document or an array // of documents. func (t Term) Insert(arg interface{}, optArgs ...InsertOpts) Term { opts := map[string]interface{}{} if len(optArgs) >= 1 { opts = optArgs[0].toMap() } return constructMethodTerm(t, "Insert", p.Term_INSERT, []interface{}{Expr(arg)}, opts) } // UpdateOpts contains the optional arguments for the Update term type UpdateOpts struct { Durability interface{} `gorethink:"durability,omitempty"` ReturnChanges interface{} `gorethink:"return_changes,omitempty"` NotAtomic interface{} `gorethink:"non_atomic,omitempty"` Conflict interface{} `gorethink:"conflict,omitempty"` } func (o *UpdateOpts) toMap() map[string]interface{} { return optArgsToMap(o) } // Update JSON documents in a table. Accepts a JSON document, a ReQL expression, // or a combination of the two. You can pass options like returnChanges that will // return the old and new values of the row you have modified. func (t Term) Update(arg interface{}, optArgs ...UpdateOpts) Term { opts := map[string]interface{}{} if len(optArgs) >= 1 { opts = optArgs[0].toMap() } return constructMethodTerm(t, "Update", p.Term_UPDATE, []interface{}{funcWrap(arg)}, opts) } // ReplaceOpts contains the optional arguments for the Replace term type ReplaceOpts struct { Durability interface{} `gorethink:"durability,omitempty"` ReturnChanges interface{} `gorethink:"return_changes,omitempty"` NotAtomic interface{} `gorethink:"non_atomic,omitempty"` } func (o *ReplaceOpts) toMap() map[string]interface{} { return optArgsToMap(o) } // Replace documents in a table. Accepts a JSON document or a ReQL expression, // and replaces the original document with the new one. The new document must // have the same primary key as the original document. func (t Term) Replace(arg interface{}, optArgs ...ReplaceOpts) Term { opts := map[string]interface{}{} if len(optArgs) >= 1 { opts = optArgs[0].toMap() } return constructMethodTerm(t, "Replace", p.Term_REPLACE, []interface{}{funcWrap(arg)}, opts) } // DeleteOpts contains the optional arguments for the Delete term type DeleteOpts struct { Durability interface{} `gorethink:"durability,omitempty"` ReturnChanges interface{} `gorethink:"return_changes,omitempty"` } func (o *DeleteOpts) toMap() map[string]interface{} { return optArgsToMap(o) } // Delete one or more documents from a table. func (t Term) Delete(optArgs ...DeleteOpts) Term { opts := map[string]interface{}{} if len(optArgs) >= 1 { opts = optArgs[0].toMap() } return constructMethodTerm(t, "Delete", p.Term_DELETE, []interface{}{}, opts) } // Sync ensures that writes on a given table are written to permanent storage. // Queries that specify soft durability do not give such guarantees, so Sync // can be used to ensure the state of these queries. A call to Sync does not // return until all previous writes to the table are persisted. func (t Term) Sync(args ...interface{}) Term { return constructMethodTerm(t, "Sync", p.Term_SYNC, args, map[string]interface{}{}) } gorethink-2.0.4/query_write_test.go000066400000000000000000000076461272042630000174650ustar00rootroot00000000000000package gorethink import ( test "gopkg.in/check.v1" ) func (s *RethinkSuite) TestWriteInsert(c *test.C) { query := DB("test").Table("test").Insert(map[string]interface{}{"num": 1}) _, err := query.Run(session) c.Assert(err, test.IsNil) } func (s *RethinkSuite) TestWriteInsertChanges(c *test.C) { query := DB("test").Table("test").Insert([]interface{}{ map[string]interface{}{"num": 1}, map[string]interface{}{"num": 2}, }, InsertOpts{ReturnChanges: true}) res, err := query.RunWrite(session) c.Assert(err, test.IsNil) c.Assert(res.Inserted, test.Equals, 2) c.Assert(len(res.Changes), test.Equals, 2) } func (s *RethinkSuite) TestWriteInsertStruct(c *test.C) { var response map[string]interface{} o := object{ Name: "map[string]interface{}ect 3", Attrs: []attr{ attr{ Name: "Attr 2", Value: "Value", }, }, } query := DB("test").Table("test").Insert(o) res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response["inserted"], test.Equals, float64(1)) } func (s *RethinkSuite) TestWriteInsertStructPointer(c *test.C) { var response map[string]interface{} o := object{ Name: "map[string]interface{}ect 3", Attrs: []attr{ attr{ Name: "Attr 2", Value: "Value", }, }, } query := DB("test").Table("test").Insert(&o) res, err := query.Run(session) c.Assert(err, test.IsNil) err = res.One(&response) c.Assert(err, test.IsNil) c.Assert(response["inserted"], test.Equals, float64(1)) } func (s *RethinkSuite) TestWriteUpdate(c *test.C) { query := DB("test").Table("test").Insert(map[string]interface{}{"num": 1}) _, err := query.Run(session) c.Assert(err, test.IsNil) // Update the first row in the table query = DB("test").Table("test").Sample(1).Update(map[string]interface{}{"num": 2}) _, err = query.Run(session) c.Assert(err, test.IsNil) } func (s *RethinkSuite) TestWriteReplace(c *test.C) { query := DB("test").Table("test").Insert(map[string]interface{}{"num": 1}) _, err := query.Run(session) c.Assert(err, test.IsNil) // Replace the first row in the table query = DB("test").Table("test").Sample(1).Update(map[string]interface{}{"num": 2}) _, err = query.Run(session) c.Assert(err, test.IsNil) } func (s *RethinkSuite) TestWriteDelete(c *test.C) { query := DB("test").Table("test").Insert(map[string]interface{}{"num": 1}) _, err := query.Run(session) c.Assert(err, test.IsNil) // Delete the first row in the table query = DB("test").Table("test").Sample(1).Delete() _, err = query.Run(session) c.Assert(err, test.IsNil) } func (s *RethinkSuite) TestWriteReference(c *test.C) { author := Author{ ID: "1", Name: "JRR Tolkien", } book := Book{ ID: "1", Title: "The Lord of the Rings", Author: author, } DB("test").TableDrop("authors").Exec(session) DB("test").TableDrop("books").Exec(session) DB("test").TableCreate("authors").Exec(session) DB("test").TableCreate("books").Exec(session) _, err := DB("test").Table("authors").Insert(author).RunWrite(session) c.Assert(err, test.IsNil) _, err = DB("test").Table("books").Insert(book).RunWrite(session) c.Assert(err, test.IsNil) // Read back book + author and check result cursor, err := DB("test").Table("books").Get("1").Merge(func(p Term) interface{} { return map[string]interface{}{ "author_id": DB("test").Table("authors").Get(p.Field("author_id")), } }).Run(session) c.Assert(err, test.IsNil) var out Book err = cursor.One(&out) c.Assert(err, test.IsNil) c.Assert(out.Title, test.Equals, "The Lord of the Rings") c.Assert(out.Author.Name, test.Equals, "JRR Tolkien") } func (s *RethinkSuite) TestWriteConflict(c *test.C) { query := DB("test").Table("test").Insert(map[string]interface{}{"id": "a"}) _, err := query.RunWrite(session) c.Assert(err, test.IsNil) query = DB("test").Table("test").Insert(map[string]interface{}{"id": "a"}) _, err = query.RunWrite(session) c.Assert(IsConflictErr(err), test.Equals, true) } gorethink-2.0.4/session.go000066400000000000000000000151351272042630000155220ustar00rootroot00000000000000package gorethink import ( "crypto/tls" "sync" "time" p "gopkg.in/dancannon/gorethink.v2/ql2" ) // A Session represents a connection to a RethinkDB cluster and should be used // when executing queries. type Session struct { hosts []Host opts *ConnectOpts mu sync.RWMutex cluster *Cluster closed bool } // ConnectOpts is used to specify optional arguments when connecting to a cluster. type ConnectOpts struct { Address string `gorethink:"address,omitempty"` Addresses []string `gorethink:"addresses,omitempty"` Database string `gorethink:"database,omitempty"` Username string `gorethink:"username,omitempty"` Password string `gorethink:"password,omitempty"` AuthKey string `gorethink:"authkey,omitempty"` // Deprecated Timeout time.Duration `gorethink:"timeout,omitempty"` WriteTimeout time.Duration `gorethink:"write_timeout,omitempty"` ReadTimeout time.Duration `gorethink:"read_timeout,omitempty"` // The duration in which a connection should send a keep-alive. KeepAlivePeriod time.Duration `gorethink:"keep_alive_timeout,omitempty"` TLSConfig *tls.Config `gorethink:"tlsconfig,omitempty"` HandshakeVersion HandshakeVersion `gorethink:"handshake_version,omitempty"` MaxIdle int `gorethink:"max_idle,omitempty"` // By default a maximum of 2 connections are opened per host. MaxOpen int `gorethink:"max_open,omitempty"` // Below options are for cluster discovery, please note there is a high // probability of these changing as the API is still being worked on. // DiscoverHosts is used to enable host discovery, when true the driver // will attempt to discover any new nodes added to the cluster and then // start sending queries to these new nodes. DiscoverHosts bool `gorethink:"discover_hosts,omitempty"` // NodeRefreshInterval is used to determine how often the driver should // refresh the status of a node. // // Deprecated: This function is no longer used due to changes in the // way hosts are selected. NodeRefreshInterval time.Duration `gorethink:"node_refresh_interval,omitempty"` // HostDecayDuration is used by the go-hostpool package to calculate a weighted // score when selecting a host. By default a value of 5 minutes is used. HostDecayDuration time.Duration // Indicates whether the cursors running in this session should use json.Number instead of float64 while // unmarshaling documents with interface{}. The default is `false`. UseJSONNumber bool } func (o *ConnectOpts) toMap() map[string]interface{} { return optArgsToMap(o) } // Connect creates a new database session. To view the available connection // options see ConnectOpts. // // By default maxIdle and maxOpen are set to 1: passing values greater // than the default (e.g. MaxIdle: "10", MaxOpen: "20") will provide a // pool of re-usable connections. // // Basic connection example: // // session, err := r.Connect(r.ConnectOpts{ // Host: "localhost:28015", // Database: "test", // AuthKey: "14daak1cad13dj", // }) // // Cluster connection example: // // session, err := r.Connect(r.ConnectOpts{ // Hosts: []string{"localhost:28015", "localhost:28016"}, // Database: "test", // AuthKey: "14daak1cad13dj", // }) func Connect(opts ConnectOpts) (*Session, error) { var addresses = opts.Addresses if len(addresses) == 0 { addresses = []string{opts.Address} } hosts := make([]Host, len(addresses)) for i, address := range addresses { hostname, port := splitAddress(address) hosts[i] = NewHost(hostname, port) } if len(hosts) <= 0 { return nil, ErrNoHosts } // Connect s := &Session{ hosts: hosts, opts: &opts, } err := s.Reconnect() if err != nil { return nil, err } return s, nil } // CloseOpts allows calls to the Close function to be configured. type CloseOpts struct { NoReplyWait bool `gorethink:"noreplyWait,omitempty"` } func (o *CloseOpts) toMap() map[string]interface{} { return optArgsToMap(o) } // IsConnected returns true if session has a valid connection. func (s *Session) IsConnected() bool { if s.cluster == nil || s.closed { return false } return s.cluster.IsConnected() } // Reconnect closes and re-opens a session. func (s *Session) Reconnect(optArgs ...CloseOpts) error { var err error if err = s.Close(optArgs...); err != nil { return err } s.mu.Lock() s.cluster, err = NewCluster(s.hosts, s.opts) if err != nil { return err } s.closed = false s.mu.Unlock() return nil } // Close closes the session func (s *Session) Close(optArgs ...CloseOpts) error { s.mu.Lock() defer s.mu.Unlock() if s.closed { return nil } if len(optArgs) >= 1 { if optArgs[0].NoReplyWait { s.mu.Unlock() s.NoReplyWait() s.mu.Lock() } } if s.cluster != nil { s.cluster.Close() } s.cluster = nil s.closed = true return nil } // SetMaxIdleConns sets the maximum number of connections in the idle // connection pool. func (s *Session) SetMaxIdleConns(n int) { s.mu.Lock() defer s.mu.Unlock() s.opts.MaxIdle = n s.cluster.SetMaxIdleConns(n) } // SetMaxOpenConns sets the maximum number of open connections to the database. func (s *Session) SetMaxOpenConns(n int) { s.mu.Lock() defer s.mu.Unlock() s.opts.MaxOpen = n s.cluster.SetMaxOpenConns(n) } // NoReplyWait ensures that previous queries with the noreply flag have been // processed by the server. Note that this guarantee only applies to queries // run on the given connection func (s *Session) NoReplyWait() error { s.mu.RLock() defer s.mu.RUnlock() if s.closed { return ErrConnectionClosed } return s.cluster.Exec(Query{ Type: p.Query_NOREPLY_WAIT, }) } // Use changes the default database used func (s *Session) Use(database string) { s.mu.RLock() defer s.mu.RUnlock() s.opts.Database = database } // Query executes a ReQL query using the session to connect to the database func (s *Session) Query(q Query) (*Cursor, error) { s.mu.RLock() defer s.mu.RUnlock() if s.closed { return nil, ErrConnectionClosed } return s.cluster.Query(q) } // Exec executes a ReQL query using the session to connect to the database func (s *Session) Exec(q Query) error { s.mu.RLock() defer s.mu.RUnlock() if s.closed { return ErrConnectionClosed } return s.cluster.Exec(q) } // Server returns the server name and server UUID being used by a connection. func (s *Session) Server() (ServerResponse, error) { return s.cluster.Server() } // SetHosts resets the hosts used when connecting to the RethinkDB cluster func (s *Session) SetHosts(hosts []Host) { s.mu.Lock() defer s.mu.Unlock() s.hosts = hosts } func (s *Session) newQuery(t Term, opts map[string]interface{}) (Query, error) { return newQuery(t, opts, s.opts) } gorethink-2.0.4/session_test.go000066400000000000000000000067461272042630000165710ustar00rootroot00000000000000package gorethink import ( "os" "time" test "gopkg.in/check.v1" ) func (s *RethinkSuite) TestSessionConnect(c *test.C) { session, err := Connect(ConnectOpts{ Address: url, }) c.Assert(err, test.IsNil) row, err := Expr("Hello World").Run(session) c.Assert(err, test.IsNil) var response string err = row.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, "Hello World") } func (s *RethinkSuite) TestSessionConnectHandshakeV1_0(c *test.C) { session, err := Connect(ConnectOpts{ Address: url, HandshakeVersion: HandshakeV1_0, }) c.Assert(err, test.IsNil) row, err := Expr("Hello World").Run(session) c.Assert(err, test.IsNil) var response string err = row.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, "Hello World") } func (s *RethinkSuite) TestSessionConnectHandshakeV0_4(c *test.C) { session, err := Connect(ConnectOpts{ Address: url, HandshakeVersion: HandshakeV0_4, }) c.Assert(err, test.IsNil) row, err := Expr("Hello World").Run(session) c.Assert(err, test.IsNil) var response string err = row.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, "Hello World") } func (s *RethinkSuite) TestSessionReconnect(c *test.C) { session, err := Connect(ConnectOpts{ Address: url, }) c.Assert(err, test.IsNil) row, err := Expr("Hello World").Run(session) c.Assert(err, test.IsNil) var response string err = row.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, "Hello World") err = session.Reconnect() c.Assert(err, test.IsNil) row, err = Expr("Hello World 2").Run(session) c.Assert(err, test.IsNil) err = row.One(&response) c.Assert(err, test.IsNil) c.Assert(response, test.Equals, "Hello World 2") } func (s *RethinkSuite) TestSessionConnectError(c *test.C) { var err error _, err = Connect(ConnectOpts{ Address: "nonexistanturl", Timeout: time.Second, }) c.Assert(err, test.NotNil) c.Assert(err, test.FitsTypeOf, RQLConnectionError{}) } func (s *RethinkSuite) TestSessionClose(c *test.C) { session, err := Connect(ConnectOpts{ Address: url, }) c.Assert(err, test.IsNil) _, err = Expr("Hello World").Run(session) c.Assert(err, test.IsNil) err = session.Close() c.Assert(err, test.IsNil) _, err = Expr("Hello World").Run(session) c.Assert(err, test.NotNil) } func (s *RethinkSuite) TestSessionServer(c *test.C) { session, err := Connect(ConnectOpts{ Address: url, }) c.Assert(err, test.IsNil) server, err := session.Server() c.Assert(err, test.IsNil) c.Assert(len(server.ID) > 0, test.Equals, true) c.Assert(len(server.Name) > 0, test.Equals, true) } func (s *RethinkSuite) TestSessionConnectDatabase(c *test.C) { session, err := Connect(ConnectOpts{ Address: url, AuthKey: os.Getenv("RETHINKDB_AUTHKEY"), Database: "test2", }) c.Assert(err, test.IsNil) _, err = Table("test2").Run(session) c.Assert(err, test.NotNil) c.Assert(err.Error(), test.Equals, "gorethink: Database `test2` does not exist. in: \nr.Table(\"test2\")") } func (s *RethinkSuite) TestSessionConnectUsername(c *test.C) { session, err := Connect(ConnectOpts{ Address: url, }) c.Assert(err, test.IsNil) DB("rethinkdb").Table("users").Insert(map[string]string{ "id": "gorethink_test", "password": "password", }).Exec(session) session, err = Connect(ConnectOpts{ Address: url, Username: "gorethink_test", Password: "password", }) c.Assert(err, test.IsNil) _, err = Expr("Hello World").Run(session) c.Assert(err, test.IsNil) } gorethink-2.0.4/testdata_test.go000066400000000000000000000066371272042630000167160ustar00rootroot00000000000000package gorethink func setupTestData() { if *testdata { // Delete any preexisting databases DBDrop("test").Exec(session) DBDrop("examples").Exec(session) DBDrop("superheroes").Exec(session) DBCreate("test").Exec(session) DBCreate("examples").Exec(session) DB("examples").TableCreate("posts").Exec(session) DB("examples").TableCreate("heroes").Exec(session) DB("examples").TableCreate("users").Exec(session) DB("examples").TableCreate("games").Exec(session) DB("examples").TableCreate("games2").Exec(session) DB("examples").TableCreate("marvel").Exec(session) DB("examples").Table("posts").IndexCreate("date").Exec(session) DB("examples").Table("posts").IndexWait().Exec(session) DB("examples").Table("posts").IndexCreateFunc( "dateAndTitle", []interface{}{Row.Field("date"), Row.Field("title")}, ).Exec(session) DB("examples").Table("heroes").IndexCreate("code_name").Exec(session) DB("examples").Table("heroes").IndexWait().Exec(session) DB("examples").Table("games").IndexCreate("type").Exec(session) DB("examples").Table("games").IndexWait().Exec(session) // Create heroes table DB("examples").Table("heroes").Insert([]interface{}{ map[string]interface{}{ "id": 1, "code_name": "batman", "name": "Batman", }, map[string]interface{}{ "id": 2, "code_name": "man_of_steel", "name": "Superman", }, map[string]interface{}{ "id": 3, "code_name": "ant_man", "name": "Ant Man", }, map[string]interface{}{ "id": 4, "code_name": "flash", "name": "The Flash", }, }).Exec(session) // Create users table DB("examples").Table("users").Insert([]interface{}{ map[string]interface{}{ "id": "william", "email": "william@rethinkdb.com", "age": 30, }, map[string]interface{}{ "id": "lara", "email": "lara@rethinkdb.com", "age": 30, }, map[string]interface{}{ "id": "john", "email": "john@rethinkdb.com", "age": 19, }, map[string]interface{}{ "id": "jane", "email": "jane@rethinkdb.com", "age": 45, }, map[string]interface{}{ "id": "bob", "email": "bob@rethinkdb.com", "age": 24, }, map[string]interface{}{ "id": "brad", "email": "brad@gmail.com", "age": 15, }, }).Exec(session) // Create games table DB("examples").Table("games").Insert([]interface{}{ map[string]interface{}{"id": 2, "player": "Bob", "points": 15, "type": "ranked"}, map[string]interface{}{"id": 5, "player": "Alice", "points": 7, "type": "free"}, map[string]interface{}{"id": 11, "player": "Bob", "points": 10, "type": "free"}, map[string]interface{}{"id": 12, "player": "Alice", "points": 2, "type": "free"}, }).Exec(session) // Create games2 table DB("examples").Table("games2").Insert([]interface{}{ map[string]interface{}{"id": 1, "matches": map[string]interface{}{"a": []int{1, 2, 3}, "b": []int{4, 5, 6}}}, map[string]interface{}{"id": 2, "matches": map[string]interface{}{"b": []int{100}, "c": []int{7, 8, 9}}}, map[string]interface{}{"id": 3, "matches": map[string]interface{}{"a": []int{10, 20}, "c": []int{70, 80}}}, }).Exec(session) // Create marvel table DB("examples").Table("marvel").Insert([]interface{}{ map[string]interface{}{"name": "Iron Man", "victories": 214}, map[string]interface{}{"name": "Jubilee", "victories": 9}, }).Exec(session) } } gorethink-2.0.4/types/000077500000000000000000000000001272042630000146475ustar00rootroot00000000000000gorethink-2.0.4/types/geometry.go000066400000000000000000000112131272042630000170270ustar00rootroot00000000000000package types import ( "fmt" ) type Geometry struct { Type string Point Point Line Line Lines Lines } func (g Geometry) MarshalRQL() (interface{}, error) { switch g.Type { case "Point": return g.Point.MarshalRQL() case "LineString": return g.Line.MarshalRQL() case "Polygon": return g.Lines.MarshalRQL() default: return nil, fmt.Errorf("pseudo-type GEOMETRY object field 'type' %s is not valid", g.Type) } } func (g *Geometry) UnmarshalRQL(data interface{}) error { if data, ok := data.(Geometry); ok { g.Type = data.Type g.Point = data.Point g.Line = data.Line g.Lines = data.Lines return nil } m, ok := data.(map[string]interface{}) if !ok { return fmt.Errorf("pseudo-type GEOMETRY object is not valid") } typ, ok := m["type"] if !ok { return fmt.Errorf("pseudo-type GEOMETRY object is not valid, expects 'type' field") } coords, ok := m["coordinates"] if !ok { return fmt.Errorf("pseudo-type GEOMETRY object is not valid, expects 'coordinates' field") } var err error switch typ { case "Point": g.Type = "Point" g.Point, err = UnmarshalPoint(coords) case "LineString": g.Type = "LineString" g.Line, err = UnmarshalLineString(coords) case "Polygon": g.Type = "Polygon" g.Lines, err = UnmarshalPolygon(coords) default: return fmt.Errorf("pseudo-type GEOMETRY object has invalid type") } if err != nil { return err } return nil } type Point struct { Lon float64 Lat float64 } type Line []Point type Lines []Line func (p Point) Coords() interface{} { return []interface{}{p.Lon, p.Lat} } func (p Point) MarshalRQL() (interface{}, error) { return map[string]interface{}{ "$reql_type$": "GEOMETRY", "coordinates": p.Coords(), "type": "Point", }, nil } func (p *Point) UnmarshalRQL(data interface{}) error { g := &Geometry{} err := g.UnmarshalRQL(data) if err != nil { return err } if g.Type != "Point" { return fmt.Errorf("pseudo-type GEOMETRY object has type %s, expected type %s", g.Type, "Point") } p.Lat = g.Point.Lat p.Lon = g.Point.Lon return nil } func (l Line) Coords() interface{} { coords := make([]interface{}, len(l)) for i, point := range l { coords[i] = point.Coords() } return coords } func (l Line) MarshalRQL() (interface{}, error) { return map[string]interface{}{ "$reql_type$": "GEOMETRY", "coordinates": l.Coords(), "type": "LineString", }, nil } func (l *Line) UnmarshalRQL(data interface{}) error { g := &Geometry{} err := g.UnmarshalRQL(data) if err != nil { return err } if g.Type != "LineString" { return fmt.Errorf("pseudo-type GEOMETRY object has type %s, expected type %s", g.Type, "LineString") } *l = g.Line return nil } func (l Lines) Coords() interface{} { coords := make([]interface{}, len(l)) for i, line := range l { coords[i] = line.Coords() } return coords } func (l Lines) MarshalRQL() (interface{}, error) { return map[string]interface{}{ "$reql_type$": "GEOMETRY", "coordinates": l.Coords(), "type": "Polygon", }, nil } func (l *Lines) UnmarshalRQL(data interface{}) error { g := &Geometry{} err := g.UnmarshalRQL(data) if err != nil { return err } if g.Type != "Polygon" { return fmt.Errorf("pseudo-type GEOMETRY object has type %s, expected type %s", g.Type, "Polygon") } *l = g.Lines return nil } func UnmarshalPoint(v interface{}) (Point, error) { coords, ok := v.([]interface{}) if !ok { return Point{}, fmt.Errorf("pseudo-type GEOMETRY object field 'coordinates' is not valid") } if len(coords) != 2 { return Point{}, fmt.Errorf("pseudo-type GEOMETRY object field 'coordinates' is not valid") } lon, ok := coords[0].(float64) if !ok { return Point{}, fmt.Errorf("pseudo-type GEOMETRY object field 'coordinates' is not valid") } lat, ok := coords[1].(float64) if !ok { return Point{}, fmt.Errorf("pseudo-type GEOMETRY object field 'coordinates' is not valid") } return Point{ Lon: lon, Lat: lat, }, nil } func UnmarshalLineString(v interface{}) (Line, error) { points, ok := v.([]interface{}) if !ok { return Line{}, fmt.Errorf("pseudo-type GEOMETRY object field 'coordinates' is not valid") } var err error line := make(Line, len(points)) for i, coords := range points { line[i], err = UnmarshalPoint(coords) if err != nil { return Line{}, err } } return line, nil } func UnmarshalPolygon(v interface{}) (Lines, error) { lines, ok := v.([]interface{}) if !ok { return Lines{}, fmt.Errorf("pseudo-type GEOMETRY object field 'coordinates' is not valid") } var err error polygon := make(Lines, len(lines)) for i, line := range lines { polygon[i], err = UnmarshalLineString(line) if err != nil { return Lines{}, err } } return polygon, nil } gorethink-2.0.4/utils.go000066400000000000000000000127541272042630000152030ustar00rootroot00000000000000package gorethink import ( "reflect" "strconv" "strings" "sync/atomic" "gopkg.in/dancannon/gorethink.v2/encoding" p "gopkg.in/dancannon/gorethink.v2/ql2" ) // Helper functions for constructing terms // constructRootTerm is an alias for creating a new term. func constructRootTerm(name string, termType p.Term_TermType, args []interface{}, optArgs map[string]interface{}) Term { return Term{ name: name, rootTerm: true, termType: termType, args: convertTermList(args), optArgs: convertTermObj(optArgs), } } // constructMethodTerm is an alias for creating a new term. Unlike constructRootTerm // this function adds the previous expression in the tree to the argument list to // create a method term. func constructMethodTerm(prevVal Term, name string, termType p.Term_TermType, args []interface{}, optArgs map[string]interface{}) Term { args = append([]interface{}{prevVal}, args...) return Term{ name: name, rootTerm: false, termType: termType, args: convertTermList(args), optArgs: convertTermObj(optArgs), } } // Helper functions for creating internal RQL types func newQuery(t Term, qopts map[string]interface{}, copts *ConnectOpts) (q Query, err error) { queryOpts := map[string]interface{}{} for k, v := range qopts { queryOpts[k], err = Expr(v).build() if err != nil { return } } if copts.Database != "" { queryOpts["db"], err = DB(copts.Database).build() if err != nil { return } } builtTerm, err := t.build() if err != nil { return q, err } // Construct query return Query{ Type: p.Query_START, Term: &t, Opts: queryOpts, builtTerm: builtTerm, }, nil } // makeArray takes a slice of terms and produces a single MAKE_ARRAY term func makeArray(args termsList) Term { return Term{ name: "[...]", termType: p.Term_MAKE_ARRAY, args: args, } } // makeObject takes a map of terms and produces a single MAKE_OBJECT term func makeObject(args termsObj) Term { return Term{ name: "{...}", termType: p.Term_MAKE_OBJ, optArgs: args, } } var nextVarID int64 func makeFunc(f interface{}) Term { value := reflect.ValueOf(f) valueType := value.Type() var argNums = make([]interface{}, valueType.NumIn()) var args = make([]reflect.Value, valueType.NumIn()) for i := 0; i < valueType.NumIn(); i++ { // Get a slice of the VARs to use as the function arguments varID := atomic.AddInt64(&nextVarID, 1) args[i] = reflect.ValueOf(constructRootTerm("var", p.Term_VAR, []interface{}{varID}, map[string]interface{}{})) argNums[i] = varID // make sure all input arguments are of type Term if valueType.In(i).String() != "gorethink.Term" { panic("Function argument is not of type Term") } } if valueType.NumOut() != 1 { panic("Function does not have a single return value") } body := value.Call(args)[0].Interface() argsArr := makeArray(convertTermList(argNums)) return constructRootTerm("func", p.Term_FUNC, []interface{}{argsArr, body}, map[string]interface{}{}) } func funcWrap(value interface{}) Term { val := Expr(value) if implVarScan(val) && val.termType != p.Term_ARGS { return makeFunc(func(x Term) Term { return val }) } return val } func funcWrapArgs(args []interface{}) []interface{} { for i, arg := range args { args[i] = funcWrap(arg) } return args } // implVarScan recursivly checks a value to see if it contains an // IMPLICIT_VAR term. If it does it returns true func implVarScan(value Term) bool { if value.termType == p.Term_IMPLICIT_VAR { return true } for _, v := range value.args { if implVarScan(v) { return true } } for _, v := range value.optArgs { if implVarScan(v) { return true } } return false } // Convert an opt args struct to a map. func optArgsToMap(optArgs OptArgs) map[string]interface{} { data, err := encode(optArgs) if err == nil && data != nil { if m, ok := data.(map[string]interface{}); ok { return m } } return map[string]interface{}{} } // Convert a list into a slice of terms func convertTermList(l []interface{}) termsList { if len(l) == 0 { return nil } terms := make(termsList, len(l)) for i, v := range l { terms[i] = Expr(v) } return terms } // Convert a map into a map of terms func convertTermObj(o map[string]interface{}) termsObj { if len(o) == 0 { return nil } terms := make(termsObj, len(o)) for k, v := range o { terms[k] = Expr(v) } return terms } // Helper functions for debugging func allArgsToStringSlice(args termsList, optArgs termsObj) []string { allArgs := make([]string, len(args)+len(optArgs)) i := 0 for _, v := range args { allArgs[i] = v.String() i++ } for k, v := range optArgs { allArgs[i] = k + "=" + v.String() i++ } return allArgs } func argsToStringSlice(args termsList) []string { allArgs := make([]string, len(args)) for i, v := range args { allArgs[i] = v.String() } return allArgs } func optArgsToStringSlice(optArgs termsObj) []string { allArgs := make([]string, len(optArgs)) i := 0 for k, v := range optArgs { allArgs[i] = k + "=" + v.String() i++ } return allArgs } func splitAddress(address string) (hostname string, port int) { hostname = "localhost" port = 28015 addrParts := strings.Split(address, ":") if len(addrParts) >= 1 { hostname = addrParts[0] } if len(addrParts) >= 2 { port, _ = strconv.Atoi(addrParts[1]) } return } func encode(data interface{}) (interface{}, error) { if _, ok := data.(Term); ok { return data, nil } v, err := encoding.Encode(data) if err != nil { return nil, err } return v, nil } gorethink-2.0.4/wercker.yml000066400000000000000000000013501272042630000156670ustar00rootroot00000000000000box: google/golang # Services services: - rethinkdb # Build definition build: # The steps that will be executed on build steps: - setup-go-workspace - script: name: set rethinkdb_url code: | export RETHINKDB_URL=$RETHINKDB_PORT_28015_TCP_ADDR # Gets the dependencies - script: name: get dependencies code: | go get ./... # Build the project - script: name: build code: | go build ./... # Test the project - script: name: Test code: |- go get -u gopkg.in/check.v1 go test ./... -short