public async Task <DynamicListResponseDataModel> Query(DatabaseConnection databaseConnection, DynamicList dynamicList, DynamicListFetchDataModel fetchDataModel) { dynamicList.GenerateFilters(); var dynamicListResponseDataModel = new DynamicListResponseDataModel(); // Because this flow is very complicated. We MUST UPDATE this flow fequently // 1. Extract collection name for executing // 2. Prepare execution query with some parts: // 2.1 Params // 2.2 Combine with Filters // 2.3 Sort // 3. Execute query with pagination // 4. Response data // 5. Note: in case entity, we still use the same way with EntityName field // 1. Extract collection name for executing, either Entity mode or Custom mode, EntityName is the same var executingQuery = dynamicList.ListDatasource.DatabaseConnectionOptions.Query; var parsingObject = JObject.Parse(executingQuery); var executingCollectionName = (parsingObject[Constants.QUERY_KEY].First as JProperty).Name; // 2. Prepare execution query // 2.2 Combine with Text Search, Filters and Sort var mongoCollection = new MongoClient(databaseConnection.ConnectionString).GetDatabase(databaseConnection.DataSource).GetCollection <BsonDocument>(executingCollectionName); var filterDefinitionOptions = FilterDefinition <BsonDocument> .Empty; var collectionQuery = parsingObject[Constants.QUERY_KEY][executingCollectionName].ToString(Newtonsoft.Json.Formatting.Indented); foreach (var filledParam in fetchDataModel.FilledParameterOptions.FilledParameters) { collectionQuery = collectionQuery.Replace("{{" + filledParam.Name + "}}", filledParam.Value); } collectionQuery = _mongoOptions.CurrentValue.EliminateDoubleQuotes(collectionQuery); var aggregatePipes = BsonSerializer.Deserialize <BsonDocument[]>(collectionQuery).Select(a => (PipelineStageDefinition <BsonDocument, BsonDocument>)a).ToList(); var aggregateFluent = mongoCollection.Aggregate(); foreach (var pipe in aggregatePipes) { aggregateFluent = aggregateFluent.AppendStage(pipe); } // Add Text search first if had if (!string.IsNullOrEmpty(fetchDataModel.TextSearch)) { aggregateFluent = aggregateFluent.Match(CombineTextSearch(fetchDataModel.TextSearch, dynamicList.FiltersList)); } // Add Filter Options if had if (fetchDataModel.FilterGroupOptions != null && fetchDataModel.FilterGroupOptions.FilterGroups != null && fetchDataModel.FilterGroupOptions.FilterGroups.Count > 0 && (fetchDataModel.FilterGroupOptions.FilterGroups[0].FilterOptions.Count > 0)) { aggregateFluent = aggregateFluent.AppendStage(PipelineStageDefinitionBuilder.Match(BuildFilters(fetchDataModel.FilterGroupOptions.FilterGroups))); } // Projection only columns var projectDoc = new BsonDocument(); foreach (var column in dynamicList.ColumnsList.ColumnDefs) { projectDoc.Add(new BsonElement(column.Name, 1)); } var projection = new BsonDocument { { "$project", projectDoc } }; aggregateFluent = aggregateFluent.AppendStage((PipelineStageDefinition <BsonDocument, BsonDocument>)projection); // Add Sort if had if (fetchDataModel.SortOptions.SortableFields != null && fetchDataModel.SortOptions.SortableFields.Count > 0) { var sortField = fetchDataModel.SortOptions.SortableFields[0]; FieldDefinition <BsonDocument, string> field = sortField.FieldName; var sortDefinition = sortField.SortType == SortType.Asc ? Builders <BsonDocument> .Sort.Ascending(field) : Builders <BsonDocument> .Sort.Descending(field); aggregateFluent = aggregateFluent.AppendStage(PipelineStageDefinitionBuilder.Sort(sortDefinition)); } if (fetchDataModel.PaginationOptions.NeedTotalItems) { var aggregateFluentForCountTotal = aggregateFluent.Count(); var totalItems = await aggregateFluentForCountTotal.FirstOrDefaultAsync(); dynamicListResponseDataModel.TotalItems = totalItems != null ? totalItems.Count : 0; } if (fetchDataModel.PaginationOptions.NeedTotalItems && dynamicListResponseDataModel.TotalItems > 0) { // Add Pagination aggregateFluent = aggregateFluent .Skip(fetchDataModel.PaginationOptions.PageNumber * fetchDataModel.PaginationOptions.PageSize) .Limit(fetchDataModel.PaginationOptions.PageSize); using (var executingCursor = await aggregateFluent.ToCursorAsync()) { while (executingCursor.MoveNext()) { var currentBson = executingCursor.Current; foreach (var item in currentBson) { var addedFields = new List <BsonElement>(); var removedFields = new List <string>(); foreach (var elem in item) { if (elem.Value.IsObjectId) { addedFields.Add(new BsonElement(elem.Name == "_id" ? "id" : elem.Name, BsonValue.Create(elem.Value.AsObjectId.ToString()))); removedFields.Add(elem.Name); } } foreach (var removedField in removedFields) { item.Remove(removedField); } foreach (var addedField in addedFields) { item.Add(addedField); } } // Important note: this query must have one row result for extracting params and filters dynamicListResponseDataModel.Data = currentBson .Select(a => a.ToJson( new MongoDB.Bson.IO.JsonWriterSettings { OutputMode = MongoDB.Bson.IO.JsonOutputMode.Strict })).Select(b => JsonConvert.DeserializeObject <dynamic>(b, new BsonConverter(GetFormatFields(dynamicList.ColumnsList.ColumnDefs)))).ToList(); } } } else if (!fetchDataModel.PaginationOptions.NeedTotalItems) { // Add Pagination aggregateFluent = aggregateFluent .Skip(fetchDataModel.PaginationOptions.PageNumber * fetchDataModel.PaginationOptions.PageSize) .Limit(fetchDataModel.PaginationOptions.PageSize); using (var executingCursor = await aggregateFluent.ToCursorAsync()) { while (executingCursor.MoveNext()) { var currentBson = executingCursor.Current; foreach (var item in currentBson) { var addedFields = new List <BsonElement>(); var removedFields = new List <string>(); foreach (var elem in item) { if (elem.Value.IsObjectId) { addedFields.Add(new BsonElement(elem.Name == "_id" ? "id" : elem.Name, BsonValue.Create(elem.Value.AsObjectId.ToString()))); removedFields.Add(elem.Name); } } foreach (var removedField in removedFields) { item.Remove(removedField); } foreach (var addedField in addedFields) { item.Add(addedField); } } // Important note: this query must have one row result for extracting params and filters dynamicListResponseDataModel.Data = currentBson .Select(a => a.ToJson( new MongoDB.Bson.IO.JsonWriterSettings { OutputMode = MongoDB.Bson.IO.JsonOutputMode.Strict })).Select(b => JsonConvert.DeserializeObject <dynamic>(b, new BsonConverter(GetFormatFields(dynamicList.ColumnsList.ColumnDefs)))).ToList(); } } } return(dynamicListResponseDataModel); }