From 55a9f20b48c062b2ca8574e0c46d5689dcd3c97f Mon Sep 17 00:00:00 2001 From: Mathieu Fenniak Date: Mon, 20 Oct 2014 09:16:36 -0600 Subject: [PATCH 01/17] Update rethinkdb_spec to upstream fb47c1461798e0a60b207d99f2f401ab4c40678a --- .../Integration/CoreIntegrationTests.cs | 3 +- .../Integration/GroupingTests.cs | 2 + .../Integration/MultiObjectTests.cs | 2 + rethinkdb-net/Query.cs | 4 + rethinkdb-net/QueryTerm/ConcatMapQuery.cs | 2 +- rethinkdb-net/QueryTerm/GroupByQuery.cs | 5 + .../QueryTerm/GroupedMapReduceQuery.cs | 10 +- rethinkdb-net/QueryTerm/OrderByQuery.cs | 2 +- rethinkdb-net/rethinkdb_spec.cs | 180 +++++++++++++-- rethinkdb-net/rethinkdb_spec.proto | 209 ++++++++++++++---- 10 files changed, 350 insertions(+), 69 deletions(-) diff --git a/rethinkdb-net-newtonsoft-test/Integration/CoreIntegrationTests.cs b/rethinkdb-net-newtonsoft-test/Integration/CoreIntegrationTests.cs index 9059579..f6fcacd 100644 --- a/rethinkdb-net-newtonsoft-test/Integration/CoreIntegrationTests.cs +++ b/rethinkdb-net-newtonsoft-test/Integration/CoreIntegrationTests.cs @@ -73,7 +73,7 @@ static NDatabaseTests() } } - +#if false [TestFixture] public class NGroupingTests : GroupingTests { @@ -82,6 +82,7 @@ static NGroupingTests() ConnectionFactory = ConfigurationAssembler.CreateConnectionFactory("testCluster"); } } +#endif [TestFixture] diff --git a/rethinkdb-net-test/Integration/GroupingTests.cs b/rethinkdb-net-test/Integration/GroupingTests.cs index f543eb8..a333dff 100644 --- a/rethinkdb-net-test/Integration/GroupingTests.cs +++ b/rethinkdb-net-test/Integration/GroupingTests.cs @@ -9,6 +9,7 @@ namespace RethinkDb.Test.Integration { +#if false [TestFixture] public class GroupingTests : TestBase { @@ -242,4 +243,5 @@ public void GroupByAvg() Assert.That(count, Is.EqualTo(7)); } } +#endif } diff --git a/rethinkdb-net-test/Integration/MultiObjectTests.cs b/rethinkdb-net-test/Integration/MultiObjectTests.cs index 7961a47..62242db 100644 --- a/rethinkdb-net-test/Integration/MultiObjectTests.cs +++ b/rethinkdb-net-test/Integration/MultiObjectTests.cs @@ -716,6 +716,7 @@ public void ConcatMapIList() Assert.That(count, Is.EqualTo(4)); } +#if false [Test, Description("Tests that the SingleParameterLambda class can map a Parameter Expression (tag => tag) to a Term")] public void ConcatMap_OnSimpleDataType_CanUseParameterExpressionForQuery() { @@ -732,6 +733,7 @@ public void ConcatMap_OnSimpleDataType_CanUseParameterExpressionForQuery() Assert.That(enumerable, Has.Member(Tuple.Create("even", 3))); Assert.That(enumerable, Has.Member(Tuple.Create("odd", 4))); } +#endif [Test] public void Union() diff --git a/rethinkdb-net/Query.cs b/rethinkdb-net/Query.cs index 3288ce3..265b202 100644 --- a/rethinkdb-net/Query.cs +++ b/rethinkdb-net/Query.cs @@ -285,6 +285,7 @@ public static ISequenceQuery Distinct(this ISequenceQuery sequenceQuery return new DistinctQuery(sequenceQuery); } +#if false public static ISequenceQuery> GroupedMapReduce(this ISequenceQuery sequenceQuery, Expression> grouping, Expression> mapping, Expression> reduction) { return new GroupedMapReduceQuery(sequenceQuery, grouping, mapping, reduction); @@ -294,6 +295,7 @@ public static ISequenceQuery> GroupedMapReduce(sequenceQuery, grouping, mapping, reduction, @base); } +#endif public static ISequenceQuery ConcatMap(this ISequenceQuery sequenceQuery, Expression>> mapping) { @@ -310,10 +312,12 @@ public static ISingleObjectQuery Now() return new NowQuery(); } +#if false public static ISequenceQuery> GroupBy(this ISequenceQuery sequenceQuery, IGroupByReduction reductionObject, Expression> groupKeyConstructor) { return new GroupByQuery(sequenceQuery, reductionObject, groupKeyConstructor); } +#endif public static ISequenceQuery Sample(this ISequenceQuery target, int count) { diff --git a/rethinkdb-net/QueryTerm/ConcatMapQuery.cs b/rethinkdb-net/QueryTerm/ConcatMapQuery.cs index efd6952..4bb0b18 100644 --- a/rethinkdb-net/QueryTerm/ConcatMapQuery.cs +++ b/rethinkdb-net/QueryTerm/ConcatMapQuery.cs @@ -20,7 +20,7 @@ public Term GenerateTerm(IDatumConverterFactory datumConverterFactory) { var mapTerm = new Term() { - type = Term.TermType.CONCATMAP, + type = Term.TermType.CONCAT_MAP, }; mapTerm.args.Add(sequenceQuery.GenerateTerm(datumConverterFactory)); mapTerm.args.Add(ExpressionUtils.CreateFunctionTerm>(datumConverterFactory, mapExpression)); diff --git a/rethinkdb-net/QueryTerm/GroupByQuery.cs b/rethinkdb-net/QueryTerm/GroupByQuery.cs index 972b65d..61ffbe4 100644 --- a/rethinkdb-net/QueryTerm/GroupByQuery.cs +++ b/rethinkdb-net/QueryTerm/GroupByQuery.cs @@ -7,6 +7,7 @@ namespace RethinkDb.QueryTerm { +#if false public abstract class GroupByQueryBase { private readonly ISequenceQuery sequenceQuery; @@ -22,6 +23,8 @@ protected GroupByQueryBase(ISequenceQuery sequenceQuery, IGroupByReduct public Term GenerateTerm(IDatumConverterFactory datumConverterFactory) { + throw new NotSupportedException(); + /* var term = new Term() { type = Term.TermType.GROUPBY, @@ -59,6 +62,7 @@ public Term GenerateTerm(IDatumConverterFactory datumConverterFactory) term.args.Add(reductionObject.GenerateReductionObject(datumConverterFactory)); return term; + */ } private Datum GetMemberName(Expression memberReference, IDatumConverterFactory datumConverterFactory) @@ -94,4 +98,5 @@ public GroupByQuery(ISequenceQuery sequenceQuery, { } } +#endif } diff --git a/rethinkdb-net/QueryTerm/GroupedMapReduceQuery.cs b/rethinkdb-net/QueryTerm/GroupedMapReduceQuery.cs index 8c56373..4c721a9 100644 --- a/rethinkdb-net/QueryTerm/GroupedMapReduceQuery.cs +++ b/rethinkdb-net/QueryTerm/GroupedMapReduceQuery.cs @@ -5,6 +5,7 @@ namespace RethinkDb.QueryTerm { +#if false public class GroupedMapReduceQuery : ISequenceQuery> { private readonly ISequenceQuery sequenceQuery; @@ -31,8 +32,9 @@ public GroupedMapReduceQuery(ISequenceQuery sequenceQuery, Expression public Term GenerateTerm (IDatumConverterFactory datumConverterFactory) { - var retval = new Term() { - type = Term.TermType.GROUPED_MAP_REDUCE, + var retval = new Term() + { + type = Term.TermType.GROUP, }; retval.args.Add(sequenceQuery.GenerateTerm(datumConverterFactory)); retval.args.Add(ExpressionUtils.CreateFunctionTerm(datumConverterFactory, grouping)); @@ -41,7 +43,8 @@ public Term GenerateTerm (IDatumConverterFactory datumConverterFactory) if (this.baseProvided) { - retval.optargs.Add(new Term.AssocPair() { + retval.optargs.Add(new Term.AssocPair() + { key = "base", val = new Term() { type = Term.TermType.DATUM, @@ -53,5 +56,6 @@ public Term GenerateTerm (IDatumConverterFactory datumConverterFactory) return retval; } } +#endif } diff --git a/rethinkdb-net/QueryTerm/OrderByQuery.cs b/rethinkdb-net/QueryTerm/OrderByQuery.cs index 304ddd9..d1e80a0 100644 --- a/rethinkdb-net/QueryTerm/OrderByQuery.cs +++ b/rethinkdb-net/QueryTerm/OrderByQuery.cs @@ -34,7 +34,7 @@ public Term GenerateTerm(IDatumConverterFactory datumConverterFactory) var orderByTerm = new Term() { - type = Term.TermType.ORDERBY, + type = Term.TermType.ORDER_BY, }; orderByTerm.args.Add(sequenceQuery.GenerateTerm(datumConverterFactory)); orderByTerm.args.AddRange(GetMembers(datumConverterFactory, out indexOrderBy)); diff --git a/rethinkdb-net/rethinkdb_spec.cs b/rethinkdb-net/rethinkdb_spec.cs index 798dbc0..85ae92c 100644 --- a/rethinkdb-net/rethinkdb_spec.cs +++ b/rethinkdb-net/rethinkdb_spec.cs @@ -23,7 +23,21 @@ public enum Version V0_1 = 1063369270, [global::ProtoBuf.ProtoEnum(Name=@"V0_2", Value=1915781601)] - V0_2 = 1915781601 + V0_2 = 1915781601, + + [global::ProtoBuf.ProtoEnum(Name=@"V0_3", Value=1601562686)] + V0_3 = 1601562686 + } + + [global::ProtoBuf.ProtoContract(Name=@"Protocol")] + public enum Protocol + { + + [global::ProtoBuf.ProtoEnum(Name=@"PROTOBUF", Value=656407617)] + PROTOBUF = 656407617, + + [global::ProtoBuf.ProtoEnum(Name=@"JSON", Value=2120839367)] + JSON = 2120839367 } private global::ProtoBuf.IExtension extensionObject; @@ -72,6 +86,15 @@ public bool OBSOLETE_noreply get { return _OBSOLETE_noreply; } set { _OBSOLETE_noreply = value; } } + + private bool _accepts_r_json = (bool)false; + [global::ProtoBuf.ProtoMember(5, IsRequired = false, Name=@"accepts_r_json", DataFormat = global::ProtoBuf.DataFormat.Default)] + [global::System.ComponentModel.DefaultValue((bool)false)] + public bool accepts_r_json + { + get { return _accepts_r_json; } + set { _accepts_r_json = value; } + } private readonly global::System.Collections.Generic.List _global_optargs = new global::System.Collections.Generic.List(); [global::ProtoBuf.ProtoMember(6, Name=@"global_optargs", DataFormat = global::ProtoBuf.DataFormat.Default)] public global::System.Collections.Generic.List global_optargs @@ -118,7 +141,10 @@ public enum QueryType CONTINUE = 2, [global::ProtoBuf.ProtoEnum(Name=@"STOP", Value=3)] - STOP = 3 + STOP = 3, + + [global::ProtoBuf.ProtoEnum(Name=@"NOREPLY_WAIT", Value=4)] + NOREPLY_WAIT = 4 } private global::ProtoBuf.IExtension extensionObject; @@ -230,6 +256,15 @@ public Backtrace backtrace get { return _backtrace; } set { _backtrace = value; } } + + private Datum _profile = null; + [global::ProtoBuf.ProtoMember(5, IsRequired = false, Name=@"profile", DataFormat = global::ProtoBuf.DataFormat.Default)] + [global::System.ComponentModel.DefaultValue(null)] + public Datum profile + { + get { return _profile; } + set { _profile = value; } + } [global::ProtoBuf.ProtoContract(Name=@"ResponseType")] public enum ResponseType { @@ -243,6 +278,12 @@ public enum ResponseType [global::ProtoBuf.ProtoEnum(Name=@"SUCCESS_PARTIAL", Value=3)] SUCCESS_PARTIAL = 3, + [global::ProtoBuf.ProtoEnum(Name=@"SUCCESS_FEED", Value=5)] + SUCCESS_FEED = 5, + + [global::ProtoBuf.ProtoEnum(Name=@"WAIT_COMPLETE", Value=4)] + WAIT_COMPLETE = 4, + [global::ProtoBuf.ProtoEnum(Name=@"CLIENT_ERROR", Value=16)] CLIENT_ERROR = 16, @@ -361,7 +402,10 @@ public enum DatumType R_ARRAY = 5, [global::ProtoBuf.ProtoEnum(Name=@"R_OBJECT", Value=6)] - R_OBJECT = 6 + R_OBJECT = 6, + + [global::ProtoBuf.ProtoEnum(Name=@"R_JSON", Value=7)] + R_JSON = 7 } private global::ProtoBuf.IExtension extensionObject; @@ -453,6 +497,12 @@ public enum TermType [global::ProtoBuf.ProtoEnum(Name=@"JAVASCRIPT", Value=11)] JAVASCRIPT = 11, + [global::ProtoBuf.ProtoEnum(Name=@"UUID", Value=169)] + UUID = 169, + + [global::ProtoBuf.ProtoEnum(Name=@"HTTP", Value=153)] + HTTP = 153, + [global::ProtoBuf.ProtoEnum(Name=@"ERROR", Value=12)] ERROR = 12, @@ -549,6 +599,9 @@ public enum TermType [global::ProtoBuf.ProtoEnum(Name=@"KEYS", Value=94)] KEYS = 94, + [global::ProtoBuf.ProtoEnum(Name=@"OBJECT", Value=143)] + OBJECT = 143, + [global::ProtoBuf.ProtoEnum(Name=@"HAS_FIELDS", Value=32)] HAS_FIELDS = 32, @@ -576,11 +629,11 @@ public enum TermType [global::ProtoBuf.ProtoEnum(Name=@"FILTER", Value=39)] FILTER = 39, - [global::ProtoBuf.ProtoEnum(Name=@"CONCATMAP", Value=40)] - CONCATMAP = 40, + [global::ProtoBuf.ProtoEnum(Name=@"CONCAT_MAP", Value=40)] + CONCAT_MAP = 40, - [global::ProtoBuf.ProtoEnum(Name=@"ORDERBY", Value=41)] - ORDERBY = 41, + [global::ProtoBuf.ProtoEnum(Name=@"ORDER_BY", Value=41)] + ORDER_BY = 41, [global::ProtoBuf.ProtoEnum(Name=@"DISTINCT", Value=42)] DISTINCT = 42, @@ -597,11 +650,8 @@ public enum TermType [global::ProtoBuf.ProtoEnum(Name=@"NTH", Value=45)] NTH = 45, - [global::ProtoBuf.ProtoEnum(Name=@"GROUPED_MAP_REDUCE", Value=46)] - GROUPED_MAP_REDUCE = 46, - - [global::ProtoBuf.ProtoEnum(Name=@"GROUPBY", Value=47)] - GROUPBY = 47, + [global::ProtoBuf.ProtoEnum(Name=@"BRACKET", Value=170)] + BRACKET = 170, [global::ProtoBuf.ProtoEnum(Name=@"INNER_JOIN", Value=48)] INNER_JOIN = 48, @@ -615,6 +665,9 @@ public enum TermType [global::ProtoBuf.ProtoEnum(Name=@"ZIP", Value=72)] ZIP = 72, + [global::ProtoBuf.ProtoEnum(Name=@"RANGE", Value=173)] + RANGE = 173, + [global::ProtoBuf.ProtoEnum(Name=@"INSERT_AT", Value=82)] INSERT_AT = 82, @@ -630,8 +683,8 @@ public enum TermType [global::ProtoBuf.ProtoEnum(Name=@"COERCE_TO", Value=51)] COERCE_TO = 51, - [global::ProtoBuf.ProtoEnum(Name=@"TYPEOF", Value=52)] - TYPEOF = 52, + [global::ProtoBuf.ProtoEnum(Name=@"TYPE_OF", Value=52)] + TYPE_OF = 52, [global::ProtoBuf.ProtoEnum(Name=@"UPDATE", Value=53)] UPDATE = 53, @@ -663,6 +716,9 @@ public enum TermType [global::ProtoBuf.ProtoEnum(Name=@"TABLE_LIST", Value=62)] TABLE_LIST = 62, + [global::ProtoBuf.ProtoEnum(Name=@"SYNC", Value=138)] + SYNC = 138, + [global::ProtoBuf.ProtoEnum(Name=@"INDEX_CREATE", Value=75)] INDEX_CREATE = 75, @@ -672,6 +728,15 @@ public enum TermType [global::ProtoBuf.ProtoEnum(Name=@"INDEX_LIST", Value=77)] INDEX_LIST = 77, + [global::ProtoBuf.ProtoEnum(Name=@"INDEX_STATUS", Value=139)] + INDEX_STATUS = 139, + + [global::ProtoBuf.ProtoEnum(Name=@"INDEX_WAIT", Value=140)] + INDEX_WAIT = 140, + + [global::ProtoBuf.ProtoEnum(Name=@"INDEX_RENAME", Value=156)] + INDEX_RENAME = 156, + [global::ProtoBuf.ProtoEnum(Name=@"FUNCALL", Value=64)] FUNCALL = 64, @@ -684,8 +749,8 @@ public enum TermType [global::ProtoBuf.ProtoEnum(Name=@"ALL", Value=67)] ALL = 67, - [global::ProtoBuf.ProtoEnum(Name=@"FOREACH", Value=68)] - FOREACH = 68, + [global::ProtoBuf.ProtoEnum(Name=@"FOR_EACH", Value=68)] + FOR_EACH = 68, [global::ProtoBuf.ProtoEnum(Name=@"FUNC", Value=69)] FUNC = 69, @@ -702,6 +767,12 @@ public enum TermType [global::ProtoBuf.ProtoEnum(Name=@"MATCH", Value=97)] MATCH = 97, + [global::ProtoBuf.ProtoEnum(Name=@"UPCASE", Value=141)] + UPCASE = 141, + + [global::ProtoBuf.ProtoEnum(Name=@"DOWNCASE", Value=142)] + DOWNCASE = 142, + [global::ProtoBuf.ProtoEnum(Name=@"SAMPLE", Value=81)] SAMPLE = 81, @@ -711,6 +782,9 @@ public enum TermType [global::ProtoBuf.ProtoEnum(Name=@"JSON", Value=98)] JSON = 98, + [global::ProtoBuf.ProtoEnum(Name=@"TO_JSON_STRING", Value=172)] + TO_JSON_STRING = 172, + [global::ProtoBuf.ProtoEnum(Name=@"ISO8601", Value=99)] ISO8601 = 99, @@ -826,7 +900,79 @@ public enum TermType DECEMBER = 125, [global::ProtoBuf.ProtoEnum(Name=@"LITERAL", Value=137)] - LITERAL = 137 + LITERAL = 137, + + [global::ProtoBuf.ProtoEnum(Name=@"GROUP", Value=144)] + GROUP = 144, + + [global::ProtoBuf.ProtoEnum(Name=@"SUM", Value=145)] + SUM = 145, + + [global::ProtoBuf.ProtoEnum(Name=@"AVG", Value=146)] + AVG = 146, + + [global::ProtoBuf.ProtoEnum(Name=@"MIN", Value=147)] + MIN = 147, + + [global::ProtoBuf.ProtoEnum(Name=@"MAX", Value=148)] + MAX = 148, + + [global::ProtoBuf.ProtoEnum(Name=@"SPLIT", Value=149)] + SPLIT = 149, + + [global::ProtoBuf.ProtoEnum(Name=@"UNGROUP", Value=150)] + UNGROUP = 150, + + [global::ProtoBuf.ProtoEnum(Name=@"RANDOM", Value=151)] + RANDOM = 151, + + [global::ProtoBuf.ProtoEnum(Name=@"CHANGES", Value=152)] + CHANGES = 152, + + [global::ProtoBuf.ProtoEnum(Name=@"ARGS", Value=154)] + ARGS = 154, + + [global::ProtoBuf.ProtoEnum(Name=@"BINARY", Value=155)] + BINARY = 155, + + [global::ProtoBuf.ProtoEnum(Name=@"GEOJSON", Value=157)] + GEOJSON = 157, + + [global::ProtoBuf.ProtoEnum(Name=@"TO_GEOJSON", Value=158)] + TO_GEOJSON = 158, + + [global::ProtoBuf.ProtoEnum(Name=@"POINT", Value=159)] + POINT = 159, + + [global::ProtoBuf.ProtoEnum(Name=@"LINE", Value=160)] + LINE = 160, + + [global::ProtoBuf.ProtoEnum(Name=@"POLYGON", Value=161)] + POLYGON = 161, + + [global::ProtoBuf.ProtoEnum(Name=@"DISTANCE", Value=162)] + DISTANCE = 162, + + [global::ProtoBuf.ProtoEnum(Name=@"INTERSECTS", Value=163)] + INTERSECTS = 163, + + [global::ProtoBuf.ProtoEnum(Name=@"INCLUDES", Value=164)] + INCLUDES = 164, + + [global::ProtoBuf.ProtoEnum(Name=@"CIRCLE", Value=165)] + CIRCLE = 165, + + [global::ProtoBuf.ProtoEnum(Name=@"GET_INTERSECTING", Value=166)] + GET_INTERSECTING = 166, + + [global::ProtoBuf.ProtoEnum(Name=@"FILL", Value=167)] + FILL = 167, + + [global::ProtoBuf.ProtoEnum(Name=@"GET_NEAREST", Value=168)] + GET_NEAREST = 168, + + [global::ProtoBuf.ProtoEnum(Name=@"POLYGON_SUB", Value=171)] + POLYGON_SUB = 171 } private global::ProtoBuf.IExtension extensionObject; diff --git a/rethinkdb-net/rethinkdb_spec.proto b/rethinkdb-net/rethinkdb_spec.proto index 4942b05..a5ba1ef 100644 --- a/rethinkdb-net/rethinkdb_spec.proto +++ b/rethinkdb-net/rethinkdb_spec.proto @@ -3,7 +3,7 @@ //////////////////////////////////////////////////////////////////////////////// // Process: When you first open a connection, send the magic number -// for the version of the protobuf you're targetting (in the [Version] +// 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. @@ -11,10 +11,16 @@ // 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). 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. +// 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 @@ -33,13 +39,20 @@ // token to get more results from the original query. //////////////////////////////////////////////////////////////////////////////// -// This enum contains the magic numbers for your version. See **THE HIGH-LEVEL -// VIEW** for what to do with it. 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; + V0_1 = 0x3f61ba36; + V0_2 = 0x723081e1; // Authorization key during handshake + V0_3 = 0x5f75e83e; // Authorization key and protocol during handshake + } + + // The protocol to use after the handshake, specified in V0_3 + enum Protocol { + PROTOBUF = 0x271ffc41; + JSON = 0x7e6970c7; } } @@ -48,12 +61,16 @@ message VersionDummy { // We need to wrap it like this for some // * 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]. 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. } optional QueryType type = 1; // A [Term] is how we represent the operations we want a query to perform. @@ -64,6 +81,11 @@ message Query { // 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; @@ -96,6 +118,8 @@ message Response { // the same token as this response, you will get // more of the sequence. Keep sending [CONTINUE] // queries until you get back [SUCCESS_SEQUENCE]. + SUCCESS_FEED = 5; // Like [SUCCESS_PARTIAL] but for feeds. + WAIT_COMPLETE = 4; // A [NOREPLY_WAIT] query completed. // These response types indicate failure. CLIENT_ERROR = 16; // Means the client is buggy. An example is if the @@ -125,8 +149,17 @@ message Response { // 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 @@ -140,6 +173,10 @@ message Datum { 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; @@ -209,16 +246,35 @@ message Term { 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 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.) + // 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 @@ -284,6 +340,8 @@ message Term { // | Sequence, STRING -> Sequence // Return an array containing the keys of the object. KEYS = 94; // 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. @@ -301,8 +359,11 @@ message Term { // Sequence Ops // Get all elements of a sequence between two values. - BETWEEN = 36; // StreamSelection, DATUM, DATUM, {:index:!STRING} -> StreamSelection - REDUCE = 37; // Sequence, Function(2), {base:DATUM} -> DATUM + // 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 = 36; // StreamSelection, DATUM, DATUM, {index:!STRING, right_bound:STRING, left_bound:STRING} -> StreamSelection + REDUCE = 37; // Sequence, Function(2) -> DATUM MAP = 38; // Sequence, Function(1) -> Sequence // Filter a sequence with either a function or a shortcut @@ -315,9 +376,9 @@ message Term { 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. - CONCATMAP = 40; // Sequence, Function(1) -> Sequence + CONCAT_MAP = 40; // Sequence, Function(1) -> Sequence // Order a sequence based on one or more attributes. - ORDERBY = 41; // Sequence, (!STRING | Ordering)... -> Sequence + ORDER_BY = 41; // Sequence, (!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 @@ -328,24 +389,19 @@ message Term { UNION = 44; // Sequence... -> Sequence // Get the Nth element of a sequence. NTH = 45; // Sequence, NUMBER -> DATUM - // Takes a sequence, and three functions: - // - A function to group the sequence by. - // - A function to map over the groups. - // - A reduction to apply to each of the groups. - GROUPED_MAP_REDUCE = 46; // Sequence, Function(1), Function(1), Function(2), {base:DATUM} -> ARRAY - // Groups a sequence by one or more attributes, and then applies a reduction. - // The third argument is a special object literal giving the kind of operation to be - // performed and any necessary arguments. - // At present, GROUPBY suports the following operations - // * {'COUNT': } - count the size of the group - // * {'SUM': attr} - sum the values of the given attribute across the group - // * {'AVG': attr} - average the values of the given attribute across the group - GROUPBY = 47; // Sequence, ARRAY, !GROUP_BY_OBJECT -> Sequence + // 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. @@ -363,25 +419,27 @@ message Term { // 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. TYPEOF(true) = "BOOL") - TYPEOF = 52; // Top -> STRING + // 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_vals:BOOL} -> OBJECT | - // SingleSelection, Function(1), {non_atomic:BOOL, durability:STRING, return_vals:BOOL} -> OBJECT | - // StreamSelection, OBJECT, {non_atomic:BOOL, durability:STRING, return_vals:BOOL} -> OBJECT | - // SingleSelection, OBJECT, {non_atomic:BOOL, durability:STRING, return_vals:BOOL} -> OBJECT + 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_vals:BOOL} -> OBJECT | SingleSelection -> OBJECT + 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_vals:BOOL} -> OBJECT | SingleSelection, Function(1), {non_atomic:BOOL, durability:STRING, return_vals:BOOL} -> OBJECT - // Inserts into a table. If `upsert` is true, overwrites entries with - // the same primary key (otherwise errors). - INSERT = 56; // Table, OBJECT, {upsert:BOOL, durability:STRING, return_vals:BOOL} -> OBJECT | Table, Sequence, {upsert:BOOL, durability:STRING, return_vals:BOOL} -> OBJECT + 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. @@ -393,8 +451,8 @@ message Term { // 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, {datacenter:STRING, primary_key:STRING, cache_size:NUMBER, durability:STRING} -> OBJECT - // STRING, {datacenter:STRING, primary_key:STRING, cache_size:NUMBER, durability:STRING} -> OBJECT + TABLE_CREATE = 60; // Database, STRING, {datacenter:STRING, primary_key:STRING, durability:STRING} -> OBJECT + // STRING, {datacenter:STRING, primary_key:STRING, durability:STRING} -> OBJECT // Drops a table with a particular name from a particular // database. (You may omit the first argument to use the // default database.) @@ -404,14 +462,26 @@ message Term { // omit the first argument to use the default database.) TABLE_LIST = 62; // Database -> ARRAY // -> ARRAY + // Ensures that previously issued soft-durability writes are complete and + // written to disk. + SYNC = 138; // Table -> OBJECT // * Secondary indexes OPs // Creates a new secondary index with a particular name and definition. - INDEX_CREATE = 75; // Table, STRING, Function(1) -> OBJECT + 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[, blocks_processed:NUMBER, blocks_total: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 @@ -428,7 +498,7 @@ message Term { ALL = 67; // BOOL... -> BOOL // Calls its Function with each entry in the sequence // and executes the array of terms that Function returns. - FOREACH = 68; // Sequence, Function(1) -> OBJECT + FOR_EACH = 68; // Sequence, Function(1) -> OBJECT //////////////////////////////////////////////////////////////////////////////// ////////// Special Terms @@ -484,6 +554,10 @@ message Term { // 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 @@ -500,6 +574,11 @@ message Term { // 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. @@ -567,6 +646,44 @@ message Term { // Indicates to MERGE to replace the other object rather than merge it. LITERAL = 137; // 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) } optional TermType type = 1; From 808efe9897c566846a1b161e54bd12ce5197b837 Mon Sep 17 00:00:00 2001 From: Mathieu Fenniak Date: Mon, 20 Oct 2014 09:26:05 -0600 Subject: [PATCH 02/17] Remove "base" from Reduce as this is no longer supported by RethinkDB --- rethinkdb-net-test/Integration/TableTests.cs | 30 +++++++------------- rethinkdb-net/Query.cs | 5 ---- rethinkdb-net/QueryTerm/ReduceQuery.cs | 22 -------------- 3 files changed, 10 insertions(+), 47 deletions(-) diff --git a/rethinkdb-net-test/Integration/TableTests.cs b/rethinkdb-net-test/Integration/TableTests.cs index b2a676f..a4b23b3 100644 --- a/rethinkdb-net-test/Integration/TableTests.cs +++ b/rethinkdb-net-test/Integration/TableTests.cs @@ -139,27 +139,17 @@ private async Task DoMultiInsertWithIds() } [Test] - public void Reduce() + public void ReduceEmptyTable() { - DoReduce().Wait(); - } - - private async Task DoReduce() - { - var resp = await connection.RunAsync(testTable.Reduce((acc, val) => new TestObject() { SomeNumber = acc.SomeNumber + val.SomeNumber }, new TestObject() { SomeNumber = -1 })); - Assert.That(resp.SomeNumber, Is.EqualTo(-1)); - } - - [Test] - public void ReduceToPrimitive() - { - DoReduceToPrimitive().Wait(); - } - - private async Task DoReduceToPrimitive() - { - var resp = await connection.RunAsync(testTable.Map(o => o.SomeNumber).Reduce((acc, val) => acc + val, -1.0)); - Assert.That(resp, Is.EqualTo(-1.0)); + try + { + connection.Run(testTable.Reduce((acc, val) => new TestObject() { SomeNumber = acc.SomeNumber + val.SomeNumber })); + Assert.Fail("Expected exception"); + } + catch (AggregateException ex) + { + Assert.That(ex.InnerException is RethinkDbRuntimeException); + } } [Test] diff --git a/rethinkdb-net/Query.cs b/rethinkdb-net/Query.cs index 265b202..a5fc584 100644 --- a/rethinkdb-net/Query.cs +++ b/rethinkdb-net/Query.cs @@ -270,11 +270,6 @@ public static ISingleObjectQuery Reduce(this ISequenceQuery sequenceQue return new ReduceQuery(sequenceQuery, reduceFunction); } - public static ISingleObjectQuery Reduce(this ISequenceQuery sequenceQuery, Expression> reduceFunction, T @base) - { - return new ReduceQuery(sequenceQuery, reduceFunction, @base); - } - public static ISingleObjectQuery Nth(this ISequenceQuery sequenceQuery, int index) { return new NthQuery(sequenceQuery, index); diff --git a/rethinkdb-net/QueryTerm/ReduceQuery.cs b/rethinkdb-net/QueryTerm/ReduceQuery.cs index 5a38ebb..49f8a3b 100644 --- a/rethinkdb-net/QueryTerm/ReduceQuery.cs +++ b/rethinkdb-net/QueryTerm/ReduceQuery.cs @@ -9,21 +9,11 @@ public class ReduceQuery : ISingleObjectQuery { private readonly ISequenceQuery sequenceQuery; private readonly Expression> reduceFunction; - private readonly bool baseProvided; - private readonly T @base; public ReduceQuery(ISequenceQuery sequenceQuery, Expression> reduceFunction) { this.sequenceQuery = sequenceQuery; this.reduceFunction = reduceFunction; - this.baseProvided = false; - } - - public ReduceQuery(ISequenceQuery sequenceQuery, Expression> reduceFunction, T @base) - : this (sequenceQuery, reduceFunction) - { - this.baseProvided = true; - this.@base = @base; } public Term GenerateTerm(IDatumConverterFactory datumConverterFactory) @@ -34,18 +24,6 @@ public Term GenerateTerm(IDatumConverterFactory datumConverterFactory) }; reduceTerm.args.Add(sequenceQuery.GenerateTerm(datumConverterFactory)); reduceTerm.args.Add(ExpressionUtils.CreateFunctionTerm(datumConverterFactory, reduceFunction)); - - if (this.baseProvided) - { - reduceTerm.optargs.Add(new Term.AssocPair() { - key = "base", - val = new Term() { - type = Term.TermType.DATUM, - datum = datumConverterFactory.Get().ConvertObject(@base) - } - }); - } - return reduceTerm; } } From 28e7390c829bedd6bc29525a7ed67a44070360ee Mon Sep 17 00:00:00 2001 From: Mathieu Fenniak Date: Mon, 20 Oct 2014 09:41:03 -0600 Subject: [PATCH 03/17] Compatibility for rethinkdb/rethinkdb#1382 changes ...AndReturnValues changed to ...AndReturnChanges, and return DML object updated to support multiple updates in the operation via Changes array. --- .../Integration/ComplexObjectTests.cs | 35 +++++++++++-------- .../Integration/SingleObjectTests.cs | 35 +++++++++++-------- rethinkdb-net/DmlResponse.cs | 7 ++++ rethinkdb-net/Query.cs | 6 ++-- .../QueryTerm/DeleteAndReturnValueQuery.cs | 2 +- .../QueryTerm/ReplaceAndReturnValueQuery.cs | 2 +- .../QueryTerm/UpdateAndReturnValueQuery.cs | 2 +- 7 files changed, 53 insertions(+), 36 deletions(-) diff --git a/rethinkdb-net-newtonsoft-test/Integration/ComplexObjectTests.cs b/rethinkdb-net-newtonsoft-test/Integration/ComplexObjectTests.cs index ed479fc..b4cfd51 100644 --- a/rethinkdb-net-newtonsoft-test/Integration/ComplexObjectTests.cs +++ b/rethinkdb-net-newtonsoft-test/Integration/ComplexObjectTests.cs @@ -105,30 +105,33 @@ private async Task DoReplace() [Test] public void ReplaceAndReturnValue() { - var resp = connection.Run(testTable.Get(insertedObject.Id).ReplaceAndReturnValue(new ComplexObject() {Id = insertedObject.Id, Name = "Jack Black"})); + var resp = connection.Run(testTable.Get(insertedObject.Id).ReplaceAndReturnChanges(new ComplexObject() {Id = insertedObject.Id, Name = "Jack Black"})); Assert.That(resp, Is.Not.Null); Assert.That(resp.FirstError, Is.Null); Assert.That(resp.Replaced, Is.EqualTo(1)); Assert.That(resp.GeneratedKeys, Is.Null); - Assert.That(resp.OldValue, Is.Not.Null); - Assert.That(resp.OldValue.Name, Is.EqualTo("Brian Chavez")); - Assert.That(resp.NewValue, Is.Not.Null); - Assert.That(resp.NewValue.Name, Is.EqualTo("Jack Black")); + Assert.That(resp.Changes, Is.Not.Null); + Assert.That(resp.Changes, Has.Length.EqualTo(1)); + Assert.That(resp.Changes[0].OldValue, Is.Not.Null); + Assert.That(resp.Changes[0].OldValue.Name, Is.EqualTo("Brian Chavez")); + Assert.That(resp.Changes[0].NewValue, Is.Not.Null); + Assert.That(resp.Changes[0].NewValue.Name, Is.EqualTo("Jack Black")); } [Test] public void UpdateAndReturnValue() { - var resp = connection.Run(testTable.Get(insertedObject.Id).UpdateAndReturnValue(o => new ComplexObject() {Name = "Hello " + o.Id + "!"})); + var resp = connection.Run(testTable.Get(insertedObject.Id).UpdateAndReturnChanges(o => new ComplexObject() {Name = "Hello " + o.Id + "!"})); Assert.That(resp, Is.Not.Null); Assert.That(resp.FirstError, Is.Null); Assert.That(resp.Replaced, Is.EqualTo(1)); - Assert.That(resp.NewValue, Is.Not.Null); - Assert.That(resp.OldValue, Is.Not.Null); - - Assert.That(resp.OldValue.Name, Is.EqualTo("Brian Chavez")); - Assert.That(resp.NewValue.Name, Is.EqualTo("Hello " + resp.OldValue.Id + "!")); + Assert.That(resp.Changes, Is.Not.Null); + Assert.That(resp.Changes, Has.Length.EqualTo(1)); + Assert.That(resp.Changes[0].NewValue, Is.Not.Null); + Assert.That(resp.Changes[0].OldValue, Is.Not.Null); + Assert.That(resp.Changes[0].OldValue.Name, Is.EqualTo("Brian Chavez")); + Assert.That(resp.Changes[0].NewValue.Name, Is.EqualTo("Hello " + resp.Changes[0].OldValue.Id + "!")); } [Test] @@ -149,14 +152,16 @@ private async Task DoDelete() [Test] public void DeleteAndReturnValues() { - var resp = connection.Run(testTable.Get(insertedObject.Id).DeleteAndReturnValue()); + var resp = connection.Run(testTable.Get(insertedObject.Id).DeleteAndReturnChanges()); Assert.That(resp, Is.Not.Null); Assert.That(resp.FirstError, Is.Null); Assert.That(resp.Deleted, Is.EqualTo(1)); Assert.That(resp.GeneratedKeys, Is.Null); - Assert.That(resp.OldValue, Is.Not.Null); - Assert.That(resp.OldValue.Id, Is.EqualTo(insertedObject.Id)); - Assert.That(resp.NewValue, Is.Null); + Assert.That(resp.Changes, Is.Not.Null); + Assert.That(resp.Changes, Has.Length.EqualTo(1)); + Assert.That(resp.Changes[0].OldValue, Is.Not.Null); + Assert.That(resp.Changes[0].OldValue.Id, Is.EqualTo(insertedObject.Id)); + Assert.That(resp.Changes[0].NewValue, Is.Null); } [Test] diff --git a/rethinkdb-net-test/Integration/SingleObjectTests.cs b/rethinkdb-net-test/Integration/SingleObjectTests.cs index 128f0b8..31875aa 100644 --- a/rethinkdb-net-test/Integration/SingleObjectTests.cs +++ b/rethinkdb-net-test/Integration/SingleObjectTests.cs @@ -86,30 +86,33 @@ private async Task DoReplace() [Test] public void ReplaceAndReturnValue() { - var resp = connection.Run(testTable.Get(insertedObject.Id).ReplaceAndReturnValue(new TestObject() { Id = insertedObject.Id, Name = "Jack Black" })); + var resp = connection.Run(testTable.Get(insertedObject.Id).ReplaceAndReturnChanges(new TestObject() { Id = insertedObject.Id, Name = "Jack Black" })); Assert.That(resp, Is.Not.Null); Assert.That(resp.FirstError, Is.Null); Assert.That(resp.Replaced, Is.EqualTo(1)); Assert.That(resp.GeneratedKeys, Is.Null); - Assert.That(resp.OldValue, Is.Not.Null); - Assert.That(resp.OldValue.Name, Is.EqualTo("Jim Brown")); - Assert.That(resp.NewValue, Is.Not.Null); - Assert.That(resp.NewValue.Name, Is.EqualTo("Jack Black")); + Assert.That(resp.Changes, Is.Not.Null); + Assert.That(resp.Changes, Has.Length.EqualTo(1)); + Assert.That(resp.Changes[0].OldValue, Is.Not.Null); + Assert.That(resp.Changes[0].OldValue.Name, Is.EqualTo("Jim Brown")); + Assert.That(resp.Changes[0].NewValue, Is.Not.Null); + Assert.That(resp.Changes[0].NewValue.Name, Is.EqualTo("Jack Black")); } [Test] public void UpdateAndReturnValue() { - var resp = connection.Run(testTable.Get(insertedObject.Id).UpdateAndReturnValue(o => new TestObject() { Name = "Hello " + o.Id + "!" })); + var resp = connection.Run(testTable.Get(insertedObject.Id).UpdateAndReturnChanges(o => new TestObject() { Name = "Hello " + o.Id + "!" })); Assert.That(resp, Is.Not.Null); Assert.That(resp.FirstError, Is.Null); Assert.That(resp.Replaced, Is.EqualTo(1)); - Assert.That(resp.NewValue, Is.Not.Null); - Assert.That(resp.OldValue, Is.Not.Null); - - Assert.That(resp.OldValue.Name, Is.EqualTo("Jim Brown")); - Assert.That(resp.NewValue.Name, Is.EqualTo("Hello " + resp.OldValue.Id + "!")); + Assert.That(resp.Changes, Is.Not.Null); + Assert.That(resp.Changes, Has.Length.EqualTo(1)); + Assert.That(resp.Changes[0].NewValue, Is.Not.Null); + Assert.That(resp.Changes[0].OldValue, Is.Not.Null); + Assert.That(resp.Changes[0].OldValue.Name, Is.EqualTo("Jim Brown")); + Assert.That(resp.Changes[0].NewValue.Name, Is.EqualTo("Hello " + resp.Changes[0].OldValue.Id + "!")); } [Test] @@ -130,14 +133,16 @@ private async Task DoDelete() [Test] public void DeleteAndReturnValues() { - var resp = connection.Run(testTable.Get(insertedObject.Id).DeleteAndReturnValue()); + var resp = connection.Run(testTable.Get(insertedObject.Id).DeleteAndReturnChanges()); Assert.That(resp, Is.Not.Null); Assert.That(resp.FirstError, Is.Null); Assert.That(resp.Deleted, Is.EqualTo(1)); Assert.That(resp.GeneratedKeys, Is.Null); - Assert.That(resp.OldValue, Is.Not.Null); - Assert.That(resp.OldValue.Id, Is.EqualTo(insertedObject.Id)); - Assert.That(resp.NewValue, Is.Null); + Assert.That(resp.Changes, Is.Not.Null); + Assert.That(resp.Changes, Has.Length.EqualTo(1)); + Assert.That(resp.Changes[0].OldValue, Is.Not.Null); + Assert.That(resp.Changes[0].OldValue.Id, Is.EqualTo(insertedObject.Id)); + Assert.That(resp.Changes[0].NewValue, Is.Null); } [Test] diff --git a/rethinkdb-net/DmlResponse.cs b/rethinkdb-net/DmlResponse.cs index 1358329..27c8f1c 100644 --- a/rethinkdb-net/DmlResponse.cs +++ b/rethinkdb-net/DmlResponse.cs @@ -38,6 +38,13 @@ public class DmlResponse [DataContract] public class DmlResponse : DmlResponse + { + [DataMember(Name = "changes")] + public DmlResponseChange[] Changes; + } + + [DataContract] + public class DmlResponseChange { [DataMember(Name = "old_val")] public T OldValue; diff --git a/rethinkdb-net/Query.cs b/rethinkdb-net/Query.cs index a5fc584..e39fca7 100644 --- a/rethinkdb-net/Query.cs +++ b/rethinkdb-net/Query.cs @@ -119,7 +119,7 @@ public static IWriteQuery Update(this IMutableSingleObjectQuery< return new UpdateQuery(target, updateExpression, nonAtomic); } - public static IWriteQuery> UpdateAndReturnValue(this IMutableSingleObjectQuery target, Expression> updateExpression, bool nonAtomic = false) + public static IWriteQuery> UpdateAndReturnChanges(this IMutableSingleObjectQuery target, Expression> updateExpression, bool nonAtomic = false) { return new UpdateAndReturnValueQuery(target, updateExpression, nonAtomic); } @@ -134,7 +134,7 @@ public static IWriteQuery Delete(this IMutableSingleObjectQuery< return new DeleteQuery(target); } - public static IWriteQuery> DeleteAndReturnValue(this IMutableSingleObjectQuery target) + public static IWriteQuery> DeleteAndReturnChanges(this IMutableSingleObjectQuery target) { return new DeleteAndReturnValueQuery(target); } @@ -144,7 +144,7 @@ public static IWriteQuery Replace(this IMutableSingleObjectQuery return new ReplaceQuery(target, newObject, nonAtomic); } - public static IWriteQuery> ReplaceAndReturnValue(this IMutableSingleObjectQuery target, T newObject, bool nonAtomic = false) + public static IWriteQuery> ReplaceAndReturnChanges(this IMutableSingleObjectQuery target, T newObject, bool nonAtomic = false) { return new ReplaceAndReturnValueQuery(target, newObject, nonAtomic); } diff --git a/rethinkdb-net/QueryTerm/DeleteAndReturnValueQuery.cs b/rethinkdb-net/QueryTerm/DeleteAndReturnValueQuery.cs index a8a3b2b..a129141 100644 --- a/rethinkdb-net/QueryTerm/DeleteAndReturnValueQuery.cs +++ b/rethinkdb-net/QueryTerm/DeleteAndReturnValueQuery.cs @@ -12,7 +12,7 @@ public DeleteAndReturnValueQuery(IMutableSingleObjectQuery getTerm) protected override void AddOptionalArguments(Term updateTerm) { updateTerm.optargs.Add(new Term.AssocPair() { - key = "return_vals", + key = "return_changes", val = new Term() { type = Term.TermType.DATUM, datum = new Datum() { diff --git a/rethinkdb-net/QueryTerm/ReplaceAndReturnValueQuery.cs b/rethinkdb-net/QueryTerm/ReplaceAndReturnValueQuery.cs index 01186e0..c22631c 100644 --- a/rethinkdb-net/QueryTerm/ReplaceAndReturnValueQuery.cs +++ b/rethinkdb-net/QueryTerm/ReplaceAndReturnValueQuery.cs @@ -12,7 +12,7 @@ public ReplaceAndReturnValueQuery(IMutableSingleObjectQuery getTerm, T newObj protected override void AddOptionalArguments(Term updateTerm) { updateTerm.optargs.Add(new Term.AssocPair() { - key = "return_vals", + key = "return_changes", val = new Term() { type = Term.TermType.DATUM, datum = new Datum() { diff --git a/rethinkdb-net/QueryTerm/UpdateAndReturnValueQuery.cs b/rethinkdb-net/QueryTerm/UpdateAndReturnValueQuery.cs index d519010..09875ad 100644 --- a/rethinkdb-net/QueryTerm/UpdateAndReturnValueQuery.cs +++ b/rethinkdb-net/QueryTerm/UpdateAndReturnValueQuery.cs @@ -14,7 +14,7 @@ public UpdateAndReturnValueQuery(IMutableSingleObjectQuery singleObjectTerm, protected override void AddOptionalArguments(Term updateTerm) { updateTerm.optargs.Add(new Term.AssocPair() { - key = "return_vals", + key = "return_changes", val = new Term() { type = Term.TermType.DATUM, datum = new Datum() { From 3c738bfde59139e4a777402804ee02e624412c01 Mon Sep 17 00:00:00 2001 From: Mathieu Fenniak Date: Mon, 20 Oct 2014 12:18:16 -0600 Subject: [PATCH 04/17] Stub out new Grouping & Aggregation methods, implement Group by Index Introduces a new datum converter for grouped data, DictionaryDatumConverterFactory, which only supports $reql_type$ GROUPED_DATA, not generic IDictionary support. Some more of the old grouping support is commented out with #if...#endif until all new grouping support is completed. --- .../Integration/GroupingTests.cs | 143 ++++++++++++++++- rethinkdb-net/Connection.cs | 3 +- .../DictionaryDatumConverterFactory.cs | 95 +++++++++++ rethinkdb-net/Interfaces/IGroupByReduction.cs | 13 -- rethinkdb-net/Interfaces/IGroupingQuery.cs | 10 ++ rethinkdb-net/Query.cs | 149 ++++++++++++++---- rethinkdb-net/QueryTerm/AvgReduction.cs | 3 +- rethinkdb-net/QueryTerm/CountQuery.cs | 2 +- rethinkdb-net/QueryTerm/CountReduction.cs | 2 + rethinkdb-net/QueryTerm/GroupQuery.cs | 42 +++++ rethinkdb-net/QueryTerm/SumReduction.cs | 3 +- rethinkdb-net/rethinkdb-net.csproj | 4 +- 12 files changed, 411 insertions(+), 58 deletions(-) create mode 100644 rethinkdb-net/DatumConverters/DictionaryDatumConverterFactory.cs delete mode 100644 rethinkdb-net/Interfaces/IGroupByReduction.cs create mode 100644 rethinkdb-net/Interfaces/IGroupingQuery.cs create mode 100644 rethinkdb-net/QueryTerm/GroupQuery.cs diff --git a/rethinkdb-net-test/Integration/GroupingTests.cs b/rethinkdb-net-test/Integration/GroupingTests.cs index a333dff..411b0b7 100644 --- a/rethinkdb-net-test/Integration/GroupingTests.cs +++ b/rethinkdb-net-test/Integration/GroupingTests.cs @@ -1,15 +1,11 @@ using System; using NUnit.Framework; using RethinkDb; -using System.Net; using System.Linq; -using System.Threading.Tasks; using System.Collections.Generic; -using System.Linq.Expressions; namespace RethinkDb.Test.Integration { -#if false [TestFixture] public class GroupingTests : TestBase { @@ -26,7 +22,8 @@ public override void TestFixtureSetUp() public virtual void SetUp() { testTable = Query.Db("test").Table("table"); - connection.RunAsync(testTable.Insert(new TestObject[] { + connection.Run(testTable.Insert(new TestObject[] + { new TestObject() { Name = "1", SomeNumber = 1 }, new TestObject() { Name = "1", SomeNumber = 1 }, new TestObject() { Name = "2", SomeNumber = 2 }, @@ -39,15 +36,145 @@ public virtual void SetUp() new TestObject() { Name = "6", SomeNumber = 6 }, new TestObject() { Name = "6", SomeNumber = 6 }, new TestObject() { Name = "7", SomeNumber = 7 }, - })).Wait(); + })); + connection.Run(testTable.IndexCreate("name", to => to.Name)); } [TearDown] public virtual void TearDown() { - connection.RunAsync(testTable.Delete()).Wait(); + connection.Run(testTable.IndexDrop("name")); + connection.Run(testTable.Delete()); + } + + [Test] + public void GroupByIndex() + { + var query = testTable.Group("name"); + + int count = 0; + foreach (var record in connection.Run(query)) + { + var groupName = record.Key; + var objects = record.Value; + + switch (groupName) + { + case "1": + case "3": + case "6": + Assert.That(objects.Count(), Is.EqualTo(2)); + break; + case "2": + Assert.That(objects.Count(), Is.EqualTo(3)); + break; + case "4": + case "5": + case "7": + Assert.That(objects.Count(), Is.EqualTo(1)); + break; + default: + Assert.Fail("Unexpected group name: {0}", groupName); + break; + } + + ++count; + } + + Assert.That(count, Is.EqualTo(7)); + } + + [Test] + public void GroupByOneField() + { + throw new NotImplementedException(); + } + + [Test] + public void GroupByTwoFields() + { + throw new NotImplementedException(); + } + + [Test] + public void GroupByThreeFields() + { + throw new NotImplementedException(); + } + + [Test] + public void GroupAndMaxAggregate() + { + throw new NotImplementedException(); } + [Test] + public void MaxAggregate() + { + throw new NotImplementedException(); + } + + [Test] + public void GroupAndMinAggregate() + { + throw new NotImplementedException(); + } + + [Test] + public void MinAggregate() + { + throw new NotImplementedException(); + } + + [Test] + public void GroupAndAverageAggregate() + { + throw new NotImplementedException(); + } + + [Test] + public void AverageAggregate() + { + throw new NotImplementedException(); + } + + [Test] + public void GroupAndSumAggregate() + { + throw new NotImplementedException(); + } + + [Test] + public void SumAggregate() + { + throw new NotImplementedException(); + } + + [Test] + public void GroupAndCountAggregate() + { + throw new NotImplementedException(); + } + + [Test] + public void CountAggregate() + { + throw new NotImplementedException(); + } + + [Test] + public void GroupAndContainsAggregate() + { + throw new NotImplementedException(); + } + + [Test] + public void ContainsAggregate() + { + throw new NotImplementedException(); + } + +#if false [Test] public void GroupedMapReduce() { @@ -242,6 +369,6 @@ public void GroupByAvg() Assert.That(count, Is.EqualTo(7)); } + #endif } -#endif } diff --git a/rethinkdb-net/Connection.cs b/rethinkdb-net/Connection.cs index be6b718..9f70355 100644 --- a/rethinkdb-net/Connection.cs +++ b/rethinkdb-net/Connection.cs @@ -41,7 +41,8 @@ public Connection() EnumDatumConverterFactory.Instance, NullableDatumConverterFactory.Instance, ListDatumConverterFactory.Instance, - TimeSpanDatumConverterFactory.Instance + TimeSpanDatumConverterFactory.Instance, + DictionaryDatumConverterFactory.Instance ); ConnectTimeout = QueryTimeout = TimeSpan.FromSeconds(30); } diff --git a/rethinkdb-net/DatumConverters/DictionaryDatumConverterFactory.cs b/rethinkdb-net/DatumConverters/DictionaryDatumConverterFactory.cs new file mode 100644 index 0000000..0cdac33 --- /dev/null +++ b/rethinkdb-net/DatumConverters/DictionaryDatumConverterFactory.cs @@ -0,0 +1,95 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using RethinkDb.Spec; + +namespace RethinkDb.DatumConverters +{ + public class DictionaryDatumConverterFactory : AbstractDatumConverterFactory + { + public static readonly DictionaryDatumConverterFactory Instance = new DictionaryDatumConverterFactory(); + + private DictionaryDatumConverterFactory() + { + } + + public override bool TryGet(IDatumConverterFactory rootDatumConverterFactory, out IDatumConverter datumConverter) + { + datumConverter = null; + if (rootDatumConverterFactory == null) + throw new ArgumentNullException("rootDatumConverterFactory"); + + if (typeof(T).IsGenericType && typeof(T).GetGenericTypeDefinition() == typeof(IDictionary<,>)) + { + Type converterType = typeof(DictionaryDatumConverter<,>).MakeGenericType(typeof(T).GetGenericArguments()); + datumConverter = (IDatumConverter)Activator.CreateInstance(converterType, rootDatumConverterFactory); + return true; + } + else + return false; + } + } + + public class DictionaryDatumConverter : AbstractReferenceTypeDatumConverter> + { + private readonly IDatumConverter keyTypeConverter; + private readonly IDatumConverter valueTypeConverter; + + public DictionaryDatumConverter(IDatumConverterFactory rootDatumConverterFactory) + { + this.keyTypeConverter = rootDatumConverterFactory.Get(); + this.valueTypeConverter = rootDatumConverterFactory.Get(); + } + + #region IDatumConverter Members + + public override IDictionary ConvertDatum(Datum datum) + { + if (datum.type == Datum.DatumType.R_NULL) + { + return null; + } + else if (datum.type == Datum.DatumType.R_OBJECT) + { + var keys = datum.r_object.ToDictionary(kvp => kvp.key, kvp => kvp.val); + + Datum typeDatum; + if (!keys.TryGetValue("$reql_type$", out typeDatum)) + throw new NotSupportedException("Object without $reql_type$ key cannot be converted to a dictionary"); + if (typeDatum.type != Datum.DatumType.R_STR || typeDatum.r_str != "GROUPED_DATA") + throw new NotSupportedException("Object without $reql_type$ = GROUPED_DATA cannot be converted to a dictionary"); + + Datum dataDatum; + if (!keys.TryGetValue("data", out dataDatum)) + throw new NotSupportedException("Object without data key cannot be converted to a dictionary"); + if (dataDatum.type != Datum.DatumType.R_ARRAY) + throw new NotSupportedException("Object's data key must be an array type"); + + var retval = new Dictionary(dataDatum.r_array.Count); + foreach (var item in dataDatum.r_array) + { + if (item.type != Datum.DatumType.R_ARRAY || item.r_array.Count != 2) + throw new NotSupportedException("GROUPED_DATA data is expected to contain array elements of two items, a key and a value"); + var key = keyTypeConverter.ConvertDatum(item.r_array[0]); + var value = valueTypeConverter.ConvertDatum(item.r_array[1]); + retval[key] = value; + } + + return retval; + } + else + { + throw new NotSupportedException("Attempted to cast Datum to array, but Datum was unsupported type " + datum.type); + } + } + + public override Spec.Datum ConvertObject(IDictionary dictionary) + { + //if (dictionary == null) + // return new Spec.Datum() { type = Spec.Datum.DatumType.R_NULL }; + throw new NotImplementedException("IDictionary objects are only currently supported for reading Group results"); + } + + #endregion + } +} diff --git a/rethinkdb-net/Interfaces/IGroupByReduction.cs b/rethinkdb-net/Interfaces/IGroupByReduction.cs deleted file mode 100644 index bc34df1..0000000 --- a/rethinkdb-net/Interfaces/IGroupByReduction.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.ComponentModel; -using RethinkDb.Spec; - -namespace RethinkDb -{ - [ImmutableObject(true)] - public interface IGroupByReduction - { - Term GenerateReductionObject(IDatumConverterFactory datumConverterFactory); - } -} - diff --git a/rethinkdb-net/Interfaces/IGroupingQuery.cs b/rethinkdb-net/Interfaces/IGroupingQuery.cs new file mode 100644 index 0000000..6181f2c --- /dev/null +++ b/rethinkdb-net/Interfaces/IGroupingQuery.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using RethinkDb.Spec; + +namespace RethinkDb +{ + public interface IGroupingQuery : IScalarQuery> + { + } +} diff --git a/rethinkdb-net/Query.cs b/rethinkdb-net/Query.cs index e39fca7..ccb4a36 100644 --- a/rethinkdb-net/Query.cs +++ b/rethinkdb-net/Query.cs @@ -154,11 +154,6 @@ public static ISequenceQuery Between(this ISequenceQ return new BetweenQuery(target, leftKey, rightKey, indexName, leftBound, rightBound); } - public static ISingleObjectQuery Count(this ISequenceQuery target) - { - return new CountQuery(target); - } - public static ISingleObjectQuery Expr(T @object) { return new ExprQuery(@object); @@ -280,18 +275,6 @@ public static ISequenceQuery Distinct(this ISequenceQuery sequenceQuery return new DistinctQuery(sequenceQuery); } -#if false - public static ISequenceQuery> GroupedMapReduce(this ISequenceQuery sequenceQuery, Expression> grouping, Expression> mapping, Expression> reduction) - { - return new GroupedMapReduceQuery(sequenceQuery, grouping, mapping, reduction); - } - - public static ISequenceQuery> GroupedMapReduce(this ISequenceQuery sequenceQuery, Expression> grouping, Expression> mapping, Expression> reduction, TMap @base) - { - return new GroupedMapReduceQuery(sequenceQuery, grouping, mapping, reduction, @base); - } -#endif - public static ISequenceQuery ConcatMap(this ISequenceQuery sequenceQuery, Expression>> mapping) { return new ConcatMapQuery(sequenceQuery, mapping); @@ -307,13 +290,6 @@ public static ISingleObjectQuery Now() return new NowQuery(); } -#if false - public static ISequenceQuery> GroupBy(this ISequenceQuery sequenceQuery, IGroupByReduction reductionObject, Expression> groupKeyConstructor) - { - return new GroupByQuery(sequenceQuery, reductionObject, groupKeyConstructor); - } -#endif - public static ISequenceQuery Sample(this ISequenceQuery target, int count) { return new SampleQuery(target, count); @@ -329,24 +305,133 @@ public static ISingleObjectQuery HasFields(this ISingleObjectQuery t return new HasFieldsSingleObjectQuery(target, fields); } - #endregion - #region Prebuilt GroupBy reductions + #region Grouping and Aggregation + + public static IGroupingQuery Group( + // Can only use indexName on Group on a TABLE, not any arbitrary sequence + this ITableQuery table, + string indexName + ) + { + return new GroupQuery(table, indexName); + } + + public static IGroupingQuery> Group( + this ISequenceQuery sequenceQuery, + Expression> key + ) + { + throw new NotImplementedException(); + } + + public static IGroupingQuery, IEnumerable> Group( + this ISequenceQuery sequenceQuery, + Expression> key1, + Expression> key2 + ) + { + throw new NotImplementedException(); + } + + public static IGroupingQuery, IEnumerable> Group( + this ISequenceQuery sequenceQuery, + Expression> key1, + Expression> key2, + Expression> key3 + ) + { + throw new NotImplementedException(); + } - public static IGroupByReduction Count() + public static IGroupingQuery Min( + this IGroupingQuery> groupingQuery, + Expression> field = null + ) { - return CountReduction.Instance; + throw new NotImplementedException(); } - public static IGroupByReduction Sum(Expression> numericMemberReference) + public static ISingleObjectQuery Min( + this ISequenceQuery sequenceQuery, + Expression> field = null + ) { - return new SumReduction(numericMemberReference); + throw new NotImplementedException(); } - public static IGroupByReduction Avg(Expression> numericMemberReference) + public static IGroupingQuery> Max( + this IGroupingQuery> groupingQuery, + Expression> field = null + ) { - return new AvgReduction(numericMemberReference); + throw new NotImplementedException(); } + public static ISingleObjectQuery Max( + this ISequenceQuery sequenceQuery, + Expression> field = null + ) + { + throw new NotImplementedException(); + } + + public static IGroupingQuery Avg( + this IGroupingQuery> groupingQuery, + Expression> field = null + ) + { + throw new NotImplementedException(); + } + + public static ISingleObjectQuery Avg( + this ISequenceQuery groupingQuery, + Expression> field = null + ) + { + throw new NotImplementedException(); + } + + public static IGroupingQuery Sum( + this IGroupingQuery> groupingQuery, + Expression> field = null + ) + { + throw new NotImplementedException(); + } + + public static ISingleObjectQuery Sum( + this ISequenceQuery groupingQuery, + Expression> field = null + ) + { + throw new NotImplementedException(); + } + + public static IGroupingQuery Count( + this IGroupingQuery> groupingQuery, + Expression> predicate = null + ) + { + throw new NotImplementedException(); + } + + public static ISingleObjectQuery Count( + this ISequenceQuery target, + Expression> predicate = null + ) + { + return new CountQuery(target); + } + + public static IGroupingQuery Contains( + this IGroupingQuery> groupingQuery, + Expression> predicate = null + ) + { + throw new NotImplementedException(); + } + + #endregion #endregion } } diff --git a/rethinkdb-net/QueryTerm/AvgReduction.cs b/rethinkdb-net/QueryTerm/AvgReduction.cs index 8583214..77fb6e0 100644 --- a/rethinkdb-net/QueryTerm/AvgReduction.cs +++ b/rethinkdb-net/QueryTerm/AvgReduction.cs @@ -5,6 +5,7 @@ namespace RethinkDb.QueryTerm { +#if false public class AvgReduction : IGroupByReduction { private readonly Expression> numericMemberReference; @@ -56,5 +57,5 @@ private string GetMemberName(IDatumConverterFactory datumConverterFactory) return fieldConverter.GetDatumFieldName(memberExpr.Member); } } +#endif } - diff --git a/rethinkdb-net/QueryTerm/CountQuery.cs b/rethinkdb-net/QueryTerm/CountQuery.cs index f75e735..1917fb8 100644 --- a/rethinkdb-net/QueryTerm/CountQuery.cs +++ b/rethinkdb-net/QueryTerm/CountQuery.cs @@ -3,7 +3,7 @@ namespace RethinkDb.QueryTerm { - public class CountQuery : ISingleObjectQuery + public class CountQuery : ISingleObjectQuery { private readonly ISequenceQuery sequenceQuery; diff --git a/rethinkdb-net/QueryTerm/CountReduction.cs b/rethinkdb-net/QueryTerm/CountReduction.cs index 5744b48..14c9f89 100644 --- a/rethinkdb-net/QueryTerm/CountReduction.cs +++ b/rethinkdb-net/QueryTerm/CountReduction.cs @@ -3,6 +3,7 @@ namespace RethinkDb.QueryTerm { +#if false public class CountReduction : IGroupByReduction { public static readonly CountReduction Instance = new CountReduction(); @@ -35,5 +36,6 @@ public Term GenerateReductionObject(IDatumConverterFactory datumConverterFactory return retval; } } +#endif } diff --git a/rethinkdb-net/QueryTerm/GroupQuery.cs b/rethinkdb-net/QueryTerm/GroupQuery.cs new file mode 100644 index 0000000..bb32f8f --- /dev/null +++ b/rethinkdb-net/QueryTerm/GroupQuery.cs @@ -0,0 +1,42 @@ +using System; +using System.Linq; +using System.Linq.Expressions; +using System.Collections.Generic; +using RethinkDb.DatumConverters; +using RethinkDb.Spec; + +namespace RethinkDb.QueryTerm +{ + public class GroupQuery : IGroupingQuery + { + private ITableQuery tableQuery; + private string indexName; + + public GroupQuery(ITableQuery tableQuery, string indexName) + { + this.tableQuery = tableQuery; + this.indexName = indexName; + } + + public Term GenerateTerm(IDatumConverterFactory datumConverterFactory) + { + var term = new Term() + { + type = Term.TermType.GROUP, + }; + term.args.Add(tableQuery.GenerateTerm(datumConverterFactory)); + term.args.Add( + new Term() + { + type = Term.TermType.DATUM, + datum = new Datum() + { + type = Datum.DatumType.R_STR, + r_str = indexName, + } + } + ); + return term; + } + } +} diff --git a/rethinkdb-net/QueryTerm/SumReduction.cs b/rethinkdb-net/QueryTerm/SumReduction.cs index a27346f..984cdf2 100644 --- a/rethinkdb-net/QueryTerm/SumReduction.cs +++ b/rethinkdb-net/QueryTerm/SumReduction.cs @@ -5,6 +5,7 @@ namespace RethinkDb.QueryTerm { +#if false public class SumReduction : IGroupByReduction { private readonly Expression> numericMemberReference; @@ -56,5 +57,5 @@ private string GetMemberName(IDatumConverterFactory datumConverterFactory) return fieldConverter.GetDatumFieldName(memberExpr.Member); } } +#endif } - diff --git a/rethinkdb-net/rethinkdb-net.csproj b/rethinkdb-net/rethinkdb-net.csproj index 57624f0..8df3f07 100644 --- a/rethinkdb-net/rethinkdb-net.csproj +++ b/rethinkdb-net/rethinkdb-net.csproj @@ -147,7 +147,6 @@ - @@ -172,6 +171,9 @@ + + + From c3e00356938006f1a6a0c4f74eda1d63300975ce Mon Sep 17 00:00:00 2001 From: Mathieu Fenniak Date: Mon, 20 Oct 2014 12:57:28 -0600 Subject: [PATCH 05/17] Support Group() by 1, 2, or 3 functions returning keys --- .../Integration/GroupingTests.cs | 122 +++++++++++++++++- rethinkdb-net/Query.cs | 28 ++-- .../QueryTerm/GroupByFunctionQuery.cs | 104 +++++++++++++++ .../{GroupQuery.cs => GroupByIndexQuery.cs} | 9 +- rethinkdb-net/rethinkdb-net.csproj | 3 +- 5 files changed, 239 insertions(+), 27 deletions(-) create mode 100644 rethinkdb-net/QueryTerm/GroupByFunctionQuery.cs rename rethinkdb-net/QueryTerm/{GroupQuery.cs => GroupByIndexQuery.cs} (75%) diff --git a/rethinkdb-net-test/Integration/GroupingTests.cs b/rethinkdb-net-test/Integration/GroupingTests.cs index 411b0b7..7215f02 100644 --- a/rethinkdb-net-test/Integration/GroupingTests.cs +++ b/rethinkdb-net-test/Integration/GroupingTests.cs @@ -26,9 +26,9 @@ public virtual void SetUp() { new TestObject() { Name = "1", SomeNumber = 1 }, new TestObject() { Name = "1", SomeNumber = 1 }, - new TestObject() { Name = "2", SomeNumber = 2 }, + new TestObject() { Name = "2", SomeNumber = 2, Tags = new string[] { "A", "B" } }, new TestObject() { Name = "2", SomeNumber = 200 }, - new TestObject() { Name = "2", SomeNumber = 2 }, + new TestObject() { Name = "2", SomeNumber = 2, Tags = new string[] { "A", "C" } }, new TestObject() { Name = "3", SomeNumber = 3 }, new TestObject() { Name = "3", SomeNumber = 3 }, new TestObject() { Name = "4", SomeNumber = 4 }, @@ -87,19 +87,131 @@ public void GroupByIndex() [Test] public void GroupByOneField() { - throw new NotImplementedException(); + var query = testTable.Group(to => to.Name); + + int count = 0; + foreach (var record in connection.Run(query)) + { + var groupName = record.Key; + var objects = record.Value; + + switch (groupName) + { + case "1": + case "3": + case "6": + Assert.That(objects.Count(), Is.EqualTo(2)); + break; + case "2": + Assert.That(objects.Count(), Is.EqualTo(3)); + break; + case "4": + case "5": + case "7": + Assert.That(objects.Count(), Is.EqualTo(1)); + break; + default: + Assert.Fail("Unexpected group name: {0}", groupName); + break; + } + + ++count; + } + + Assert.That(count, Is.EqualTo(7)); } [Test] public void GroupByTwoFields() { - throw new NotImplementedException(); + var query = testTable.Group( + to => to.Name, + to => to.SomeNumber + ); + + int count = 0; + foreach (var record in connection.Run(query)) + { + var groupKey = record.Key; + var objects = record.Value; + + if (groupKey.Equals(Tuple.Create("1", 1d)) || + groupKey.Equals(Tuple.Create("2", 2d)) || + groupKey.Equals(Tuple.Create("3", 3d)) || + groupKey.Equals(Tuple.Create("6", 6d))) + { + Assert.That(objects.Count(), Is.EqualTo(2)); + } + else if ( + groupKey.Equals(Tuple.Create("2", 200d)) || + groupKey.Equals(Tuple.Create("4", 4d)) || + groupKey.Equals(Tuple.Create("5", 5d)) || + groupKey.Equals(Tuple.Create("7", 7d))) + { + Assert.That(objects.Count(), Is.EqualTo(1)); + } + else + { + Assert.Fail("Unexpected group key: {0}", groupKey); + break; + } + + ++count; + } + + Assert.That(count, Is.EqualTo(8)); } [Test] public void GroupByThreeFields() { - throw new NotImplementedException(); + var query = testTable.Group( + to => to.Name, + to => to.SomeNumber, + to => to.Tags + ); + + int count = 0; + foreach (var record in connection.Run(query)) + { + var groupKey = record.Key; + var objects = record.Value; + + if (groupKey.Item1 == "2" && Math.Abs(groupKey.Item2 - 2d) <= Double.Epsilon) + { + if (groupKey.Item3.SequenceEqual(new string[] { "A", "B" }) || + groupKey.Item3.SequenceEqual(new string[] { "A", "C" })) + { + Assert.That(objects.Count(), Is.EqualTo(1)); + } + else + { + Assert.Fail("Unexpected Tags on (2, 2): {0}", groupKey.Item3); + } + } + else if (groupKey.Equals(Tuple.Create("1", 1d, null)) || + groupKey.Equals(Tuple.Create("3", 3d, null)) || + groupKey.Equals(Tuple.Create("6", 6d, null))) + { + Assert.That(objects.Count(), Is.EqualTo(2)); + } + else if (groupKey.Equals(Tuple.Create("2", 200d, null)) || + groupKey.Equals(Tuple.Create("4", 4d, null)) || + groupKey.Equals(Tuple.Create("5", 5d, null)) || + groupKey.Equals(Tuple.Create("7", 7d, null))) + { + Assert.That(objects.Count(), Is.EqualTo(1)); + } + else + { + Assert.Fail("Unexpected group key: {0}", groupKey); + break; + } + + ++count; + } + + Assert.That(count, Is.EqualTo(9)); } [Test] diff --git a/rethinkdb-net/Query.cs b/rethinkdb-net/Query.cs index ccb4a36..c4efd20 100644 --- a/rethinkdb-net/Query.cs +++ b/rethinkdb-net/Query.cs @@ -313,38 +313,38 @@ public static IGroupingQuery Group( string indexName ) { - return new GroupQuery(table, indexName); + return new GroupByIndexQuery(table, indexName); } - public static IGroupingQuery> Group( + public static IGroupingQuery Group( this ISequenceQuery sequenceQuery, Expression> key ) { - throw new NotImplementedException(); + return new GroupByFunctionQuery(sequenceQuery, key); } - public static IGroupingQuery, IEnumerable> Group( + public static IGroupingQuery, TRecord[]> Group( this ISequenceQuery sequenceQuery, Expression> key1, Expression> key2 ) { - throw new NotImplementedException(); + return new GroupByFunctionQuery(sequenceQuery, key1, key2); } - public static IGroupingQuery, IEnumerable> Group( + public static IGroupingQuery, TRecord[]> Group( this ISequenceQuery sequenceQuery, Expression> key1, Expression> key2, Expression> key3 ) { - throw new NotImplementedException(); + return new GroupByFunctionQuery(sequenceQuery, key1, key2, key3); } public static IGroupingQuery Min( - this IGroupingQuery> groupingQuery, + this IGroupingQuery groupingQuery, Expression> field = null ) { @@ -359,8 +359,8 @@ public static ISingleObjectQuery Min( throw new NotImplementedException(); } - public static IGroupingQuery> Max( - this IGroupingQuery> groupingQuery, + public static IGroupingQuery Max( + this IGroupingQuery groupingQuery, Expression> field = null ) { @@ -376,7 +376,7 @@ public static ISingleObjectQuery Max( } public static IGroupingQuery Avg( - this IGroupingQuery> groupingQuery, + this IGroupingQuery groupingQuery, Expression> field = null ) { @@ -392,7 +392,7 @@ public static ISingleObjectQuery Avg( } public static IGroupingQuery Sum( - this IGroupingQuery> groupingQuery, + this IGroupingQuery groupingQuery, Expression> field = null ) { @@ -408,7 +408,7 @@ public static ISingleObjectQuery Sum( } public static IGroupingQuery Count( - this IGroupingQuery> groupingQuery, + this IGroupingQuery groupingQuery, Expression> predicate = null ) { @@ -424,7 +424,7 @@ public static ISingleObjectQuery Count( } public static IGroupingQuery Contains( - this IGroupingQuery> groupingQuery, + this IGroupingQuery groupingQuery, Expression> predicate = null ) { diff --git a/rethinkdb-net/QueryTerm/GroupByFunctionQuery.cs b/rethinkdb-net/QueryTerm/GroupByFunctionQuery.cs new file mode 100644 index 0000000..d322575 --- /dev/null +++ b/rethinkdb-net/QueryTerm/GroupByFunctionQuery.cs @@ -0,0 +1,104 @@ +using System; +using System.Linq; +using System.Linq.Expressions; +using System.Collections.Generic; +using RethinkDb.DatumConverters; +using RethinkDb.Spec; + +namespace RethinkDb.QueryTerm +{ + public abstract class GroupByFunctionQueryBase : IGroupingQuery + { + private ISequenceQuery sequenceQuery; + + protected GroupByFunctionQueryBase(ISequenceQuery sequenceQuery) + { + this.sequenceQuery = sequenceQuery; + } + + protected abstract void GenerateFunctionTerms(Term term, IDatumConverterFactory datumConverterFactory); + + public Term GenerateTerm(IDatumConverterFactory datumConverterFactory) + { + var term = new Term() + { + type = Term.TermType.GROUP, + }; + term.args.Add(sequenceQuery.GenerateTerm(datumConverterFactory)); + GenerateFunctionTerms(term, datumConverterFactory); + return term; + } + } + + public class GroupByFunctionQuery : GroupByFunctionQueryBase + { + private Expression> keyExpression; + + public GroupByFunctionQuery(ISequenceQuery sequenceQuery, Expression> keyExpression) + : base(sequenceQuery) + { + this.keyExpression = keyExpression; + } + + protected override void GenerateFunctionTerms(Term term, IDatumConverterFactory datumConverterFactory) + { + if (keyExpression.NodeType != ExpressionType.Lambda) + throw new NotSupportedException("Unsupported expression type"); + term.args.Add(ExpressionUtils.CreateFunctionTerm(datumConverterFactory, keyExpression)); + } + } + + public class GroupByFunctionQuery : GroupByFunctionQueryBase> + { + private Expression> key1Expression; + private Expression> key2Expression; + + public GroupByFunctionQuery(ISequenceQuery sequenceQuery, Expression> key1Expression, Expression> key2Expression) + : base(sequenceQuery) + { + this.key1Expression = key1Expression; + this.key2Expression = key2Expression; + } + + protected override void GenerateFunctionTerms(Term term, IDatumConverterFactory datumConverterFactory) + { + if (key1Expression.NodeType != ExpressionType.Lambda) + throw new NotSupportedException("Unsupported expression type"); + term.args.Add(ExpressionUtils.CreateFunctionTerm(datumConverterFactory, key1Expression)); + + if (key2Expression.NodeType != ExpressionType.Lambda) + throw new NotSupportedException("Unsupported expression type"); + term.args.Add(ExpressionUtils.CreateFunctionTerm(datumConverterFactory, key2Expression)); + } + } + + public class GroupByFunctionQuery : GroupByFunctionQueryBase> + { + private Expression> key1Expression; + private Expression> key2Expression; + private Expression> key3Expression; + + public GroupByFunctionQuery(ISequenceQuery sequenceQuery, Expression> key1Expression, Expression> key2Expression, Expression> key3Expression) + : base(sequenceQuery) + { + this.key1Expression = key1Expression; + this.key2Expression = key2Expression; + this.key3Expression = key3Expression; + } + + protected override void GenerateFunctionTerms(Term term, IDatumConverterFactory datumConverterFactory) + { + if (key1Expression.NodeType != ExpressionType.Lambda) + throw new NotSupportedException("Unsupported expression type"); + term.args.Add(ExpressionUtils.CreateFunctionTerm(datumConverterFactory, key1Expression)); + + if (key2Expression.NodeType != ExpressionType.Lambda) + throw new NotSupportedException("Unsupported expression type"); + term.args.Add(ExpressionUtils.CreateFunctionTerm(datumConverterFactory, key2Expression)); + + if (key3Expression.NodeType != ExpressionType.Lambda) + throw new NotSupportedException("Unsupported expression type"); + term.args.Add(ExpressionUtils.CreateFunctionTerm(datumConverterFactory, key3Expression)); + } + } +} diff --git a/rethinkdb-net/QueryTerm/GroupQuery.cs b/rethinkdb-net/QueryTerm/GroupByIndexQuery.cs similarity index 75% rename from rethinkdb-net/QueryTerm/GroupQuery.cs rename to rethinkdb-net/QueryTerm/GroupByIndexQuery.cs index bb32f8f..4667237 100644 --- a/rethinkdb-net/QueryTerm/GroupQuery.cs +++ b/rethinkdb-net/QueryTerm/GroupByIndexQuery.cs @@ -1,18 +1,13 @@ -using System; -using System.Linq; -using System.Linq.Expressions; -using System.Collections.Generic; -using RethinkDb.DatumConverters; using RethinkDb.Spec; namespace RethinkDb.QueryTerm { - public class GroupQuery : IGroupingQuery + public class GroupByIndexQuery : IGroupingQuery { private ITableQuery tableQuery; private string indexName; - public GroupQuery(ITableQuery tableQuery, string indexName) + public GroupByIndexQuery(ITableQuery tableQuery, string indexName) { this.tableQuery = tableQuery; this.indexName = indexName; diff --git a/rethinkdb-net/rethinkdb-net.csproj b/rethinkdb-net/rethinkdb-net.csproj index 8df3f07..ee87f3d 100644 --- a/rethinkdb-net/rethinkdb-net.csproj +++ b/rethinkdb-net/rethinkdb-net.csproj @@ -172,8 +172,9 @@ - + + From cabc682ba06e5b703708e126e2d68fa5a8d1b4bf Mon Sep 17 00:00:00 2001 From: Mathieu Fenniak Date: Mon, 20 Oct 2014 13:12:50 -0600 Subject: [PATCH 06/17] Max aggregate support for both grouped & ungrouped queries --- .../Integration/GroupingTests.cs | 45 ++++++++++++++++++- rethinkdb-net/Query.cs | 28 ++++++------ rethinkdb-net/QueryTerm/MaxAggregateQuery.cs | 34 ++++++++++++++ .../QueryTerm/MaxGroupAggregateQuery.cs | 34 ++++++++++++++ rethinkdb-net/rethinkdb-net.csproj | 2 + 5 files changed, 127 insertions(+), 16 deletions(-) create mode 100644 rethinkdb-net/QueryTerm/MaxAggregateQuery.cs create mode 100644 rethinkdb-net/QueryTerm/MaxGroupAggregateQuery.cs diff --git a/rethinkdb-net-test/Integration/GroupingTests.cs b/rethinkdb-net-test/Integration/GroupingTests.cs index 7215f02..b2a4c2d 100644 --- a/rethinkdb-net-test/Integration/GroupingTests.cs +++ b/rethinkdb-net-test/Integration/GroupingTests.cs @@ -217,13 +217,54 @@ public void GroupByThreeFields() [Test] public void GroupAndMaxAggregate() { - throw new NotImplementedException(); + var query = testTable.Group("name").Max(to => to.SomeNumber); + + int count = 0; + foreach (var record in connection.Run(query)) + { + var groupName = record.Key; + var testObject = record.Value; + + switch (groupName) + { + case "1": + Assert.That(testObject.SomeNumber, Is.EqualTo(1)); + break; + case "2": + Assert.That(testObject.SomeNumber, Is.EqualTo(200)); + break; + case "3": + Assert.That(testObject.SomeNumber, Is.EqualTo(3)); + break; + case "4": + Assert.That(testObject.SomeNumber, Is.EqualTo(4)); + break; + case "5": + Assert.That(testObject.SomeNumber, Is.EqualTo(5)); + break; + case "6": + Assert.That(testObject.SomeNumber, Is.EqualTo(6)); + break; + case "7": + Assert.That(testObject.SomeNumber, Is.EqualTo(7)); + break; + default: + Assert.Fail("Unexpected group name: {0}", groupName); + break; + } + + ++count; + } + + Assert.That(count, Is.EqualTo(7)); } [Test] public void MaxAggregate() { - throw new NotImplementedException(); + var query = testTable.Max(to => to.SomeNumber); + var testObject = connection.Run(query); + Assert.That(testObject.SomeNumber, Is.EqualTo(200)); } [Test] diff --git a/rethinkdb-net/Query.cs b/rethinkdb-net/Query.cs index c4efd20..4b38cab 100644 --- a/rethinkdb-net/Query.cs +++ b/rethinkdb-net/Query.cs @@ -343,41 +343,41 @@ Expression> key3 return new GroupByFunctionQuery(sequenceQuery, key1, key2, key3); } - public static IGroupingQuery Min( + public static IGroupingQuery Min( this IGroupingQuery groupingQuery, - Expression> field = null + Expression> field = null ) { throw new NotImplementedException(); } - public static ISingleObjectQuery Min( + public static ISingleObjectQuery Min( this ISequenceQuery sequenceQuery, - Expression> field = null + Expression> field = null ) { throw new NotImplementedException(); } - public static IGroupingQuery Max( + public static IGroupingQuery Max( this IGroupingQuery groupingQuery, - Expression> field = null + Expression> field = null ) { - throw new NotImplementedException(); + return new MaxGroupAggregateQuery(groupingQuery, field); } - public static ISingleObjectQuery Max( + public static ISingleObjectQuery Max( this ISequenceQuery sequenceQuery, - Expression> field = null + Expression> field = null ) { - throw new NotImplementedException(); + return new MaxAggregateQuery(sequenceQuery, field); } public static IGroupingQuery Avg( this IGroupingQuery groupingQuery, - Expression> field = null + Expression> field = null ) { throw new NotImplementedException(); @@ -385,7 +385,7 @@ public static IGroupingQuery Avg( public static ISingleObjectQuery Avg( this ISequenceQuery groupingQuery, - Expression> field = null + Expression> field = null ) { throw new NotImplementedException(); @@ -393,7 +393,7 @@ public static ISingleObjectQuery Avg( public static IGroupingQuery Sum( this IGroupingQuery groupingQuery, - Expression> field = null + Expression> field = null ) { throw new NotImplementedException(); @@ -401,7 +401,7 @@ public static IGroupingQuery Sum( public static ISingleObjectQuery Sum( this ISequenceQuery groupingQuery, - Expression> field = null + Expression> field = null ) { throw new NotImplementedException(); diff --git a/rethinkdb-net/QueryTerm/MaxAggregateQuery.cs b/rethinkdb-net/QueryTerm/MaxAggregateQuery.cs new file mode 100644 index 0000000..125f36e --- /dev/null +++ b/rethinkdb-net/QueryTerm/MaxAggregateQuery.cs @@ -0,0 +1,34 @@ +using RethinkDb.Spec; +using System; +using System.Linq.Expressions; + +namespace RethinkDb.QueryTerm +{ + public class MaxAggregateQuery : ISingleObjectQuery + { + private readonly ISequenceQuery sequenceQuery; + private readonly Expression> field; + + public MaxAggregateQuery(ISequenceQuery sequenceQuery, Expression> field) + { + this.sequenceQuery = sequenceQuery; + this.field = field; + } + + public Term GenerateTerm(IDatumConverterFactory datumConverterFactory) + { + var term = new Term() + { + type = Term.TermType.MAX, + }; + term.args.Add(sequenceQuery.GenerateTerm(datumConverterFactory)); + if (field != null) + { + if (field.NodeType != ExpressionType.Lambda) + throw new NotSupportedException("Unsupported expression type"); + term.args.Add(ExpressionUtils.CreateFunctionTerm(datumConverterFactory, field)); + } + return term; + } + } +} \ No newline at end of file diff --git a/rethinkdb-net/QueryTerm/MaxGroupAggregateQuery.cs b/rethinkdb-net/QueryTerm/MaxGroupAggregateQuery.cs new file mode 100644 index 0000000..0e8ff82 --- /dev/null +++ b/rethinkdb-net/QueryTerm/MaxGroupAggregateQuery.cs @@ -0,0 +1,34 @@ +using RethinkDb.Spec; +using System; +using System.Linq.Expressions; + +namespace RethinkDb.QueryTerm +{ + public class MaxGroupAggregateQuery : IGroupingQuery + { + private readonly IGroupingQuery groupingQuery; + private readonly Expression> field; + + public MaxGroupAggregateQuery(IGroupingQuery groupingQuery, Expression> field) + { + this.groupingQuery = groupingQuery; + this.field = field; + } + + public Term GenerateTerm(IDatumConverterFactory datumConverterFactory) + { + var term = new Term() + { + type = Term.TermType.MAX, + }; + term.args.Add(groupingQuery.GenerateTerm(datumConverterFactory)); + if (field != null) + { + if (field.NodeType != ExpressionType.Lambda) + throw new NotSupportedException("Unsupported expression type"); + term.args.Add(ExpressionUtils.CreateFunctionTerm(datumConverterFactory, field)); + } + return term; + } + } +} \ No newline at end of file diff --git a/rethinkdb-net/rethinkdb-net.csproj b/rethinkdb-net/rethinkdb-net.csproj index ee87f3d..19d261b 100644 --- a/rethinkdb-net/rethinkdb-net.csproj +++ b/rethinkdb-net/rethinkdb-net.csproj @@ -175,6 +175,8 @@ + + From 551cf59f36ab8553bbb85c7e2464d7bcfff8533e Mon Sep 17 00:00:00 2001 From: Mathieu Fenniak Date: Mon, 20 Oct 2014 13:16:17 -0600 Subject: [PATCH 07/17] Min aggregate support for both grouped & ungrouped queries --- .../Integration/GroupingTests.cs | 45 ++++++++++++++++++- rethinkdb-net/Query.cs | 4 +- rethinkdb-net/QueryTerm/MinAggregateQuery.cs | 34 ++++++++++++++ .../QueryTerm/MinGroupAggregateQuery.cs | 34 ++++++++++++++ rethinkdb-net/rethinkdb-net.csproj | 2 + 5 files changed, 115 insertions(+), 4 deletions(-) create mode 100644 rethinkdb-net/QueryTerm/MinAggregateQuery.cs create mode 100644 rethinkdb-net/QueryTerm/MinGroupAggregateQuery.cs diff --git a/rethinkdb-net-test/Integration/GroupingTests.cs b/rethinkdb-net-test/Integration/GroupingTests.cs index b2a4c2d..e8dc106 100644 --- a/rethinkdb-net-test/Integration/GroupingTests.cs +++ b/rethinkdb-net-test/Integration/GroupingTests.cs @@ -270,13 +270,54 @@ public void MaxAggregate() [Test] public void GroupAndMinAggregate() { - throw new NotImplementedException(); + var query = testTable.Group("name").Min(to => to.SomeNumber); + + int count = 0; + foreach (var record in connection.Run(query)) + { + var groupName = record.Key; + var testObject = record.Value; + + switch (groupName) + { + case "1": + Assert.That(testObject.SomeNumber, Is.EqualTo(1)); + break; + case "2": + Assert.That(testObject.SomeNumber, Is.EqualTo(2)); + break; + case "3": + Assert.That(testObject.SomeNumber, Is.EqualTo(3)); + break; + case "4": + Assert.That(testObject.SomeNumber, Is.EqualTo(4)); + break; + case "5": + Assert.That(testObject.SomeNumber, Is.EqualTo(5)); + break; + case "6": + Assert.That(testObject.SomeNumber, Is.EqualTo(6)); + break; + case "7": + Assert.That(testObject.SomeNumber, Is.EqualTo(7)); + break; + default: + Assert.Fail("Unexpected group name: {0}", groupName); + break; + } + + ++count; + } + + Assert.That(count, Is.EqualTo(7)); } [Test] public void MinAggregate() { - throw new NotImplementedException(); + var query = testTable.Min(to => to.SomeNumber); + var testObject = connection.Run(query); + Assert.That(testObject.SomeNumber, Is.EqualTo(1)); } [Test] diff --git a/rethinkdb-net/Query.cs b/rethinkdb-net/Query.cs index 4b38cab..9f38d9f 100644 --- a/rethinkdb-net/Query.cs +++ b/rethinkdb-net/Query.cs @@ -348,7 +348,7 @@ public static IGroupingQuery Min Expression> field = null ) { - throw new NotImplementedException(); + return new MinGroupAggregateQuery(groupingQuery, field); } public static ISingleObjectQuery Min( @@ -356,7 +356,7 @@ public static ISingleObjectQuery Min( Expression> field = null ) { - throw new NotImplementedException(); + return new MinAggregateQuery(sequenceQuery, field); } public static IGroupingQuery Max( diff --git a/rethinkdb-net/QueryTerm/MinAggregateQuery.cs b/rethinkdb-net/QueryTerm/MinAggregateQuery.cs new file mode 100644 index 0000000..1cd50cc --- /dev/null +++ b/rethinkdb-net/QueryTerm/MinAggregateQuery.cs @@ -0,0 +1,34 @@ +using RethinkDb.Spec; +using System; +using System.Linq.Expressions; + +namespace RethinkDb.QueryTerm +{ + public class MinAggregateQuery : ISingleObjectQuery + { + private readonly ISequenceQuery sequenceQuery; + private readonly Expression> field; + + public MinAggregateQuery(ISequenceQuery sequenceQuery, Expression> field) + { + this.sequenceQuery = sequenceQuery; + this.field = field; + } + + public Term GenerateTerm(IDatumConverterFactory datumConverterFactory) + { + var term = new Term() + { + type = Term.TermType.MIN, + }; + term.args.Add(sequenceQuery.GenerateTerm(datumConverterFactory)); + if (field != null) + { + if (field.NodeType != ExpressionType.Lambda) + throw new NotSupportedException("Unsupported expression type"); + term.args.Add(ExpressionUtils.CreateFunctionTerm(datumConverterFactory, field)); + } + return term; + } + } +} \ No newline at end of file diff --git a/rethinkdb-net/QueryTerm/MinGroupAggregateQuery.cs b/rethinkdb-net/QueryTerm/MinGroupAggregateQuery.cs new file mode 100644 index 0000000..44dfa09 --- /dev/null +++ b/rethinkdb-net/QueryTerm/MinGroupAggregateQuery.cs @@ -0,0 +1,34 @@ +using RethinkDb.Spec; +using System; +using System.Linq.Expressions; + +namespace RethinkDb.QueryTerm +{ + public class MinGroupAggregateQuery : IGroupingQuery + { + private readonly IGroupingQuery groupingQuery; + private readonly Expression> field; + + public MinGroupAggregateQuery(IGroupingQuery groupingQuery, Expression> field) + { + this.groupingQuery = groupingQuery; + this.field = field; + } + + public Term GenerateTerm(IDatumConverterFactory datumConverterFactory) + { + var term = new Term() + { + type = Term.TermType.MIN, + }; + term.args.Add(groupingQuery.GenerateTerm(datumConverterFactory)); + if (field != null) + { + if (field.NodeType != ExpressionType.Lambda) + throw new NotSupportedException("Unsupported expression type"); + term.args.Add(ExpressionUtils.CreateFunctionTerm(datumConverterFactory, field)); + } + return term; + } + } +} \ No newline at end of file diff --git a/rethinkdb-net/rethinkdb-net.csproj b/rethinkdb-net/rethinkdb-net.csproj index 19d261b..a0a52c5 100644 --- a/rethinkdb-net/rethinkdb-net.csproj +++ b/rethinkdb-net/rethinkdb-net.csproj @@ -177,6 +177,8 @@ + + From fa0a28af06469e70c01df6a8bc1e24531b8d4a29 Mon Sep 17 00:00:00 2001 From: Mathieu Fenniak Date: Mon, 20 Oct 2014 13:22:43 -0600 Subject: [PATCH 08/17] Avg aggregate support for both grouped & ungrouped queries --- .../Integration/GroupingTests.cs | 45 ++++++++++++++++++- rethinkdb-net/Query.cs | 6 +-- rethinkdb-net/QueryTerm/AvgAggregateQuery.cs | 34 ++++++++++++++ .../QueryTerm/AvgGroupAggregateQuery.cs | 34 ++++++++++++++ rethinkdb-net/rethinkdb-net.csproj | 2 + 5 files changed, 116 insertions(+), 5 deletions(-) create mode 100644 rethinkdb-net/QueryTerm/AvgAggregateQuery.cs create mode 100644 rethinkdb-net/QueryTerm/AvgGroupAggregateQuery.cs diff --git a/rethinkdb-net-test/Integration/GroupingTests.cs b/rethinkdb-net-test/Integration/GroupingTests.cs index e8dc106..c082c17 100644 --- a/rethinkdb-net-test/Integration/GroupingTests.cs +++ b/rethinkdb-net-test/Integration/GroupingTests.cs @@ -323,13 +323,54 @@ public void MinAggregate() [Test] public void GroupAndAverageAggregate() { - throw new NotImplementedException(); + var query = testTable.Group("name").Avg(to => to.SomeNumber); + + int count = 0; + foreach (var record in connection.Run(query)) + { + var groupName = record.Key; + var average = record.Value; + + switch (groupName) + { + case "1": + Assert.That(average, Is.EqualTo(1)); + break; + case "2": + Assert.That(average, Is.EqualTo(68)); + break; + case "3": + Assert.That(average, Is.EqualTo(3)); + break; + case "4": + Assert.That(average, Is.EqualTo(4)); + break; + case "5": + Assert.That(average, Is.EqualTo(5)); + break; + case "6": + Assert.That(average, Is.EqualTo(6)); + break; + case "7": + Assert.That(average, Is.EqualTo(7)); + break; + default: + Assert.Fail("Unexpected group name: {0}", groupName); + break; + } + + ++count; + } + + Assert.That(count, Is.EqualTo(7)); } [Test] public void AverageAggregate() { - throw new NotImplementedException(); + var query = testTable.Avg(to => to.SomeNumber); + var average = connection.Run(query); + Assert.That(average, Is.EqualTo(20.0d)); } [Test] diff --git a/rethinkdb-net/Query.cs b/rethinkdb-net/Query.cs index 9f38d9f..68a92ec 100644 --- a/rethinkdb-net/Query.cs +++ b/rethinkdb-net/Query.cs @@ -380,15 +380,15 @@ public static IGroupingQuery Avg( Expression> field = null ) { - throw new NotImplementedException(); + return new AvgGroupAggregateQuery(groupingQuery, field); } public static ISingleObjectQuery Avg( - this ISequenceQuery groupingQuery, + this ISequenceQuery sequenceQuery, Expression> field = null ) { - throw new NotImplementedException(); + return new AvgAggregateQuery(sequenceQuery, field); } public static IGroupingQuery Sum( diff --git a/rethinkdb-net/QueryTerm/AvgAggregateQuery.cs b/rethinkdb-net/QueryTerm/AvgAggregateQuery.cs new file mode 100644 index 0000000..2b2d415 --- /dev/null +++ b/rethinkdb-net/QueryTerm/AvgAggregateQuery.cs @@ -0,0 +1,34 @@ +using RethinkDb.Spec; +using System; +using System.Linq.Expressions; + +namespace RethinkDb.QueryTerm +{ + public class AvgAggregateQuery : ISingleObjectQuery + { + private readonly ISequenceQuery sequenceQuery; + private readonly Expression> field; + + public AvgAggregateQuery(ISequenceQuery sequenceQuery, Expression> field) + { + this.sequenceQuery = sequenceQuery; + this.field = field; + } + + public Term GenerateTerm(IDatumConverterFactory datumConverterFactory) + { + var term = new Term() + { + type = Term.TermType.AVG, + }; + term.args.Add(sequenceQuery.GenerateTerm(datumConverterFactory)); + if (field != null) + { + if (field.NodeType != ExpressionType.Lambda) + throw new NotSupportedException("Unsupported expression type"); + term.args.Add(ExpressionUtils.CreateFunctionTerm(datumConverterFactory, field)); + } + return term; + } + } +} \ No newline at end of file diff --git a/rethinkdb-net/QueryTerm/AvgGroupAggregateQuery.cs b/rethinkdb-net/QueryTerm/AvgGroupAggregateQuery.cs new file mode 100644 index 0000000..d9b303f --- /dev/null +++ b/rethinkdb-net/QueryTerm/AvgGroupAggregateQuery.cs @@ -0,0 +1,34 @@ +using RethinkDb.Spec; +using System; +using System.Linq.Expressions; + +namespace RethinkDb.QueryTerm +{ + public class AvgGroupAggregateQuery : IGroupingQuery + { + private readonly IGroupingQuery groupingQuery; + private readonly Expression> field; + + public AvgGroupAggregateQuery(IGroupingQuery groupingQuery, Expression> field) + { + this.groupingQuery = groupingQuery; + this.field = field; + } + + public Term GenerateTerm(IDatumConverterFactory datumConverterFactory) + { + var term = new Term() + { + type = Term.TermType.AVG, + }; + term.args.Add(groupingQuery.GenerateTerm(datumConverterFactory)); + if (field != null) + { + if (field.NodeType != ExpressionType.Lambda) + throw new NotSupportedException("Unsupported expression type"); + term.args.Add(ExpressionUtils.CreateFunctionTerm(datumConverterFactory, field)); + } + return term; + } + } +} \ No newline at end of file diff --git a/rethinkdb-net/rethinkdb-net.csproj b/rethinkdb-net/rethinkdb-net.csproj index a0a52c5..804f634 100644 --- a/rethinkdb-net/rethinkdb-net.csproj +++ b/rethinkdb-net/rethinkdb-net.csproj @@ -179,6 +179,8 @@ + + From b27a1018061678f17f8df08c310d7e332f3fa847 Mon Sep 17 00:00:00 2001 From: Mathieu Fenniak Date: Mon, 20 Oct 2014 13:26:31 -0600 Subject: [PATCH 09/17] Sum aggregate support for both grouped & ungrouped queries --- .../Integration/GroupingTests.cs | 45 ++++++++++++++++++- rethinkdb-net/Query.cs | 6 +-- rethinkdb-net/QueryTerm/SumAggregateQuery.cs | 34 ++++++++++++++ .../QueryTerm/SumGroupAggregateQuery.cs | 34 ++++++++++++++ rethinkdb-net/rethinkdb-net.csproj | 2 + 5 files changed, 116 insertions(+), 5 deletions(-) create mode 100644 rethinkdb-net/QueryTerm/SumAggregateQuery.cs create mode 100644 rethinkdb-net/QueryTerm/SumGroupAggregateQuery.cs diff --git a/rethinkdb-net-test/Integration/GroupingTests.cs b/rethinkdb-net-test/Integration/GroupingTests.cs index c082c17..5736f70 100644 --- a/rethinkdb-net-test/Integration/GroupingTests.cs +++ b/rethinkdb-net-test/Integration/GroupingTests.cs @@ -376,13 +376,54 @@ public void AverageAggregate() [Test] public void GroupAndSumAggregate() { - throw new NotImplementedException(); + var query = testTable.Group("name").Sum(to => to.SomeNumber); + + int count = 0; + foreach (var record in connection.Run(query)) + { + var groupName = record.Key; + var average = record.Value; + + switch (groupName) + { + case "1": + Assert.That(average, Is.EqualTo(2)); + break; + case "2": + Assert.That(average, Is.EqualTo(204)); + break; + case "3": + Assert.That(average, Is.EqualTo(6)); + break; + case "4": + Assert.That(average, Is.EqualTo(4)); + break; + case "5": + Assert.That(average, Is.EqualTo(5)); + break; + case "6": + Assert.That(average, Is.EqualTo(12)); + break; + case "7": + Assert.That(average, Is.EqualTo(7)); + break; + default: + Assert.Fail("Unexpected group name: {0}", groupName); + break; + } + + ++count; + } + + Assert.That(count, Is.EqualTo(7)); } [Test] public void SumAggregate() { - throw new NotImplementedException(); + var query = testTable.Sum(to => to.SomeNumber); + var average = connection.Run(query); + Assert.That(average, Is.EqualTo(240)); } [Test] diff --git a/rethinkdb-net/Query.cs b/rethinkdb-net/Query.cs index 68a92ec..98278b0 100644 --- a/rethinkdb-net/Query.cs +++ b/rethinkdb-net/Query.cs @@ -396,15 +396,15 @@ public static IGroupingQuery Sum( Expression> field = null ) { - throw new NotImplementedException(); + return new SumGroupAggregateQuery(groupingQuery, field); } public static ISingleObjectQuery Sum( - this ISequenceQuery groupingQuery, + this ISequenceQuery sequenceQuery, Expression> field = null ) { - throw new NotImplementedException(); + return new SumAggregateQuery(sequenceQuery, field); } public static IGroupingQuery Count( diff --git a/rethinkdb-net/QueryTerm/SumAggregateQuery.cs b/rethinkdb-net/QueryTerm/SumAggregateQuery.cs new file mode 100644 index 0000000..9655579 --- /dev/null +++ b/rethinkdb-net/QueryTerm/SumAggregateQuery.cs @@ -0,0 +1,34 @@ +using RethinkDb.Spec; +using System; +using System.Linq.Expressions; + +namespace RethinkDb.QueryTerm +{ + public class SumAggregateQuery : ISingleObjectQuery + { + private readonly ISequenceQuery sequenceQuery; + private readonly Expression> field; + + public SumAggregateQuery(ISequenceQuery sequenceQuery, Expression> field) + { + this.sequenceQuery = sequenceQuery; + this.field = field; + } + + public Term GenerateTerm(IDatumConverterFactory datumConverterFactory) + { + var term = new Term() + { + type = Term.TermType.SUM, + }; + term.args.Add(sequenceQuery.GenerateTerm(datumConverterFactory)); + if (field != null) + { + if (field.NodeType != ExpressionType.Lambda) + throw new NotSupportedException("Unsupported expression type"); + term.args.Add(ExpressionUtils.CreateFunctionTerm(datumConverterFactory, field)); + } + return term; + } + } +} \ No newline at end of file diff --git a/rethinkdb-net/QueryTerm/SumGroupAggregateQuery.cs b/rethinkdb-net/QueryTerm/SumGroupAggregateQuery.cs new file mode 100644 index 0000000..f36d46c --- /dev/null +++ b/rethinkdb-net/QueryTerm/SumGroupAggregateQuery.cs @@ -0,0 +1,34 @@ +using RethinkDb.Spec; +using System; +using System.Linq.Expressions; + +namespace RethinkDb.QueryTerm +{ + public class SumGroupAggregateQuery : IGroupingQuery + { + private readonly IGroupingQuery groupingQuery; + private readonly Expression> field; + + public SumGroupAggregateQuery(IGroupingQuery groupingQuery, Expression> field) + { + this.groupingQuery = groupingQuery; + this.field = field; + } + + public Term GenerateTerm(IDatumConverterFactory datumConverterFactory) + { + var term = new Term() + { + type = Term.TermType.SUM, + }; + term.args.Add(groupingQuery.GenerateTerm(datumConverterFactory)); + if (field != null) + { + if (field.NodeType != ExpressionType.Lambda) + throw new NotSupportedException("Unsupported expression type"); + term.args.Add(ExpressionUtils.CreateFunctionTerm(datumConverterFactory, field)); + } + return term; + } + } +} \ No newline at end of file diff --git a/rethinkdb-net/rethinkdb-net.csproj b/rethinkdb-net/rethinkdb-net.csproj index 804f634..affcc11 100644 --- a/rethinkdb-net/rethinkdb-net.csproj +++ b/rethinkdb-net/rethinkdb-net.csproj @@ -181,6 +181,8 @@ + + From c72953569337f6a8271e511930479815ba8fbed3 Mon Sep 17 00:00:00 2001 From: Mathieu Fenniak Date: Mon, 20 Oct 2014 13:46:46 -0600 Subject: [PATCH 10/17] Make GroupingTests do setup in test fixture setup --- rethinkdb-net-test/Integration/GroupingTests.cs | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/rethinkdb-net-test/Integration/GroupingTests.cs b/rethinkdb-net-test/Integration/GroupingTests.cs index 5736f70..a0ea0c9 100644 --- a/rethinkdb-net-test/Integration/GroupingTests.cs +++ b/rethinkdb-net-test/Integration/GroupingTests.cs @@ -14,16 +14,12 @@ public class GroupingTests : TestBase public override void TestFixtureSetUp() { base.TestFixtureSetUp(); - connection.RunAsync(Query.DbCreate("test")).Wait(); - connection.RunAsync(Query.Db("test").TableCreate("table")).Wait(); - } + connection.Run(Query.DbCreate("test")); + connection.Run(Query.Db("test").TableCreate("table")); - [SetUp] - public virtual void SetUp() - { testTable = Query.Db("test").Table("table"); connection.Run(testTable.Insert(new TestObject[] - { + { new TestObject() { Name = "1", SomeNumber = 1 }, new TestObject() { Name = "1", SomeNumber = 1 }, new TestObject() { Name = "2", SomeNumber = 2, Tags = new string[] { "A", "B" } }, @@ -40,13 +36,6 @@ public virtual void SetUp() connection.Run(testTable.IndexCreate("name", to => to.Name)); } - [TearDown] - public virtual void TearDown() - { - connection.Run(testTable.IndexDrop("name")); - connection.Run(testTable.Delete()); - } - [Test] public void GroupByIndex() { From bb1e25838d3f6dcd05d18d7ca6215479e7ec621a Mon Sep 17 00:00:00 2001 From: Mathieu Fenniak Date: Mon, 20 Oct 2014 13:48:07 -0600 Subject: [PATCH 11/17] Count aggregate support for both grouped & ungrouped queries Note that the behaviour of Count when no records match the predicate in the grouped query is surprising. I've written to the rethinkdb Google Group for confirmation that the behaviour is as-designed. --- .../Integration/GroupingTests.cs | 39 ++++++++++++++++++- rethinkdb-net/Query.cs | 4 +- .../QueryTerm/CountAggregateQuery.cs | 34 ++++++++++++++++ .../QueryTerm/CountGroupAggregateQuery.cs | 34 ++++++++++++++++ rethinkdb-net/QueryTerm/CountQuery.cs | 25 ------------ rethinkdb-net/rethinkdb-net.csproj | 3 +- 6 files changed, 109 insertions(+), 30 deletions(-) create mode 100644 rethinkdb-net/QueryTerm/CountAggregateQuery.cs create mode 100644 rethinkdb-net/QueryTerm/CountGroupAggregateQuery.cs delete mode 100644 rethinkdb-net/QueryTerm/CountQuery.cs diff --git a/rethinkdb-net-test/Integration/GroupingTests.cs b/rethinkdb-net-test/Integration/GroupingTests.cs index a0ea0c9..756d6f1 100644 --- a/rethinkdb-net-test/Integration/GroupingTests.cs +++ b/rethinkdb-net-test/Integration/GroupingTests.cs @@ -418,13 +418,48 @@ public void SumAggregate() [Test] public void GroupAndCountAggregate() { - throw new NotImplementedException(); + var query = testTable.Group(to => to.Name).Count(to => to.SomeNumber > 1); + + int count = 0; + foreach (var record in connection.Run(query)) + { + var groupName = record.Key; + var objectCount = record.Value; + + switch (groupName) + { + // Surprisingly missing; https://groups.google.com/forum/#!topic/rethinkdb/HXCHeTthF64 + //case "1": + // Assert.That(objectCount, Is.EqualTo(0)); + // break; + case "3": + case "6": + Assert.That(objectCount, Is.EqualTo(2)); + break; + case "2": + Assert.That(objectCount, Is.EqualTo(3)); + break; + case "4": + case "5": + case "7": + Assert.That(objectCount, Is.EqualTo(1)); + break; + default: + Assert.Fail("Unexpected group name: {0}", groupName); + break; + } + + ++count; + } + + Assert.That(count, Is.EqualTo(6)); } [Test] public void CountAggregate() { - throw new NotImplementedException(); + var count = connection.Run(testTable.Count(to => to.SomeNumber > 1)); + Assert.That(count, Is.EqualTo(10)); } [Test] diff --git a/rethinkdb-net/Query.cs b/rethinkdb-net/Query.cs index 98278b0..35cb262 100644 --- a/rethinkdb-net/Query.cs +++ b/rethinkdb-net/Query.cs @@ -412,7 +412,7 @@ public static IGroupingQuery Count( Expression> predicate = null ) { - throw new NotImplementedException(); + return new CountGroupAggregateQuery(groupingQuery, predicate); } public static ISingleObjectQuery Count( @@ -420,7 +420,7 @@ public static ISingleObjectQuery Count( Expression> predicate = null ) { - return new CountQuery(target); + return new CountAggregateQuery(target, predicate); } public static IGroupingQuery Contains( diff --git a/rethinkdb-net/QueryTerm/CountAggregateQuery.cs b/rethinkdb-net/QueryTerm/CountAggregateQuery.cs new file mode 100644 index 0000000..62da6ac --- /dev/null +++ b/rethinkdb-net/QueryTerm/CountAggregateQuery.cs @@ -0,0 +1,34 @@ +using RethinkDb.Spec; +using System; +using System.Linq.Expressions; + +namespace RethinkDb.QueryTerm +{ + public class CountAggregateQuery : ISingleObjectQuery + { + private readonly ISequenceQuery sequenceQuery; + private readonly Expression> predicate; + + public CountAggregateQuery(ISequenceQuery sequenceQuery, Expression> predicate = null) + { + this.sequenceQuery = sequenceQuery; + this.predicate = predicate; + } + + public Term GenerateTerm(IDatumConverterFactory datumConverterFactory) + { + var countTerm = new Term() + { + type = Term.TermType.COUNT, + }; + countTerm.args.Add(sequenceQuery.GenerateTerm(datumConverterFactory)); + if (predicate != null) + { + if (predicate.NodeType != ExpressionType.Lambda) + throw new NotSupportedException("Unsupported expression type"); + countTerm.args.Add(ExpressionUtils.CreateFunctionTerm(datumConverterFactory, predicate)); + } + return countTerm; + } + } +} diff --git a/rethinkdb-net/QueryTerm/CountGroupAggregateQuery.cs b/rethinkdb-net/QueryTerm/CountGroupAggregateQuery.cs new file mode 100644 index 0000000..e921547 --- /dev/null +++ b/rethinkdb-net/QueryTerm/CountGroupAggregateQuery.cs @@ -0,0 +1,34 @@ +using RethinkDb.Spec; +using System; +using System.Linq.Expressions; + +namespace RethinkDb.QueryTerm +{ + public class CountGroupAggregateQuery : IGroupingQuery + { + private readonly IGroupingQuery groupingQuery; + private readonly Expression> predicate; + + public CountGroupAggregateQuery(IGroupingQuery groupingQuery, Expression> predicate) + { + this.groupingQuery = groupingQuery; + this.predicate = predicate; + } + + public Term GenerateTerm(IDatumConverterFactory datumConverterFactory) + { + var term = new Term() + { + type = Term.TermType.COUNT, + }; + term.args.Add(groupingQuery.GenerateTerm(datumConverterFactory)); + if (predicate != null) + { + if (predicate.NodeType != ExpressionType.Lambda) + throw new NotSupportedException("Unsupported expression type"); + term.args.Add(ExpressionUtils.CreateFunctionTerm(datumConverterFactory, predicate)); + } + return term; + } + } +} diff --git a/rethinkdb-net/QueryTerm/CountQuery.cs b/rethinkdb-net/QueryTerm/CountQuery.cs deleted file mode 100644 index 1917fb8..0000000 --- a/rethinkdb-net/QueryTerm/CountQuery.cs +++ /dev/null @@ -1,25 +0,0 @@ -using RethinkDb.Spec; -using System; - -namespace RethinkDb.QueryTerm -{ - public class CountQuery : ISingleObjectQuery - { - private readonly ISequenceQuery sequenceQuery; - - public CountQuery(ISequenceQuery sequenceQuery) - { - this.sequenceQuery = sequenceQuery; - } - - public Term GenerateTerm(IDatumConverterFactory datumConverterFactory) - { - var countTerm = new Term() - { - type = Term.TermType.COUNT, - }; - countTerm.args.Add(sequenceQuery.GenerateTerm(datumConverterFactory)); - return countTerm; - } - } -} \ No newline at end of file diff --git a/rethinkdb-net/rethinkdb-net.csproj b/rethinkdb-net/rethinkdb-net.csproj index affcc11..3fd80ca 100644 --- a/rethinkdb-net/rethinkdb-net.csproj +++ b/rethinkdb-net/rethinkdb-net.csproj @@ -71,7 +71,6 @@ - @@ -183,6 +182,8 @@ + + From 36db00001f7e8321728d939b10532af87267e6e5 Mon Sep 17 00:00:00 2001 From: Mathieu Fenniak Date: Mon, 20 Oct 2014 13:54:24 -0600 Subject: [PATCH 12/17] Contains aggregate support for both grouped & ungrouped queries --- .../Integration/GroupingTests.cs | 38 +++++++++++++++++-- rethinkdb-net/Query.cs | 10 ++++- .../QueryTerm/ContainsAggregateQuery.cs | 34 +++++++++++++++++ .../QueryTerm/ContainsGroupAggregateQuery.cs | 34 +++++++++++++++++ rethinkdb-net/rethinkdb-net.csproj | 2 + 5 files changed, 114 insertions(+), 4 deletions(-) create mode 100644 rethinkdb-net/QueryTerm/ContainsAggregateQuery.cs create mode 100644 rethinkdb-net/QueryTerm/ContainsGroupAggregateQuery.cs diff --git a/rethinkdb-net-test/Integration/GroupingTests.cs b/rethinkdb-net-test/Integration/GroupingTests.cs index 756d6f1..ef23786 100644 --- a/rethinkdb-net-test/Integration/GroupingTests.cs +++ b/rethinkdb-net-test/Integration/GroupingTests.cs @@ -19,7 +19,7 @@ public override void TestFixtureSetUp() testTable = Query.Db("test").Table("table"); connection.Run(testTable.Insert(new TestObject[] - { + { new TestObject() { Name = "1", SomeNumber = 1 }, new TestObject() { Name = "1", SomeNumber = 1 }, new TestObject() { Name = "2", SomeNumber = 2, Tags = new string[] { "A", "B" } }, @@ -465,13 +465,45 @@ public void CountAggregate() [Test] public void GroupAndContainsAggregate() { - throw new NotImplementedException(); + var query = testTable.Group(to => to.Name).Contains(to => to.SomeNumber > 1); + + int count = 0; + foreach (var record in connection.Run(query)) + { + var groupName = record.Key; + var predicateResult = record.Value; + + switch (groupName) + { + case "1": + Assert.That(predicateResult, Is.False); + break; + case "3": + case "2": + case "4": + case "5": + case "6": + case "7": + Assert.That(predicateResult, Is.True); + break; + default: + Assert.Fail("Unexpected group name: {0}", groupName); + break; + } + + ++count; + } + + Assert.That(count, Is.EqualTo(7)); } [Test] public void ContainsAggregate() { - throw new NotImplementedException(); + var contains = connection.Run(testTable.Contains(to => to.SomeNumber > 1)); + Assert.That(contains, Is.True); + contains = connection.Run(testTable.Contains(to => to.SomeNumber > 1000)); + Assert.That(contains, Is.False); } #if false diff --git a/rethinkdb-net/Query.cs b/rethinkdb-net/Query.cs index 35cb262..06fc64b 100644 --- a/rethinkdb-net/Query.cs +++ b/rethinkdb-net/Query.cs @@ -428,7 +428,15 @@ public static IGroupingQuery Contains( Expression> predicate = null ) { - throw new NotImplementedException(); + return new ContainsGroupAggregateQuery(groupingQuery, predicate); + } + + public static ISingleObjectQuery Contains( + this ISequenceQuery target, + Expression> predicate = null + ) + { + return new ContainsAggregateQuery(target, predicate); } #endregion diff --git a/rethinkdb-net/QueryTerm/ContainsAggregateQuery.cs b/rethinkdb-net/QueryTerm/ContainsAggregateQuery.cs new file mode 100644 index 0000000..74e04b1 --- /dev/null +++ b/rethinkdb-net/QueryTerm/ContainsAggregateQuery.cs @@ -0,0 +1,34 @@ +using RethinkDb.Spec; +using System; +using System.Linq.Expressions; + +namespace RethinkDb.QueryTerm +{ + public class ContainsAggregateQuery : ISingleObjectQuery + { + private readonly ISequenceQuery sequenceQuery; + private readonly Expression> predicate; + + public ContainsAggregateQuery(ISequenceQuery sequenceQuery, Expression> predicate = null) + { + this.sequenceQuery = sequenceQuery; + this.predicate = predicate; + } + + public Term GenerateTerm(IDatumConverterFactory datumConverterFactory) + { + var countTerm = new Term() + { + type = Term.TermType.CONTAINS, + }; + countTerm.args.Add(sequenceQuery.GenerateTerm(datumConverterFactory)); + if (predicate != null) + { + if (predicate.NodeType != ExpressionType.Lambda) + throw new NotSupportedException("Unsupported expression type"); + countTerm.args.Add(ExpressionUtils.CreateFunctionTerm(datumConverterFactory, predicate)); + } + return countTerm; + } + } +} diff --git a/rethinkdb-net/QueryTerm/ContainsGroupAggregateQuery.cs b/rethinkdb-net/QueryTerm/ContainsGroupAggregateQuery.cs new file mode 100644 index 0000000..fb6cfc2 --- /dev/null +++ b/rethinkdb-net/QueryTerm/ContainsGroupAggregateQuery.cs @@ -0,0 +1,34 @@ +using RethinkDb.Spec; +using System; +using System.Linq.Expressions; + +namespace RethinkDb.QueryTerm +{ + public class ContainsGroupAggregateQuery : IGroupingQuery + { + private readonly IGroupingQuery groupingQuery; + private readonly Expression> predicate; + + public ContainsGroupAggregateQuery(IGroupingQuery groupingQuery, Expression> predicate) + { + this.groupingQuery = groupingQuery; + this.predicate = predicate; + } + + public Term GenerateTerm(IDatumConverterFactory datumConverterFactory) + { + var term = new Term() + { + type = Term.TermType.CONTAINS, + }; + term.args.Add(groupingQuery.GenerateTerm(datumConverterFactory)); + if (predicate != null) + { + if (predicate.NodeType != ExpressionType.Lambda) + throw new NotSupportedException("Unsupported expression type"); + term.args.Add(ExpressionUtils.CreateFunctionTerm(datumConverterFactory, predicate)); + } + return term; + } + } +} diff --git a/rethinkdb-net/rethinkdb-net.csproj b/rethinkdb-net/rethinkdb-net.csproj index 3fd80ca..2b14653 100644 --- a/rethinkdb-net/rethinkdb-net.csproj +++ b/rethinkdb-net/rethinkdb-net.csproj @@ -184,6 +184,8 @@ + + From 15fe8ec8bf7b27120b993f1a6607a662384f70b3 Mon Sep 17 00:00:00 2001 From: Mathieu Fenniak Date: Mon, 20 Oct 2014 14:06:46 -0600 Subject: [PATCH 13/17] Cleanup commented out code from obsolete group-by support --- .../Integration/CoreIntegrationTests.cs | 3 +- .../Integration/GroupingTests.cs | 197 ------------------ .../Integration/MultiObjectTests.cs | 13 +- rethinkdb-net/QueryTerm/AvgReduction.cs | 61 ------ rethinkdb-net/QueryTerm/CountReduction.cs | 41 ---- rethinkdb-net/QueryTerm/GroupByQuery.cs | 102 --------- .../QueryTerm/GroupedMapReduceQuery.cs | 61 ------ rethinkdb-net/QueryTerm/SumReduction.cs | 61 ------ rethinkdb-net/rethinkdb-net.csproj | 5 - 9 files changed, 5 insertions(+), 539 deletions(-) delete mode 100644 rethinkdb-net/QueryTerm/AvgReduction.cs delete mode 100644 rethinkdb-net/QueryTerm/CountReduction.cs delete mode 100644 rethinkdb-net/QueryTerm/GroupByQuery.cs delete mode 100644 rethinkdb-net/QueryTerm/GroupedMapReduceQuery.cs delete mode 100644 rethinkdb-net/QueryTerm/SumReduction.cs diff --git a/rethinkdb-net-newtonsoft-test/Integration/CoreIntegrationTests.cs b/rethinkdb-net-newtonsoft-test/Integration/CoreIntegrationTests.cs index f6fcacd..9059579 100644 --- a/rethinkdb-net-newtonsoft-test/Integration/CoreIntegrationTests.cs +++ b/rethinkdb-net-newtonsoft-test/Integration/CoreIntegrationTests.cs @@ -73,7 +73,7 @@ static NDatabaseTests() } } -#if false + [TestFixture] public class NGroupingTests : GroupingTests { @@ -82,7 +82,6 @@ static NGroupingTests() ConnectionFactory = ConfigurationAssembler.CreateConnectionFactory("testCluster"); } } -#endif [TestFixture] diff --git a/rethinkdb-net-test/Integration/GroupingTests.cs b/rethinkdb-net-test/Integration/GroupingTests.cs index ef23786..cbfb215 100644 --- a/rethinkdb-net-test/Integration/GroupingTests.cs +++ b/rethinkdb-net-test/Integration/GroupingTests.cs @@ -505,202 +505,5 @@ public void ContainsAggregate() contains = connection.Run(testTable.Contains(to => to.SomeNumber > 1000)); Assert.That(contains, Is.False); } - -#if false - [Test] - public void GroupedMapReduce() - { - var query = testTable.GroupedMapReduce( - to => to.Name, // group - to => 1.0, // map - (leftCount, rightCount) => leftCount + rightCount // reduce - ); - - int count = 0; - foreach (var record in connection.Run(query)) - { - var groupName = record.Item1; - var reduceCount = record.Item2; - - switch (groupName) - { - case "1": - case "3": - case "6": - Assert.That(reduceCount, Is.EqualTo(2)); - break; - case "2": - Assert.That(reduceCount, Is.EqualTo(3)); - break; - case "4": - case "5": - case "7": - Assert.That(reduceCount, Is.EqualTo(1)); - break; - } - - ++count; - } - - Assert.That(count, Is.EqualTo(7)); - } - - [Test] - public void GroupByCount() - { - // Same query and results as GroupedMapReduce test - var query = testTable.GroupBy(Query.Count(), to => new { name = to.Name }); - - int count = 0; - foreach (var record in connection.Run(query)) - { - var groupName = record.Item1.name; - var reduceCount = record.Item2; - - switch (groupName) - { - case "1": - case "3": - case "6": - Assert.That(reduceCount, Is.EqualTo(2)); - break; - case "2": - Assert.That(reduceCount, Is.EqualTo(3)); - break; - case "4": - case "5": - case "7": - Assert.That(reduceCount, Is.EqualTo(1)); - break; - } - - ++count; - } - - Assert.That(count, Is.EqualTo(7)); - } - - [Test] - public void GroupByTwoParams() - { - var query = testTable.GroupBy(Query.Count(), to => new { name = to.Name, number = to.SomeNumber }); - - int count = 0; - foreach (var record in connection.Run(query)) - { - var groupName = record.Item1.name; - var someNumber = record.Item1.number; - var reduceCount = record.Item2; - - switch (groupName) - { - case "1": - case "3": - case "6": - Assert.That(reduceCount, Is.EqualTo(2)); - break; - case "2": - if (someNumber == 2) - Assert.That(reduceCount, Is.EqualTo(2)); - else if (someNumber == 200) - Assert.That(reduceCount, Is.EqualTo(1)); - break; - case "4": - case "5": - case "7": - Assert.That(reduceCount, Is.EqualTo(1)); - break; - } - - ++count; - } - - Assert.That(count, Is.EqualTo(8)); - } - - [Test] - public void GroupBySum() - { - var query = testTable.GroupBy(Query.Sum(to => to.SomeNumber), to => new { name = to.Name }); - - int count = 0; - foreach (var record in connection.Run(query)) - { - var groupName = record.Item1.name; - var reduceSum = record.Item2; - - switch (groupName) - { - case "1": - Assert.That(reduceSum, Is.EqualTo(2)); - break; - case "2": - Assert.That(reduceSum, Is.EqualTo(204)); - break; - case "3": - Assert.That(reduceSum, Is.EqualTo(6)); - break; - case "4": - Assert.That(reduceSum, Is.EqualTo(4)); - break; - case "5": - Assert.That(reduceSum, Is.EqualTo(5)); - break; - case "6": - Assert.That(reduceSum, Is.EqualTo(12)); - break; - case "7": - Assert.That(reduceSum, Is.EqualTo(7)); - break; - } - - ++count; - } - - Assert.That(count, Is.EqualTo(7)); - } - - [Test] - public void GroupByAvg() - { - var query = testTable.GroupBy(Query.Avg(to => to.SomeNumber), to => new { Name = to.Name }); - - int count = 0; - foreach (var record in connection.Run(query)) - { - var groupName = record.Item1.Name; - var reduceSum = record.Item2; - - switch (groupName) - { - case "1": - Assert.That(reduceSum, Is.EqualTo(1)); - break; - case "2": - Assert.That(reduceSum, Is.EqualTo(68)); - break; - case "3": - Assert.That(reduceSum, Is.EqualTo(3)); - break; - case "4": - Assert.That(reduceSum, Is.EqualTo(4)); - break; - case "5": - Assert.That(reduceSum, Is.EqualTo(5)); - break; - case "6": - Assert.That(reduceSum, Is.EqualTo(6)); - break; - case "7": - Assert.That(reduceSum, Is.EqualTo(7)); - break; - } - - ++count; - } - - Assert.That(count, Is.EqualTo(7)); - } - #endif } } diff --git a/rethinkdb-net-test/Integration/MultiObjectTests.cs b/rethinkdb-net-test/Integration/MultiObjectTests.cs index 62242db..a500a6c 100644 --- a/rethinkdb-net-test/Integration/MultiObjectTests.cs +++ b/rethinkdb-net-test/Integration/MultiObjectTests.cs @@ -716,24 +716,19 @@ public void ConcatMapIList() Assert.That(count, Is.EqualTo(4)); } -#if false [Test, Description("Tests that the SingleParameterLambda class can map a Parameter Expression (tag => tag) to a Term")] public void ConcatMap_OnSimpleDataType_CanUseParameterExpressionForQuery() { var query = testTable .ConcatMap(to => to.Tags) - .GroupedMapReduce( - tag => tag, - tag => 1, - (l, r) => l+r); + .Group(tag => tag) + .Count(); var enumerable = connection.Run(query); - Assert.That(enumerable.Count(), Is.EqualTo(2)); - Assert.That(enumerable, Has.Member(Tuple.Create("even", 3))); - Assert.That(enumerable, Has.Member(Tuple.Create("odd", 4))); + Assert.That(enumerable["even"], Is.EqualTo(3)); + Assert.That(enumerable["odd"], Is.EqualTo(4)); } -#endif [Test] public void Union() diff --git a/rethinkdb-net/QueryTerm/AvgReduction.cs b/rethinkdb-net/QueryTerm/AvgReduction.cs deleted file mode 100644 index 77fb6e0..0000000 --- a/rethinkdb-net/QueryTerm/AvgReduction.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using System.Linq.Expressions; -using RethinkDb.DatumConverters; -using RethinkDb.Spec; - -namespace RethinkDb.QueryTerm -{ -#if false - public class AvgReduction : IGroupByReduction - { - private readonly Expression> numericMemberReference; - - public AvgReduction(Expression> numericMemberReference) - { - this.numericMemberReference = numericMemberReference; - } - - public Term GenerateReductionObject(IDatumConverterFactory datumConverterFactory) - { - var retval = new Term() { - type = Term.TermType.MAKE_OBJ - }; - retval.optargs.Add(new Term.AssocPair() { - key = "AVG", - val = new Term() { - type = Term.TermType.DATUM, - datum = new Datum() { - type = Datum.DatumType.R_STR, - r_str = GetMemberName(datumConverterFactory) - } - } - }); - return retval; - } - - private string GetMemberName(IDatumConverterFactory datumConverterFactory) - { - var datumConverter = datumConverterFactory.Get(); - var fieldConverter = datumConverter as IObjectDatumConverter; - if (fieldConverter == null) - throw new NotSupportedException("Cannot map member access into ReQL without implementing IObjectDatumConverter"); - - if (numericMemberReference.NodeType != ExpressionType.Lambda) - throw new NotSupportedException("Unsupported expression type " + numericMemberReference.Type + "; expected Lambda"); - - var body = ((LambdaExpression)numericMemberReference).Body; - MemberExpression memberExpr; - - if (body.NodeType == ExpressionType.MemberAccess) - memberExpr = (MemberExpression)body; - else - throw new NotSupportedException("Unsupported expression type " + body.NodeType + "; expected MemberAccess"); - - if (memberExpr.Expression.NodeType != ExpressionType.Parameter) - throw new NotSupportedException("Unrecognized member access pattern"); - - return fieldConverter.GetDatumFieldName(memberExpr.Member); - } - } -#endif -} diff --git a/rethinkdb-net/QueryTerm/CountReduction.cs b/rethinkdb-net/QueryTerm/CountReduction.cs deleted file mode 100644 index 14c9f89..0000000 --- a/rethinkdb-net/QueryTerm/CountReduction.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using RethinkDb.Spec; - -namespace RethinkDb.QueryTerm -{ -#if false - public class CountReduction : IGroupByReduction - { - public static readonly CountReduction Instance = new CountReduction(); - private Term retval; - - private CountReduction() - { - } - - public Term GenerateReductionObject(IDatumConverterFactory datumConverterFactory) - { - if (retval == null) - { - var newValue = new Term() { - type = Term.TermType.MAKE_OBJ - }; - newValue.optargs.Add(new Term.AssocPair() { - key = "COUNT", - val = new Term() { - type = Term.TermType.DATUM, - datum = new Datum() { - type = Datum.DatumType.R_BOOL, - r_bool = true - } - } - }); - retval = newValue; - } - - return retval; - } - } -#endif -} - diff --git a/rethinkdb-net/QueryTerm/GroupByQuery.cs b/rethinkdb-net/QueryTerm/GroupByQuery.cs deleted file mode 100644 index 61ffbe4..0000000 --- a/rethinkdb-net/QueryTerm/GroupByQuery.cs +++ /dev/null @@ -1,102 +0,0 @@ -using System; -using System.Linq; -using System.Linq.Expressions; -using System.Collections.Generic; -using RethinkDb.DatumConverters; -using RethinkDb.Spec; - -namespace RethinkDb.QueryTerm -{ -#if false - public abstract class GroupByQueryBase - { - private readonly ISequenceQuery sequenceQuery; - private readonly IGroupByReduction reductionObject; - private readonly Expression groupKeyConstructor; - - protected GroupByQueryBase(ISequenceQuery sequenceQuery, IGroupByReduction reductionObject, Expression groupKeyConstructor) - { - this.sequenceQuery = sequenceQuery; - this.reductionObject = reductionObject; - this.groupKeyConstructor = groupKeyConstructor; - } - - public Term GenerateTerm(IDatumConverterFactory datumConverterFactory) - { - throw new NotSupportedException(); - /* - var term = new Term() - { - type = Term.TermType.GROUPBY, - }; - term.args.Add(sequenceQuery.GenerateTerm(datumConverterFactory)); - - var propertyTerm = new Term() { - type = Term.TermType.MAKE_ARRAY - }; - - if (groupKeyConstructor.NodeType != ExpressionType.Lambda) - throw new NotSupportedException("Unsupported expression type " + groupKeyConstructor.NodeType + "; expected Lambda"); - - var body = ((LambdaExpression)groupKeyConstructor).Body; - if (body.NodeType != ExpressionType.New) - throw new NotSupportedException("GroupByQuery expects an expression in the form of: new { key1 = ...[, keyN = ...] }"); - - var newExpression = (NewExpression)body; - if (!AnonymousTypeDatumConverterFactory.Instance.IsTypeSupported(newExpression.Type)) - throw new NotSupportedException(String.Format("Unsupported type in New expression: {0}; only anonymous types are supported", newExpression.Type)); - - foreach (var property in newExpression.Type.GetProperties().Select((p, i) => new { Property = p, Index = i })) - { - var key = property.Property.Name; - var value = GetMemberName(newExpression.Arguments[property.Index], datumConverterFactory); - if (!String.Equals(key, value.r_str, StringComparison.InvariantCultureIgnoreCase)) - throw new Exception(String.Format("Anonymous type property name ({0}) must equal the member name ({1})", key, value.r_str)); - propertyTerm.args.Add(new Term() { - type = Term.TermType.DATUM, - datum = value, - }); - } - term.args.Add(propertyTerm); - - term.args.Add(reductionObject.GenerateReductionObject(datumConverterFactory)); - - return term; - */ - } - - private Datum GetMemberName(Expression memberReference, IDatumConverterFactory datumConverterFactory) - { - var datumConverter = datumConverterFactory.Get(); - var fieldConverter = datumConverter as IObjectDatumConverter; - if (fieldConverter == null) - throw new NotSupportedException("Cannot map member access into ReQL without implementing IObjectDatumConverter"); - - MemberExpression memberExpr; - if (memberReference.NodeType == ExpressionType.MemberAccess) - memberExpr = (MemberExpression)memberReference; - else - throw new NotSupportedException("Unsupported expression type " + memberReference.NodeType + "; expected MemberAccess"); - - if (memberExpr.Expression.NodeType != ExpressionType.Parameter) - throw new NotSupportedException("Unrecognized member access pattern"); - - return new Datum() { - type = Datum.DatumType.R_STR, - r_str = fieldConverter.GetDatumFieldName(memberExpr.Member) - }; - } - } - - public class GroupByQuery - : GroupByQueryBase, ISequenceQuery> - { - public GroupByQuery(ISequenceQuery sequenceQuery, - IGroupByReduction reductionObject, - Expression> groupKeyConstructor) - : base(sequenceQuery, reductionObject, groupKeyConstructor) - { - } - } -#endif -} diff --git a/rethinkdb-net/QueryTerm/GroupedMapReduceQuery.cs b/rethinkdb-net/QueryTerm/GroupedMapReduceQuery.cs deleted file mode 100644 index 4c721a9..0000000 --- a/rethinkdb-net/QueryTerm/GroupedMapReduceQuery.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using System.Linq.Expressions; -using RethinkDb.DatumConverters; -using RethinkDb.Spec; - -namespace RethinkDb.QueryTerm -{ -#if false - public class GroupedMapReduceQuery : ISequenceQuery> - { - private readonly ISequenceQuery sequenceQuery; - private readonly Expression> grouping; - private readonly Expression> mapping; - private readonly Expression> reduction; - private readonly bool baseProvided; - private readonly TMap @base; - - public GroupedMapReduceQuery(ISequenceQuery sequenceQuery, Expression> grouping, Expression> mapping, Expression> reduction) - { - this.sequenceQuery = sequenceQuery; - this.grouping = grouping; - this.mapping = mapping; - this.reduction = reduction; - } - - public GroupedMapReduceQuery(ISequenceQuery sequenceQuery, Expression> grouping, Expression> mapping, Expression> reduction, TMap @base) - : this(sequenceQuery, grouping, mapping, reduction) - { - this.baseProvided = true; - this.@base = @base; - } - - public Term GenerateTerm (IDatumConverterFactory datumConverterFactory) - { - var retval = new Term() - { - type = Term.TermType.GROUP, - }; - retval.args.Add(sequenceQuery.GenerateTerm(datumConverterFactory)); - retval.args.Add(ExpressionUtils.CreateFunctionTerm(datumConverterFactory, grouping)); - retval.args.Add(ExpressionUtils.CreateFunctionTerm(datumConverterFactory, mapping)); - retval.args.Add(ExpressionUtils.CreateFunctionTerm(datumConverterFactory, reduction)); - - if (this.baseProvided) - { - retval.optargs.Add(new Term.AssocPair() - { - key = "base", - val = new Term() { - type = Term.TermType.DATUM, - datum = datumConverterFactory.Get().ConvertObject(@base) - } - }); - } - - return retval; - } - } -#endif -} - diff --git a/rethinkdb-net/QueryTerm/SumReduction.cs b/rethinkdb-net/QueryTerm/SumReduction.cs deleted file mode 100644 index 984cdf2..0000000 --- a/rethinkdb-net/QueryTerm/SumReduction.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using System.Linq.Expressions; -using RethinkDb.DatumConverters; -using RethinkDb.Spec; - -namespace RethinkDb.QueryTerm -{ -#if false - public class SumReduction : IGroupByReduction - { - private readonly Expression> numericMemberReference; - - public SumReduction(Expression> numericMemberReference) - { - this.numericMemberReference = numericMemberReference; - } - - public Term GenerateReductionObject(IDatumConverterFactory datumConverterFactory) - { - var retval = new Term() { - type = Term.TermType.MAKE_OBJ - }; - retval.optargs.Add(new Term.AssocPair() { - key = "SUM", - val = new Term() { - type = Term.TermType.DATUM, - datum = new Datum() { - type = Datum.DatumType.R_STR, - r_str = GetMemberName(datumConverterFactory) - } - } - }); - return retval; - } - - private string GetMemberName(IDatumConverterFactory datumConverterFactory) - { - var datumConverter = datumConverterFactory.Get(); - var fieldConverter = datumConverter as IObjectDatumConverter; - if (fieldConverter == null) - throw new NotSupportedException("Cannot map member access into ReQL without implementing IObjectDatumConverter"); - - if (numericMemberReference.NodeType != ExpressionType.Lambda) - throw new NotSupportedException("Unsupported expression type " + numericMemberReference.Type + "; expected Lambda"); - - var body = ((LambdaExpression)numericMemberReference).Body; - MemberExpression memberExpr; - - if (body.NodeType == ExpressionType.MemberAccess) - memberExpr = (MemberExpression)body; - else - throw new NotSupportedException("Unsupported expression type " + body.NodeType + "; expected MemberAccess"); - - if (memberExpr.Expression.NodeType != ExpressionType.Parameter) - throw new NotSupportedException("Unrecognized member access pattern"); - - return fieldConverter.GetDatumFieldName(memberExpr.Member); - } - } -#endif -} diff --git a/rethinkdb-net/rethinkdb-net.csproj b/rethinkdb-net/rethinkdb-net.csproj index 2b14653..a750342 100644 --- a/rethinkdb-net/rethinkdb-net.csproj +++ b/rethinkdb-net/rethinkdb-net.csproj @@ -99,14 +99,9 @@ - - - - - From fdcd8e192c98db9aa8d80f70a1bd24f50107f871 Mon Sep 17 00:00:00 2001 From: Mathieu Fenniak Date: Mon, 20 Oct 2014 14:19:37 -0600 Subject: [PATCH 14/17] Use IGroupingDictionary for $reql_type$=GROUPED_DATA IGroupingDictionary is a type that's only supported for read, it's automatically returned from Group() queries and grouped aggregations, and it's also used by rethinkdb-net-newtonsoft because the GROUPED_DATA reql_type has a special format that can't be intrinsically guessed by the JSON parser. IGroupingDictionary<,> is an IDictionary<,> so it shouldn't even be noticed by the consumers. --- .../NewtonsoftDatumConverterFactory.cs | 4 +++ rethinkdb-net/Connection.cs | 2 +- ...roupingDictionaryDatumConverterFactory.cs} | 31 ++++++++++++------- .../Interfaces/IGroupingDictionary.cs | 12 +++++++ rethinkdb-net/Interfaces/IGroupingQuery.cs | 4 +-- rethinkdb-net/rethinkdb-net.csproj | 3 +- 6 files changed, 40 insertions(+), 16 deletions(-) rename rethinkdb-net/DatumConverters/{DictionaryDatumConverterFactory.cs => GroupingDictionaryDatumConverterFactory.cs} (69%) create mode 100644 rethinkdb-net/Interfaces/IGroupingDictionary.cs diff --git a/rethinkdb-net-newtonsoft/Configuration/NewtonsoftDatumConverterFactory.cs b/rethinkdb-net-newtonsoft/Configuration/NewtonsoftDatumConverterFactory.cs index 9fcba51..18f23bf 100644 --- a/rethinkdb-net-newtonsoft/Configuration/NewtonsoftDatumConverterFactory.cs +++ b/rethinkdb-net-newtonsoft/Configuration/NewtonsoftDatumConverterFactory.cs @@ -9,6 +9,10 @@ public class NewtonsoftDatumConverterFactory : AbstractDatumConverterFactory public override bool TryGet(IDatumConverterFactory rootDatumConverterFactory, out IDatumConverter datumConverter) { + // Use rethinkdb-net's support for $reql_type$=GROUPED_DATA return values. + if (GroupingDictionaryDatumConverterFactory.Instance.TryGet(rootDatumConverterFactory, out datumConverter)) + return true; + //I guess we could have some more specific checks here //but if we get here last in the NewtonsoftSerializer order, then //I suppose we can handle it if no preceding converters could handle it. diff --git a/rethinkdb-net/Connection.cs b/rethinkdb-net/Connection.cs index 9f70355..c79a200 100644 --- a/rethinkdb-net/Connection.cs +++ b/rethinkdb-net/Connection.cs @@ -42,7 +42,7 @@ public Connection() NullableDatumConverterFactory.Instance, ListDatumConverterFactory.Instance, TimeSpanDatumConverterFactory.Instance, - DictionaryDatumConverterFactory.Instance + GroupingDictionaryDatumConverterFactory.Instance ); ConnectTimeout = QueryTimeout = TimeSpan.FromSeconds(30); } diff --git a/rethinkdb-net/DatumConverters/DictionaryDatumConverterFactory.cs b/rethinkdb-net/DatumConverters/GroupingDictionaryDatumConverterFactory.cs similarity index 69% rename from rethinkdb-net/DatumConverters/DictionaryDatumConverterFactory.cs rename to rethinkdb-net/DatumConverters/GroupingDictionaryDatumConverterFactory.cs index 0cdac33..cf06eb8 100644 --- a/rethinkdb-net/DatumConverters/DictionaryDatumConverterFactory.cs +++ b/rethinkdb-net/DatumConverters/GroupingDictionaryDatumConverterFactory.cs @@ -5,11 +5,12 @@ namespace RethinkDb.DatumConverters { - public class DictionaryDatumConverterFactory : AbstractDatumConverterFactory + // A special datum converter to support RethinkDB's $reql_type$ = GROUPED_DATA return values. + public class GroupingDictionaryDatumConverterFactory : AbstractDatumConverterFactory { - public static readonly DictionaryDatumConverterFactory Instance = new DictionaryDatumConverterFactory(); + public static readonly GroupingDictionaryDatumConverterFactory Instance = new GroupingDictionaryDatumConverterFactory(); - private DictionaryDatumConverterFactory() + private GroupingDictionaryDatumConverterFactory() { } @@ -19,9 +20,9 @@ public override bool TryGet(IDatumConverterFactory rootDatumConverterFactory, if (rootDatumConverterFactory == null) throw new ArgumentNullException("rootDatumConverterFactory"); - if (typeof(T).IsGenericType && typeof(T).GetGenericTypeDefinition() == typeof(IDictionary<,>)) + if (typeof(T).IsGenericType && typeof(T).GetGenericTypeDefinition() == typeof(IGroupingDictionary<,>)) { - Type converterType = typeof(DictionaryDatumConverter<,>).MakeGenericType(typeof(T).GetGenericArguments()); + Type converterType = typeof(GroupingDictionaryDatumConverter<,>).MakeGenericType(typeof(T).GetGenericArguments()); datumConverter = (IDatumConverter)Activator.CreateInstance(converterType, rootDatumConverterFactory); return true; } @@ -30,12 +31,12 @@ public override bool TryGet(IDatumConverterFactory rootDatumConverterFactory, } } - public class DictionaryDatumConverter : AbstractReferenceTypeDatumConverter> + public class GroupingDictionaryDatumConverter : AbstractReferenceTypeDatumConverter> { private readonly IDatumConverter keyTypeConverter; private readonly IDatumConverter valueTypeConverter; - public DictionaryDatumConverter(IDatumConverterFactory rootDatumConverterFactory) + public GroupingDictionaryDatumConverter(IDatumConverterFactory rootDatumConverterFactory) { this.keyTypeConverter = rootDatumConverterFactory.Get(); this.valueTypeConverter = rootDatumConverterFactory.Get(); @@ -43,7 +44,7 @@ public DictionaryDatumConverter(IDatumConverterFactory rootDatumConverterFactory #region IDatumConverter Members - public override IDictionary ConvertDatum(Datum datum) + public override IGroupingDictionary ConvertDatum(Datum datum) { if (datum.type == Datum.DatumType.R_NULL) { @@ -65,7 +66,7 @@ public override IDictionary ConvertDatum(Datum datum) if (dataDatum.type != Datum.DatumType.R_ARRAY) throw new NotSupportedException("Object's data key must be an array type"); - var retval = new Dictionary(dataDatum.r_array.Count); + var retval = new GroupingDictionary(dataDatum.r_array.Count); foreach (var item in dataDatum.r_array) { if (item.type != Datum.DatumType.R_ARRAY || item.r_array.Count != 2) @@ -83,13 +84,21 @@ public override IDictionary ConvertDatum(Datum datum) } } - public override Spec.Datum ConvertObject(IDictionary dictionary) + public override Spec.Datum ConvertObject(IGroupingDictionary dictionary) { //if (dictionary == null) // return new Spec.Datum() { type = Spec.Datum.DatumType.R_NULL }; - throw new NotImplementedException("IDictionary objects are only currently supported for reading Group results"); + throw new NotImplementedException("IGroupingDictionary objects are only currently supported for reading Group results"); } #endregion } + + class GroupingDictionary : Dictionary, IGroupingDictionary + { + public GroupingDictionary(int capacity) + : base(capacity) + { + } + } } diff --git a/rethinkdb-net/Interfaces/IGroupingDictionary.cs b/rethinkdb-net/Interfaces/IGroupingDictionary.cs new file mode 100644 index 0000000..902ced5 --- /dev/null +++ b/rethinkdb-net/Interfaces/IGroupingDictionary.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; + +namespace RethinkDb +{ + // This interface is used to support $reql_type$=GROUPED_DATA being returned from the server. In order to use + // grouping capabilities (eg. .Group() queries), a datum converter must always be registered to read this type + // from the server. GroupingDictionaryDatumConverterFactory is provided to support this. + public interface IGroupingDictionary : IDictionary + { + } +} diff --git a/rethinkdb-net/Interfaces/IGroupingQuery.cs b/rethinkdb-net/Interfaces/IGroupingQuery.cs index 6181f2c..ef3d19f 100644 --- a/rethinkdb-net/Interfaces/IGroupingQuery.cs +++ b/rethinkdb-net/Interfaces/IGroupingQuery.cs @@ -1,10 +1,8 @@ using System; -using System.Collections.Generic; -using RethinkDb.Spec; namespace RethinkDb { - public interface IGroupingQuery : IScalarQuery> + public interface IGroupingQuery : IScalarQuery> { } } diff --git a/rethinkdb-net/rethinkdb-net.csproj b/rethinkdb-net/rethinkdb-net.csproj index a750342..3f6b77c 100644 --- a/rethinkdb-net/rethinkdb-net.csproj +++ b/rethinkdb-net/rethinkdb-net.csproj @@ -166,7 +166,6 @@ - @@ -181,6 +180,8 @@ + + From 07d086904a3b4abed7bc59fb43afc13c6dbc31a6 Mon Sep 17 00:00:00 2001 From: Mathieu Fenniak Date: Mon, 20 Oct 2014 14:46:07 -0600 Subject: [PATCH 15/17] Support IGroupingQuery.Ungroup() --- .../Integration/GroupingTests.cs | 20 ++++++++++++++ rethinkdb-net/Query.cs | 5 ++++ rethinkdb-net/QueryTerm/UngroupQuery.cs | 26 +++++++++++++++++++ rethinkdb-net/UngroupObject.cs | 15 +++++++++++ rethinkdb-net/rethinkdb-net.csproj | 2 ++ 5 files changed, 68 insertions(+) create mode 100644 rethinkdb-net/QueryTerm/UngroupQuery.cs create mode 100644 rethinkdb-net/UngroupObject.cs diff --git a/rethinkdb-net-test/Integration/GroupingTests.cs b/rethinkdb-net-test/Integration/GroupingTests.cs index cbfb215..0e79068 100644 --- a/rethinkdb-net-test/Integration/GroupingTests.cs +++ b/rethinkdb-net-test/Integration/GroupingTests.cs @@ -505,5 +505,25 @@ public void ContainsAggregate() contains = connection.Run(testTable.Contains(to => to.SomeNumber > 1000)); Assert.That(contains, Is.False); } + + [Test] + public void Ungroup() + { + var query = testTable + .Group(to => to.Name) + .Count() + .Ungroup() + .OrderBy(t => t.Reduction).ThenBy(t => t.Group); + + var result = connection.Run(query).ToArray(); + Assert.That(result, Has.Length.EqualTo(7)); + Assert.That(result[0].Group, Is.EqualTo("4")); Assert.That(result[0].Reduction, Is.EqualTo(1)); + Assert.That(result[1].Group, Is.EqualTo("5")); Assert.That(result[1].Reduction, Is.EqualTo(1)); + Assert.That(result[2].Group, Is.EqualTo("7")); Assert.That(result[2].Reduction, Is.EqualTo(1)); + Assert.That(result[3].Group, Is.EqualTo("1")); Assert.That(result[3].Reduction, Is.EqualTo(2)); + Assert.That(result[4].Group, Is.EqualTo("3")); Assert.That(result[4].Reduction, Is.EqualTo(2)); + Assert.That(result[5].Group, Is.EqualTo("6")); Assert.That(result[5].Reduction, Is.EqualTo(2)); + Assert.That(result[6].Group, Is.EqualTo("2")); Assert.That(result[6].Reduction, Is.EqualTo(3)); + } } } diff --git a/rethinkdb-net/Query.cs b/rethinkdb-net/Query.cs index 06fc64b..5a776a7 100644 --- a/rethinkdb-net/Query.cs +++ b/rethinkdb-net/Query.cs @@ -343,6 +343,11 @@ Expression> key3 return new GroupByFunctionQuery(sequenceQuery, key1, key2, key3); } + public static ISequenceQuery> Ungroup(this IGroupingQuery groupingQuery) + { + return new UngroupQuery(groupingQuery); + } + public static IGroupingQuery Min( this IGroupingQuery groupingQuery, Expression> field = null diff --git a/rethinkdb-net/QueryTerm/UngroupQuery.cs b/rethinkdb-net/QueryTerm/UngroupQuery.cs new file mode 100644 index 0000000..c420e0b --- /dev/null +++ b/rethinkdb-net/QueryTerm/UngroupQuery.cs @@ -0,0 +1,26 @@ +using RethinkDb.Spec; +using System; +using System.Linq.Expressions; + +namespace RethinkDb.QueryTerm +{ + public class UngroupQuery : ISequenceQuery> + { + private readonly IGroupingQuery groupingQuery; + + public UngroupQuery(IGroupingQuery groupingQuery) + { + this.groupingQuery = groupingQuery; + } + + public Term GenerateTerm(IDatumConverterFactory datumConverterFactory) + { + var term = new Term() + { + type = Term.TermType.UNGROUP, + }; + term.args.Add(groupingQuery.GenerateTerm(datumConverterFactory)); + return term; + } + } +} diff --git a/rethinkdb-net/UngroupObject.cs b/rethinkdb-net/UngroupObject.cs new file mode 100644 index 0000000..0fb9f5a --- /dev/null +++ b/rethinkdb-net/UngroupObject.cs @@ -0,0 +1,15 @@ +using System; +using System.Runtime.Serialization; + +namespace RethinkDb +{ + [DataContract] + public class UngroupObject + { + [DataMember(Name = "group")] + public TGroup Group; + + [DataMember(Name = "reduction")] + public TReduction Reduction; + } +} diff --git a/rethinkdb-net/rethinkdb-net.csproj b/rethinkdb-net/rethinkdb-net.csproj index 3f6b77c..b0c8695 100644 --- a/rethinkdb-net/rethinkdb-net.csproj +++ b/rethinkdb-net/rethinkdb-net.csproj @@ -182,6 +182,8 @@ + + From 9c79abad197d5e36e7ac594b1bf3c14b66062e93 Mon Sep 17 00:00:00 2001 From: Mathieu Fenniak Date: Mon, 20 Oct 2014 15:14:14 -0600 Subject: [PATCH 16/17] Support .Map() and .Reduce() on IGroupingQuery Allows user-defined aggregations using grouped-map-reduce, rather than just the RethinkDB pretty supported aggregations. --- .../Integration/GroupingTests.cs | 49 +++++++++++++++++++ rethinkdb-net/Query.cs | 14 ++++++ rethinkdb-net/QueryTerm/MapGroupQuery.cs | 29 +++++++++++ rethinkdb-net/QueryTerm/ReduceGroupQuery.cs | 30 ++++++++++++ rethinkdb-net/rethinkdb-net.csproj | 2 + 5 files changed, 124 insertions(+) create mode 100644 rethinkdb-net/QueryTerm/MapGroupQuery.cs create mode 100644 rethinkdb-net/QueryTerm/ReduceGroupQuery.cs diff --git a/rethinkdb-net-test/Integration/GroupingTests.cs b/rethinkdb-net-test/Integration/GroupingTests.cs index 0e79068..f601bcc 100644 --- a/rethinkdb-net-test/Integration/GroupingTests.cs +++ b/rethinkdb-net-test/Integration/GroupingTests.cs @@ -525,5 +525,54 @@ public void Ungroup() Assert.That(result[5].Group, Is.EqualTo("6")); Assert.That(result[5].Reduction, Is.EqualTo(2)); Assert.That(result[6].Group, Is.EqualTo("2")); Assert.That(result[6].Reduction, Is.EqualTo(3)); } + + [Test] + public void GroupedMapReduce() + { + // This is functionally the same as .Group().Sum(), but tests that Map and Reduce work on grouping queries. + var query = testTable + .Group(to => to.Name) + .Map(to => to.SomeNumber) + .Reduce((l, r) => l + r); + + int count = 0; + foreach (var record in connection.Run(query)) + { + var groupName = record.Key; + var average = record.Value; + + switch (groupName) + { + case "1": + Assert.That(average, Is.EqualTo(2)); + break; + case "2": + Assert.That(average, Is.EqualTo(204)); + break; + case "3": + Assert.That(average, Is.EqualTo(6)); + break; + case "4": + Assert.That(average, Is.EqualTo(4)); + break; + case "5": + Assert.That(average, Is.EqualTo(5)); + break; + case "6": + Assert.That(average, Is.EqualTo(12)); + break; + case "7": + Assert.That(average, Is.EqualTo(7)); + break; + default: + Assert.Fail("Unexpected group name: {0}", groupName); + break; + } + + ++count; + } + + Assert.That(count, Is.EqualTo(7)); + } } } diff --git a/rethinkdb-net/Query.cs b/rethinkdb-net/Query.cs index 5a776a7..8d7499e 100644 --- a/rethinkdb-net/Query.cs +++ b/rethinkdb-net/Query.cs @@ -348,6 +348,20 @@ public static ISequenceQuery> Ungroup( return new UngroupQuery(groupingQuery); } + public static IGroupingQuery Map( + this IGroupingQuery groupingQuery, + Expression> mapExpression) + { + return new MapGroupQuery(groupingQuery, mapExpression); + } + + public static IGroupingQuery Reduce( + this IGroupingQuery groupingQuery, + Expression> reduceFunction) + { + return new ReduceGroupQuery(groupingQuery, reduceFunction); + } + public static IGroupingQuery Min( this IGroupingQuery groupingQuery, Expression> field = null diff --git a/rethinkdb-net/QueryTerm/MapGroupQuery.cs b/rethinkdb-net/QueryTerm/MapGroupQuery.cs new file mode 100644 index 0000000..367c311 --- /dev/null +++ b/rethinkdb-net/QueryTerm/MapGroupQuery.cs @@ -0,0 +1,29 @@ +using RethinkDb.Spec; +using System; +using System.Linq.Expressions; + +namespace RethinkDb.QueryTerm +{ + public class MapGroupQuery : IGroupingQuery + { + private readonly IGroupingQuery groupingQuery; + private readonly Expression> mapExpression; + + public MapGroupQuery(IGroupingQuery groupingQuery, Expression> mapExpression) + { + this.groupingQuery = groupingQuery; + this.mapExpression = mapExpression; + } + + public Term GenerateTerm(IDatumConverterFactory datumConverterFactory) + { + var mapTerm = new Term() + { + type = Term.TermType.MAP, + }; + mapTerm.args.Add(groupingQuery.GenerateTerm(datumConverterFactory)); + mapTerm.args.Add(ExpressionUtils.CreateFunctionTerm(datumConverterFactory, mapExpression)); + return mapTerm; + } + } +} diff --git a/rethinkdb-net/QueryTerm/ReduceGroupQuery.cs b/rethinkdb-net/QueryTerm/ReduceGroupQuery.cs new file mode 100644 index 0000000..7657f60 --- /dev/null +++ b/rethinkdb-net/QueryTerm/ReduceGroupQuery.cs @@ -0,0 +1,30 @@ +using System; +using System.Linq.Expressions; +using RethinkDb.DatumConverters; +using RethinkDb.Spec; + +namespace RethinkDb.QueryTerm +{ + public class ReduceGroupQuery : IGroupingQuery + { + private readonly IGroupingQuery groupingQuery; + private readonly Expression> reduceFunction; + + public ReduceGroupQuery(IGroupingQuery groupingQuery, Expression> reduceFunction) + { + this.groupingQuery = groupingQuery; + this.reduceFunction = reduceFunction; + } + + public Term GenerateTerm(IDatumConverterFactory datumConverterFactory) + { + var reduceTerm = new Term() + { + type = Term.TermType.REDUCE, + }; + reduceTerm.args.Add(groupingQuery.GenerateTerm(datumConverterFactory)); + reduceTerm.args.Add(ExpressionUtils.CreateFunctionTerm(datumConverterFactory, reduceFunction)); + return reduceTerm; + } + } +} diff --git a/rethinkdb-net/rethinkdb-net.csproj b/rethinkdb-net/rethinkdb-net.csproj index b0c8695..4303113 100644 --- a/rethinkdb-net/rethinkdb-net.csproj +++ b/rethinkdb-net/rethinkdb-net.csproj @@ -184,6 +184,8 @@ + + From a7195e8e4c0ab1691c5d636cd1f78d07c5af43ee Mon Sep 17 00:00:00 2001 From: Mathieu Fenniak Date: Mon, 20 Oct 2014 19:14:39 -0600 Subject: [PATCH 17/17] Update release notes for RethinkDB 1.15 compatibility changes. --- RELEASE-NOTES.md | 20 ++++++++++++++++++++ rethinkdb-net.nuspec | 2 +- rethinkdb-net.sln | 1 + 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index dccc901..90b6bc3 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -4,6 +4,14 @@ ### Features +* Upgrade to support RethinkDB version 1.15. [PR #173](https://github.com/mfenniak/rethinkdb-net/issues/173) & [Issue #171](https://github.com/mfenniak/rethinkdb-net/issues/171) + + * New Group method can be used for grouping on an index value, or between 1 and 3 different key values. + + * Count aggregate now supports a predicate for counting only matching rows. + + * Max, Min, Avg, Sum, Count, and Contains aggregates are now fully supported. Previously only Avg and Sum aggregates were supported. + * Support for serializing and deserializing TimeSpan data types, which was added to the Newtonsoft serializer but not the basic serialization implementation. [PR #152](https://github.com/mfenniak/rethinkdb-net/issues/152) * Expressions now support the addition of DateTime and TimeSpan objects, as well as DateTime and DateTimeOffset's Add methods (eg. AddHours, AddDays). [PR #152](https://github.com/mfenniak/rethinkdb-net/issues/152), [Issue #158](https://github.com/mfenniak/rethinkdb-net/issues/158) Note, AddMonths is not supported. @@ -12,6 +20,18 @@ * Support for OrderBy on indexes. [Issue #162](https://github.com/mfenniak/rethinkdb-net/issues/162) +### Breaking Changes + +* [PR #173](https://github.com/mfenniak/rethinkdb-net/issues/173) contained a number of breaking changes to maintain consistency with RethinkDB driver changes on other platforms and remove functionality that is no longer supported by RethinkDB. + + * Remove base parameter from Reduce(); it's been removed in RethinkDB and instead an error occurs when attempting to reduce an empty sequence, and the only element is returned when reducing a single-element sequence. Part of [PR #173](https://github.com/mfenniak/rethinkdb-net/issues/173). + + * UpdateAndReturnValues, InsertAndReturnValues, and DeleteAndReturnValues have all been renamed to "...ReturnChanges", and their return value has changed to support returning multiple changes. These changes are for compatibility and to maintain consistency with other RethinkDB drivers. Part of [PR #173](https://github.com/mfenniak/rethinkdb-net/issues/173). + + * GroupedMapReduce has been removed for consistency with other RethinkDB drivers. .Group(...).Map(...).Reduce(...) can be used as an alternative. Part of [PR #173](https://github.com/mfenniak/rethinkdb-net/issues/173). + + * GroupBy and its prebuilt aggregates have been removed for consistency with other RethinkDB drivers. .Group() followed by an aggregate can be used instead. Part of [PR #173](https://github.com/mfenniak/rethinkdb-net/issues/173). + ## 0.7.0.0 (2013-11-02) diff --git a/rethinkdb-net.nuspec b/rethinkdb-net.nuspec index b4dad13..3ba1e82 100644 --- a/rethinkdb-net.nuspec +++ b/rethinkdb-net.nuspec @@ -12,7 +12,7 @@ - rethinkdb-net compatible with RethinkDB v1.10, see https://github.com/mfenniak/rethinkdb-net/blob/master/RELEASE-NOTES.md for detailed release notes. + rethinkdb-net compatible with RethinkDB v1.15, see https://github.com/mfenniak/rethinkdb-net/blob/master/RELEASE-NOTES.md for detailed release notes. diff --git a/rethinkdb-net.sln b/rethinkdb-net.sln index e287098..71eafdf 100644 --- a/rethinkdb-net.sln +++ b/rethinkdb-net.sln @@ -12,6 +12,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution rethinkdb-net.nuspec = rethinkdb-net.nuspec LICENSE.txt = LICENSE.txt RELEASE-NOTES.md = RELEASE-NOTES.md + rethinkdb-net-newtonsoft.nuspec = rethinkdb-net-newtonsoft.nuspec EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Examples", "Examples", "{9F8AB94D-246B-420B-91C7-145B94D75FA3}"