/* Copyright 2010-present MongoDB Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Threading; using System.Threading.Tasks; using MongoDB.Bson; using MongoDB.Bson.Serialization; using MongoDB.Driver.Core.Misc; namespace MongoDB.Driver { /// /// Extension methods for /// public static class IAggregateFluentExtensions { /// /// Appends a $bucket stage to the pipeline. /// /// The type of the result. /// The type of the value. /// The aggregate. /// The expression providing the value to group by. /// The bucket boundaries. /// The options. /// The fluent aggregate interface. public static IAggregateFluent> Bucket( this IAggregateFluent aggregate, Expression> groupBy, IEnumerable boundaries, AggregateBucketOptions options = null) { Ensure.IsNotNull(aggregate, nameof(aggregate)); return aggregate.AppendStage(PipelineStageDefinitionBuilder.Bucket(groupBy, boundaries, options)); } /// /// Appends a $bucket stage to the pipeline. /// /// The type of the result. /// The type of the value. /// The type of the new result. /// The aggregate. /// The expression providing the value to group by. /// The bucket boundaries. /// The output projection. /// The options. /// The fluent aggregate interface. public static IAggregateFluent Bucket( this IAggregateFluent aggregate, Expression> groupBy, IEnumerable boundaries, Expression, TNewResult>> output, AggregateBucketOptions options = null) { Ensure.IsNotNull(aggregate, nameof(aggregate)); return aggregate.AppendStage(PipelineStageDefinitionBuilder.Bucket(groupBy, boundaries, output, options)); } /// /// Appends a $bucketAuto stage to the pipeline. /// /// The type of the result. /// The type of the value. /// The aggregate. /// The expression providing the value to group by. /// The number of buckets. /// The options (optional). /// The fluent aggregate interface. public static IAggregateFluent> BucketAuto( this IAggregateFluent aggregate, Expression> groupBy, int buckets, AggregateBucketAutoOptions options = null) { Ensure.IsNotNull(aggregate, nameof(aggregate)); return aggregate.AppendStage(PipelineStageDefinitionBuilder.BucketAuto(groupBy, buckets, options)); } /// /// Appends a $bucketAuto stage to the pipeline. /// /// The type of the result. /// The type of the value. /// The type of the new result. /// The aggregate. /// The expression providing the value to group by. /// The number of buckets. /// The output projection. /// The options (optional). /// The fluent aggregate interface. public static IAggregateFluent BucketAuto( this IAggregateFluent aggregate, Expression> groupBy, int buckets, Expression, TNewResult>> output, AggregateBucketAutoOptions options = null) { Ensure.IsNotNull(aggregate, nameof(aggregate)); return aggregate.AppendStage(PipelineStageDefinitionBuilder.BucketAuto(groupBy, buckets, output, options)); } /// /// Appends a $facet stage to the pipeline. /// /// The type of the result. /// The aggregate. /// The facets. /// The fluent aggregate interface. public static IAggregateFluent Facet( this IAggregateFluent aggregate, IEnumerable> facets) { Ensure.IsNotNull(aggregate, nameof(aggregate)); return aggregate.AppendStage(PipelineStageDefinitionBuilder.Facet(facets)); } /// /// Appends a $facet stage to the pipeline. /// /// The type of the result. /// The aggregate. /// The facets. /// The fluent aggregate interface. public static IAggregateFluent Facet( this IAggregateFluent aggregate, params AggregateFacet[] facets) { Ensure.IsNotNull(aggregate, nameof(aggregate)); return aggregate.AppendStage(PipelineStageDefinitionBuilder.Facet(facets)); } /// /// Appends a $facet stage to the pipeline. /// /// The type of the result. /// The type of the new result. /// The aggregate. /// The facets. /// /// The fluent aggregate interface. /// public static IAggregateFluent Facet( this IAggregateFluent aggregate, params AggregateFacet[] facets) { Ensure.IsNotNull(aggregate, nameof(aggregate)); return aggregate.AppendStage(PipelineStageDefinitionBuilder.Facet(facets)); } /// /// Appends a $graphLookup stage to the pipeline. /// /// The type of the result. /// The type of the from documents. /// The type of the connect from field (must be either TConnectTo or a type that implements IEnumerable{TConnectTo}). /// The type of the connect to field. /// The type of the start with expression (must be either TConnectTo or a type that implements IEnumerable{TConnectTo}). /// The type of the as field. /// The type of the new result (must be same as TResult with an additional as field). /// The aggregate. /// The from collection. /// The connect from field. /// The connect to field. /// The start with value. /// The as field. /// The options. /// The fluent aggregate interface. public static IAggregateFluent GraphLookup( this IAggregateFluent aggregate, IMongoCollection from, FieldDefinition connectFromField, FieldDefinition connectToField, AggregateExpressionDefinition startWith, FieldDefinition @as, AggregateGraphLookupOptions options = null) where TAs : IEnumerable { Ensure.IsNotNull(aggregate, nameof(aggregate)); return aggregate.AppendStage(PipelineStageDefinitionBuilder.GraphLookup(from, connectFromField, connectToField, startWith, @as, options)); } /// /// Appends a $graphLookup stage to the pipeline. /// /// The type of the result. /// The type of the from documents. /// The aggregate. /// The from collection. /// The connect from field. /// The connect to field. /// The start with value. /// The as field. /// The depth field. /// The fluent aggregate interface. public static IAggregateFluent GraphLookup( this IAggregateFluent aggregate, IMongoCollection from, FieldDefinition connectFromField, FieldDefinition connectToField, AggregateExpressionDefinition startWith, FieldDefinition> @as, FieldDefinition depthField = null) { Ensure.IsNotNull(aggregate, nameof(aggregate)); return aggregate.AppendStage(PipelineStageDefinitionBuilder.GraphLookup(from, connectFromField, connectToField, startWith, @as, depthField)); } /// /// Appends a $graphLookup stage to the pipeline. /// /// The type of the result. /// The type of the new result (must be same as TResult with an additional as field). /// The type of the from documents. /// The type of the connect from field (must be either TConnectTo or a type that implements IEnumerable{TConnectTo}). /// The type of the connect to field. /// The type of the start with expression (must be either TConnectTo or a type that implements IEnumerable{TConnectTo}). /// The type of the as field. /// The aggregate. /// The from collection. /// The connect from field. /// The connect to field. /// The start with value. /// The as field. /// The options. /// The fluent aggregate interface. public static IAggregateFluent GraphLookup( this IAggregateFluent aggregate, IMongoCollection from, Expression> connectFromField, Expression> connectToField, Expression> startWith, Expression> @as, AggregateGraphLookupOptions options = null) where TAs : IEnumerable { Ensure.IsNotNull(aggregate, nameof(aggregate)); return aggregate.AppendStage(PipelineStageDefinitionBuilder.GraphLookup(from, connectFromField, connectToField, startWith, @as, options, aggregate.Options?.TranslationOptions)); } /// /// Appends a $graphLookup stage to the pipeline. /// /// The type of the result. /// The type of the from documents. /// The type of the connect from field (must be either TConnectTo or a type that implements IEnumerable{TConnectTo}). /// The type of the connect to field. /// The type of the start with expression (must be either TConnectTo or a type that implements IEnumerable{TConnectTo}). /// The type of the as field elements. /// The type of the as field. /// The type of the new result (must be same as TResult with an additional as field). /// The aggregate. /// The from collection. /// The connect from field. /// The connect to field. /// The start with value. /// The as field. /// The depth field. /// The options. /// The fluent aggregate interface. public static IAggregateFluent GraphLookup( this IAggregateFluent aggregate, IMongoCollection from, Expression> connectFromField, Expression> connectToField, Expression> startWith, Expression> @as, Expression> depthField, AggregateGraphLookupOptions options = null) where TAs : IEnumerable { Ensure.IsNotNull(aggregate, nameof(aggregate)); return aggregate.AppendStage(PipelineStageDefinitionBuilder.GraphLookup(from, connectFromField, connectToField, startWith, @as, depthField, options, aggregate.Options?.TranslationOptions)); } /// /// Appends a group stage to the pipeline. /// /// The type of the result. /// The aggregate. /// The group projection. /// /// The fluent aggregate interface. /// public static IAggregateFluent Group(this IAggregateFluent aggregate, ProjectionDefinition group) { Ensure.IsNotNull(aggregate, nameof(aggregate)); return aggregate.AppendStage(PipelineStageDefinitionBuilder.Group(group)); } /// /// Appends a group stage to the pipeline. /// /// The type of the result. /// The type of the key. /// The type of the new result. /// The aggregate. /// The id. /// The group projection. /// /// The fluent aggregate interface. /// public static IAggregateFluent Group(this IAggregateFluent aggregate, Expression> id, Expression, TNewResult>> group) { Ensure.IsNotNull(aggregate, nameof(aggregate)); return aggregate.AppendStage(PipelineStageDefinitionBuilder.Group(id, group)); } /// /// Appends a lookup stage to the pipeline. /// /// The type of the result. /// The aggregate. /// Name of the foreign collection. /// The local field. /// The foreign field. /// The field in the result to place the foreign matches. /// The fluent aggregate interface. public static IAggregateFluent Lookup( this IAggregateFluent aggregate, string foreignCollectionName, FieldDefinition localField, FieldDefinition foreignField, FieldDefinition @as) { Ensure.IsNotNull(aggregate, nameof(aggregate)); Ensure.IsNotNull(foreignCollectionName, nameof(foreignCollectionName)); var foreignCollection = aggregate.Database.GetCollection(foreignCollectionName); return aggregate.AppendStage(PipelineStageDefinitionBuilder.Lookup(foreignCollection, localField, foreignField, @as)); } /// /// Appends a lookup stage to the pipeline. /// /// The type of the result. /// The type of the foreign collection. /// The type of the new result. /// The aggregate. /// The foreign collection. /// The local field. /// The foreign field. /// The field in the result to place the foreign matches. /// The options. /// The fluent aggregate interface. public static IAggregateFluent Lookup( this IAggregateFluent aggregate, IMongoCollection foreignCollection, Expression> localField, Expression> foreignField, Expression> @as, AggregateLookupOptions options = null) { Ensure.IsNotNull(aggregate, nameof(aggregate)); return aggregate.AppendStage(PipelineStageDefinitionBuilder.Lookup(foreignCollection, localField, foreignField, @as, options)); } /// /// Appends a lookup stage to the pipeline. /// /// The type of the result. /// The aggregate. /// The foreign collection. /// The "let" definition. /// The lookup pipeline. /// The as field in the result in which to place the results of the lookup pipeline. /// The fluent aggregate interface. public static IAggregateFluent Lookup( this IAggregateFluent aggregate, IMongoCollection foreignCollection, BsonDocument let, PipelineDefinition lookupPipeline, FieldDefinition> @as) { Ensure.IsNotNull(aggregate, nameof(aggregate)); Ensure.IsNotNull(foreignCollection, nameof(foreignCollection)); return aggregate.AppendStage(PipelineStageDefinitionBuilder.Lookup, BsonDocument>( foreignCollection, let, lookupPipeline, @as)); } /// /// Appends a lookup stage to the pipeline. /// /// The type of the result. /// The type of the foreign collection documents. /// The type of the as field elements. /// The type of the as field. /// The type of the new result. /// The aggregate. /// The foreign collection. /// The "let" definition. /// The lookup pipeline. /// The as field in in which to place the results of the lookup pipeline. /// The options. /// The fluent aggregate interface. public static IAggregateFluent Lookup( this IAggregateFluent aggregate, IMongoCollection foreignCollection, BsonDocument let, PipelineDefinition lookupPipeline, Expression> @as, AggregateLookupOptions options = null) where TAs : IEnumerable { Ensure.IsNotNull(aggregate, nameof(aggregate)); return aggregate.AppendStage(PipelineStageDefinitionBuilder.Lookup( foreignCollection, let, lookupPipeline, @as, options)); } /// /// Appends a match stage to the pipeline. /// /// The type of the result. /// The aggregate. /// The filter. /// /// The fluent aggregate interface. /// public static IAggregateFluent Match(this IAggregateFluent aggregate, Expression> filter) { Ensure.IsNotNull(aggregate, nameof(aggregate)); return aggregate.AppendStage(PipelineStageDefinitionBuilder.Match(filter)); } /// /// Appends a project stage to the pipeline. /// /// The type of the result. /// The aggregate. /// The projection. /// /// The fluent aggregate interface. /// public static IAggregateFluent Project(this IAggregateFluent aggregate, ProjectionDefinition projection) { Ensure.IsNotNull(aggregate, nameof(aggregate)); return aggregate.AppendStage(PipelineStageDefinitionBuilder.Project(projection)); } /// /// Appends a project stage to the pipeline. /// /// The type of the result. /// The type of the new result. /// The aggregate. /// The projection. /// /// The fluent aggregate interface. /// public static IAggregateFluent Project(this IAggregateFluent aggregate, Expression> projection) { Ensure.IsNotNull(aggregate, nameof(aggregate)); return aggregate.AppendStage(PipelineStageDefinitionBuilder.Project(projection)); } /// /// Appends a $replaceRoot stage to the pipeline. /// /// The type of the result. /// The type of the new result. /// The aggregate. /// The new root. /// /// The fluent aggregate interface. /// public static IAggregateFluent ReplaceRoot( this IAggregateFluent aggregate, Expression> newRoot) { Ensure.IsNotNull(aggregate, nameof(aggregate)); return aggregate.AppendStage(PipelineStageDefinitionBuilder.ReplaceRoot(newRoot)); } /// /// Appends an ascending sort stage to the pipeline. /// /// The type of the result. /// The aggregate. /// The field to sort by. /// /// The fluent aggregate interface. /// public static IOrderedAggregateFluent SortBy(this IAggregateFluent aggregate, Expression> field) { Ensure.IsNotNull(aggregate, nameof(aggregate)); Ensure.IsNotNull(field, nameof(field)); var sort = Builders.Sort.Ascending(field); return (IOrderedAggregateFluent)aggregate.AppendStage(PipelineStageDefinitionBuilder.Sort(sort)); } /// /// Appends a sortByCount stage to the pipeline. /// /// The type of the result. /// The type of the key. /// The aggregate. /// The id. /// /// The fluent aggregate interface. /// public static IAggregateFluent> SortByCount( this IAggregateFluent aggregate, Expression> id) { Ensure.IsNotNull(aggregate, nameof(aggregate)); return aggregate.AppendStage(PipelineStageDefinitionBuilder.SortByCount(id)); } /// /// Appends a descending sort stage to the pipeline. /// /// The type of the result. /// The aggregate. /// The field to sort by. /// /// The fluent aggregate interface. /// public static IOrderedAggregateFluent SortByDescending(this IAggregateFluent aggregate, Expression> field) { Ensure.IsNotNull(aggregate, nameof(aggregate)); Ensure.IsNotNull(field, nameof(field)); var sort = Builders.Sort.Descending(field); return (IOrderedAggregateFluent)aggregate.AppendStage(PipelineStageDefinitionBuilder.Sort(sort)); } /// /// Modifies the current sort stage by appending an ascending field specification to it. /// /// The type of the result. /// The aggregate. /// The field to sort by. /// /// The fluent aggregate interface. /// public static IOrderedAggregateFluent ThenBy(this IOrderedAggregateFluent aggregate, Expression> field) { Ensure.IsNotNull(aggregate, nameof(aggregate)); return aggregate.ThenBy(Builders.Sort.Ascending(field)); } /// /// Modifies the current sort stage by appending a descending field specification to it. /// /// The type of the result. /// The aggregate. /// The field to sort by. /// /// The fluent aggregate interface. /// public static IOrderedAggregateFluent ThenByDescending(this IOrderedAggregateFluent aggregate, Expression> field) { Ensure.IsNotNull(aggregate, nameof(aggregate)); return aggregate.ThenBy(Builders.Sort.Descending(field)); } /// /// Appends an unwind stage to the pipeline. /// /// The type of the result. /// The aggregate. /// The field to unwind. /// /// The fluent aggregate interface. /// public static IAggregateFluent Unwind(this IAggregateFluent aggregate, FieldDefinition field) { Ensure.IsNotNull(aggregate, nameof(aggregate)); return aggregate.AppendStage(PipelineStageDefinitionBuilder.Unwind(field)); } /// /// Appends an unwind stage to the pipeline. /// /// The type of the result. /// The aggregate. /// The field to unwind. /// /// The fluent aggregate interface. /// public static IAggregateFluent Unwind(this IAggregateFluent aggregate, Expression> field) { Ensure.IsNotNull(aggregate, nameof(aggregate)); return aggregate.AppendStage(PipelineStageDefinitionBuilder.Unwind(field)); } /// /// Appends an unwind stage to the pipeline. /// /// The type of the result. /// The type of the new result. /// The aggregate. /// The field to unwind. /// The new result serializer. /// /// The fluent aggregate interface. /// [Obsolete("Use the Unwind overload which takes an options parameter.")] public static IAggregateFluent Unwind(this IAggregateFluent aggregate, Expression> field, IBsonSerializer newResultSerializer) { Ensure.IsNotNull(aggregate, nameof(aggregate)); return aggregate.AppendStage(PipelineStageDefinitionBuilder.Unwind(field, new AggregateUnwindOptions { ResultSerializer = newResultSerializer })); } /// /// Appends an unwind stage to the pipeline. /// /// The type of the result. /// The type of the new result. /// The aggregate. /// The field to unwind. /// The options. /// /// The fluent aggregate interface. /// public static IAggregateFluent Unwind(this IAggregateFluent aggregate, Expression> field, AggregateUnwindOptions options = null) { Ensure.IsNotNull(aggregate, nameof(aggregate)); return aggregate.AppendStage(PipelineStageDefinitionBuilder.Unwind(field, options)); } /// /// Returns the first document of the aggregate result. /// /// The type of the result. /// The aggregate. /// The cancellation token. /// /// The fluent aggregate interface. /// public static TResult First(this IAggregateFluent aggregate, CancellationToken cancellationToken = default(CancellationToken)) { Ensure.IsNotNull(aggregate, nameof(aggregate)); return IAsyncCursorSourceExtensions.First(aggregate.Limit(1), cancellationToken); } /// /// Returns the first document of the aggregate result. /// /// The type of the result. /// The aggregate. /// The cancellation token. /// /// The fluent aggregate interface. /// public static Task FirstAsync(this IAggregateFluent aggregate, CancellationToken cancellationToken = default(CancellationToken)) { Ensure.IsNotNull(aggregate, nameof(aggregate)); return IAsyncCursorSourceExtensions.FirstAsync(aggregate.Limit(1), cancellationToken); } /// /// Returns the first document of the aggregate result, or the default value if the result set is empty. /// /// The type of the result. /// The aggregate. /// The cancellation token. /// /// The fluent aggregate interface. /// public static TResult FirstOrDefault(this IAggregateFluent aggregate, CancellationToken cancellationToken = default(CancellationToken)) { Ensure.IsNotNull(aggregate, nameof(aggregate)); return IAsyncCursorSourceExtensions.FirstOrDefault(aggregate.Limit(1), cancellationToken); } /// /// Returns the first document of the aggregate result, or the default value if the result set is empty. /// /// The type of the result. /// The aggregate. /// The cancellation token. /// /// The fluent aggregate interface. /// public static Task FirstOrDefaultAsync(this IAggregateFluent aggregate, CancellationToken cancellationToken = default(CancellationToken)) { Ensure.IsNotNull(aggregate, nameof(aggregate)); return IAsyncCursorSourceExtensions.FirstOrDefaultAsync(aggregate.Limit(1), cancellationToken); } /// /// Returns the only document of the aggregate result. Throws an exception if the result set does not contain exactly one document. /// /// The type of the result. /// The aggregate. /// The cancellation token. /// /// The fluent aggregate interface. /// public static TResult Single(this IAggregateFluent aggregate, CancellationToken cancellationToken = default(CancellationToken)) { Ensure.IsNotNull(aggregate, nameof(aggregate)); return IAsyncCursorSourceExtensions.Single(aggregate.Limit(2), cancellationToken); } /// /// Returns the only document of the aggregate result. Throws an exception if the result set does not contain exactly one document. /// /// The type of the result. /// The aggregate. /// The cancellation token. /// /// The fluent aggregate interface. /// public static Task SingleAsync(this IAggregateFluent aggregate, CancellationToken cancellationToken = default(CancellationToken)) { Ensure.IsNotNull(aggregate, nameof(aggregate)); return IAsyncCursorSourceExtensions.SingleAsync(aggregate.Limit(2), cancellationToken); } /// /// Returns the only document of the aggregate result, or the default value if the result set is empty. Throws an exception if the result set contains more than one document. /// /// The type of the result. /// The aggregate. /// The cancellation token. /// /// The fluent aggregate interface. /// public static TResult SingleOrDefault(this IAggregateFluent aggregate, CancellationToken cancellationToken = default(CancellationToken)) { Ensure.IsNotNull(aggregate, nameof(aggregate)); return IAsyncCursorSourceExtensions.SingleOrDefault(aggregate.Limit(2), cancellationToken); } /// /// Returns the only document of the aggregate result, or the default value if the result set is empty. Throws an exception if the result set contains more than one document. /// /// The type of the result. /// The aggregate. /// The cancellation token. /// /// The fluent aggregate interface. /// public static Task SingleOrDefaultAsync(this IAggregateFluent aggregate, CancellationToken cancellationToken = default(CancellationToken)) { Ensure.IsNotNull(aggregate, nameof(aggregate)); return IAsyncCursorSourceExtensions.SingleOrDefaultAsync(aggregate.Limit(2), cancellationToken); } } }