Skip to content

Run Helpers

bchavez edited this page Jun 10, 2019 · 30 revisions

The official RethinkDB drivers expect developers to know the type of response returned from a given query. The C# driver and official Java driver follow the same paradigm.

In order for the C# driver to maintain API compatibility with the official drivers, the .Run() method return a dynamic type. The underlying dynamic type is determined by Newtonsoft and represents a best effort guess as to what the underlying type is. The Dynamic Language Runtime (DLR) Integration goes into further detail.

There are two consequences to using the .Run() method:

  1. The execution context of .Run() goes through the DLR. The DLR incurs a slight performance tax.
  2. An extra syntax burden is placed on the developer to get a strongly-typed result from .Run().

The use of run helpers solve both issues above. To illustrate, consider the following example:

Cursor<Change<Hero>> changeFeed = R.Db("marvel").Table("heros")
                                   .Changes().Run<Cursor<Change<Foo>>();

The syntax is a bit verbose and it invokes the DLR via .Run<T>(). The above query can be written more simply (and gain a slight edge in performance) by using the .RunChanges helper as shown below:

var changeFeed = R.Db("marvel").Table("heroes")
                  .Changes().RunChanges<Hero>();

The full list of Run Helpers are outlined here:


.RunAtom

When the response type for a query is a SUCCESS_ATOM the driver is expected to return an object of T. Typically, queries that return singleRowSelection are atom responses.

Hero result = R.Db("marvel").Table("heros")
              .Get("iron_man")
              .Run<Hero>(conn);

var result = R.Db("marvel").Table("heros")
              .Get("iron_man")
              .RunAtom<Hero>(conn);

.RunWrite for Inserts, Updates, and Deletes

.RunWrite() helps with DML (data manipulation language) type of result queries. .RunWrite() should be used to perform inserts, updates and deletes as shown below:

var result = R.Db("marvel").Table("heros")
              .Insert({"name":"Iron Man"})
              .RunWrite(conn);
/*
{
    "deleted": 0,
    "errors": 0,
    "inserted": 1,
    "replaced": 0,
    "skipped": 0,
    "unchanged": 0
}
*/

In the example above, .RunWrite returns a result that is a strongly typed Result helper object with strongly typed properties.

Result Assertions

With Result types, assertions can be made about the result. For example:

var result = R.Db("marvel").Table("heros")
              .Insert({"name":"Iron Man"})
              .RunWrite(conn)
              .AssertNoErrors()
              .AssertInserted(1);

The example above ensures that there are 0 errors and 1 document inserted. Otherwise an exception is thrown.


.RunCursor

When the response type for a query is a SUCCESS_SEQUENCE or SUCCESS_PARTIAL the driver is expected to return a cursor.

Cursor<Hero> all = R.Db("marvel")
                    .Table("heros")
                    .GetAll("iron_man", "hulk", "thor")
                    .Run<Hero>(conn);

var all = R.Db("marvel")
           .Table("heros")
           .GetAll("iron_man", "hulk", "thor")
           .RunCursor<Hero>(conn);

.RunResult<T>

.RunResult<T>() helps deserialize T object atom (SUCCESS_ATOM) or finished sequences List<T> (SUCCESS_SEQUENCE) List<T> server responses.

WARNING

The main purpose of RunResult<T> is to handle both SUCCESS_ATOM and SUCCESS_SEQUENCE response types which are both fully completed query responses. Both response types can be directly built into T.

Fundamentally, SUCCESS_SEQUENCE is actually a fully completed Cursor response. This means the driver has all the response data necessary to fully satisfy the query in its totality. Instead of going through the headache of creating a formal Cursor<T> object (when a Cursor<T> is not really needed), RunResult<T> simply builds T off the completed Cursor response. RunResult will work anywhere between small to medium response sizes (up to about 100K items). When the server decides the initial response is "too large" to send down the wire, the server will switch the response type from SUCCESS_SEQUENCE to SUCCESS_PARTIAL; in which case, RunResult<T> will no longer work. RunCursor<T> is recommended if query responses are expected to be larger than 100K items.


.RunChanges<T>()

Change feed declarations can be cumbersome with a dynamic .Run(). For example,

Cursor<Change<Hero>> changeFeed = R.Db("marvel").Table("heros")
                                   .Changes().Run<Cursor<Change<Foo>>();

Using the RunChanges<T> helper reduces verbosity while type safety for changeFeed is preserved as Cursor<Change<Hero>>:

var changeFeed = R.Db("marvel").Table("heros")
                  .Changes().RunChanges<Hero>();

Change<T> is a helper type that contains two properties, NewValue and OldValue of type T. When subscribing to .Changes() the changeFeed cursor will fill with Change<T> items when the changeFeed cursor is enumerated over and as changes happen.

NOTE

JObject, JArray, JToken types should not be used with this run helper. Since .RunChanges<T> is a wrapper for .RunCursor<Change<T>> developers may experience this GOTCHA. It is recommended that if T is any JToken type such as JObject, you should replace .RunChanges<JObject> with .RunCursor<JObject>.


.RunGrouping<TKey,TItem>()

Consider the following query:

IEnumerable<GroupedResult<string,Game>> result = 
    R.Expr(games).Group("player")
     .Run<GroupedResult<string, Game>>(conn);

foreach( var group in result )
{
    Console.WriteLine($">>>> KEY:{group.Key}");
    group.Dump();
}

The same query can be simplified with the RunGrouping helper:

var result = 
    R.Expr(games).Group("player")
     .RunGrouping<string, Game>(conn);

foreach( var group in result )
{
    Console.WriteLine($">>>> KEY:{group.Key}");
    group.Dump();
}

result is still typed as IEnumerable<GroupedResult<string,Game>>.

Clone this wiki locally