/* 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.IO; using MongoDB.Bson.Serialization; using MongoDB.Driver.Core.Misc; namespace MongoDB.Driver { /// /// Extension methods for an index keys definition. /// public static class IndexKeysDefinitionExtensions { /// /// Combines an existing index keys definition with an ascending index key definition. /// /// The type of the document. /// The keys. /// The field. /// /// A combined index keys definition. /// public static IndexKeysDefinition Ascending(this IndexKeysDefinition keys, FieldDefinition field) { var builder = Builders.IndexKeys; return builder.Combine(keys, builder.Ascending(field)); } /// /// Combines an existing index keys definition with an ascending index key definition. /// /// The type of the document. /// The keys. /// The field. /// /// A combined index keys definition. /// public static IndexKeysDefinition Ascending(this IndexKeysDefinition keys, Expression> field) { var builder = Builders.IndexKeys; return builder.Combine(keys, builder.Ascending(field)); } /// /// Combines an existing index keys definition with a descending index key definition. /// /// The type of the document. /// The keys. /// The field. /// /// A combined index keys definition. /// public static IndexKeysDefinition Descending(this IndexKeysDefinition keys, FieldDefinition field) { var builder = Builders.IndexKeys; return builder.Combine(keys, builder.Descending(field)); } /// /// Combines an existing index keys definition with a descending index key definition. /// /// The type of the document. /// The keys. /// The field. /// /// A combined index keys definition. /// public static IndexKeysDefinition Descending(this IndexKeysDefinition keys, Expression> field) { var builder = Builders.IndexKeys; return builder.Combine(keys, builder.Descending(field)); } /// /// Combines an existing index keys definition with a 2d index key definition. /// /// The type of the document. /// The keys. /// The field. /// /// A combined index keys definition. /// public static IndexKeysDefinition Geo2D(this IndexKeysDefinition keys, FieldDefinition field) { var builder = Builders.IndexKeys; return builder.Combine(keys, builder.Geo2D(field)); } /// /// Combines an existing index keys definition with a 2d index key definition. /// /// The type of the document. /// The keys. /// The field. /// /// A combined index keys definition. /// public static IndexKeysDefinition Geo2D(this IndexKeysDefinition keys, Expression> field) { var builder = Builders.IndexKeys; return builder.Combine(keys, builder.Geo2D(field)); } /// /// Combines an existing index keys definition with a geo haystack index key definition. /// /// The type of the document. /// The keys. /// The field. /// Name of the additional field. /// /// A combined index keys definition. /// public static IndexKeysDefinition GeoHaystack(this IndexKeysDefinition keys, FieldDefinition field, FieldDefinition additionalFieldName = null) { var builder = Builders.IndexKeys; return builder.Combine(keys, builder.GeoHaystack(field, additionalFieldName)); } /// /// Combines an existing index keys definition with a geo haystack index key definition. /// /// The type of the document. /// The keys. /// The field. /// Name of the additional field. /// /// A combined index keys definition. /// public static IndexKeysDefinition GeoHaystack(this IndexKeysDefinition keys, Expression> field, Expression> additionalFieldName = null) { var builder = Builders.IndexKeys; return builder.Combine(keys, builder.GeoHaystack(field, additionalFieldName)); } /// /// Combines an existing index keys definition with a 2dsphere index key definition. /// /// The type of the document. /// The keys. /// The field. /// /// A combined index keys definition. /// public static IndexKeysDefinition Geo2DSphere(this IndexKeysDefinition keys, FieldDefinition field) { var builder = Builders.IndexKeys; return builder.Combine(keys, builder.Geo2DSphere(field)); } /// /// Combines an existing index keys definition with a 2dsphere index key definition. /// /// The type of the document. /// The keys. /// The field. /// /// A combined index keys definition. /// public static IndexKeysDefinition Geo2DSphere(this IndexKeysDefinition keys, Expression> field) { var builder = Builders.IndexKeys; return builder.Combine(keys, builder.Geo2DSphere(field)); } /// /// Combines an existing index keys definition with a hashed index key definition. /// /// The type of the document. /// The keys. /// The field. /// /// A combined index keys definition. /// public static IndexKeysDefinition Hashed(this IndexKeysDefinition keys, FieldDefinition field) { var builder = Builders.IndexKeys; return builder.Combine(keys, builder.Hashed(field)); } /// /// Combines an existing index keys definition with a hashed index key definition. /// /// The type of the document. /// The keys. /// The field. /// /// A combined index keys definition. /// public static IndexKeysDefinition Hashed(this IndexKeysDefinition keys, Expression> field) { var builder = Builders.IndexKeys; return builder.Combine(keys, builder.Hashed(field)); } /// /// Combines an existing index keys definition with a text index key definition. /// /// The type of the document. /// The keys. /// The field. /// /// A combined index keys definition. /// public static IndexKeysDefinition Text(this IndexKeysDefinition keys, FieldDefinition field) { var builder = Builders.IndexKeys; return builder.Combine(keys, builder.Text(field)); } /// /// Combines an existing index keys definition with a text index key definition. /// /// The type of the document. /// The keys. /// The field. /// /// A combined index keys definition. /// public static IndexKeysDefinition Text(this IndexKeysDefinition keys, Expression> field) { var builder = Builders.IndexKeys; return builder.Combine(keys, builder.Text(field)); } } /// /// A builder for an . /// /// The type of the document. public sealed class IndexKeysDefinitionBuilder { /// /// Creates an ascending index key definition. /// /// The field. /// An ascending index key definition. public IndexKeysDefinition Ascending(FieldDefinition field) { return new DirectionalIndexKeyDefinition(field, SortDirection.Ascending); } /// /// Creates an ascending index key definition. /// /// The field. /// An ascending index key definition. public IndexKeysDefinition Ascending(Expression> field) { return Ascending(new ExpressionFieldDefinition(field)); } /// /// Creates a combined index keys definition. /// /// The keys. /// A combined index keys definition. public IndexKeysDefinition Combine(params IndexKeysDefinition[] keys) { return Combine((IEnumerable>)keys); } /// /// Creates a combined index keys definition. /// /// The keys. /// A combined index keys definition. public IndexKeysDefinition Combine(IEnumerable> keys) { return new CombinedIndexKeysDefinition(keys); } /// /// Creates a descending index key definition. /// /// The field. /// A descending index key definition. public IndexKeysDefinition Descending(FieldDefinition field) { return new DirectionalIndexKeyDefinition(field, SortDirection.Descending); } /// /// Creates a descending index key definition. /// /// The field. /// A descending index key definition. public IndexKeysDefinition Descending(Expression> field) { return Descending(new ExpressionFieldDefinition(field)); } /// /// Creates a 2d index key definition. /// /// The field. /// A 2d index key definition. public IndexKeysDefinition Geo2D(FieldDefinition field) { return new SimpleIndexKeyDefinition(field, "2d"); } /// /// Creates a 2d index key definition. /// /// The field. /// A 2d index key definition. public IndexKeysDefinition Geo2D(Expression> field) { return Geo2D(new ExpressionFieldDefinition(field)); } /// /// Creates a geo haystack index key definition. /// /// The field. /// Name of the additional field. /// /// A geo haystack index key definition. /// public IndexKeysDefinition GeoHaystack(FieldDefinition field, FieldDefinition additionalFieldName = null) { return new GeoHaystackIndexKeyDefinition(field, additionalFieldName); } /// /// Creates a geo haystack index key definition. /// /// The field. /// Name of the additional field. /// /// A geo haystack index key definition. /// public IndexKeysDefinition GeoHaystack(Expression> field, Expression> additionalFieldName = null) { FieldDefinition additional = additionalFieldName == null ? null : new ExpressionFieldDefinition(additionalFieldName); return GeoHaystack(new ExpressionFieldDefinition(field), additional); } /// /// Creates a 2dsphere index key definition. /// /// The field. /// A 2dsphere index key definition. public IndexKeysDefinition Geo2DSphere(FieldDefinition field) { return new SimpleIndexKeyDefinition(field, "2dsphere"); } /// /// Creates a 2dsphere index key definition. /// /// The field. /// A 2dsphere index key definition. public IndexKeysDefinition Geo2DSphere(Expression> field) { return Geo2DSphere(new ExpressionFieldDefinition(field)); } /// /// Creates a hashed index key definition. /// /// The field. /// A hashed index key definition. public IndexKeysDefinition Hashed(FieldDefinition field) { return new SimpleIndexKeyDefinition(field, "hashed"); } /// /// Creates a hashed index key definition. /// /// The field. /// A hashed index key definition. public IndexKeysDefinition Hashed(Expression> field) { return Hashed(new ExpressionFieldDefinition(field)); } /// /// Creates a text index key definition. /// /// The field. /// A text index key definition. public IndexKeysDefinition Text(FieldDefinition field) { return new SimpleIndexKeyDefinition(field, "text"); } /// /// Creates a text index key definition. /// /// The field. /// A text index key definition. public IndexKeysDefinition Text(Expression> field) { return Text(new ExpressionFieldDefinition(field)); } } internal sealed class CombinedIndexKeysDefinition : IndexKeysDefinition { private readonly List> _keys; public CombinedIndexKeysDefinition(IEnumerable> keys) { _keys = Ensure.IsNotNull(keys, nameof(keys)).ToList(); } public override BsonDocument Render(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry) { var document = new BsonDocument(); foreach (var key in _keys) { var renderedKey = key.Render(documentSerializer, serializerRegistry); foreach (var element in renderedKey.Elements) { if (document.Contains(element.Name)) { var message = string.Format( "The index keys definition contains multiple values for the field '{0}'.", element.Name); throw new MongoException(message); } document.Add(element); } } return document; } } internal sealed class DirectionalIndexKeyDefinition : IndexKeysDefinition { private readonly FieldDefinition _field; private readonly SortDirection _direction; public DirectionalIndexKeyDefinition(FieldDefinition field, SortDirection direction) { _field = Ensure.IsNotNull(field, nameof(field)); _direction = direction; } public override BsonDocument Render(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry) { var renderedField = _field.Render(documentSerializer, serializerRegistry); BsonValue value; switch (_direction) { case SortDirection.Ascending: value = 1; break; case SortDirection.Descending: value = -1; break; default: throw new InvalidOperationException("Unknown value for " + typeof(SortDirection) + "."); } return new BsonDocument(renderedField.FieldName, value); } } internal sealed class GeoHaystackIndexKeyDefinition : IndexKeysDefinition { private readonly FieldDefinition _field; private readonly FieldDefinition _additionalFieldName; public GeoHaystackIndexKeyDefinition(FieldDefinition field, FieldDefinition additionalFieldName = null) { _field = Ensure.IsNotNull(field, nameof(field)); _additionalFieldName = additionalFieldName; } public override BsonDocument Render(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry) { var renderedField = _field.Render(documentSerializer, serializerRegistry); var document = new BsonDocument(renderedField.FieldName, "geoHaystack"); if (_additionalFieldName != null) { var additionalRenderedField = _additionalFieldName.Render(documentSerializer, serializerRegistry); document.Add(additionalRenderedField.FieldName, 1); } return document; } } internal sealed class SimpleIndexKeyDefinition : IndexKeysDefinition { private readonly FieldDefinition _field; private readonly string _type; public SimpleIndexKeyDefinition(FieldDefinition field, string type) { _field = Ensure.IsNotNull(field, nameof(field)); _type = type; } public override BsonDocument Render(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry) { var renderedField = _field.Render(documentSerializer, serializerRegistry); return new BsonDocument(renderedField.FieldName, _type); } } }