public QueryOptimization(Snapshot snapshot, Query query, IEnumerable<BsonDocument> source, Collation collation) { if (query.Select == null) throw new ArgumentNullException(nameof(query.Select)); _snapshot = snapshot; _query = query; _collation = collation; _queryPlan = new QueryPlan(snapshot.CollectionName) { // define index only if source are external collection Index = source != null ? new IndexVirtual(source) : null, Select = new Select(_query.Select, _query.Select.UseSource), ForUpdate = query.ForUpdate, Limit = query.Limit, Offset = query.Offset }; }
/// <summary> /// Abstract method to be implement according pipe workflow /// </summary> public abstract IAsyncEnumerable <BsonDocument> Pipe(IAsyncEnumerable <IndexNode> nodes, QueryPlan query);
/// <summary> /// Query Pipe order /// - LoadDocument /// - IncludeBefore /// - Filter /// - OrderBy /// - OffSet /// - Limit /// - IncludeAfter /// - Select /// </summary> public override IEnumerable <BsonDocument> Pipe(IEnumerable <IndexNode> nodes, QueryPlan query) { // starts pipe loading document var source = this.LoadDocument(nodes); // do includes in result before filter foreach (var path in query.IncludeBefore) { source = this.Include(source, path); } // filter results according expressions foreach (var expr in query.Filters) { source = this.Filter(source, expr); } if (query.OrderBy != null) { // pipe: orderby with offset+limit source = this.OrderBy(source, query.OrderBy.Expression, query.OrderBy.Order, query.Offset, query.Limit); } else { // pipe: apply offset (no orderby) if (query.Offset > 0) { source = source.Skip(query.Offset); } // pipe: apply limit (no orderby) if (query.Limit < int.MaxValue) { source = source.Take(query.Limit); } } // do includes in result after filter foreach (var path in query.IncludeAfter) { source = this.Include(source, path); } // if is an aggregate query, run select transform over all resultset - will return a single value if (query.Select.All) { return(this.SelectAll(source, query.Select.Expression)); } // run select transform in each document and return a new document or value else { return(this.Select(source, query.Select.Expression)); } }
/// <summary> /// GroupBy Pipe Order /// - LoadDocument /// - Filter /// - OrderBy (to GroupBy) /// - GroupBy /// - HavingSelectGroupBy /// - OffSet /// - Limit /// </summary> public override IEnumerable <BsonDocument> Pipe(IEnumerable <IndexNode> nodes, QueryPlan query) { // starts pipe loading document var source = this.LoadDocument(nodes); // filter results according filter expressions foreach (var expr in query.Filters) { source = this.Filter(source, expr); } // run orderBy used in GroupBy (if not already ordered by index) if (query.OrderBy != null) { source = this.OrderBy(source, query.OrderBy.Expression, query.OrderBy.Order, 0, int.MaxValue); } // apply groupby var groups = this.GroupBy(source, query.GroupBy); // apply group filter and transform result var result = this.SelectGroupBy(groups, query.GroupBy); // apply offset if (query.Offset > 0) { result = result.Skip(query.Offset); } // apply limit if (query.Limit < int.MaxValue) { result = result.Take(query.Limit); } return(result); }
/// <summary> /// Query Pipe order /// - LoadDocument /// - IncludeBefore /// - Filter /// - OrderBy /// - OffSet /// - Limit /// - IncludeAfter /// - Select /// </summary> public override async IAsyncEnumerable <BsonDocument> Pipe(IAsyncEnumerable <IndexNode> nodes, QueryPlan query) { // starts pipe loading document var source = this.LoadDocument(nodes); // do includes in result before filter foreach (var path in query.IncludeBefore) { source = this.Include(source, path); } // filter results according expressions foreach (var expr in query.Filters) { source = this.Filter(source, expr); } if (query.OrderBy != null) { // pipe: orderby with offset+limit source = this.OrderBy(source, query.OrderBy.Expression, query.OrderBy.Order, query.Offset, query.Limit); } else { // pipe: apply offset (no orderby) if (query.Offset > 0) { source = source.SkipAsync(query.Offset); } // pipe: apply limit (no orderby) if (query.Limit < int.MaxValue) { source = source.TakeAsync(query.Limit); } } // do includes in result after filter foreach (var path in query.IncludeAfter) { source = this.Include(source, path); } var result = query.Select.All ? this.SelectAll(source, query.Select.Expression) : this.Select(source, query.Select.Expression); await foreach (var doc in result) { yield return(doc); } }