| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478 |
- /* 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 MongoDB.Bson;
- using MongoDB.Bson.Serialization;
- using MongoDB.Driver.Core.Misc;
- namespace MongoDB.Driver
- {
- /// <summary>
- /// Extension methods for projections.
- /// </summary>
- public static class ProjectionDefinitionExtensions
- {
- /// <summary>
- /// Combines an existing projection with a projection that filters the contents of an array.
- /// </summary>
- /// <typeparam name="TDocument">The type of the document.</typeparam>
- /// <typeparam name="TItem">The type of the item.</typeparam>
- /// <param name="projection">The projection.</param>
- /// <param name="field">The field.</param>
- /// <param name="filter">The filter.</param>
- /// <returns>
- /// A combined projection.
- /// </returns>
- public static ProjectionDefinition<TDocument> ElemMatch<TDocument, TItem>(this ProjectionDefinition<TDocument> projection, FieldDefinition<TDocument> field, FilterDefinition<TItem> filter)
- {
- var builder = Builders<TDocument>.Projection;
- return builder.Combine(projection, builder.ElemMatch(field, filter));
- }
- /// <summary>
- /// Combines an existing projection with a projection that filters the contents of an array.
- /// </summary>
- /// <typeparam name="TDocument">The type of the document.</typeparam>
- /// <typeparam name="TItem">The type of the item.</typeparam>
- /// <param name="projection">The projection.</param>
- /// <param name="field">The field.</param>
- /// <param name="filter">The filter.</param>
- /// <returns>
- /// A combined projection.
- /// </returns>
- public static ProjectionDefinition<TDocument> ElemMatch<TDocument, TItem>(this ProjectionDefinition<TDocument> projection, Expression<Func<TDocument, IEnumerable<TItem>>> field, FilterDefinition<TItem> filter)
- {
- var builder = Builders<TDocument>.Projection;
- return builder.Combine(projection, builder.ElemMatch(field, filter));
- }
- /// <summary>
- /// Combines an existing projection with a projection that filters the contents of an array.
- /// </summary>
- /// <typeparam name="TDocument">The type of the document.</typeparam>
- /// <typeparam name="TItem">The type of the item.</typeparam>
- /// <param name="projection">The projection.</param>
- /// <param name="field">The field.</param>
- /// <param name="filter">The filter.</param>
- /// <returns>
- /// A combined projection.
- /// </returns>
- public static ProjectionDefinition<TDocument> ElemMatch<TDocument, TItem>(this ProjectionDefinition<TDocument> projection, Expression<Func<TDocument, IEnumerable<TItem>>> field, Expression<Func<TItem, bool>> filter)
- {
- var builder = Builders<TDocument>.Projection;
- return builder.Combine(projection, builder.ElemMatch(field, filter));
- }
- /// <summary>
- /// Combines an existing projection with a projection that excludes a field.
- /// </summary>
- /// <typeparam name="TDocument">The type of the document.</typeparam>
- /// <param name="projection">The projection.</param>
- /// <param name="field">The field.</param>
- /// <returns>
- /// A combined projection.
- /// </returns>
- public static ProjectionDefinition<TDocument> Exclude<TDocument>(this ProjectionDefinition<TDocument> projection, FieldDefinition<TDocument> field)
- {
- var builder = Builders<TDocument>.Projection;
- return builder.Combine(projection, builder.Exclude(field));
- }
- /// <summary>
- /// Combines an existing projection with a projection that excludes a field.
- /// </summary>
- /// <typeparam name="TDocument">The type of the document.</typeparam>
- /// <param name="projection">The projection.</param>
- /// <param name="field">The field.</param>
- /// <returns>
- /// A combined projection.
- /// </returns>
- public static ProjectionDefinition<TDocument> Exclude<TDocument>(this ProjectionDefinition<TDocument> projection, Expression<Func<TDocument, object>> field)
- {
- var builder = Builders<TDocument>.Projection;
- return builder.Combine(projection, builder.Exclude(field));
- }
- /// <summary>
- /// Combines an existing projection with a projection that includes a field.
- /// </summary>
- /// <typeparam name="TDocument">The type of the document.</typeparam>
- /// <param name="projection">The projection.</param>
- /// <param name="field">The field.</param>
- /// <returns>
- /// A combined projection.
- /// </returns>
- public static ProjectionDefinition<TDocument> Include<TDocument>(this ProjectionDefinition<TDocument> projection, FieldDefinition<TDocument> field)
- {
- var builder = Builders<TDocument>.Projection;
- return builder.Combine(projection, builder.Include(field));
- }
- /// <summary>
- /// Combines an existing projection with a projection that includes a field.
- /// </summary>
- /// <typeparam name="TDocument">The type of the document.</typeparam>
- /// <param name="projection">The projection.</param>
- /// <param name="field">The field.</param>
- /// <returns>
- /// A combined projection.
- /// </returns>
- public static ProjectionDefinition<TDocument> Include<TDocument>(this ProjectionDefinition<TDocument> projection, Expression<Func<TDocument, object>> field)
- {
- var builder = Builders<TDocument>.Projection;
- return builder.Combine(projection, builder.Include(field));
- }
- /// <summary>
- /// Combines an existing projection with a text score projection.
- /// </summary>
- /// <typeparam name="TDocument">The type of the document.</typeparam>
- /// <param name="projection">The projection.</param>
- /// <param name="field">The field.</param>
- /// <returns>
- /// A combined projection.
- /// </returns>
- public static ProjectionDefinition<TDocument> MetaTextScore<TDocument>(this ProjectionDefinition<TDocument> projection, string field)
- {
- var builder = Builders<TDocument>.Projection;
- return builder.Combine(projection, builder.MetaTextScore(field));
- }
- /// <summary>
- /// Combines an existing projection with an array slice projection.
- /// </summary>
- /// <typeparam name="TDocument">The type of the document.</typeparam>
- /// <param name="projection">The projection.</param>
- /// <param name="field">The field.</param>
- /// <param name="skip">The skip.</param>
- /// <param name="limit">The limit.</param>
- /// <returns>
- /// A combined projection.
- /// </returns>
- public static ProjectionDefinition<TDocument> Slice<TDocument>(this ProjectionDefinition<TDocument> projection, FieldDefinition<TDocument> field, int skip, int? limit = null)
- {
- var builder = Builders<TDocument>.Projection;
- return builder.Combine(projection, builder.Slice(field, skip, limit));
- }
- /// <summary>
- /// Combines an existing projection with an array slice projection.
- /// </summary>
- /// <typeparam name="TDocument">The type of the document.</typeparam>
- /// <param name="projection">The projection.</param>
- /// <param name="field">The field.</param>
- /// <param name="skip">The skip.</param>
- /// <param name="limit">The limit.</param>
- /// <returns>
- /// A combined projection.
- /// </returns>
- public static ProjectionDefinition<TDocument> Slice<TDocument>(this ProjectionDefinition<TDocument> projection, Expression<Func<TDocument, object>> field, int skip, int? limit = null)
- {
- var builder = Builders<TDocument>.Projection;
- return builder.Combine(projection, builder.Slice(field, skip, limit));
- }
- }
- /// <summary>
- /// A builder for a projection.
- /// </summary>
- /// <typeparam name="TSource">The type of the source.</typeparam>
- public sealed class ProjectionDefinitionBuilder<TSource>
- {
- /// <summary>
- /// Creates a client side projection that is implemented solely by using a different serializer.
- /// </summary>
- /// <typeparam name="TProjection">The type of the projection.</typeparam>
- /// <param name="projectionSerializer">The projection serializer.</param>
- /// <returns>A client side deserialization projection.</returns>
- public ProjectionDefinition<TSource, TProjection> As<TProjection>(IBsonSerializer<TProjection> projectionSerializer = null)
- {
- return new ClientSideDeserializationProjectionDefinition<TSource, TProjection>(projectionSerializer);
- }
- /// <summary>
- /// Combines the specified projections.
- /// </summary>
- /// <param name="projections">The projections.</param>
- /// <returns>
- /// A combined projection.
- /// </returns>
- public ProjectionDefinition<TSource> Combine(params ProjectionDefinition<TSource>[] projections)
- {
- return Combine((IEnumerable<ProjectionDefinition<TSource>>)projections);
- }
- /// <summary>
- /// Combines the specified projections.
- /// </summary>
- /// <param name="projections">The projections.</param>
- /// <returns>
- /// A combined projection.
- /// </returns>
- public ProjectionDefinition<TSource> Combine(IEnumerable<ProjectionDefinition<TSource>> projections)
- {
- return new CombinedProjectionDefinition<TSource>(projections);
- }
- /// <summary>
- /// Creates a projection that filters the contents of an array.
- /// </summary>
- /// <typeparam name="TItem">The type of the item.</typeparam>
- /// <param name="field">The field.</param>
- /// <param name="filter">The filter.</param>
- /// <returns>
- /// An array filtering projection.
- /// </returns>
- public ProjectionDefinition<TSource> ElemMatch<TItem>(FieldDefinition<TSource> field, FilterDefinition<TItem> filter)
- {
- return new ElementMatchProjectionDefinition<TSource, TItem>(field, filter);
- }
- /// <summary>
- /// Creates a projection that filters the contents of an array.
- /// </summary>
- /// <typeparam name="TItem">The type of the item.</typeparam>
- /// <param name="field">The field.</param>
- /// <param name="filter">The filter.</param>
- /// <returns>
- /// An array filtering projection.
- /// </returns>
- public ProjectionDefinition<TSource> ElemMatch<TItem>(Expression<Func<TSource, IEnumerable<TItem>>> field, FilterDefinition<TItem> filter)
- {
- return ElemMatch(new ExpressionFieldDefinition<TSource>(field), filter);
- }
- /// <summary>
- /// Creates a projection that filters the contents of an array.
- /// </summary>
- /// <typeparam name="TItem">The type of the item.</typeparam>
- /// <param name="field">The field.</param>
- /// <param name="filter">The filter.</param>
- /// <returns>
- /// An array filtering projection.
- /// </returns>
- public ProjectionDefinition<TSource> ElemMatch<TItem>(Expression<Func<TSource, IEnumerable<TItem>>> field, Expression<Func<TItem, bool>> filter)
- {
- return ElemMatch(new ExpressionFieldDefinition<TSource>(field), new ExpressionFilterDefinition<TItem>(filter));
- }
- /// <summary>
- /// Creates a projection that excludes a field.
- /// </summary>
- /// <param name="field">The field.</param>
- /// <returns>
- /// An exclusion projection.
- /// </returns>
- public ProjectionDefinition<TSource> Exclude(FieldDefinition<TSource> field)
- {
- return new SingleFieldProjectionDefinition<TSource>(field, 0);
- }
- /// <summary>
- /// Creates a projection that excludes a field.
- /// </summary>
- /// <param name="field">The field.</param>
- /// <returns>
- /// An exclusion projection.
- /// </returns>
- public ProjectionDefinition<TSource> Exclude(Expression<Func<TSource, object>> field)
- {
- return Exclude(new ExpressionFieldDefinition<TSource>(field));
- }
- /// <summary>
- /// Creates a projection based on the expression.
- /// </summary>
- /// <typeparam name="TProjection">The type of the result.</typeparam>
- /// <param name="expression">The expression.</param>
- /// <returns>
- /// An expression projection.
- /// </returns>
- public ProjectionDefinition<TSource, TProjection> Expression<TProjection>(Expression<Func<TSource, TProjection>> expression)
- {
- return new FindExpressionProjectionDefinition<TSource, TProjection>(expression);
- }
- /// <summary>
- /// Creates a projection that includes a field.
- /// </summary>
- /// <param name="field">The field.</param>
- /// <returns>
- /// An inclusion projection.
- /// </returns>
- public ProjectionDefinition<TSource> Include(FieldDefinition<TSource> field)
- {
- return new SingleFieldProjectionDefinition<TSource>(field, 1);
- }
- /// <summary>
- /// Creates a projection that includes a field.
- /// </summary>
- /// <param name="field">The field.</param>
- /// <returns>
- /// An inclusion projection.
- /// </returns>
- public ProjectionDefinition<TSource> Include(Expression<Func<TSource, object>> field)
- {
- return Include(new ExpressionFieldDefinition<TSource>(field));
- }
- /// <summary>
- /// Creates a text score projection.
- /// </summary>
- /// <param name="field">The field.</param>
- /// <returns>
- /// A text score projection.
- /// </returns>
- public ProjectionDefinition<TSource> MetaTextScore(string field)
- {
- return new SingleFieldProjectionDefinition<TSource>(field, new BsonDocument("$meta", "textScore"));
- }
- /// <summary>
- /// Creates an array slice projection.
- /// </summary>
- /// <param name="field">The field.</param>
- /// <param name="skip">The skip.</param>
- /// <param name="limit">The limit.</param>
- /// <returns>
- /// An array slice projection.
- /// </returns>
- public ProjectionDefinition<TSource> Slice(FieldDefinition<TSource> field, int skip, int? limit = null)
- {
- var value = limit.HasValue ? (BsonValue)new BsonArray { skip, limit.Value } : skip;
- return new SingleFieldProjectionDefinition<TSource>(field, new BsonDocument("$slice", value));
- }
- /// <summary>
- /// Creates an array slice projection.
- /// </summary>
- /// <param name="field">The field.</param>
- /// <param name="skip">The skip.</param>
- /// <param name="limit">The limit.</param>
- /// <returns>
- /// An array slice projection.
- /// </returns>
- public ProjectionDefinition<TSource> Slice(Expression<Func<TSource, object>> field, int skip, int? limit = null)
- {
- return Slice(new ExpressionFieldDefinition<TSource>(field), skip, limit);
- }
- }
- internal sealed class CombinedProjectionDefinition<TSource> : ProjectionDefinition<TSource>
- {
- private readonly List<ProjectionDefinition<TSource>> _projections;
- public CombinedProjectionDefinition(IEnumerable<ProjectionDefinition<TSource>> projections)
- {
- _projections = Ensure.IsNotNull(projections, nameof(projections)).ToList();
- }
- public override BsonDocument Render(IBsonSerializer<TSource> sourceSerializer, IBsonSerializerRegistry serializerRegistry)
- {
- var document = new BsonDocument();
- foreach (var projection in _projections)
- {
- var renderedProjection = projection.Render(sourceSerializer, serializerRegistry);
- foreach (var element in renderedProjection.Elements)
- {
- // last one wins
- document.Remove(element.Name);
- document.Add(element);
- }
- }
- return document;
- }
- }
- internal sealed class ElementMatchProjectionDefinition<TSource, TItem> : ProjectionDefinition<TSource>
- {
- private readonly FieldDefinition<TSource> _field;
- private readonly FilterDefinition<TItem> _filter;
- public ElementMatchProjectionDefinition(FieldDefinition<TSource> field, FilterDefinition<TItem> filter)
- {
- _field = Ensure.IsNotNull(field, nameof(field));
- _filter = filter;
- }
- public override BsonDocument Render(IBsonSerializer<TSource> sourceSerializer, IBsonSerializerRegistry serializerRegistry)
- {
- var renderedField = _field.Render(sourceSerializer, serializerRegistry);
- IBsonSerializer<TItem> itemSerializer;
- if (renderedField.FieldSerializer != null)
- {
- var arraySerializer = renderedField.FieldSerializer as IBsonArraySerializer;
- BsonSerializationInfo itemSerializationInfo;
- if (arraySerializer == null || !arraySerializer.TryGetItemSerializationInfo(out itemSerializationInfo))
- {
- var message = string.Format("The serializer for field '{0}' must implement IBsonArraySerializer and provide item serialization info.", renderedField.FieldName);
- throw new InvalidOperationException(message);
- }
- itemSerializer = (IBsonSerializer<TItem>)itemSerializationInfo.Serializer;
- }
- else
- {
- itemSerializer = serializerRegistry.GetSerializer<TItem>();
- }
- var renderedFilter = _filter.Render(itemSerializer, serializerRegistry);
- return new BsonDocument(renderedField.FieldName, new BsonDocument("$elemMatch", renderedFilter));
- }
- }
- internal sealed class PositionalOperatorProjectionDefinition<TSource> : ProjectionDefinition<TSource>
- {
- private readonly FieldDefinition<TSource> _field;
- public PositionalOperatorProjectionDefinition(FieldDefinition<TSource> field)
- {
- _field = Ensure.IsNotNull(field, nameof(field));
- }
- public override BsonDocument Render(IBsonSerializer<TSource> sourceSerializer, IBsonSerializerRegistry serializerRegistry)
- {
- var renderedField = _field.Render(sourceSerializer, serializerRegistry);
- return new BsonDocument(renderedField.FieldName + ".$", 1);
- }
- }
- internal sealed class SingleFieldProjectionDefinition<TSource> : ProjectionDefinition<TSource>
- {
- private readonly FieldDefinition<TSource> _field;
- private readonly BsonValue _value;
- public SingleFieldProjectionDefinition(FieldDefinition<TSource> field, BsonValue value)
- {
- _field = Ensure.IsNotNull(field, nameof(field));
- _value = Ensure.IsNotNull(value, nameof(value));
- }
- public override BsonDocument Render(IBsonSerializer<TSource> sourceSerializer, IBsonSerializerRegistry serializerRegistry)
- {
- var renderedField = _field.Render(sourceSerializer, serializerRegistry);
- return new BsonDocument(renderedField.FieldName, _value);
- }
- }
- }
|