Esempio n. 1
0
        public static (long?LastProcessedCompareExchangeReferenceEtag, long?LastProcessedCompareExchangeReferenceTombstoneEtag) GetLastProcessedCompareExchangeReferenceEtags(Index index, AbstractStaticIndexBase compiled, TransactionOperationContext indexContext)
        {
            long?lastProcessedCompareExchangeReferenceEtag          = null;
            long?lastProcessedCompareExchangeReferenceTombstoneEtag = null;

            foreach (var collection in compiled.CollectionsWithCompareExchangeReferences)
            {
                var lastProcessedCompareExchangeReferenceEtagForCollection = index._indexStorage.ReferencesForCompareExchange.ReadLastProcessedReferenceEtag(indexContext.Transaction.InnerTransaction, collection, IndexStorage.CompareExchangeReferences.CompareExchange);
                lastProcessedCompareExchangeReferenceEtag = lastProcessedCompareExchangeReferenceEtag.HasValue
                    ? Math.Max(lastProcessedCompareExchangeReferenceEtag.Value, lastProcessedCompareExchangeReferenceEtagForCollection)
                    : lastProcessedCompareExchangeReferenceEtagForCollection;

                var lastProcessedCompareExchangeReferenceTombstoneEtagForCollection = index._indexStorage.ReferencesForCompareExchange.ReadLastProcessedReferenceTombstoneEtag(indexContext.Transaction.InnerTransaction, collection, IndexStorage.CompareExchangeReferences.CompareExchange);
                lastProcessedCompareExchangeReferenceTombstoneEtag = lastProcessedCompareExchangeReferenceTombstoneEtag.HasValue
                    ? Math.Max(lastProcessedCompareExchangeReferenceTombstoneEtag.Value, lastProcessedCompareExchangeReferenceTombstoneEtagForCollection)
                    : lastProcessedCompareExchangeReferenceTombstoneEtagForCollection;
            }

            return(lastProcessedCompareExchangeReferenceEtag, lastProcessedCompareExchangeReferenceTombstoneEtag);
        }
Esempio n. 2
0
        private static bool IsStaleDueToReferences(Index index, AbstractStaticIndexBase compiled, QueryOperationContext queryContext, TransactionOperationContext indexContext, long?referenceCutoff, long?compareExchangeReferenceCutoff, List <string> stalenessReasons)
        {
            foreach (var collection in index.Collections)
            {
                long lastIndexedEtag = -1;

                if (compiled.ReferencedCollections.TryGetValue(collection, out HashSet <CollectionName> referencedCollections))
                {
                    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)
                    {
                        foreach (var referencedCollection in referencedCollections)
                        {
                            var lastDocEtag = queryContext.Documents.DocumentDatabase.DocumentsStorage.GetLastDocumentEtag(queryContext.Documents.Transaction.InnerTransaction, referencedCollection.Name);
                            var lastProcessedReferenceEtag = index._indexStorage.ReferencesForDocuments.ReadLastProcessedReferenceEtag(indexContext.Transaction.InnerTransaction, collection, referencedCollection);
                            var lastProcessedTombstoneEtag = index._indexStorage.ReferencesForDocuments.ReadLastProcessedReferenceTombstoneEtag(indexContext.Transaction.InnerTransaction, collection, referencedCollection);

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

                                    var lastDoc = queryContext.Documents.DocumentDatabase.DocumentsStorage.GetByEtag(queryContext.Documents, 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 = queryContext.Documents.DocumentDatabase.DocumentsStorage.GetLastTombstoneEtag(queryContext.Documents.Transaction.InnerTransaction, referencedCollection.Name);

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

                                    var lastTombstone = queryContext.Documents.DocumentDatabase.DocumentsStorage.GetTombstoneByEtag(queryContext.Documents, 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 = queryContext.Documents.DocumentDatabase.DocumentsStorage.GetByEtag(queryContext.Documents, 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 = queryContext.Documents.DocumentDatabase.DocumentsStorage.HasTombstonesWithEtagGreaterThanStartAndLowerThanOrEqualToEnd(queryContext.Documents, referencedCollection.Name,
                                                                                                                                                                           lastProcessedTombstoneEtag,
                                                                                                                                                                           referenceCutoff.Value);

                                if (hasTombstones)
                                {
                                    if (stalenessReasons == null)
                                    {
                                        return(true);
                                    }

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

                if (compiled.CollectionsWithCompareExchangeReferences.Contains(collection))
                {
                    if (lastIndexedEtag == -1)
                    {
                        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)
                    {
                        var lastCompareExchangeEtag    = queryContext.Documents.DocumentDatabase.ServerStore.Cluster.GetLastCompareExchangeIndexForDatabase(queryContext.Server, queryContext.Documents.DocumentDatabase.Name);
                        var lastProcessedReferenceEtag = index._indexStorage.ReferencesForCompareExchange.ReadLastProcessedReferenceEtag(indexContext.Transaction.InnerTransaction, collection, referencedCollection: IndexStorage.CompareExchangeReferences.CompareExchange);

                        if (compareExchangeReferenceCutoff == null)
                        {
                            if (lastCompareExchangeEtag > lastProcessedReferenceEtag)
                            {
                                if (stalenessReasons == null)
                                {
                                    return(true);
                                }

                                stalenessReasons.Add($"There are still some compare exchange references to process for collection '{collection}'. The last compare exchange etag is '{lastCompareExchangeEtag:#,#;;0}', but last processed compare exchange etag for that collection is '{lastProcessedReferenceEtag:#,#;;0}'.");
                            }

                            var lastTombstoneEtag          = queryContext.Documents.DocumentDatabase.ServerStore.Cluster.GetLastCompareExchangeTombstoneIndexForDatabase(queryContext.Server, queryContext.Documents.DocumentDatabase.Name);
                            var lastProcessedTombstoneEtag = index._indexStorage.ReferencesForCompareExchange.ReadLastProcessedReferenceTombstoneEtag(indexContext.Transaction.InnerTransaction, collection, referencedCollection: IndexStorage.CompareExchangeReferences.CompareExchange);

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

                                stalenessReasons.Add($"There are still some compare exchange tombstone references to process for collection '{collection}'. The last compare exchange tombstone etag is '{lastTombstoneEtag:#,#;;0}', but last processed compare exchange tombstone etag for that collection is '{lastProcessedTombstoneEtag:#,#;;0}'.");
                            }
                        }
                        else
                        {
                            var minCompareExchangeEtag = Math.Min(compareExchangeReferenceCutoff.Value, lastCompareExchangeEtag);
                            if (minCompareExchangeEtag > lastProcessedReferenceEtag)
                            {
                                if (stalenessReasons == null)
                                {
                                    return(true);
                                }

                                stalenessReasons.Add($"There are still some compare exchange references to process for collection '{collection}'. The last compare exchange etag is '{lastCompareExchangeEtag:#,#;;0}' with cutoff set to '{compareExchangeReferenceCutoff.Value}', but last processed compare exchange etag for that collection is '{lastProcessedReferenceEtag:#,#;;0}'.");
                            }

                            var lastProcessedTombstoneEtag = index._indexStorage.ReferencesForCompareExchange.ReadLastProcessedReferenceTombstoneEtag(indexContext.Transaction.InnerTransaction, collection, referencedCollection: IndexStorage.CompareExchangeReferences.CompareExchange);
                            var hasTombstones = queryContext.Documents.DocumentDatabase.ServerStore.Cluster.HasCompareExchangeTombstonesWithEtagGreaterThanStartAndLowerThanOrEqualToEnd(queryContext.Server, queryContext.Documents.DocumentDatabase.Name,
                                                                                                                                                                                         lastProcessedTombstoneEtag,
                                                                                                                                                                                         compareExchangeReferenceCutoff.Value);

                            if (hasTombstones)
                            {
                                if (stalenessReasons == null)
                                {
                                    return(true);
                                }

                                stalenessReasons.Add($"There are still some compare exchange tombstones to process for collection '{collection}' with etag range '{lastProcessedTombstoneEtag} - {compareExchangeReferenceCutoff.Value}'.");
                            }
                        }
                    }
                }
            }

            return(stalenessReasons?.Count > 0);
        }
Esempio n. 3
0
        private void FillCollectionEtags(Transaction tx,
                                         Dictionary <string, IndexTransactionCache.CollectionEtags> map)
        {
            AbstractStaticIndexBase compiled = null;

            if (_index.Type.IsStatic())
            {
                switch (_index)
                {
                case MapIndex mapIndex:
                    compiled = mapIndex._compiled;
                    break;

                case MapReduceIndex mapReduceIndex:
                    compiled = mapReduceIndex._compiled;
                    break;

                case MapCountersIndex mapCountersIndex:
                    compiled = mapCountersIndex._compiled;
                    break;

                case MapTimeSeriesIndex mapTimeSeriesIndex:
                    compiled = mapTimeSeriesIndex._compiled;
                    break;
                }
            }

            foreach (string collection in _index.Collections)
            {
                using (Slice.From(tx.LowLevelTransaction.Allocator, collection, out Slice collectionSlice))
                {
                    var etags = new IndexTransactionCache.CollectionEtags
                    {
                        LastIndexedEtag = IndexStorage.ReadLastEtag(tx,
                                                                    IndexStorage.IndexSchema.EtagsTree,
                                                                    collectionSlice
                                                                    ),
                        LastProcessedTombstoneEtag = IndexStorage.ReadLastEtag(tx,
                                                                               IndexStorage.IndexSchema.EtagsTombstoneTree,
                                                                               collectionSlice
                                                                               )
                    };

                    if (compiled?.CollectionsWithCompareExchangeReferences.Contains(collection) == true)
                    {
                        etags.LastReferencedEtagsForCompareExchange = new IndexTransactionCache.ReferenceCollectionEtags
                        {
                            LastEtag = _index._indexStorage.ReferencesForCompareExchange.ReadLastProcessedReferenceEtag(tx, collection, IndexStorage.CompareExchangeReferences.CompareExchange),
                            LastProcessedTombstoneEtag = _index._indexStorage.ReferencesForCompareExchange.ReadLastProcessedReferenceTombstoneEtag(tx, collection, IndexStorage.CompareExchangeReferences.CompareExchange)
                        };
                    }

                    map[collection] = etags;
                }
            }

            var referencedCollections = _index.GetReferencedCollections();

            if (referencedCollections == null || referencedCollections.Count == 0)
            {
                return;
            }

            foreach (var(src, collections) in referencedCollections)
            {
                var collectionEtags = map[src];
                collectionEtags.LastReferencedEtags ??= new Dictionary <string, IndexTransactionCache.ReferenceCollectionEtags>(StringComparer.OrdinalIgnoreCase);
                foreach (var collectionName in collections)
                {
                    collectionEtags.LastReferencedEtags[collectionName.Name] = new IndexTransactionCache.ReferenceCollectionEtags
                    {
                        LastEtag = _index._indexStorage.ReferencesForDocuments.ReadLastProcessedReferenceEtag(tx, src, collectionName),
                        LastProcessedTombstoneEtag = _index._indexStorage.ReferencesForDocuments.ReadLastProcessedReferenceTombstoneEtag(tx, src, collectionName),
                    };
                }
            }
        }
Esempio n. 4
0
 private static void ThrowMissingGroupByFieldsInMapOutput(object output, Dictionary <string, CompiledIndexField> groupByFields, AbstractStaticIndexBase 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.Select(x => x.Key))}{Environment.NewLine}" +
               $"Compiled index def:{Environment.NewLine}{compiledIndex.Source}");
 }
Esempio n. 5
0
 private static MapReduceIndexDefinition CreateIndexDefinition(IndexDefinition definition, RavenConfiguration configuration, long indexVersion, out AbstractStaticIndexBase staticIndex)
 {
     staticIndex = IndexCompilationCache.GetIndexInstance(definition, configuration);
     return(new MapReduceIndexDefinition(definition, staticIndex.Maps.Keys, staticIndex.OutputFields, staticIndex.GroupByFields, staticIndex.HasDynamicFields, staticIndex.CollectionsWithCompareExchangeReferences.Count > 0, indexVersion));
 }
Esempio n. 6
0
        public static void ValidateReduceResultsCollectionName(IndexDefinition definition, AbstractStaticIndexBase 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.");
                        }
                    }
            }
        }
        protected override int GetFields <T>(T instance, LazyStringValue key, LazyStringValue sourceDocumentId, 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 (sourceDocumentId != null)
            {
                instance.Add(GetOrCreateSourceDocumentIdField(sourceDocumentId));
                newFields++;
            }

            if (_storeValue)
            {
                var storedValue = JsBlittableBridge.Translate(indexContext,
                                                              documentToProcess.Engine,
                                                              documentToProcess);

                instance.Add(GetStoredValueField(storedValue, writeBuffer));
                newFields++;
            }

            if (TryGetBoostedValue(documentToProcess, out var boostedValue, out var documentBoost))
            {
                if (IsObject(boostedValue) == false)
                {
                    throw new InvalidOperationException($"Invalid boosted value. Expected object but got '{boostedValue.Type}' with value '{boostedValue}'.");
                }

                documentToProcess = boostedValue.AsObject();
            }

            foreach (var(property, propertyDescriptor) in documentToProcess.GetOwnProperties())
            {
                var propertyAsString = property.AsString();

                if (_fields.TryGetValue(propertyAsString, out var field) == false)
                {
                    field = _fields[propertyAsString] = IndexField.Create(propertyAsString, new IndexFieldOptions(), _allFields);
                }

                object value;
                float? propertyBoost = null;
                int    numberOfCreatedFields;
                var    actualValue = propertyDescriptor.Value;
                var    isObject    = IsObject(actualValue);
                if (isObject)
                {
                    if (TryGetBoostedValue(actualValue.AsObject(), out boostedValue, out propertyBoost))
                    {
                        actualValue = boostedValue;
                        isObject    = IsObject(actualValue);
                    }

                    if (isObject)
                    {
                        //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(propertyAsString, actualValue.AsObject(), field);
                        if (val != null)
                        {
                            if (val.IsObject() && val.AsObject().TryGetValue(SpatialPropertyName, 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);
                                numberOfCreatedFields = GetRegularFields(instance, field, CreateValueForIndexing(value, propertyBoost), indexContext, out _);

                                newFields += numberOfCreatedFields;

                                BoostDocument(instance, numberOfCreatedFields, documentBoost);

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

                                continue;
                            }
                        }

                        var objectValue = actualValue.AsObject();
                        if (objectValue.HasOwnProperty(SpatialPropertyName) && objectValue.TryGetValue(SpatialPropertyName, out var inner))
                        {
                            SpatialField spatialField;
                            IEnumerable <AbstractField> spatial;
                            if (inner.IsString())
                            {
                                spatialField = AbstractStaticIndexBase.GetOrCreateSpatialField(field.Name);
                                spatial      = AbstractStaticIndexBase.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 = AbstractStaticIndexBase.GetOrCreateSpatialField(field.Name);
                                    spatial      = AbstractStaticIndexBase.CreateSpatialField(spatialField, lat.AsNumber(), lng.AsNumber());
                                }
                                else
                                {
                                    continue; //Ignoring bad spatial field
                                }
                            }
                            else
                            {
                                continue; //Ignoring bad spatial field
                            }

                            numberOfCreatedFields = GetRegularFields(instance, field, CreateValueForIndexing(spatial, propertyBoost), indexContext, out _);

                            newFields += numberOfCreatedFields;

                            BoostDocument(instance, numberOfCreatedFields, documentBoost);

                            continue;
                        }
                    }
                }

                value = TypeConverter.ToBlittableSupportedType(actualValue, flattenArrays: false, forIndexing: true, engine: documentToProcess.Engine, context: indexContext);
                numberOfCreatedFields = GetRegularFields(instance, field, CreateValueForIndexing(value, propertyBoost), indexContext, out _);

                newFields += numberOfCreatedFields;

                BoostDocument(instance, numberOfCreatedFields, documentBoost);

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

            return(newFields);