Переглянути джерело

- support PlainDistinctSearch

Lith 4 місяців тому
батько
коміт
44883615ee
24 змінених файлів з 840 додано та 794 видалено
  1. 3 17
      src/Vitorm.MongoDB/DbContext.cs
  2. 115 0
      src/Vitorm.MongoDB/EntityReader/EntityReader.cs
  3. 15 0
      src/Vitorm.MongoDB/EntityReader/IArgReader.cs
  4. 35 0
      src/Vitorm.MongoDB/EntityReader/ValueReader.cs
  5. 2 13
      src/Vitorm.MongoDB/QueryExecutor/Async/ToListAsync.cs
  6. 1 1
      src/Vitorm.MongoDB/QueryExecutor/Sync/Count.cs
  7. 2 12
      src/Vitorm.MongoDB/QueryExecutor/Sync/ToList.cs
  8. 101 0
      src/Vitorm.MongoDB/SearchExecutor/GroupExecutor.ToList.cs
  9. 104 0
      src/Vitorm.MongoDB/SearchExecutor/GroupExecutor.ToListAsync.cs
  10. 0 306
      src/Vitorm.MongoDB/SearchExecutor/GroupExecutor.cs
  11. 10 3
      src/Vitorm.MongoDB/SearchExecutor/ISearchExecutor.cs
  12. 65 0
      src/Vitorm.MongoDB/SearchExecutor/PlainDistinctSearchExecutor.ToList.cs
  13. 58 0
      src/Vitorm.MongoDB/SearchExecutor/PlainDistinctSearchExecutor.ToListAsync.cs
  14. 91 0
      src/Vitorm.MongoDB/SearchExecutor/PlainDistinctSearchExecutor.cs
  15. 63 0
      src/Vitorm.MongoDB/SearchExecutor/PlainSearchExecutor.ToList.cs
  16. 65 0
      src/Vitorm.MongoDB/SearchExecutor/PlainSearchExecutor.ToListAsync.cs
  17. 25 142
      src/Vitorm.MongoDB/SearchExecutor/PlainSearchExecutor.cs
  18. 0 24
      src/Vitorm.MongoDB/SearchExecutor/SearchExecutorArgument.cs
  19. 0 54
      test/Vitorm.MongoDB.MsTest/CommonTest/Orm_Extensions_ExecuteDeleteAsync_Test.cs
  20. 0 54
      test/Vitorm.MongoDB.MsTest/CommonTest/Orm_Extensions_ExecuteDelete_Test.cs
  21. 0 78
      test/Vitorm.MongoDB.MsTest/CommonTest/Orm_Extensions_ExecuteUpdateAsync_Test.cs
  22. 0 80
      test/Vitorm.MongoDB.MsTest/CommonTest/Orm_Extensions_ExecuteUpdate_Test.cs
  23. 10 10
      test/Vitorm.MongoDB.MsTest/CommonTest/Query_Distinct_Test.cs
  24. 75 0
      test/Vitorm.MongoDB.MsTest/CustomTest/Query_Method_Test.cs

+ 3 - 17
src/Vitorm.MongoDB/DbContext.cs

@@ -2,7 +2,6 @@
 using System.Collections;
 using System.Collections.Generic;
 using System.Linq;
-using System.Threading.Tasks;
 
 using MongoDB.Bson;
 using MongoDB.Driver;
@@ -88,26 +87,13 @@ namespace Vitorm.MongoDB
         public static List<ISearchExecutor> defaultSearchExecutor = new() {
             new PlainSearchExecutor(),
             new GroupExecutor(),
+            new PlainDistinctSearchExecutor(),
         };
         public List<ISearchExecutor> searchExecutor = defaultSearchExecutor;
 
-        public virtual async Task<bool> ExecuteSearchAsync<Entity, ResultEntity>(SearchExecutorArgument<ResultEntity> arg)
+        public virtual ISearchExecutor GetSearchExecutor(QueryExecutorArgument arg)
         {
-            foreach (var executor in searchExecutor)
-            {
-                var success = await executor.ExecuteSearchAsync<Entity, ResultEntity>(arg);
-                if (success) return true;
-            }
-            throw new NotSupportedException("not supported Search");
-        }
-        public virtual bool ExecuteSearch<Entity, ResultEntity>(SearchExecutorArgument<ResultEntity> arg)
-        {
-            foreach (var executor in searchExecutor)
-            {
-                var success = executor.ExecuteSearch<Entity, ResultEntity>(arg);
-                if (success) return true;
-            }
-            throw new NotSupportedException("not supported Search");
+            return searchExecutor.FirstOrDefault(m => m.IsMatch(arg));
         }
         #endregion
 

+ 115 - 0
src/Vitorm.MongoDB/EntityReader/EntityReader.cs

@@ -0,0 +1,115 @@
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Linq;
+
+using MongoDB.Bson;
+
+using Vit.Linq.ExpressionNodes;
+using Vit.Linq.ExpressionNodes.ComponentModel;
+
+namespace Vitorm.MongoDB.EntityReader
+{
+    /// <summary>
+    ///  get all sql column values, compile EntityGenerator to Lambda .  Invoke the lambda when reading rows , pass sql column values as lambda args. 
+    /// </summary>
+    public class EntityReader
+    {
+        public List<IArgReader> entityArgReaders = new List<IArgReader>();
+        protected Delegate lambdaCreateEntity;
+
+        public void Init(DbContext dbContext, Type entityType, ExpressionNode resultSelector)
+        {
+            var cloner = new ExpressionNodeCloner();
+            cloner.clone = (node) =>
+            {
+                if (node?.nodeType == NodeType.Member)
+                {
+                    ExpressionNode_Member member = node;
+
+                    var argName = GetArgument(dbContext, member);
+
+                    if (argName != null)
+                    {
+                        return (true, ExpressionNode.Member(parameterName: argName, memberName: null));
+                    }
+                }
+                else if (node?.nodeType == NodeType.MethodCall)
+                {
+                    ExpressionNode_MethodCall methodCall = node;
+
+                    //// deal with aggregate functions like Sum(id)
+                    //if (methodCall.methodCall_typeName == "Enumerable")
+                    //{
+                    //    string argName = null;
+
+                    //    var sqlColumnSentence = sqlTranslateService.EvalExpression(arg, node);
+                    //    var columnType = methodCall.MethodCall_GetReturnType();
+                    //    argName = GetArgument(config, sqlColumnSentence, columnType);
+                    //    if (argName != null)
+                    //    {
+                    //        return (true, ExpressionNode.Member(parameterName: argName, memberName: null));
+                    //    }
+                    //}
+                    throw new InvalidOperationException();
+                }
+                return default;
+            };
+            ExpressionNode newResultSelector = cloner.Clone(resultSelector);
+
+            // compile ResultCreate lambda
+            lambdaCreateEntity = CompileExpression(dbContext.convertService, entityArgReaders.Select(m => m.argName).ToArray(), newResultSelector);
+        }
+
+        public object ReadEntity(BsonDocument reader)
+        {
+            var lambdaArgs = entityArgReaders.Select(m => m.Read(reader)).ToArray();
+            var entity = lambdaCreateEntity.DynamicInvoke(lambdaArgs);
+            return entity;
+        }
+
+
+
+        protected string GetArgument(DbContext dbContext, ExpressionNode_Member member)
+        {
+            var fieldPath = dbContext.translateService.GetFieldPath(new() { dbContext = dbContext }, (ExpressionNode)member);
+
+            IArgReader argReader = entityArgReaders.FirstOrDefault(reader => reader.fieldPath == fieldPath);
+
+            if (argReader == null)
+            {
+                var argName = "arg_" + entityArgReaders.Count;
+
+                var argType = member.Member_GetType();
+
+                bool isValueType = TypeUtil.IsValueType(argType);
+                if (isValueType)
+                {
+                    // Value arg 
+                    argReader = new ValueReader(argType, fieldPath, argName);
+                }
+                else
+                {
+                    // Entity arg
+                    //var entityDescriptor = config.queryTranslateArgument.dbContext.GetEntityDescriptor(argType);
+
+                    //argReader = new ModelReader(config.sqlColumns, config.sqlTranslateService, tableName, argUniqueKey, argName, entityDescriptor);
+                }
+                entityArgReaders.Add(argReader);
+            }
+            return argReader.argName;
+        }
+
+
+        Delegate CompileExpression(ExpressionConvertService convertService, string[] parameterNames, ExpressionNode newExp)
+        {
+            var lambdaNode = ExpressionNode.Lambda(entityArgReaders.Select(m => m.argName).ToArray(), newExp);
+            // var strNode = Json.Serialize(lambdaNode);
+
+            var lambdaExp = convertService.ConvertToCode_LambdaExpression(lambdaNode, entityArgReaders.Select(m => m.entityType).ToArray());
+
+            return lambdaExp.Compile();
+        }
+
+    }
+}

+ 15 - 0
src/Vitorm.MongoDB/EntityReader/IArgReader.cs

@@ -0,0 +1,15 @@
+using System;
+
+using MongoDB.Bson;
+
+namespace Vitorm.MongoDB.EntityReader
+{
+    public interface IArgReader
+    {
+        string fieldPath { get; }
+        string argName { get; }
+        Type entityType { get; }
+        object Read(BsonDocument reader);
+    }
+
+}

+ 35 - 0
src/Vitorm.MongoDB/EntityReader/ValueReader.cs

@@ -0,0 +1,35 @@
+using System;
+
+using MongoDB.Bson;
+
+namespace Vitorm.MongoDB.EntityReader
+{
+
+    class ValueReader : IArgReader
+    {
+        public string argName { get; set; }
+
+        public string fieldPath { get; set; }
+
+        protected Type valueType { get; set; }
+        protected Type underlyingType;
+        public Type entityType { get => valueType; }
+
+        public ValueReader(Type valueType, string fieldPath, string argName)
+        {
+            this.valueType = valueType;
+            underlyingType = TypeUtil.GetUnderlyingType(valueType);
+
+            this.fieldPath = fieldPath;
+            this.argName = argName;
+        }
+        public object Read(BsonDocument reader)
+        {
+            var bsonValue = reader[argName];
+            var value = BsonTypeMapper.MapToDotNetValue(bsonValue);
+            return TypeUtil.ConvertToUnderlyingType(value, underlyingType);
+        }
+    }
+
+
+}

+ 2 - 13
src/Vitorm.MongoDB/QueryExecutor/Async/ToListAsync.cs

@@ -8,7 +8,6 @@ using MongoDB.Driver;
 
 using Vit.Linq;
 
-using Vitorm.MongoDB.SearchExecutor;
 using Vitorm.StreamQuery;
 
 namespace Vitorm.MongoDB.QueryExecutor
@@ -49,19 +48,9 @@ namespace Vitorm.MongoDB.QueryExecutor
             .MakeGenericMethod(entityType, resultEntityType);
 
 
-
-        public static async Task<List<ResultEntity>> Execute<Entity, ResultEntity>(QueryExecutorArgument execArg)
+        public static Task<List<ResultEntity>> Execute<Entity, ResultEntity>(QueryExecutorArgument arg)
         {
-            var combinedStream = execArg.combinedStream;
-            var dbContext = execArg.dbContext;
-
-            var searchArg = new SearchExecutorArgument<ResultEntity> { execArg = execArg };
-            searchArg.getList = true;
-            searchArg.getTotalCount = false;
-
-            await dbContext.ExecuteSearchAsync<Entity, ResultEntity>(searchArg);
-
-            return searchArg.list;
+            return arg.dbContext.GetSearchExecutor(arg)?.ToListAsync<Entity, ResultEntity>(arg);
         }
 
 

+ 1 - 1
src/Vitorm.MongoDB/QueryExecutor/Sync/Count.cs

@@ -94,7 +94,7 @@ namespace Vitorm.MongoDB.QueryExecutor
             var database = dbContext.dbConfig.GetDatabase();
             var collection = database.GetCollection<BsonDocument>(entityDescriptor.tableName);
 
-            var pipeline = GroupExecutor.GetAggregatePipeline(execArg);
+            var pipeline = GroupExecutor.GetPipeline(execArg);
             pipeline = pipeline.Concat(new[] { new BsonDocument("$count", "count") }).ToArray();
 
 

+ 2 - 12
src/Vitorm.MongoDB/QueryExecutor/Sync/ToList.cs

@@ -5,7 +5,6 @@ using System.Reflection;
 
 using MongoDB.Driver;
 
-using Vitorm.MongoDB.SearchExecutor;
 using Vitorm.StreamQuery;
 
 namespace Vitorm.MongoDB.QueryExecutor
@@ -46,18 +45,9 @@ namespace Vitorm.MongoDB.QueryExecutor
 
 
 
-        public static List<ResultEntity> Execute<Entity, ResultEntity>(QueryExecutorArgument execArg)
+        public static List<ResultEntity> Execute<Entity, ResultEntity>(QueryExecutorArgument arg)
         {
-            var combinedStream = execArg.combinedStream;
-            var dbContext = execArg.dbContext;
-
-            var searchArg = new SearchExecutorArgument<ResultEntity> { execArg = execArg };
-            searchArg.getList = true;
-            searchArg.getTotalCount = false;
-
-            dbContext.ExecuteSearch<Entity, ResultEntity>(searchArg);
-
-            return searchArg.list;
+            return arg.dbContext.GetSearchExecutor(arg)?.ToList<Entity, ResultEntity>(arg);
         }
 
 

+ 101 - 0
src/Vitorm.MongoDB/SearchExecutor/GroupExecutor.ToList.cs

@@ -0,0 +1,101 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+
+using MongoDB.Bson;
+using MongoDB.Driver;
+
+using Vitorm.Entity;
+using Vitorm.MongoDB.QueryExecutor;
+
+namespace Vitorm.MongoDB.SearchExecutor
+{
+    public partial class GroupExecutor
+    {
+
+        public List<ResultEntity> ToList<Entity, ResultEntity>(QueryExecutorArgument arg)
+        {
+            Type keyType;
+            var resultSelector = arg.combinedStream.select.resultSelector;
+            if (resultSelector == null)
+            {
+                keyType = typeof(ResultEntity);
+            }
+            else
+            {
+                var groupType = resultSelector.Lambda_GetParamTypes()[0];
+                keyType = groupType.GetGenericArguments()[0];
+            }
+
+            return (List<ResultEntity>)ToList_MethodInfo(typeof(Entity), typeof(ResultEntity), keyType).Invoke(null, new[] { arg });
+        }
+
+
+        private static MethodInfo ToList_MethodInfo_;
+        static MethodInfo ToList_MethodInfo(Type entityType, Type resultEntityType, Type keyType) =>
+            (ToList_MethodInfo_ ??= new Func<QueryExecutorArgument, List<string>>(ToList<string, string, string>).Method.GetGenericMethodDefinition())
+            .MakeGenericMethod(entityType, resultEntityType, keyType);
+
+
+        static List<ResultEntity> ToList<Entity, ResultEntity, Key>(QueryExecutorArgument arg)
+        {
+            var combinedStream = arg.combinedStream;
+            var dbContext = arg.dbContext;
+            var entityDescriptor = dbContext.GetEntityDescriptor(typeof(Entity));
+
+
+            using var cursor = Execute<Entity, ResultEntity>(arg, entityDescriptor);
+
+            var lambdaExpression = combinedStream.select.resultSelector.Lambda_GetLambdaExpression();
+            var delSelect = (Func<IGrouping<Key, Entity>, ResultEntity>)lambdaExpression.Compile();
+
+            var groups = ReadGroups<Key, Entity>(dbContext, entityDescriptor, cursor);
+
+            return groups.Select(delSelect).ToList();
+
+        }
+        static IAsyncCursor<BsonDocument> Execute<Entity, ResultEntity>(QueryExecutorArgument arg, IEntityDescriptor entityDescriptor)
+        {
+            var dbContext = arg.dbContext;
+            var database = dbContext.dbConfig.GetDatabase();
+            var collection = database.GetCollection<BsonDocument>(entityDescriptor.tableName);
+
+            var pipeline = GetPipeline(arg);
+
+            // Event_OnExecuting
+            dbContext.Event_OnExecuting(new Lazy<ExecuteEventArgument>(() => new ExecuteEventArgument(
+                dbContext: dbContext,
+                executeString: pipeline.ToJson(),
+                extraParam: new()
+                {
+                    ["entityDescriptor"] = entityDescriptor,
+                    ["Method"] = arg.combinedStream.method ?? "ToList",
+                    ["combinedStream"] = arg.combinedStream,
+                }))
+            );
+
+            // Execute aggregation
+            return dbContext.session == null ? collection.Aggregate<BsonDocument>(pipeline) : collection.Aggregate<BsonDocument>(dbContext.session, pipeline);
+        }
+        static IEnumerable<IGrouping<Key, Element>> ReadGroups<Key, Element>(DbContext dbContext, IEntityDescriptor entityDescriptor, IAsyncCursor<BsonDocument> cursor)
+        {
+            while (cursor.MoveNext())
+            {
+                foreach (BsonDocument document in cursor.Current)
+                {
+                    yield return new Grouping<Key, Element>(dbContext, entityDescriptor, document);
+                }
+            }
+        }
+
+
+
+
+
+
+
+
+
+    }
+}

+ 104 - 0
src/Vitorm.MongoDB/SearchExecutor/GroupExecutor.ToListAsync.cs

@@ -0,0 +1,104 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Threading.Tasks;
+
+using MongoDB.Bson;
+using MongoDB.Driver;
+
+using Vitorm.Entity;
+using Vitorm.MongoDB.QueryExecutor;
+
+namespace Vitorm.MongoDB.SearchExecutor
+{
+    public partial class GroupExecutor
+    {
+
+        public Task<List<ResultEntity>> ToListAsync<Entity, ResultEntity>(QueryExecutorArgument arg)
+        {
+            Type keyType;
+            var resultSelector = arg.combinedStream.select.resultSelector;
+            if (resultSelector == null)
+            {
+                keyType = typeof(ResultEntity);
+            }
+            else
+            {
+                var groupType = resultSelector.Lambda_GetParamTypes()[0];
+                keyType = groupType.GetGenericArguments()[0];
+            }
+
+            return (Task<List<ResultEntity>>)ToListAsync_MethodInfo(typeof(Entity), typeof(ResultEntity), keyType).Invoke(null, new[] { arg });
+        }
+
+
+        private static MethodInfo ToListAsync_MethodInfo_;
+        static MethodInfo ToListAsync_MethodInfo(Type entityType, Type resultEntityType, Type keyType) =>
+            (ToListAsync_MethodInfo_ ??= new Func<QueryExecutorArgument, Task<List<string>>>(ToListAsync<string, string, string>).Method.GetGenericMethodDefinition())
+            .MakeGenericMethod(entityType, resultEntityType, keyType);
+
+
+        static async Task<List<ResultEntity>> ToListAsync<Entity, ResultEntity, Key>(QueryExecutorArgument arg)
+        {
+            var combinedStream = arg.combinedStream;
+            var dbContext = arg.dbContext;
+            var entityDescriptor = dbContext.GetEntityDescriptor(typeof(Entity));
+
+
+            using var cursor = await ExecuteAsync<Entity, ResultEntity>(arg, entityDescriptor);
+
+            var lambdaExpression = combinedStream.select.resultSelector.Lambda_GetLambdaExpression();
+            var delSelect = (Func<IGrouping<Key, Entity>, ResultEntity>)lambdaExpression.Compile();
+
+            var list = await ReadListAsync<Entity, ResultEntity, Key>(dbContext, entityDescriptor, cursor, delSelect);
+
+            return list;
+
+        }
+        static Task<IAsyncCursor<BsonDocument>> ExecuteAsync<Entity, ResultEntity>(QueryExecutorArgument arg, IEntityDescriptor entityDescriptor)
+        {
+            var dbContext = arg.dbContext;
+            var database = dbContext.dbConfig.GetDatabase();
+            var collection = database.GetCollection<BsonDocument>(entityDescriptor.tableName);
+
+            var pipeline = GetPipeline(arg);
+
+            // Event_OnExecuting
+            dbContext.Event_OnExecuting(new Lazy<ExecuteEventArgument>(() => new ExecuteEventArgument(
+                dbContext: dbContext,
+                executeString: pipeline.ToJson(),
+                extraParam: new()
+                {
+                    ["entityDescriptor"] = entityDescriptor,
+                    ["Method"] = arg.combinedStream.method ?? "ToListAsync",
+                    ["combinedStream"] = arg.combinedStream,
+                }))
+            );
+
+            // Execute aggregation
+            return dbContext.session == null ? collection.AggregateAsync<BsonDocument>(pipeline) : collection.AggregateAsync<BsonDocument>(dbContext.session, pipeline);
+        }
+
+        static async Task<List<ResultEntity>> ReadListAsync<Entity, ResultEntity, Key>(DbContext dbContext, IEntityDescriptor entityDescriptor, IAsyncCursor<BsonDocument> cursor, Func<IGrouping<Key, Entity>, ResultEntity> Select)
+        {
+            List<ResultEntity> list = new();
+            while (await cursor.MoveNextAsync())
+            {
+                foreach (BsonDocument document in cursor.Current)
+                {
+                    var group = new Grouping<Key, Entity>(dbContext, entityDescriptor, document);
+                    list.Add(Select(group));
+                }
+            }
+            return list;
+        }
+
+
+
+
+
+
+
+    }
+}

+ 0 - 306
src/Vitorm.MongoDB/SearchExecutor/GroupExecutor.cs

@@ -1,306 +0,0 @@
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Linq;
-using System.Reflection;
-using System.Threading.Tasks;
-
-using MongoDB.Bson;
-using MongoDB.Bson.Serialization;
-using MongoDB.Driver;
-
-using Vit.Linq.ExpressionNodes.ComponentModel;
-
-using Vitorm.Entity;
-using Vitorm.MongoDB.QueryExecutor;
-using Vitorm.StreamQuery;
-
-namespace Vitorm.MongoDB.SearchExecutor
-{
-    public class GroupExecutor : ISearchExecutor
-    {
-        protected virtual bool IsMatch<ResultEntity>(SearchExecutorArgument<ResultEntity> arg)
-        {
-            CombinedStream combinedStream = arg.combinedStream;
-
-            var dbContext = arg.dbContext;
-
-            //if (combinedStream.source is not SourceStream) return false;
-            if (!combinedStream.isGroupedStream) return false;
-            if (combinedStream.joins?.Any() == true) return false;
-            if (combinedStream.distinct != null) return false;
-
-            return true;
-        }
-
-
-        public async Task<bool> ExecuteSearchAsync<Entity, ResultEntity>(SearchExecutorArgument<ResultEntity> arg)
-        {
-            if (!IsMatch(arg)) return false;
-
-
-            return false;
-        }
-
-
-
-        public bool ExecuteSearch<Entity, ResultEntity>(SearchExecutorArgument<ResultEntity> arg)
-        {
-            if (!IsMatch(arg)) return false;
-
-
-            Type keyType;
-            var resultSelector = arg.combinedStream.select.resultSelector;
-            if (resultSelector == null)
-            {
-                keyType = typeof(ResultEntity);
-            }
-            else
-            {
-                var groupType = resultSelector.Lambda_GetParamTypes()[0];
-                keyType = groupType.GetGenericArguments()[0];
-            }
-
-            return (bool)Execute_MethodInfo(typeof(Entity), typeof(ResultEntity), keyType).Invoke(null, new[] { arg });
-        }
-
-        private static MethodInfo Execute_MethodInfo_;
-        static MethodInfo Execute_MethodInfo(Type entityType, Type resultEntityType, Type keyType) =>
-            (Execute_MethodInfo_ ??= new Func<SearchExecutorArgument<string>, bool>(Execute<string, string, string>).Method.GetGenericMethodDefinition())
-            .MakeGenericMethod(entityType, resultEntityType, keyType);
-
-        static bool Execute<Entity, ResultEntity, Key>(SearchExecutorArgument<ResultEntity> arg)
-        {
-            if (arg.getList)
-            {
-                var combinedStream = arg.combinedStream;
-                var dbContext = arg.dbContext;
-                var entityDescriptor = dbContext.GetEntityDescriptor(typeof(Entity));
-
-                if (combinedStream.select?.resultSelector == null)
-                {
-                    throw new NotImplementedException();
-                }
-
-                using var cursor = ExecuteQuery<Entity, ResultEntity>(arg, entityDescriptor);
-
-                var lambdaExpression = combinedStream.select.resultSelector.Lambda_GetLambdaExpression();
-                var delSelect = (Func<IGrouping<Key, Entity>, ResultEntity>)lambdaExpression.Compile();
-
-                var groups = ReadGroups<Key, Entity>(dbContext, entityDescriptor, cursor);
-
-                arg.list = groups.Select(delSelect).ToList();
-                return true;
-            }
-
-
-            return false;
-        }
-
-        static IEnumerable<IGrouping<Key, Element>> ReadGroups<Key, Element>(DbContext dbContext, IEntityDescriptor entityDescriptor, IAsyncCursor<BsonDocument> cursor)
-        {
-            while (cursor.MoveNext())
-            {
-                foreach (BsonDocument document in cursor.Current)
-                {
-                    yield return new Grouping<Key, Element>(dbContext, entityDescriptor, document);
-                }
-            }
-        }
-        class Grouping<TKey, TElement> : IGrouping<TKey, TElement>
-        {
-            class KeyWrap
-            {
-                public TKey Key { get; set; }
-            }
-            public Grouping(DbContext dbContext, IEntityDescriptor entityDescriptor, BsonDocument document)
-            {
-                // #1 read key
-                var docKey = document["key"];
-                if (docKey.IsBsonDocument)
-                {
-                    Key = BsonSerializer.Deserialize<TKey>(docKey.AsBsonDocument);
-                }
-                else
-                {
-                    Key = BsonSerializer.Deserialize<KeyWrap>(new BsonDocument("Key", docKey)).Key;
-                }
-
-                // #2 read list
-                list = document["items"]?.AsBsonArray.AsQueryable()?.Select(item => dbContext.Deserialize<TElement>(item.AsBsonDocument, entityDescriptor));
-            }
-            public TKey Key { get; private set; }
-
-            IEnumerable<TElement> list;
-
-            public IEnumerator<TElement> GetEnumerator() => list.GetEnumerator();
-
-            IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
-        }
-
-        static IAsyncCursor<BsonDocument> ExecuteQuery<Entity, ResultEntity>(SearchExecutorArgument<ResultEntity> arg, IEntityDescriptor entityDescriptor)
-        {
-            var dbContext = arg.dbContext;
-            var database = dbContext.dbConfig.GetDatabase();
-            var collection = database.GetCollection<BsonDocument>(entityDescriptor.tableName);
-
-            var pipeline = GetAggregatePipeline(arg.execArg);
-
-            // Execute aggregation
-            return dbContext.session == null ? collection.Aggregate<BsonDocument>(pipeline) : collection.Aggregate<BsonDocument>(dbContext.session, pipeline);
-        }
-
-        public static BsonDocument[] GetAggregatePipeline(QueryExecutorArgument arg)
-        {
-            CombinedStream combinedStream = arg.combinedStream;
-            var dbContext = arg.dbContext;
-            var translateService = dbContext.translateService;
-
-            // #2 filter
-            var filter = combinedStream?.where == null ? null : translateService.TranslateFilter(arg, combinedStream.where);
-
-            // #3 groupByFields
-            List<(string field, string fieldAs)> groupFields = new();
-            BsonValue groupByFields;
-            BsonValue projectFields;
-            var groupFieldArg = new QueryExecutorArgument_GroupFilter(arg, groupFields);
-
-            #region groupByFields
-            {
-                var node = combinedStream.groupByFields;
-
-                if (node?.nodeType == NodeType.New)
-                {
-                    ExpressionNode_New newNode = node;
-                    newNode.constructorArgs.ForEach(nodeArg =>
-                    {
-                        var fieldAs = nodeArg.name;
-                        var field = arg.GetFieldPath(nodeArg.value);
-                        groupFields.Add((field, fieldAs));
-                    });
-
-                    groupByFields = new BsonDocument(groupFields.ToDictionary(field => field.fieldAs, field => "$" + field.field));
-                    projectFields = new BsonDocument(groupFields.ToDictionary(field => field.fieldAs, field => "$_id." + field.fieldAs));
-                }
-                else if (node?.nodeType == NodeType.Member)
-                {
-                    string fieldAs = null;
-                    var field = arg.GetFieldPath(node);
-                    groupFields.Add((field, fieldAs));
-
-                    groupByFields = "$" + field;
-                    projectFields = "$_id";
-                }
-                else
-                {
-                    throw new NotSupportedException("[GroupExecutor] groupByFields is not valid: NodeType must be New or Member");
-                }
-            }
-            #endregion
-
-            // #4 filter to groups
-            var groupFilter = combinedStream.having == null ? null : translateService.TranslateFilter(groupFieldArg, combinedStream.having);
-
-
-            // #5 order by fields
-            var orderFields = GetOrderFields(groupFieldArg);
-            var orderByFields = orderFields == null ? null : new BsonDocument(orderFields.ToDictionary(field => field.field, field => BsonValue.Create(field.asc ? 1 : -1)));
-
-            // Aggregation pipeline
-            var pipeline = new[]
-            {
-                    //new BsonDocument("$match", new BsonDocument("userId", new BsonDocument("$gt", 1))),
-                    filter == null ? null : new BsonDocument("$match", filter),
-
-                    new BsonDocument("$group", new BsonDocument
-                    {
-                        //{ "_id", new BsonDocument { { "userFatherId", "$userFatherId" }, { "userMotherId", "$userMotherId" } } },
-                        { "_id", groupByFields },
-                        { "count", new BsonDocument("$sum", 1) },
-                        { "items", new BsonDocument("$push", "$$ROOT") },
-                    }),
-
-                    //new BsonDocument("$match", new BsonDocument("count", new BsonDocument("$gte", 1))),
-                    groupFilter == null ? null : new BsonDocument("$match", groupFilter),
-
-                    //new BsonDocument("$sort", new BsonDocument("count", -1)),
-                     orderByFields == null ? null : new BsonDocument("$sort", orderByFields),
-
-                    new BsonDocument("$project", new BsonDocument
-                    {
-                        //{ "key",  new BsonDocument
-                        //    {
-                        //        { "userFatherId", "$_id.userFatherId" },
-                        //        { "userMotherId", "$_id.userMotherId" },
-                        //   }
-                        //},
-                        { "key", projectFields },
-                        { "items", 1 },
-                        { "count", 1 },
-                        { "_id", 0 },
-                    }),
-
-                    //new BsonDocument("$skip", 1),
-                    combinedStream.skip>0 ? new BsonDocument("$skip", combinedStream.skip.Value) : null ,
-
-                    //new BsonDocument("$limit", 5),
-                    combinedStream.take>0 ? new BsonDocument("$limit", combinedStream.take.Value) : null ,
-
-            }.Where(m => m != null).ToArray();
-
-            return pipeline;
-        }
-
-
-
-        static List<(string field, bool asc, int index)> GetOrderFields(QueryExecutorArgument arg)
-        {
-            return arg.combinedStream.orders?.Select((orderField, index) =>
-            {
-                var field = arg.GetFieldPath(orderField.member);
-                return (field, orderField.asc, index);
-            }).ToList();
-        }
-
-
-        public class QueryExecutorArgument_GroupFilter : QueryExecutorArgument
-        {
-            List<(string field, string fieldAs)> groupFields;
-            public QueryExecutorArgument_GroupFilter(QueryExecutorArgument arg, List<(string field, string fieldAs)> groupFields)
-            {
-                this.combinedStream = arg.combinedStream;
-                this.dbContext = arg.dbContext;
-                this.expression = arg.expression;
-                this.expressionResultType = arg.expressionResultType;
-                this.groupFields = groupFields;
-            }
-
-            public override string GetFieldPath(ExpressionNode member)
-            {
-                switch (member?.nodeType)
-                {
-                    case NodeType.MethodCall:
-                        {
-                            ExpressionNode_MethodCall methodCall = member;
-
-                            switch (methodCall.methodName)
-                            {
-                                // ##1 Count
-                                case nameof(Enumerable.Count) when methodCall.@object is null && methodCall.arguments.Length == 1:
-                                    {
-                                        return "count";
-                                    }
-                            }
-                            break;
-                        }
-                }
-                var field = base.GetFieldPath(member);
-                var fieldAs = groupFields.First(f => f.field == field).fieldAs;
-                if (fieldAs == null) return "_id";
-                return "_id." + fieldAs;
-            }
-        }
-
-
-    }
-}

+ 10 - 3
src/Vitorm.MongoDB/SearchExecutor/ISearchExecutor.cs

@@ -1,11 +1,18 @@
-using System.Threading.Tasks;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+using Vitorm.MongoDB.QueryExecutor;
 
 namespace Vitorm.MongoDB.SearchExecutor
 {
     public interface ISearchExecutor
     {
-        Task<bool> ExecuteSearchAsync<Entity, ResultEntity>(SearchExecutorArgument<ResultEntity> arg);
+        bool IsMatch(QueryExecutorArgument arg);
+        List<ResultEntity> ToList<Entity, ResultEntity>(QueryExecutorArgument arg);
+
+        Task<List<ResultEntity>> ToListAsync<Entity, ResultEntity>(QueryExecutorArgument arg);
+
+
 
-        bool ExecuteSearch<Entity, ResultEntity>(SearchExecutorArgument<ResultEntity> arg);
     }
 }

+ 65 - 0
src/Vitorm.MongoDB/SearchExecutor/PlainDistinctSearchExecutor.ToList.cs

@@ -0,0 +1,65 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+using MongoDB.Bson;
+using MongoDB.Driver;
+
+using Vitorm.MongoDB.QueryExecutor;
+using Vitorm.StreamQuery;
+
+
+namespace Vitorm.MongoDB.SearchExecutor
+{
+    public partial class PlainDistinctSearchExecutor
+    {
+
+        public List<ResultEntity> ToList<Entity, ResultEntity>(QueryExecutorArgument arg)
+        {
+            return ReadList<Entity, ResultEntity>(arg).ToList();
+        }
+
+        IEnumerable<ResultEntity> ReadList<Entity, ResultEntity>(QueryExecutorArgument arg)
+        {
+            CombinedStream combinedStream = arg.combinedStream;
+            var dbContext = arg.dbContext;
+            var entityDescriptor = dbContext.GetEntityDescriptor(typeof(Entity));
+
+
+            var entityReader = new EntityReader.EntityReader();
+            entityReader.Init(dbContext, typeof(Entity), combinedStream.select.fields);
+
+
+            var pipeline = GetPipeline<Entity, ResultEntity>(arg, entityReader);
+
+            // Event_OnExecuting
+            dbContext.Event_OnExecuting(new Lazy<ExecuteEventArgument>(() => new ExecuteEventArgument(
+                dbContext: dbContext,
+                executeString: pipeline.ToJson(),
+                extraParam: new()
+                {
+                    ["entityDescriptor"] = entityDescriptor,
+                    ["Method"] = arg.combinedStream.method ?? "ToList",
+                    ["combinedStream"] = combinedStream,
+                }))
+            );
+
+
+            var database = dbContext.dbConfig.GetDatabase();
+            var collection = database.GetCollection<BsonDocument>(entityDescriptor.tableName);
+            using var cursor = dbContext.session == null ? collection.Aggregate<BsonDocument>(pipeline) : collection.Aggregate<BsonDocument>(dbContext.session, pipeline);
+
+            while (cursor.MoveNext())
+            {
+                foreach (BsonDocument document in cursor.Current)
+                {
+                    var group = document["_id"].AsBsonDocument;
+                    yield return (ResultEntity)entityReader.ReadEntity(group);
+                }
+            }
+        }
+
+
+
+    }
+}

+ 58 - 0
src/Vitorm.MongoDB/SearchExecutor/PlainDistinctSearchExecutor.ToListAsync.cs

@@ -0,0 +1,58 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+using MongoDB.Bson;
+
+using Vitorm.MongoDB.QueryExecutor;
+using Vitorm.StreamQuery;
+
+
+namespace Vitorm.MongoDB.SearchExecutor
+{
+    public partial class PlainDistinctSearchExecutor
+    {
+        public async Task<List<ResultEntity>> ToListAsync<Entity, ResultEntity>(QueryExecutorArgument arg)
+        {
+            CombinedStream combinedStream = arg.combinedStream;
+            var dbContext = arg.dbContext;
+            var entityDescriptor = dbContext.GetEntityDescriptor(typeof(Entity));
+
+            var entityReader = new EntityReader.EntityReader();
+            entityReader.Init(dbContext, typeof(Entity), combinedStream.select.fields);
+
+            var pipeline = GetPipeline<Entity, ResultEntity>(arg, entityReader);
+
+
+            // Event_OnExecuting
+            dbContext.Event_OnExecuting(new Lazy<ExecuteEventArgument>(() => new ExecuteEventArgument(
+                dbContext: dbContext,
+                executeString: pipeline.ToJson(),
+                extraParam: new()
+                {
+                    ["entityDescriptor"] = entityDescriptor,
+                    ["Method"] = arg.combinedStream.method ?? "ToListAsync",
+                    ["combinedStream"] = combinedStream,
+                }))
+            );
+
+
+            var database = dbContext.dbConfig.GetDatabase();
+            var collection = database.GetCollection<BsonDocument>(entityDescriptor.tableName);
+            using var cursor = dbContext.session == null ? await collection.AggregateAsync<BsonDocument>(pipeline) : await collection.AggregateAsync<BsonDocument>(dbContext.session, pipeline);
+
+
+            var list = new List<ResultEntity>();
+            while (await cursor.MoveNextAsync())
+            {
+                foreach (BsonDocument document in cursor.Current)
+                {
+                    var group = document["_id"].AsBsonDocument;
+                    list.Add((ResultEntity)entityReader.ReadEntity(group));
+                }
+            }
+            return list;
+        }
+
+    }
+}

+ 91 - 0
src/Vitorm.MongoDB/SearchExecutor/PlainDistinctSearchExecutor.cs

@@ -0,0 +1,91 @@
+using System.Collections.Generic;
+using System.Linq;
+
+using MongoDB.Bson;
+using MongoDB.Driver;
+
+using Vitorm.MongoDB.QueryExecutor;
+using Vitorm.StreamQuery;
+
+using static Vitorm.MongoDB.SearchExecutor.GroupExecutor;
+
+
+namespace Vitorm.MongoDB.SearchExecutor
+{
+    public partial class PlainDistinctSearchExecutor : ISearchExecutor
+    {
+        public virtual bool IsMatch(QueryExecutorArgument arg)
+        {
+            CombinedStream combinedStream = arg.combinedStream;
+
+            var dbContext = arg.dbContext;
+
+            if (combinedStream.source is not SourceStream) return false;
+            if (combinedStream.isGroupedStream) return false;
+            if (combinedStream.joins?.Any() == true) return false;
+            if (combinedStream.distinct != true) return false;
+            if (combinedStream.select == null) return false;
+
+            return true;
+        }
+
+
+
+        public static BsonDocument[] GetPipeline<Entity, ResultEntity>(QueryExecutorArgument arg, EntityReader.EntityReader entityReader)
+        {
+            // #1
+            CombinedStream combinedStream = arg.combinedStream;
+            var dbContext = arg.dbContext;
+            var translateService = dbContext.translateService;
+
+            // #2 filter
+            var filter = combinedStream?.where == null ? null : translateService.TranslateFilter(arg, combinedStream.where);
+
+            // #3 groupByFields
+            List<(string field, string fieldAs)> groupFields = entityReader.entityArgReaders.Select(f => (f.fieldPath, f.argName)).ToList();
+            var groupByFields = new BsonDocument(groupFields.ToDictionary(f => f.fieldAs, f => "$" + f.field));
+            var groupFieldArg = new QueryExecutorArgument_GroupFilter(arg, groupFields);
+
+            // #5 order by fields
+            List<(string field, bool asc, int index)> orderFields = combinedStream.orders?.Select((orderField, index) =>
+            {
+                var field = groupFieldArg.GetFieldPath(orderField.member);
+                return (field, orderField.asc, index);
+            }).ToList();
+            var orderByFields = orderFields == null ? null : new BsonDocument(orderFields.ToDictionary(field => field.field, field => BsonValue.Create(field.asc ? 1 : -1)));
+
+
+            // Aggregation pipeline
+            var pipeline = new[]
+            {
+                    //new BsonDocument("$match", new BsonDocument("userId", new BsonDocument("$gt", 1))),
+                    filter == null ? null : new BsonDocument("$match", filter),
+
+                    new BsonDocument("$group", new BsonDocument
+                    {
+                        //{ "_id", new BsonDocument { { "userFatherId", "$userFatherId" }, { "userMotherId", "$userMotherId" } } },
+                        { "_id", groupByFields },
+                    }),
+                     
+
+                    //new BsonDocument("$sort", new BsonDocument("count", -1)),
+                     orderByFields == null ? null : new BsonDocument("$sort", orderByFields),
+
+                    new BsonDocument("$project", new BsonDocument
+                    {
+                        { "_id", 1 },
+                    }),
+
+                    //new BsonDocument("$skip", 1),
+                    combinedStream.skip>0 ? new BsonDocument("$skip", combinedStream.skip.Value) : null ,
+
+                    //new BsonDocument("$limit", 5),
+                    combinedStream.take>0 ? new BsonDocument("$limit", combinedStream.take.Value) : null ,
+
+            }.Where(m => m != null).ToArray();
+
+            return pipeline;
+
+        }
+    }
+}

+ 63 - 0
src/Vitorm.MongoDB/SearchExecutor/PlainSearchExecutor.ToList.cs

@@ -0,0 +1,63 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+using MongoDB.Bson;
+using MongoDB.Driver;
+
+using Vitorm.Entity;
+using Vitorm.MongoDB.QueryExecutor;
+using Vitorm.StreamQuery;
+
+
+namespace Vitorm.MongoDB.SearchExecutor
+{
+    public partial class PlainSearchExecutor : ISearchExecutor
+    {
+
+        public List<ResultEntity> ToList<Entity, ResultEntity>(QueryExecutorArgument arg)
+        {
+            CombinedStream combinedStream = arg.combinedStream;
+            var dbContext = arg.dbContext;
+            var entityDescriptor = dbContext.GetEntityDescriptor(typeof(Entity));
+
+            var fluent = ExecuteQuery<Entity, ResultEntity>(arg, entityDescriptor);
+
+
+            List<ResultEntity> result;
+
+            using var cursor = fluent.ToCursor();
+            if (combinedStream.select?.resultSelector != null)
+            {
+                // Select
+                var lambdaExp = combinedStream.select.resultSelector.Lambda_GetLambdaExpression();
+
+                var delSelect = (Func<Entity, ResultEntity>)lambdaExp.Compile();
+                Type resultEntityType = typeof(ResultEntity);
+
+                var entities = ReadList<Entity>(dbContext, entityDescriptor, cursor);
+
+                result = entities.Select(delSelect).ToList();
+            }
+            else
+            {
+                result = ReadList<ResultEntity>(dbContext, entityDescriptor, cursor).ToList();
+            }
+
+            return result;
+        }
+
+        public static IEnumerable<Entity> ReadList<Entity>(DbContext dbContext, IEntityDescriptor entityDescriptor, IAsyncCursor<BsonDocument> cursor)
+        {
+            while (cursor.MoveNext())
+            {
+                IEnumerable<BsonDocument> batch = cursor.Current;
+                foreach (BsonDocument document in batch)
+                {
+                    yield return dbContext.Deserialize<Entity>(document, entityDescriptor);
+                }
+            }
+        }
+
+    }
+}

+ 65 - 0
src/Vitorm.MongoDB/SearchExecutor/PlainSearchExecutor.ToListAsync.cs

@@ -0,0 +1,65 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+using MongoDB.Bson;
+using MongoDB.Driver;
+
+using Vitorm.Entity;
+using Vitorm.MongoDB.QueryExecutor;
+using Vitorm.StreamQuery;
+
+
+namespace Vitorm.MongoDB.SearchExecutor
+{
+    public partial class PlainSearchExecutor : ISearchExecutor
+    {
+
+        public async Task<List<ResultEntity>> ToListAsync<Entity, ResultEntity>(QueryExecutorArgument arg)
+        {
+            CombinedStream combinedStream = arg.combinedStream;
+            var dbContext = arg.dbContext;
+            var entityDescriptor = dbContext.GetEntityDescriptor(typeof(Entity));
+
+            var fluent = ExecuteQuery<Entity, ResultEntity>(arg, entityDescriptor);
+
+
+            List<ResultEntity> result;
+
+            using var cursor = await fluent.ToCursorAsync();
+            if (combinedStream.select?.resultSelector != null)
+            {
+                // Select
+                var lambdaExp = combinedStream.select.resultSelector.Lambda_GetLambdaExpression();
+
+                var delSelect = lambdaExp.Compile() as Func<Entity, ResultEntity>;
+
+                var entities = await ReadListAsync<Entity>(dbContext, entityDescriptor, cursor);
+                result = entities.Select(delSelect).ToList();
+            }
+            else
+            {
+                result = await ReadListAsync<ResultEntity>(dbContext, entityDescriptor, cursor);
+            }
+
+            return result;
+        }
+
+        static async Task<List<Entity>> ReadListAsync<Entity>(DbContext dbContext, IEntityDescriptor entityDescriptor, IAsyncCursor<BsonDocument> cursor)
+        {
+            var list = new List<Entity>();
+            while (await cursor.MoveNextAsync())
+            {
+                foreach (BsonDocument document in cursor.Current)
+                {
+                    var entity = dbContext.Deserialize<Entity>(document, entityDescriptor);
+                    list.Add(entity);
+                }
+            }
+            return list;
+        }
+
+
+    }
+}

+ 25 - 142
src/Vitorm.MongoDB/SearchExecutor/PlainSearchExecutor.cs

@@ -1,12 +1,11 @@
 using System;
-using System.Collections.Generic;
 using System.Linq;
-using System.Threading.Tasks;
 
 using MongoDB.Bson;
 using MongoDB.Driver;
 
 using Vitorm.Entity;
+using Vitorm.MongoDB.QueryExecutor;
 using Vitorm.StreamQuery;
 
 
@@ -14,7 +13,7 @@ namespace Vitorm.MongoDB.SearchExecutor
 {
     public partial class PlainSearchExecutor : ISearchExecutor
     {
-        protected virtual bool IsMatch<ResultEntity>(SearchExecutorArgument<ResultEntity> arg)
+        public virtual bool IsMatch(QueryExecutorArgument arg)
         {
             CombinedStream combinedStream = arg.combinedStream;
 
@@ -29,129 +28,9 @@ namespace Vitorm.MongoDB.SearchExecutor
         }
 
 
-        #region Async
 
-        public async Task<bool> ExecuteSearchAsync<Entity, ResultEntity>(SearchExecutorArgument<ResultEntity> arg)
-        {
-            if (!IsMatch(arg)) return false;
-
-            #region getList
-            if (arg.getList)
-            {
-                CombinedStream combinedStream = arg.combinedStream;
-                var dbContext = arg.dbContext;
-                var entityDescriptor = dbContext.GetEntityDescriptor(typeof(Entity));
-
-                var fluent = ExecuteQuery<Entity, ResultEntity>(arg, entityDescriptor);
-
-
-                List<ResultEntity> result;
-
-                using var cursor = await fluent.ToCursorAsync();
-                if (combinedStream.select?.resultSelector != null)
-                {
-                    // Select
-                    var lambdaExp = combinedStream.select.resultSelector.Lambda_GetLambdaExpression();
-
-                    var delSelect = lambdaExp.Compile() as Func<Entity, ResultEntity>;
-
-                    var entities = await ReadListAsync<Entity>(dbContext, entityDescriptor, cursor);
-                    result = entities.Select(delSelect).ToList();
-                }
-                else
-                {
-                    result = await ReadListAsync<ResultEntity>(dbContext, entityDescriptor, cursor);
-                }
-
-                arg.list = result;
-                return true;
-            }
-            #endregion
-
-            return false;
-        }
 
-        static async Task<List<Entity>> ReadListAsync<Entity>(DbContext dbContext, IEntityDescriptor entityDescriptor, IAsyncCursor<BsonDocument> cursor)
-        {
-            var list = new List<Entity>();
-            while (await cursor.MoveNextAsync())
-            {
-                IEnumerable<BsonDocument> batch = cursor.Current;
-                foreach (BsonDocument document in batch)
-                {
-                    var entity = dbContext.Deserialize<Entity>(document, entityDescriptor);
-                    list.Add(entity);
-                }
-            }
-            return list;
-        }
-        #endregion
-
-
-
-        #region Sync
-
-        public bool ExecuteSearch<Entity, ResultEntity>(SearchExecutorArgument<ResultEntity> arg)
-        {
-            if (!IsMatch(arg)) return false;
-
-            #region getList
-            if (arg.getList)
-            {
-                CombinedStream combinedStream = arg.combinedStream;
-                var dbContext = arg.dbContext;
-                var entityDescriptor = dbContext.GetEntityDescriptor(typeof(Entity));
-
-                var fluent = ExecuteQuery<Entity, ResultEntity>(arg, entityDescriptor);
-
-
-                // #6 read entity
-                List<ResultEntity> result;
-
-                using var cursor = fluent.ToCursor();
-                if (combinedStream.select?.resultSelector != null)
-                {
-                    // Select
-                    var lambdaExp = combinedStream.select.resultSelector.Lambda_GetLambdaExpression();
-
-                    var delSelect = (Func<Entity, ResultEntity>)lambdaExp.Compile();
-                    Type resultEntityType = typeof(ResultEntity);
-
-                    var entities = ReadList<Entity>(dbContext, entityDescriptor, cursor);
-
-                    result = entities.Select(delSelect).ToList();
-                }
-                else
-                {
-                    result = ReadList<ResultEntity>(dbContext, entityDescriptor, cursor).ToList();
-                }
-
-                arg.list = result;
-                return true;
-            }
-            #endregion
-
-            return false;
-        }
-
-        public static IEnumerable<Entity> ReadList<Entity>(DbContext dbContext, IEntityDescriptor entityDescriptor, IAsyncCursor<BsonDocument> cursor)
-        {
-            while (cursor.MoveNext())
-            {
-                IEnumerable<BsonDocument> batch = cursor.Current;
-                foreach (BsonDocument document in batch)
-                {
-                    yield return dbContext.Deserialize<Entity>(document, entityDescriptor);
-                }
-            }
-        }
-        #endregion
-
-
-
-
-
-        IFindFluent<BsonDocument, BsonDocument> ExecuteQuery<Entity, ResultEntity>(SearchExecutorArgument<ResultEntity> arg, IEntityDescriptor entityDescriptor)
+        IFindFluent<BsonDocument, BsonDocument> ExecuteQuery<Entity, ResultEntity>(QueryExecutorArgument arg, IEntityDescriptor entityDescriptor)
         {
             // #1
             CombinedStream combinedStream = arg.combinedStream;
@@ -159,13 +38,20 @@ namespace Vitorm.MongoDB.SearchExecutor
             var translateService = dbContext.translateService;
 
             // #2 filter
-            var filter = translateService.TranslateFilter(arg.execArg, combinedStream);
+            var filter = translateService.TranslateFilter(arg, combinedStream);
 
-            // #3 execute query
-            var database = dbContext.dbConfig.GetDatabase();
-            var collection = database.GetCollection<BsonDocument>(entityDescriptor.tableName);
-            var fluent = dbContext.session == null ? collection.Find(filter) : collection.Find(dbContext.session, filter);
 
+            // #3 sortDoc
+            BsonDocument sortDoc = null;
+            if (combinedStream.orders?.Any() == true)
+            {
+                sortDoc = new BsonDocument();
+                combinedStream.orders.ForEach(item =>
+                {
+                    var fieldPath = translateService.GetFieldPath(arg, item.member);
+                    sortDoc.Add(fieldPath, BsonValue.Create(item.asc ? 1 : -1));
+                });
+            }
 
             // #4 Event_OnExecuting
             dbContext.Event_OnExecuting(new Lazy<ExecuteEventArgument>(() => new ExecuteEventArgument(
@@ -174,25 +60,22 @@ namespace Vitorm.MongoDB.SearchExecutor
                 extraParam: new()
                 {
                     ["entityDescriptor"] = entityDescriptor,
-                    ["Method"] = arg.execArg.combinedStream.method ?? "ToList",
+                    ["Method"] = arg.combinedStream.method ?? "ToList",
                     ["combinedStream"] = combinedStream,
+                    ["sortDoc"] = sortDoc,
                 }))
             );
 
-            // #5 sortDoc
-            BsonDocument sortDoc = null;
-            if (combinedStream.orders?.Any() == true)
-            {
-                sortDoc = new BsonDocument();
-                combinedStream.orders.ForEach(item =>
-                {
-                    var fieldPath = translateService.GetFieldPath(arg.execArg, item.member);
-                    sortDoc.Add(fieldPath, BsonValue.Create(item.asc ? 1 : -1));
-                });
-            }
+
+            // #5 execute query
+            var database = dbContext.dbConfig.GetDatabase();
+            var collection = database.GetCollection<BsonDocument>(entityDescriptor.tableName);
+            var fluent = dbContext.session == null ? collection.Find(filter) : collection.Find(dbContext.session, filter);
+
+            // #6 execute query
             if (sortDoc != null) fluent = fluent.Sort(sortDoc);
 
-            // #6 skip take
+            // #7 skip take
             if (combinedStream.skip > 0) fluent = fluent.Skip(combinedStream.skip);
             if (combinedStream.take > 0) fluent = fluent.Limit(combinedStream.take);
 

+ 0 - 24
src/Vitorm.MongoDB/SearchExecutor/SearchExecutorArgument.cs

@@ -1,24 +0,0 @@
-using System.Collections.Generic;
-
-using Vitorm.MongoDB.QueryExecutor;
-using Vitorm.StreamQuery;
-
-namespace Vitorm.MongoDB.SearchExecutor
-{
-    public class SearchExecutorArgument<ResultEntity>
-    {
-        public QueryExecutorArgument execArg;
-        public CombinedStream combinedStream => execArg.combinedStream;
-        public DbContext dbContext => execArg.dbContext;
-
-        public bool getList;
-        public bool getTotalCount;
-
-
-        public List<ResultEntity> list;
-        public int? totalCount;
-        public object extraResult;
-
-    }
-
-}

+ 0 - 54
test/Vitorm.MongoDB.MsTest/CommonTest/Orm_Extensions_ExecuteDeleteAsync_Test.cs

@@ -1,54 +0,0 @@
-using System.Data;
-
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-
-namespace Vitorm.MsTest.CommonTest
-{
-
-    [TestClass]
-    public class Orm_Extensions_ExecuteDeleteAsync_Test
-    {
-
-        [TestMethod]
-        public async Task Test_ExecuteDelete()
-        {
-            {
-                using var dbContext = DataSource.CreateDbContext();
-                var userQuery = dbContext.Query<User>();
-
-                var query = from user in userQuery
-                            from father in userQuery.Where(father => user.fatherId == father.id).DefaultIfEmpty()
-                            where user.id <= 5 && father != null
-                            select new
-                            {
-                                user,
-                                father
-                            };
-
-                var rowCount = await query.ExecuteDeleteAsync();
-
-                Assert.AreEqual(3, rowCount);
-
-                var newUsers = userQuery.ToList();
-                Assert.AreEqual(3, newUsers.Count());
-                Assert.AreEqual(4, newUsers.First().id);
-                Assert.AreEqual(6, newUsers.Last().id);
-            }
-
-            {
-                using var dbContext = DataSource.CreateDbContext();
-                var userQuery = dbContext.Query<User>();
-
-                var rowCount = await userQuery.Where(m => m.id == 2 || m.id == 4).ExecuteDeleteAsync();
-
-                Assert.AreEqual(2, rowCount);
-
-                var newUsers = userQuery.ToList();
-                Assert.AreEqual(4, newUsers.Count());
-                Assert.AreEqual(1, newUsers.First().id);
-                Assert.AreEqual(3, newUsers[1].id);
-                Assert.AreEqual(5, newUsers[2].id);
-            }
-        }
-    }
-}

+ 0 - 54
test/Vitorm.MongoDB.MsTest/CommonTest/Orm_Extensions_ExecuteDelete_Test.cs

@@ -1,54 +0,0 @@
-using System.Data;
-
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-
-namespace Vitorm.MsTest.CommonTest
-{
-
-    [TestClass]
-    public class Orm_Extensions_ExecuteDelete_Test
-    {
-
-        [TestMethod]
-        public void Test_ExecuteDelete()
-        {
-            {
-                using var dbContext = DataSource.CreateDbContext();
-                var userQuery = dbContext.Query<User>();
-
-                var query = from user in userQuery
-                            from father in userQuery.Where(father => user.fatherId == father.id).DefaultIfEmpty()
-                            where user.id <= 5 && father != null
-                            select new
-                            {
-                                user,
-                                father
-                            };
-
-                var rowCount = query.ExecuteDelete();
-
-                Assert.AreEqual(3, rowCount);
-
-                var newUsers = userQuery.ToList();
-                Assert.AreEqual(3, newUsers.Count());
-                Assert.AreEqual(4, newUsers.First().id);
-                Assert.AreEqual(6, newUsers.Last().id);
-            }
-
-            {
-                using var dbContext = DataSource.CreateDbContext();
-                var userQuery = dbContext.Query<User>();
-
-                var rowCount = userQuery.Where(m => m.id == 2 || m.id == 4).ExecuteDelete();
-
-                Assert.AreEqual(2, rowCount);
-
-                var newUsers = userQuery.ToList();
-                Assert.AreEqual(4, newUsers.Count());
-                Assert.AreEqual(1, newUsers.First().id);
-                Assert.AreEqual(3, newUsers[1].id);
-                Assert.AreEqual(5, newUsers[2].id);
-            }
-        }
-    }
-}

+ 0 - 78
test/Vitorm.MongoDB.MsTest/CommonTest/Orm_Extensions_ExecuteUpdateAsync_Test.cs

@@ -1,78 +0,0 @@
-using System.Data;
-
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-
-namespace Vitorm.MsTest.CommonTest
-{
-    [TestClass]
-    public class Orm_Extensions_ExecuteUpdateAsync_Test
-    {
-        [TestMethod]
-        public async Task Test_ExecuteUpdate()
-        {
-            using var dbContext = DataSource.CreateDbContext();
-            var userQuery = dbContext.Query<User>();
-
-            {
-                var count = await userQuery.ExecuteUpdateAsync(row => new User
-                {
-                    name = "u_" + row.id + "_" + (row.fatherId.ToString() ?? "") + "_" + (row.motherId.ToString() ?? ""),
-                    birth = DateTime.Parse("2021-01-11 00:00:00")
-                });
-
-                Assert.AreEqual(6, count);
-
-                var userList = userQuery.ToList();
-                Assert.AreEqual("u_1_4_6", userList.First().name);
-                Assert.AreEqual(DateTime.Parse("2021-01-11 00:00:00"), userList.First().birth);
-                Assert.AreEqual("u_6__", userList.Last().name);
-            }
-
-            {
-                var query = from user in userQuery
-                            from father in userQuery.Where(father => user.fatherId == father.id).DefaultIfEmpty()
-                            select new
-                            {
-                                user,
-                                father,
-                                user.motherId
-                            };
-
-                var count = await query.ExecuteUpdateAsync(row => new User
-                {
-                    name = "u2_" + row.user.id + "_" + (row.father.id.ToString() ?? "") + "_" + (row.motherId.ToString() ?? "")
-                });
-                Assert.AreEqual(6, count);
-
-
-                var userList = userQuery.ToList();
-                Assert.AreEqual("u2_1_4_6", userList.First().name);
-                Assert.AreEqual("u2_6__", userList.Last().name);
-            }
-
-            {
-                var query = from user in userQuery
-                            from father in userQuery.Where(father => user.fatherId == father.id).DefaultIfEmpty()
-                            where user.id <= 5 && father != null
-                            select new
-                            {
-                                user,
-                                father,
-                                user.motherId
-                            };
-
-                var count = await query.ExecuteUpdateAsync(row => new User
-                {
-                    name = "u3_" + row.user.id + "_" + (row.father.id.ToString() ?? "") + "_" + (row.motherId.ToString() ?? "")
-                });
-                Assert.AreEqual(3, count);
-
-
-                var userList = userQuery.ToList();
-                Assert.AreEqual("u3_1_4_6", userList[0].name);
-                Assert.AreEqual("u3_3_5_6", userList[2].name);
-                Assert.AreEqual("u2_4__", userList[3].name);
-            }
-        }
-    }
-}

+ 0 - 80
test/Vitorm.MongoDB.MsTest/CommonTest/Orm_Extensions_ExecuteUpdate_Test.cs

@@ -1,80 +0,0 @@
-using System.Data;
-
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-
-namespace Vitorm.MsTest.CommonTest
-{
-
-    [TestClass]
-    public class Orm_Extensions_ExecuteUpdate_Test
-    {
-
-        [TestMethod]
-        public void Test_ExecuteUpdate()
-        {
-            using var dbContext = DataSource.CreateDbContext();
-            var userQuery = dbContext.Query<User>();
-
-            {
-                var count = userQuery.ExecuteUpdate(row => new User
-                {
-                    name = "u_" + row.id + "_" + (row.fatherId.ToString() ?? "") + "_" + (row.motherId.ToString() ?? ""),
-                    birth = DateTime.Parse("2021-01-11 00:00:00")
-                });
-
-                Assert.AreEqual(6, count);
-
-                var userList = userQuery.ToList();
-                Assert.AreEqual("u_1_4_6", userList.First().name);
-                Assert.AreEqual(DateTime.Parse("2021-01-11 00:00:00"), userList.First().birth);
-                Assert.AreEqual("u_6__", userList.Last().name);
-            }
-
-            {
-                var query = from user in userQuery
-                            from father in userQuery.Where(father => user.fatherId == father.id).DefaultIfEmpty()
-                            select new
-                            {
-                                user,
-                                father,
-                                user.motherId
-                            };
-
-                var count = query.ExecuteUpdate(row => new User
-                {
-                    name = "u2_" + row.user.id + "_" + (row.father.id.ToString() ?? "") + "_" + (row.motherId.ToString() ?? "")
-                });
-                Assert.AreEqual(6, count);
-
-
-                var userList = userQuery.ToList();
-                Assert.AreEqual("u2_1_4_6", userList.First().name);
-                Assert.AreEqual("u2_6__", userList.Last().name);
-            }
-
-            {
-                var query = from user in userQuery
-                            from father in userQuery.Where(father => user.fatherId == father.id).DefaultIfEmpty()
-                            where user.id <= 5 && father != null
-                            select new
-                            {
-                                user,
-                                father,
-                                user.motherId
-                            };
-
-                var count = query.ExecuteUpdate(row => new User
-                {
-                    name = "u3_" + row.user.id + "_" + (row.father.id.ToString() ?? "") + "_" + (row.motherId.ToString() ?? "")
-                });
-                Assert.AreEqual(3, count);
-
-
-                var userList = userQuery.ToList();
-                Assert.AreEqual("u3_1_4_6", userList[0].name);
-                Assert.AreEqual("u3_3_5_6", userList[2].name);
-                Assert.AreEqual("u2_4__", userList[3].name);
-            }
-        }
-    }
-}

+ 10 - 10
test/Vitorm.MongoDB.MsTest/CommonTest/Query_Distinct_Test.cs

@@ -45,20 +45,20 @@ namespace Vitorm.MsTest.CommonTest
                 Assert.AreEqual(0, fatherIds.Except(new int?[] { 4, 5, null }).Count());
                 Assert.AreEqual(0, motherIds.Except(new int?[] { 6, null }).Count());
             }
-            {
-                var query = userQuery.Select(u => new { user = u, u.fatherId, u.motherId }).Distinct();
+            //{
+            //    var query = userQuery.Select(u => new { user = u, u.fatherId, u.motherId }).Distinct();
 
-                var userList = query.ToList();
+            //    var userList = query.ToList();
 
-                Assert.AreEqual(6, userList.Count);
-            }
-            {
-                var query = userQuery.Distinct();
+            //    Assert.AreEqual(6, userList.Count);
+            //}
+            //{
+            //    var query = userQuery.Distinct();
 
-                var userList = query.ToList();
+            //    var userList = query.ToList();
 
-                Assert.AreEqual(6, userList.Count);
-            }
+            //    Assert.AreEqual(6, userList.Count);
+            //}
 
         }
 

+ 75 - 0
test/Vitorm.MongoDB.MsTest/CustomTest/Query_Method_Test.cs

@@ -0,0 +1,75 @@
+using System.Data;
+
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+using Vit.Linq;
+
+namespace Vitorm.MsTest.CustomTest
+{
+
+    [TestClass]
+    public class Query_Method_Test
+    {
+
+        [TestMethod]
+        public async Task ToListAsync()
+        {
+            using var dbContext = DataSource.CreateDbContext();
+            var userQuery = dbContext.Query<User>();
+
+            // PlainQuery
+            {
+                var list = await userQuery.OrderBy(m => m.fatherId).Select(m => new { fatherId = m.fatherId }).ToListAsync();
+                var ids = String.Join(',', list.Select(m => m.fatherId));
+                Assert.AreEqual(",,,4,4,5", ids);
+            }
+
+            // Group
+            {
+                var list = await userQuery.GroupBy(m => m.fatherId).OrderBy(g => g.Key).Select(g => new { fatherId = g.Key }).ToListAsync();
+                var ids = String.Join(',', list.Select(m => m.fatherId));
+                Assert.AreEqual(",4,5", ids);
+            }
+
+            // PlainDistinctSearch
+            {
+                var list = await userQuery.Select(m => new { fatherId = m.fatherId }).OrderBy(m => m.fatherId).Distinct().ToListAsync();
+                var ids = String.Join(',', list.Select(m => m.fatherId));
+                Assert.AreEqual(",4,5", ids);
+            }
+        }
+
+
+
+        [TestMethod]
+        public void ToList()
+        {
+            using var dbContext = DataSource.CreateDbContext();
+            var userQuery = dbContext.Query<User>();
+
+            // PlainQuery
+            {
+                var list = userQuery.OrderBy(m => m.fatherId).Select(m => new { fatherId = m.fatherId }).ToList();
+                var ids = String.Join(',', list.Select(m => m.fatherId));
+                Assert.AreEqual(",,,4,4,5", ids);
+            }
+
+            // Group
+            {
+                var list = userQuery.GroupBy(m => m.fatherId).OrderBy(g => g.Key).Select(g => new { fatherId = g.Key }).ToList();
+                var ids = String.Join(',', list.Select(m => m.fatherId));
+                Assert.AreEqual(",4,5", ids);
+            }
+
+            // PlainDistinctSearch
+            {
+                var list = userQuery.Select(m => new { fatherId = m.fatherId }).OrderBy(m => m.fatherId).Distinct().ToList();
+                var ids = String.Join(',', list.Select(m => m.fatherId));
+                Assert.AreEqual(",4,5", ids);
+            }
+        }
+
+
+
+    }
+}