using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Remotion.Linq; using Remotion.Linq.Clauses.ResultOperators; using RethinkDb.Driver.Ast; using RethinkDb.Driver.Net; namespace RethinkDb.Driver.Linq { public class RethinkQueryExecutor : IQueryExecutor { private readonly Table table; private readonly IConnection connection; public RethinkQueryExecutor( Table table, IConnection connection ) { this.table = table; this.connection = connection; } protected virtual void ProcessQuery( ReqlAst query ) { } public T ExecuteScalar( QueryModel queryModel ) { var visitor = new RethinkDbQueryModelVisitor( table ); visitor.VisitQueryModel( queryModel ); var query = visitor.Query; ProcessQuery( query ); var result = query.Run( connection ); if( queryModel.ResultOperators.FirstOrDefault() is AnyResultOperator ) return result > 0; if( queryModel.ResultOperators.FirstOrDefault() is AllResultOperator ) return result == 0; return (T)result; } public T ExecuteSingle( QueryModel queryModel, bool returnDefaultWhenEmpty ) { var visitor = new RethinkDbQueryModelVisitor( table ); visitor.VisitQueryModel( queryModel ); var query = visitor.Query; ProcessQuery( query ); try { return query.RunResult( connection ); } catch( ReqlNonExistenceError ex ) { if( ShouldReturnDefault( queryModel ) ) return default( T ); throw new InvalidOperationException( ex.Message ); } } private static bool ShouldReturnDefault( QueryModel queryModel ) { var firstResultOperator = queryModel.ResultOperators.FirstOrDefault(); return ( firstResultOperator as FirstResultOperator )?.ReturnDefaultWhenEmpty ?? ( firstResultOperator as LastResultOperator )?.ReturnDefaultWhenEmpty ?? false; } public IEnumerable ExecuteCollection( QueryModel queryModel ) { var visitor = new RethinkDbQueryModelVisitor( table ); visitor.VisitQueryModel( queryModel ); var query = visitor.Query; ProcessQuery( query ); if( typeof(T).GetTypeInfo().IsGenericType && typeof(T).GetGenericTypeDefinition() == typeof(IGrouping<,>) ) { return typeof(RethinkQueryExecutor) .GetMethod(nameof(DeserializeGrouping), BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.DeclaredOnly) .MakeGenericMethod( typeof(T).GetGenericArguments()[0], typeof(T).GetGenericArguments()[1]) .Invoke(null, new object[] { query.Run(connection) as JArray }) as IEnumerable; } if( query is Get ) { return new List { query.RunResult(connection) }; } return query.RunResult>( connection ); } private static List> DeserializeGrouping( JArray groups ) { var result = new List>(); foreach( var group in groups ) { var groupArray = (JObject)group; result.Add( new RethinkDbGroup { Key = groupArray["group"].ToObject(), Reduction = JsonConvert.DeserializeObject>( groupArray["reduction"].ToString() ) } ); } return result; } } }