示例#1
0
        private static unsafe long CalculateIndexEtag(Index index, StaticIndexBase compiled, int length, byte *indexEtagBytes, byte *writePos, DocumentsOperationContext documentsContext, TransactionOperationContext indexContext)
        {
            foreach (var collection in index.Collections)
            {
                if (compiled.ReferencedCollections.TryGetValue(collection, out HashSet <CollectionName> referencedCollections) == false)
                {
                    continue;
                }

                foreach (var referencedCollection in referencedCollections)
                {
                    var lastDocEtag = documentsContext.DocumentDatabase.DocumentsStorage.GetLastDocumentEtag(documentsContext.Transaction.InnerTransaction, referencedCollection.Name);
                    var lastProcessedReferenceEtag = IndexStorage.ReadLastProcessedReferenceEtag(indexContext.Transaction.InnerTransaction, collection, referencedCollection);

                    var lastTombstoneEtag          = documentsContext.DocumentDatabase.DocumentsStorage.GetLastTombstoneEtag(documentsContext.Transaction.InnerTransaction, referencedCollection.Name);
                    var lastProcessedTombstoneEtag = IndexStorage.ReadLastProcessedReferenceTombstoneEtag(indexContext.Transaction.InnerTransaction, collection, referencedCollection);

                    *(long *)writePos = lastDocEtag;
                    writePos         += sizeof(long);
                    *(long *)writePos = lastProcessedReferenceEtag;
                    writePos         += sizeof(long);
                    *(long *)writePos = lastTombstoneEtag;
                    writePos         += sizeof(long);
                    *(long *)writePos = lastProcessedTombstoneEtag;
                }
            }

            unchecked
            {
                return((long)Hashing.XXHash64.Calculate(indexEtagBytes, (ulong)length));
            }
        }
示例#2
0
 public AnonymousObjectToBlittableMapResultsEnumerableWrapper(MapReduceIndex index, TransactionOperationContext indexContext)
 {
     _indexContext       = indexContext;
     _groupByFields      = index.Definition.GroupByFields;
     _isMultiMap         = index.IsMultiMap;
     _reduceKeyProcessor = new ReduceKeyProcessor(index.Definition.GroupByFields.Count, index._unmanagedBuffersPool);
     _compiledIndex      = index._compiled;
 }
示例#3
0
        private static bool IsStale(Index index, StaticIndexBase compiled, DocumentsOperationContext databaseContext, TransactionOperationContext indexContext, long?cutoff)
        {
            foreach (var collection in index.Collections)
            {
                HashSet <CollectionName> referencedCollections;
                if (compiled.ReferencedCollections.TryGetValue(collection, out referencedCollections) == false)
                {
                    continue;
                }

                var lastIndexedEtag = index._indexStorage.ReadLastIndexedEtag(indexContext.Transaction, collection);
                // we haven't handled references for that collection yet
                // in theory we could check what is the last etag for that collection in documents store
                // but this was checked earlier by the base index class
                if (lastIndexedEtag == 0)
                {
                    continue;
                }

                foreach (var referencedCollection in referencedCollections)
                {
                    var lastDocEtag = databaseContext.DocumentDatabase.DocumentsStorage.GetLastDocumentEtag(databaseContext, referencedCollection.Name);
                    var lastProcessedReferenceEtag = index._indexStorage.ReadLastProcessedReferenceEtag(indexContext.Transaction, collection, referencedCollection);

                    if (cutoff == null)
                    {
                        if (lastDocEtag > lastProcessedReferenceEtag)
                        {
                            return(true);
                        }

                        var lastTombstoneEtag          = databaseContext.DocumentDatabase.DocumentsStorage.GetLastTombstoneEtag(databaseContext, referencedCollection.Name);
                        var lastProcessedTombstoneEtag = index._indexStorage.ReadLastProcessedReferenceTombstoneEtag(indexContext.Transaction, collection, referencedCollection);

                        if (lastTombstoneEtag > lastProcessedTombstoneEtag)
                        {
                            return(true);
                        }
                    }
                    else
                    {
                        if (Math.Min(cutoff.Value, lastDocEtag) > lastProcessedReferenceEtag)
                        {
                            return(true);
                        }

                        if (databaseContext.DocumentDatabase.DocumentsStorage.GetNumberOfTombstonesWithDocumentEtagLowerThan(databaseContext, referencedCollection.Name, cutoff.Value) > 0)
                        {
                            return(true);
                        }
                    }
                }
            }

            return(false);
        }
示例#4
0
        protected MapReduceIndex(MapReduceIndexDefinition definition, StaticIndexBase compiled)
            : base(definition.IndexDefinition.Type, definition)
        {
            _compiled = compiled;

            if (_compiled.ReferencedCollections == null)
                return;

            foreach (var collection in _compiled.ReferencedCollections)
            {
                foreach (var referencedCollection in collection.Value)
                    _referencedCollections.Add(referencedCollection.Name);
            }
        }
示例#5
0
        private MapReduceIndex(int indexId, MapReduceIndexDefinition definition, StaticIndexBase compiled)
            : base(indexId, IndexType.MapReduce, definition)
        {
            _compiled = compiled;

            if (_compiled.ReferencedCollections == null)
            {
                return;
            }

            foreach (var collection in _compiled.ReferencedCollections)
            {
                foreach (var referencedCollection in collection.Value)
                {
                    _referencedCollections.Add(referencedCollection);
                }
            }
        }
示例#6
0
        protected override int GetFields <T>(T instance, LazyStringValue key, object document, JsonOperationContext indexContext, IWriteOperationBuffer writeBuffer)
        {
            if (!(document is ObjectInstance documentToProcess))
            {
                return(0);
            }

            int newFields = 0;

            if (key != null)
            {
                instance.Add(GetOrCreateKeyField(key));
                newFields++;
            }

            if (_reduceOutput)
            {
                var reduceResult = JsBlittableBridge.Translate(indexContext,
                                                               documentToProcess.Engine,
                                                               documentToProcess);

                instance.Add(GetReduceResultValueField(reduceResult, writeBuffer));
                newFields++;
            }

            foreach (var(property, propertyDescriptor) in documentToProcess.GetOwnProperties())
            {
                if (_fields.TryGetValue(property, out var field) == false)
                {
                    field = _fields[property] = IndexField.Create(property, new IndexFieldOptions(), _allFields);
                }

                object value;
                var    actualValue = propertyDescriptor.Value;
                if (actualValue.IsObject() && actualValue.IsArray() == false)
                {
                    //In case TryDetectDynamicFieldCreation finds a dynamic field it will populate 'field.Name' with the actual property name
                    //so we must use field.Name and not property from this point on.
                    var val = TryDetectDynamicFieldCreation(property, actualValue.AsObject(), field);
                    if (val != null)
                    {
                        if (val.IsObject() && val.AsObject().TryGetValue("$spatial", out _))
                        {
                            actualValue = val; //Here we populate the dynamic spatial field that will be handled below.
                        }
                        else
                        {
                            value      = TypeConverter.ToBlittableSupportedType(val, flattenArrays: false, forIndexing: true, engine: documentToProcess.Engine, context: indexContext);
                            newFields += GetRegularFields(instance, field, value, indexContext, out _);
                            continue;
                        }
                    }

                    var objectValue = actualValue.AsObject();
                    if (objectValue.HasOwnProperty("$spatial") && objectValue.TryGetValue("$spatial", out var inner))
                    {
                        SpatialField spatialField;
                        IEnumerable <AbstractField> spatial;
                        if (inner.IsString())
                        {
                            spatialField = StaticIndexBase.GetOrCreateSpatialField(field.Name);
                            spatial      = StaticIndexBase.CreateSpatialField(spatialField, inner.AsString());
                        }
                        else if (inner.IsObject())
                        {
                            var innerObject = inner.AsObject();
                            if (innerObject.HasOwnProperty("Lat") && innerObject.HasOwnProperty("Lng") && innerObject.TryGetValue("Lat", out var lat) &&
                                lat.IsNumber() && innerObject.TryGetValue("Lng", out var lng) && lng.IsNumber())
                            {
                                spatialField = StaticIndexBase.GetOrCreateSpatialField(field.Name);
                                spatial      = StaticIndexBase.CreateSpatialField(spatialField, lat.AsNumber(), lng.AsNumber());
                            }
                            else
                            {
                                continue; //Ignoring bad spatial field
                            }
                        }
                        else
                        {
                            continue; //Ignoring bad spatial field
                        }
                        newFields += GetRegularFields(instance, field, spatial, indexContext, out _);

                        continue;
                    }
                }

                value      = TypeConverter.ToBlittableSupportedType(propertyDescriptor.Value, flattenArrays: false, forIndexing: true, engine: documentToProcess.Engine, context: indexContext);
                newFields += GetRegularFields(instance, field, value, indexContext, out _);

                if (value is IDisposable toDispose)
                {
                    // the value was converted to a lucene field and isn't needed anymore
                    toDispose.Dispose();
                }
            }

            return(newFields);
        }
示例#7
0
        private static bool IsStaleDueToReferences(Index index, StaticIndexBase compiled, DocumentsOperationContext databaseContext, TransactionOperationContext indexContext, long?referenceCutoff, List <string> stalenessReasons)
        {
            foreach (var collection in index.Collections)
            {
                if (compiled.ReferencedCollections.TryGetValue(collection, out HashSet <CollectionName> referencedCollections) == false)
                {
                    continue;
                }

                var lastIndexedEtag = index._indexStorage.ReadLastIndexedEtag(indexContext.Transaction, collection);
                // we haven't handled references for that collection yet
                // in theory we could check what is the last etag for that collection in documents store
                // but this was checked earlier by the base index class
                if (lastIndexedEtag == 0)
                {
                    continue;
                }

                foreach (var referencedCollection in referencedCollections)
                {
                    var lastDocEtag = databaseContext.DocumentDatabase.DocumentsStorage.GetLastDocumentEtag(databaseContext.Transaction.InnerTransaction, referencedCollection.Name);
                    var lastProcessedReferenceEtag = IndexStorage.ReadLastProcessedReferenceEtag(indexContext.Transaction.InnerTransaction, collection, referencedCollection);
                    var lastProcessedTombstoneEtag = IndexStorage.ReadLastProcessedReferenceTombstoneEtag(indexContext.Transaction.InnerTransaction, collection, referencedCollection);

                    if (referenceCutoff == null)
                    {
                        if (lastDocEtag > lastProcessedReferenceEtag)
                        {
                            if (stalenessReasons == null)
                            {
                                return(true);
                            }

                            var lastDoc = databaseContext.DocumentDatabase.DocumentsStorage.GetByEtag(databaseContext, lastDocEtag);

                            stalenessReasons.Add($"There are still some document references to process from collection '{referencedCollection.Name}'. " +
                                                 $"The last document etag in that collection is '{lastDocEtag:#,#;;0}' " +
                                                 $"({Constants.Documents.Metadata.Id}: '{lastDoc.Id}', " +
                                                 $"{Constants.Documents.Metadata.LastModified}: '{lastDoc.LastModified}'), " +
                                                 $"but last processed document etag for that collection is '{lastProcessedReferenceEtag:#,#;;0}'.");
                        }

                        var lastTombstoneEtag = databaseContext.DocumentDatabase.DocumentsStorage.GetLastTombstoneEtag(databaseContext.Transaction.InnerTransaction, referencedCollection.Name);

                        if (lastTombstoneEtag > lastProcessedTombstoneEtag)
                        {
                            if (stalenessReasons == null)
                            {
                                return(true);
                            }

                            var lastTombstone = databaseContext.DocumentDatabase.DocumentsStorage.GetTombstoneByEtag(databaseContext, lastTombstoneEtag);

                            stalenessReasons.Add($"There are still some tombstone references to process from collection '{referencedCollection.Name}'. " +
                                                 $"The last tombstone etag in that collection is '{lastTombstoneEtag:#,#;;0}' " +
                                                 $"({Constants.Documents.Metadata.Id}: '{lastTombstone.LowerId}', " +
                                                 $"{Constants.Documents.Metadata.LastModified}: '{lastTombstone.LastModified}'), " +
                                                 $"but last processed tombstone etag for that collection is '{lastProcessedTombstoneEtag:#,#;;0}'.");
                        }
                    }
                    else
                    {
                        var minDocEtag = Math.Min(referenceCutoff.Value, lastDocEtag);
                        if (minDocEtag > lastProcessedReferenceEtag)
                        {
                            if (stalenessReasons == null)
                            {
                                return(true);
                            }

                            var lastDoc = databaseContext.DocumentDatabase.DocumentsStorage.GetByEtag(databaseContext, lastDocEtag);

                            stalenessReasons.Add($"There are still some document references to process from collection '{referencedCollection.Name}'. " +
                                                 $"The last document etag in that collection is '{lastDocEtag:#,#;;0}' " +
                                                 $"({Constants.Documents.Metadata.Id}: '{lastDoc.Id}', " +
                                                 $"{Constants.Documents.Metadata.LastModified}: '{lastDoc.LastModified}') " +
                                                 $"with cutoff set to '{referenceCutoff.Value}', " +
                                                 $"but last processed document etag for that collection is '{lastProcessedReferenceEtag:#,#;;0}'.");
                        }

                        var hasTombstones = databaseContext.DocumentDatabase.DocumentsStorage.HasTombstonesWithEtagGreaterThanStartAndLowerThanOrEqualToEnd(databaseContext, referencedCollection.Name,
                                                                                                                                                            lastProcessedTombstoneEtag,
                                                                                                                                                            referenceCutoff.Value);
                        if (hasTombstones)
                        {
                            if (stalenessReasons == null)
                            {
                                return(true);
                            }

                            stalenessReasons.Add($"There are still tombstones to process from collection '{referencedCollection.Name}' with etag range '{lastProcessedTombstoneEtag} - {referenceCutoff.Value}'.");
                        }
                    }
                }
            }

            return(stalenessReasons?.Count > 0);
        }
示例#8
0
        public static void ValidateReduceResultsCollectionName(IndexDefinition definition, StaticIndexBase index, DocumentDatabase database)
        {
            var outputReduceToCollection = definition.OutputReduceToCollection;

            if (string.IsNullOrWhiteSpace(outputReduceToCollection))
            {
                return;
            }

            var collections = index.Maps.Keys.ToHashSet(StringComparer.OrdinalIgnoreCase);

            if (collections.Contains(Constants.Documents.Collections.AllDocumentsCollection))
            {
                throw new IndexInvalidException($"It is forbidden to create the '{definition.Name}' index " +
                                                $"which would output reduce results to documents in the '{outputReduceToCollection}' collection, " +
                                                $"as this index is mapping all documents " +
                                                $"and this will result in an infinite loop.");
            }

            foreach (var referencedCollection in index.ReferencedCollections)
            {
                foreach (var collectionName in referencedCollection.Value)
                {
                    collections.Add(collectionName.Name);
                }
            }
            if (collections.Contains(outputReduceToCollection))
            {
                throw new IndexInvalidException($"It is forbidden to create the '{definition.Name}' index " +
                                                $"which would output reduce results to documents in the '{outputReduceToCollection}' collection, " +
                                                $"as this index is mapping or referencing the '{outputReduceToCollection}' collection " +
                                                $"and this will result in an infinite loop.");
            }

            var indexes = database.IndexStore.GetIndexes()
                          .Where(x => x.Type == IndexType.MapReduce)
                          .Cast <MapReduceIndex>()
                          .Where(mapReduceIndex => string.IsNullOrWhiteSpace(mapReduceIndex.Definition.OutputReduceToCollection) == false &&
                                 mapReduceIndex.Name != definition.Name)
                          .ToList();

            foreach (var otherIndex in indexes)
            {
                if (otherIndex.Definition.OutputReduceToCollection.Equals(outputReduceToCollection, StringComparison.OrdinalIgnoreCase))
                {
                    var sideBySideIndex = definition.Name.StartsWith(Constants.Documents.Indexing.SideBySideIndexNamePrefix, StringComparison.OrdinalIgnoreCase);
                    if (sideBySideIndex)
                    {
                        throw new IndexInvalidException($"In order to create the '{definition.Name}' side by side index " +
                                                        $"you firstly need to set {nameof(IndexDefinition.OutputReduceToCollection)} to be null " +
                                                        $"on the '{otherIndex.Name}' index " +
                                                        $"and than delete all of the documents in the '{otherIndex.Definition.OutputReduceToCollection}' collection.");
                    }

                    throw new IndexInvalidException($"It is forbidden to create the '{definition.Name}' index " +
                                                    $"which would output reduce results to documents in the '{outputReduceToCollection}' collection, " +
                                                    $"as there is another index named '{otherIndex.Name}' " +
                                                    $"which also output reduce results to documents in the same '{outputReduceToCollection}' collection. " +
                                                    $"{nameof(IndexDefinition.OutputReduceToCollection)} must by set to unique value for each index or be null.");
                }

                var otherIndexCollections = new HashSet <string>(otherIndex.Collections);

                foreach (var referencedCollection in otherIndex.GetReferencedCollections())
                {
                    foreach (var collectionName in referencedCollection.Value)
                    {
                        otherIndexCollections.Add(collectionName.Name);
                    }
                }

                if (otherIndexCollections.Contains(outputReduceToCollection) &&
                    CheckIfThereIsAnIndexWhichWillOutputReduceDocumentsWhichWillBeUsedAsMapOnTheSpecifiedIndex(otherIndex, collections, indexes, out string description))
                {
                    description += Environment.NewLine + $"--> {definition.Name}: {string.Join(",", collections)} => *{outputReduceToCollection}*";
                    throw new IndexInvalidException($"It is forbidden to create the '{definition.Name}' index " +
                                                    $"which would output reduce results to documents in the '{outputReduceToCollection}' collection, " +
                                                    $"as '{outputReduceToCollection}' collection is consumed by other index in a way that would " +
                                                    $"lead to an infinite loop." +
                                                    Environment.NewLine + description);
                }
            }

            using (database.DocumentsStorage.ContextPool.AllocateOperationContext(out DocumentsOperationContext context))
                using (context.OpenReadTransaction())
                {
                    var stats = database.DocumentsStorage.GetCollection(outputReduceToCollection, context);
                    if (stats.Count > 0)
                    {
                        throw new IndexInvalidException($"In order to create the '{definition.Name}' index " +
                                                        $"which would output reduce results to documents in the '{outputReduceToCollection}' collection, " +
                                                        $"you firstly need to delete all of the documents in the '{stats.Name}' collection " +
                                                        $"(currently have {stats.Count} document{(stats.Count == 1 ? "" : "s")}).");
                    }
                }
        }
示例#9
0
 private static void ThrowMissingGroupByFieldsInMapOutput(object output, HashSet <string> groupByFields, StaticIndexBase compiledIndex)
 {
     throw new InvalidOperationException(
               $"The output of the mapping function does not contain all fields that the index is supposed to group by.{Environment.NewLine}" +
               $"Output: {output}{Environment.NewLine}" +
               $"Group by fields: {string.Join(",", groupByFields)}{Environment.NewLine}" +
               $"Compiled index def:{Environment.NewLine}{compiledIndex.Source}");
 }
        protected override int GetFields <T>(T instance, LazyStringValue key, object doc, JsonOperationContext indexContext, IWriteOperationBuffer writeBuffer)
        {
            int newFields = 0;

            var document = (Document)doc;

            if (key != null)
            {
                Debug.Assert(document.LowerId == null || (key == document.LowerId));

                instance.Add(GetOrCreateKeyField(key));
                newFields++;
            }

            if (_reduceOutput)
            {
                instance.Add(GetReduceResultValueField(document.Data, writeBuffer));
                newFields++;
            }

            foreach (var indexField in _fields.Values)
            {
                object value;
                if (indexField.Spatial is AutoSpatialOptions spatialOptions)
                {
                    var spatialField = CurrentIndexingScope.Current.GetOrCreateSpatialField(indexField.Name);

                    switch (spatialOptions.MethodType)
                    {
                    case AutoSpatialOptions.AutoSpatialMethodType.Wkt:
                        if (BlittableJsonTraverserHelper.TryRead(_blittableTraverser, document, spatialOptions.MethodArguments[0], out var wktValue) == false)
                        {
                            continue;
                        }

                        value = StaticIndexBase.CreateSpatialField(spatialField, wktValue);
                        break;

                    case AutoSpatialOptions.AutoSpatialMethodType.Point:
                        if (BlittableJsonTraverserHelper.TryRead(_blittableTraverser, document, spatialOptions.MethodArguments[0], out var latValue) == false)
                        {
                            continue;
                        }

                        if (BlittableJsonTraverserHelper.TryRead(_blittableTraverser, document, spatialOptions.MethodArguments[1], out var lngValue) == false)
                        {
                            continue;
                        }

                        value = StaticIndexBase.CreateSpatialField(spatialField, latValue, lngValue);
                        break;

                    default:
                        throw new ArgumentOutOfRangeException();
                    }
                }
                else
                {
                    if (BlittableJsonTraverserHelper.TryRead(_blittableTraverser, document, indexField.OriginalName ?? indexField.Name, out value) == false)
                    {
                        continue;
                    }
                }

                newFields += GetRegularFields(instance, indexField, value, indexContext, out _);
            }

            return(newFields);
        }
示例#11
0
        public static void ValidateReduceResultsCollectionName(IndexDefinition definition, StaticIndexBase index, DocumentDatabase database, bool checkIfCollectionEmpty)
        {
            var outputReduceToCollection = definition.OutputReduceToCollection;

            if (string.IsNullOrWhiteSpace(outputReduceToCollection))
            {
                return;
            }

            if (outputReduceToCollection.Equals(definition.PatternReferencesCollectionName, StringComparison.OrdinalIgnoreCase))
            {
                throw new IndexInvalidException($"Collection defined in {nameof(definition.PatternReferencesCollectionName)} must not be the same as in {nameof(definition.OutputReduceToCollection)}. Collection name: '{outputReduceToCollection}'");
            }

            var collections = index.Maps.Keys.ToHashSet(StringComparer.OrdinalIgnoreCase);

            if (collections.Contains(Constants.Documents.Collections.AllDocumentsCollection))
            {
                throw new IndexInvalidException($"It is forbidden to create the '{definition.Name}' index " +
                                                $"which would output reduce results to documents in the '{outputReduceToCollection}' collection, " +
                                                $"as this index is mapping all documents " +
                                                $"and this will result in an infinite loop.");
            }

            foreach (var referencedCollection in index.ReferencedCollections)
            {
                foreach (var collectionName in referencedCollection.Value)
                {
                    collections.Add(collectionName.Name);
                }
            }

            if (collections.Contains(outputReduceToCollection))
            {
                throw new IndexInvalidException($"It is forbidden to create the '{definition.Name}' index " +
                                                $"which would output reduce results to documents in the '{outputReduceToCollection}' collection, " +
                                                $"as this index is mapping or referencing the '{outputReduceToCollection}' collection " +
                                                $"and this will result in an infinite loop.");
            }

            var indexes = database.IndexStore.GetIndexes()
                          .Where(x => x.Type.IsStatic() && x.Type.IsMapReduce())
                          .Cast <MapReduceIndex>()
                          .Where(mapReduceIndex =>
            {
                // we have handling for side by side indexing with OutputReduceToCollection so we're checking only other indexes

                string existingIndexName = mapReduceIndex.Name.Replace(Constants.Documents.Indexing.SideBySideIndexNamePrefix, string.Empty,
                                                                       StringComparison.OrdinalIgnoreCase);

                string newIndexName = definition.Name.Replace(Constants.Documents.Indexing.SideBySideIndexNamePrefix, string.Empty,
                                                              StringComparison.OrdinalIgnoreCase);

                return(string.IsNullOrWhiteSpace(mapReduceIndex.Definition.OutputReduceToCollection) == false && string.Equals(existingIndexName, newIndexName, StringComparison.OrdinalIgnoreCase) == false);
            })
                          .ToList();

            foreach (var otherIndex in indexes)
            {
                if (otherIndex.Definition.OutputReduceToCollection.Equals(outputReduceToCollection, StringComparison.OrdinalIgnoreCase))
                {
                    throw new IndexInvalidException($"It is forbidden to create the '{definition.Name}' index " +
                                                    $"which would output reduce results to documents in the '{outputReduceToCollection}' collection, " +
                                                    $"as there is another index named '{otherIndex.Name}' " +
                                                    $"which also output reduce results to documents in the same '{outputReduceToCollection}' collection. " +
                                                    $"{nameof(IndexDefinition.OutputReduceToCollection)} must by set to unique value for each index or be null.");
                }

                var otherIndexCollections = new HashSet <string>(otherIndex.Collections);

                foreach (var referencedCollection in otherIndex.GetReferencedCollections())
                {
                    foreach (var collectionName in referencedCollection.Value)
                    {
                        otherIndexCollections.Add(collectionName.Name);
                    }
                }

                if (otherIndexCollections.Contains(outputReduceToCollection) &&
                    CheckIfThereIsAnIndexWhichWillOutputReduceDocumentsWhichWillBeUsedAsMapOnTheSpecifiedIndex(otherIndex, collections, indexes, out string description))
                {
                    description += Environment.NewLine + $"--> {definition.Name}: {string.Join(",", collections)} => *{outputReduceToCollection}*";
                    throw new IndexInvalidException($"It is forbidden to create the '{definition.Name}' index " +
                                                    $"which would output reduce results to documents in the '{outputReduceToCollection}' collection, " +
                                                    $"as '{outputReduceToCollection}' collection is consumed by other index in a way that would " +
                                                    $"lead to an infinite loop." +
                                                    Environment.NewLine + description);
                }
            }

            var existingIndexOrSideBySide = database.IndexStore.GetIndexes()
                                            .Where(x => x.Type.IsStatic() && x.Type.IsMapReduce())
                                            .Cast <MapReduceIndex>()
                                            .FirstOrDefault(x =>
            {
                var name = definition.Name.Replace(Constants.Documents.Indexing.SideBySideIndexNamePrefix, string.Empty,
                                                   StringComparison.OrdinalIgnoreCase);

                return(x.Name.Equals(name, StringComparison.OrdinalIgnoreCase) &&
                       x.Definition.ReduceOutputIndex != null);    // legacy index definitions don't have this field - side by side indexing isn't supported then
            });

            if (existingIndexOrSideBySide != null)
            {
                if (definition.OutputReduceToCollection.Equals(existingIndexOrSideBySide.Definition.OutputReduceToCollection, StringComparison.OrdinalIgnoreCase)) // we have handling for side by side indexing with OutputReduceToCollection
                {
                    checkIfCollectionEmpty = false;
                }
            }

            if (checkIfCollectionEmpty)
            {
                using (database.DocumentsStorage.ContextPool.AllocateOperationContext(out DocumentsOperationContext context))
                    using (context.OpenReadTransaction())
                    {
                        var stats = database.DocumentsStorage.GetCollection(outputReduceToCollection, context);
                        if (stats.Count > 0)
                        {
                            throw new IndexInvalidException(
                                      $"Index '{definition.Name}' is defined to output the Reduce results to documents in Collection '{outputReduceToCollection}'. " +
                                      $"This collection currently has {stats.Count} document{(stats.Count == 1 ? ' ' : 's')}. " +
                                      $"All documents in Collection '{stats.Name}' must be deleted first.");
                        }
                    }
            }
        }
示例#12
0
        public static void ValidateReduceResultsCollectionName(IndexDefinition definition, StaticIndexBase index, DocumentDatabase database)
        {
            var outputReduceToCollection = definition.OutputReduceToCollection;

            if (string.IsNullOrWhiteSpace(outputReduceToCollection))
            {
                return;
            }

            var collections = index.Maps.Keys.ToArray();

            if (collections.Contains(Constants.Documents.Collections.AllDocumentsCollection, StringComparer.OrdinalIgnoreCase))
            {
                throw new IndexInvalidException($"Cannot output documents from index ({definition.Name}) to the collection name ({outputReduceToCollection}) because the index is mapping all documents and this will result in an infinite loop.");
            }
            if (collections.Contains(outputReduceToCollection, StringComparer.OrdinalIgnoreCase))
            {
                throw new IndexInvalidException($"The collection name ({outputReduceToCollection}) cannot be used as this index ({definition.Name}) is mapping this collection and this will result in an infinite loop.");
            }

            var indexes = database.IndexStore.GetIndexes()
                          .Where(x => x.Type == IndexType.MapReduce)
                          .Cast <MapReduceIndex>()
                          .Where(mapReduceIndex => string.IsNullOrWhiteSpace(mapReduceIndex.Definition.OutputReduceToCollection) == false &&
                                 mapReduceIndex.Name != definition.Name)
                          .ToList();

            foreach (var otherIndex in indexes)
            {
                if (otherIndex.Definition.OutputReduceToCollection.Equals(outputReduceToCollection, StringComparison.OrdinalIgnoreCase))
                {
                    throw new IndexInvalidException($"The collection name ({outputReduceToCollection}) which will be used to output documents results should be unique to only one index but it is already used by another index ({otherIndex.Name}).");
                }

                if (otherIndex.Collections.Contains(outputReduceToCollection, StringComparer.OrdinalIgnoreCase) &&
                    CheckIfThereIsAnIndexWhichWillOutputReduceDocumentsWhichWillBeUsedAsMapOnTheSpecifiedIndex(otherIndex, collections, indexes, out string description))
                {
                    description += Environment.NewLine + $"--> {definition.Name}: {string.Join(",", collections)} => *{outputReduceToCollection}*";
                    throw new IndexInvalidException($"The collection name ({outputReduceToCollection}) cannot be used to output documents results as it is consumed by other index that will also output results which will lead to an infinite loop:" + Environment.NewLine + description);
                }
            }
        }