/* 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);
}
}
}