public static void WriteMetadata(this BlittableJsonTextWriter writer, Document document, BlittableJsonReaderObject metadata)
        {
            writer.WritePropertyName(Constants.Documents.Metadata.Key);
            writer.WriteStartObject();
            bool first = true;

            if (metadata != null)
            {
                var size = metadata.Count;
                var prop = new BlittableJsonReaderObject.PropertyDetails();

                for (int i = 0; i < size; i++)
                {
                    if (first == false)
                    {
                        writer.WriteComma();
                    }
                    first = false;
                    metadata.GetPropertyByIndex(i, ref prop);
                    writer.WritePropertyName(prop.Name);
                    writer.WriteValue(prop.Token & BlittableJsonReaderBase.TypesMask, prop.Value);
                }
            }

            if (first == false)
            {
                writer.WriteComma();
            }
            writer.WritePropertyName(Constants.Documents.Metadata.ChangeVector);
            writer.WriteString(document.ChangeVector);

            if (document.Flags != DocumentFlags.None)
            {
                writer.WriteComma();
                writer.WritePropertyName(Constants.Documents.Metadata.Flags);
                writer.WriteString(document.Flags.ToString());
            }
            if (document.Id != null)
            {
                writer.WriteComma();
                writer.WritePropertyName(Constants.Documents.Metadata.Id);
                writer.WriteString(document.Id);
            }
            if (document.IndexScore != null)
            {
                writer.WriteComma();
                writer.WritePropertyName(Constants.Documents.Metadata.IndexScore);
                writer.WriteDouble(document.IndexScore.Value);
            }
            if (document.LastModified != DateTime.MinValue)
            {
                writer.WriteComma();
                writer.WritePropertyName(Constants.Documents.Metadata.LastModified);
                writer.WriteDateTime(document.LastModified, isUtc: true);
            }
            writer.WriteEndObject();
        }
        private static void WriteDocumentProperties(this BlittableJsonTextWriter writer, JsonOperationContext context, Document document, Func <LazyStringValue, bool> filterMetadataProperty = null)
        {
            var first = true;
            BlittableJsonReaderObject metadata = null;
            var metadataField = context.GetLazyStringForFieldWithCaching(Raven.Server.Json.BlittableJsonTextWriterExtensions.MetadataKeySegment);

            var prop = new BlittableJsonReaderObject.PropertyDetails();

            using (var buffers = document.Data.GetPropertiesByInsertionOrder())
            {
                for (var i = 0; i < buffers.Size; i++)
                {
                    unsafe
                    {
                        document.Data.GetPropertyByIndex(buffers.Properties[i], ref prop);
                    }

                    if (metadataField.Equals(prop.Name))
                    {
                        metadata = (BlittableJsonReaderObject)prop.Value;
                        continue;
                    }
                    if (first == false)
                    {
                        writer.WriteComma();
                    }
                    first = false;
                    writer.WritePropertyName((string)prop.Name);
                    writer.WriteValue(prop.Token & BlittableJsonReaderBase.TypesMask, prop.Value);
                }
            }

            if (first == false)
            {
                writer.WriteComma();
            }
            WriteMetadata(writer, document, metadata, filterMetadataProperty);
        }
        private static void WriteDocumentProperties(this BlittableJsonTextWriter writer, JsonOperationContext context, Document document)
        {
            if (_buffers == null)
            {
                _buffers = new BlittableJsonReaderObject.PropertiesInsertionBuffer();
            }

            var first = true;
            BlittableJsonReaderObject metadata = null;
            var metadataField = context.GetLazyStringForFieldWithCaching(MetadataKeySegment);

            var size = document.Data.GetPropertiesByInsertionOrder(_buffers);
            var prop = new BlittableJsonReaderObject.PropertyDetails();

            for (var i = 0; i < size; i++)
            {
                document.Data.GetPropertyByIndex(_buffers.Properties[i], ref prop);
                if (metadataField.Equals(prop.Name))
                {
                    metadata = (BlittableJsonReaderObject)prop.Value;
                    continue;
                }
                if (first == false)
                {
                    writer.WriteComma();
                }
                first = false;
                writer.WritePropertyName(prop.Name);
                writer.WriteValue(prop.Token & BlittableJsonReaderBase.TypesMask, prop.Value);
            }

            if (first == false)
            {
                writer.WriteComma();
            }
            WriteMetadata(writer, document, metadata);
        }
        public static void WriteDocumentPropertiesWithoutMetdata(this BlittableJsonTextWriter writer, JsonOperationContext context, Document document)
        {
            if (_buffers == null)
            {
                _buffers = new BlittableJsonReaderObject.PropertiesInsertionBuffer();
            }

            var first = true;

            var size = document.Data.GetPropertiesByInsertionOrder(_buffers);
            var prop = new BlittableJsonReaderObject.PropertyDetails();

            for (var i = 0; i < size; i++)
            {
                document.Data.GetPropertyByIndex(_buffers.Properties[i], ref prop);
                if (first == false)
                {
                    writer.WriteComma();
                }
                first = false;
                writer.WritePropertyName(prop.Name);
                writer.WriteValue(prop.Token & BlittableJsonReaderBase.TypesMask, prop.Value);
            }
        }
Ejemplo n.º 5
0
        private async Task ProcessSubscriptionAsync()
        {
            if (_logger.IsInfoEnabled)
            {
                _logger.Info(
                    $"Starting processing documents for subscription {SubscriptionId} received from {TcpConnection.TcpClient.Client.RemoteEndPoint}");
            }

            using (DisposeOnDisconnect)
                using (TcpConnection.DocumentDatabase.DocumentsStorage.ContextPool.AllocateOperationContext(out DocumentsOperationContext docsContext))
                    using (RegisterForNotificationOnNewDocuments())
                    {
                        var replyFromClientTask = GetReplyFromClientAsync();

                        string lastChangeVector = null;
                        string subscriptionChangeVectorBeforeCurrentBatch = SubscriptionState.ChangeVectorForNextBatchStartingPoint;
                        var    startEtag = GetStartEtagForSubscription(docsContext, SubscriptionState);
                        var    patch     = SetupFilterScript();
                        var    fetcher   = new SubscriptionDocumentsFetcher(TcpConnection.DocumentDatabase, _options.MaxDocsPerBatch, SubscriptionId, TcpConnection.TcpClient.Client.RemoteEndPoint);
                        while (CancellationTokenSource.IsCancellationRequested == false)
                        {
                            bool anyDocumentsSentInCurrentIteration = false;

                            var sendingCurrentBatchStopwatch = Stopwatch.StartNew();

                            _buffer.SetLength(0);

                            var docsToFlush = 0;

                            using (TcpConnection.ContextPool.AllocateOperationContext(out JsonOperationContext context))
                                using (var writer = new BlittableJsonTextWriter(context, _buffer))
                                {
                                    using (docsContext.OpenReadTransaction())
                                    {
                                        foreach (var result in fetcher.GetDataToSend(docsContext, Collection, Revisions, SubscriptionState, patch, startEtag))
                                        {
                                            startEtag        = result.Doc.Etag;
                                            lastChangeVector = string.IsNullOrEmpty(SubscriptionState.ChangeVectorForNextBatchStartingPoint)
                                    ? result.Doc.ChangeVector
                                    : ChangeVectorUtils.MergeVectors(result.Doc.ChangeVector, SubscriptionState.ChangeVectorForNextBatchStartingPoint);

                                            if (result.Doc.Data == null)
                                            {
                                                if (sendingCurrentBatchStopwatch.ElapsedMilliseconds > 1000)
                                                {
                                                    await SendHeartBeat();

                                                    sendingCurrentBatchStopwatch.Restart();
                                                }

                                                continue;
                                            }

                                            anyDocumentsSentInCurrentIteration = true;
                                            writer.WriteStartObject();

                                            writer.WritePropertyName(context.GetLazyStringForFieldWithCaching(TypeSegment));
                                            writer.WriteValue(BlittableJsonToken.String, context.GetLazyStringForFieldWithCaching(DataSegment));
                                            writer.WriteComma();
                                            writer.WritePropertyName(context.GetLazyStringForFieldWithCaching(DataSegment));
                                            result.Doc.EnsureMetadata();

                                            if (result.Exception != null)
                                            {
                                                var metadata = result.Doc.Data[Client.Constants.Documents.Metadata.Key];
                                                writer.WriteValue(BlittableJsonToken.StartObject,
                                                                  docsContext.ReadObject(new DynamicJsonValue
                                                {
                                                    [Client.Constants.Documents.Metadata.Key] = metadata
                                                }, result.Doc.Id)
                                                                  );
                                                writer.WriteComma();
                                                writer.WritePropertyName(context.GetLazyStringForFieldWithCaching(ExceptionSegment));
                                                writer.WriteValue(BlittableJsonToken.String, context.GetLazyStringForFieldWithCaching(result.Exception.ToString()));
                                            }
                                            else
                                            {
                                                writer.WriteDocument(docsContext, result.Doc, metadataOnly: false);
                                            }

                                            writer.WriteEndObject();
                                            docsToFlush++;

                                            // perform flush for current batch after 1000ms of running or 1 MB
                                            if (_buffer.Length > Constants.Size.Megabyte ||
                                                sendingCurrentBatchStopwatch.ElapsedMilliseconds > 1000)
                                            {
                                                if (docsToFlush > 0)
                                                {
                                                    await FlushDocsToClient(writer, docsToFlush);

                                                    docsToFlush = 0;
                                                    sendingCurrentBatchStopwatch.Restart();
                                                }
                                                else
                                                {
                                                    await SendHeartBeat();
                                                }
                                            }
                                        }
                                    }

                                    if (anyDocumentsSentInCurrentIteration)
                                    {
                                        context.Write(writer, new DynamicJsonValue
                                        {
                                            [nameof(SubscriptionConnectionServerMessage.Type)] = nameof(SubscriptionConnectionServerMessage.MessageType.EndOfBatch)
                                        });

                                        await FlushDocsToClient(writer, docsToFlush, true);

                                        if (_logger.IsInfoEnabled)
                                        {
                                            _logger.Info(
                                                $"Finished sending a batch with {docsToFlush} documents for subscription {Options.SubscriptionName}");
                                        }
                                    }
                                }

                            if (anyDocumentsSentInCurrentIteration == false)
                            {
                                if (_logger.IsInfoEnabled)
                                {
                                    _logger.Info(
                                        $"Finished sending a batch with {docsToFlush} documents for subscription {Options.SubscriptionName}");
                                }
                                await TcpConnection.DocumentDatabase.SubscriptionStorage.AcknowledgeBatchProcessed(SubscriptionId,
                                                                                                                   Options.SubscriptionName,
                                                                                                                   lastChangeVector,
                                                                                                                   subscriptionChangeVectorBeforeCurrentBatch);

                                subscriptionChangeVectorBeforeCurrentBatch = lastChangeVector;

                                if (sendingCurrentBatchStopwatch.ElapsedMilliseconds > 1000)
                                {
                                    await SendHeartBeat();
                                }

                                using (docsContext.OpenReadTransaction())
                                {
                                    long globalEtag = TcpConnection.DocumentDatabase.DocumentsStorage.GetLastDocumentEtag(docsContext, Collection);

                                    if (globalEtag > startEtag)
                                    {
                                        continue;
                                    }
                                }

                                if (await WaitForChangedDocuments(replyFromClientTask))
                                {
                                    continue;
                                }
                            }

                            SubscriptionConnectionClientMessage clientReply;

                            while (true)
                            {
                                var result = await Task.WhenAny(replyFromClientTask,
                                                                TimeoutManager.WaitFor(TimeSpan.FromMilliseconds(5000), CancellationTokenSource.Token)).ConfigureAwait(false);

                                CancellationTokenSource.Token.ThrowIfCancellationRequested();
                                if (result == replyFromClientTask)
                                {
                                    clientReply = await replyFromClientTask;
                                    if (clientReply.Type == SubscriptionConnectionClientMessage.MessageType.DisposedNotification)
                                    {
                                        CancellationTokenSource.Cancel();
                                        break;
                                    }
                                    replyFromClientTask = GetReplyFromClientAsync();
                                    break;
                                }
                                await SendHeartBeat();
                            }

                            CancellationTokenSource.Token.ThrowIfCancellationRequested();

                            switch (clientReply.Type)
                            {
                            case SubscriptionConnectionClientMessage.MessageType.Acknowledge:
                                await TcpConnection.DocumentDatabase.SubscriptionStorage.AcknowledgeBatchProcessed(
                                    SubscriptionId,
                                    Options.SubscriptionName,
                                    lastChangeVector,
                                    subscriptionChangeVectorBeforeCurrentBatch);

                                subscriptionChangeVectorBeforeCurrentBatch = lastChangeVector;
                                Stats.LastAckReceivedAt = DateTime.UtcNow;
                                Stats.AckRate.Mark();
                                await WriteJsonAsync(new DynamicJsonValue
                                {
                                    [nameof(SubscriptionConnectionServerMessage.Type)] = nameof(SubscriptionConnectionServerMessage.MessageType.Confirm)
                                });

                                break;

                            //precaution, should not reach this case...
                            case SubscriptionConnectionClientMessage.MessageType.DisposedNotification:
                                CancellationTokenSource.Cancel();
                                break;

                            default:
                                throw new ArgumentException("Unknown message type from client " +
                                                            clientReply.Type);
                            }
                        }

                        CancellationTokenSource.Token.ThrowIfCancellationRequested();
                    }
        }
        private void WriteDocument(BlittableJsonTextWriter writer, JsonOperationContext context, Document document, HashSet <string> propertiesPreviewToSend, HashSet <string> fullPropertiesToSend)
        {
            if (_buffers == null)
            {
                _buffers = new BlittableJsonReaderObject.PropertiesInsertionBuffer();
            }

            writer.WriteStartObject();

            document.Data.TryGet(Constants.Documents.Metadata.Key, out BlittableJsonReaderObject metadata);

            bool first = true;

            var objectsStubs = new HashSet <LazyStringValue>();
            var arraysStubs  = new HashSet <LazyStringValue>();
            var trimmedValue = new HashSet <LazyStringValue>();

            var size = document.Data.GetPropertiesByInsertionOrder(_buffers);
            var prop = new BlittableJsonReaderObject.PropertyDetails();

            for (int i = 0; i < size; i++)
            {
                document.Data.GetPropertyByIndex(_buffers.Properties[i], ref prop);
                var sendFull = fullPropertiesToSend.Contains(prop.Name);
                if (sendFull || propertiesPreviewToSend.Contains(prop.Name))
                {
                    var strategy = sendFull ? ValueWriteStrategy.Passthrough : FindWriteStrategy(prop.Token & BlittableJsonReaderBase.TypesMask, prop.Value);

                    if (strategy == ValueWriteStrategy.Passthrough || strategy == ValueWriteStrategy.Trim)
                    {
                        if (first == false)
                        {
                            writer.WriteComma();
                        }
                        first = false;
                    }

                    switch (strategy)
                    {
                    case ValueWriteStrategy.Passthrough:
                        writer.WritePropertyName(prop.Name);
                        writer.WriteValue(prop.Token & BlittableJsonReaderBase.TypesMask, prop.Value);
                        break;

                    case ValueWriteStrategy.SubstituteWithArrayStub:
                        arraysStubs.Add(prop.Name);
                        break;

                    case ValueWriteStrategy.SubstituteWithObjectStub:
                        objectsStubs.Add(prop.Name);
                        break;

                    case ValueWriteStrategy.Trim:
                        writer.WritePropertyName(prop.Name);
                        WriteTrimmedValue(writer, prop.Token & BlittableJsonReaderBase.TypesMask, prop.Value);
                        trimmedValue.Add(prop.Name);
                        break;
                    }
                }
            }
            if (first == false)
            {
                writer.WriteComma();
            }

            var extraMetadataProperties = new DynamicJsonValue
            {
                [ObjectStubsKey]  = new DynamicJsonArray(objectsStubs),
                [ArrayStubsKey]   = new DynamicJsonArray(arraysStubs),
                [TrimmedValueKey] = new DynamicJsonArray(trimmedValue)
            };

            if (metadata != null)
            {
                metadata.Modifications = extraMetadataProperties;
                metadata = context.ReadObject(metadata, document.Id);
            }
            else
            {
                metadata = context.ReadObject(extraMetadataProperties, document.Id);
            }

            writer.WriteMetadata(document, metadata);
            writer.WriteEndObject();
        }
        private static void WriteMetadata(BlittableJsonTextWriter writer, Document document, BlittableJsonReaderObject metadata, Func <LazyStringValue, bool> filterMetadataProperty = null)
        {
            writer.WritePropertyName(Constants.Documents.Metadata.Key);
            writer.WriteStartObject();
            bool first = true;

            if (metadata != null)
            {
                var size = metadata.Count;
                var prop = new BlittableJsonReaderObject.PropertyDetails();

                for (int i = 0; i < size; i++)
                {
                    metadata.GetPropertyByIndex(i, ref prop);

                    if (filterMetadataProperty != null && filterMetadataProperty(prop.Name))
                    {
                        continue;
                    }

                    if (first == false)
                    {
                        writer.WriteComma();
                    }
                    first = false;
                    writer.WritePropertyName((string)prop.Name);
                    writer.WriteValue(prop.Token & BlittableJsonReaderBase.TypesMask, prop.Value);
                }
            }

            if (first == false)
            {
                writer.WriteComma();
            }
            writer.WritePropertyName(Constants.Documents.Metadata.ChangeVector);
            writer.WriteString(document.ChangeVector);

            if (document.Flags != DocumentFlags.None)
            {
                writer.WriteComma();
                writer.WritePropertyName(Constants.Documents.Metadata.Flags);
                writer.WriteString(document.Flags.ToString());
            }
            if (document.Id != null)
            {
                writer.WriteComma();
                writer.WritePropertyName(Constants.Documents.Metadata.Id);
                writer.WriteString(document.Id);
            }
            if (document.IndexScore != null)
            {
                writer.WriteComma();
                writer.WritePropertyName(Constants.Documents.Metadata.IndexScore);
                writer.WriteDouble(document.IndexScore.Value);
            }
            if (document.Distance != null)
            {
                writer.WriteComma();
                var result = document.Distance.Value;
                writer.WritePropertyName(Constants.Documents.Metadata.SpatialResult);
                writer.WriteStartObject();
                writer.WritePropertyName(nameof(result.Distance));
                writer.WriteDouble(result.Distance);
                writer.WriteComma();
                writer.WritePropertyName(nameof(result.Latitude));
                writer.WriteDouble(result.Latitude);
                writer.WriteComma();
                writer.WritePropertyName(nameof(result.Longitude));
                writer.WriteDouble(result.Longitude);
                writer.WriteEndObject();
            }
            if (document.LastModified != DateTime.MinValue)
            {
                writer.WriteComma();
                writer.WritePropertyName(Constants.Documents.Metadata.LastModified);
                writer.WriteDateTime(document.LastModified, isUtc: true);
            }
            writer.WriteEndObject();
        }
Ejemplo n.º 8
0
        /// <summary>
        /// Iterates on a batch in document collection, process it and send documents if found any match
        /// </summary>
        /// <param name="docsContext"></param>
        /// <param name="sendingCurrentBatchStopwatch"></param>
        /// <returns>Whether succeeded finding any documents to send</returns>
        private async Task <bool> TrySendingBatchToClient(DocumentsOperationContext docsContext, Stopwatch sendingCurrentBatchStopwatch)
        {
            bool anyDocumentsSentInCurrentIteration = false;
            int  docsToFlush = 0;

            using (var writer = new BlittableJsonTextWriter(docsContext, _buffer))
            {
                using (docsContext.OpenReadTransaction())
                {
                    foreach (var result in _documentsFetcher.GetDataToSend(docsContext, _startEtag))
                    {
                        _startEtag        = result.Doc.Etag;
                        _lastChangeVector = string.IsNullOrEmpty(SubscriptionState.ChangeVectorForNextBatchStartingPoint)
                            ? result.Doc.ChangeVector
                            : ChangeVectorUtils.MergeVectors(result.Doc.ChangeVector, SubscriptionState.ChangeVectorForNextBatchStartingPoint);

                        if (result.Doc.Data == null)
                        {
                            if (sendingCurrentBatchStopwatch.ElapsedMilliseconds > 1000)
                            {
                                await SendHeartBeat();

                                sendingCurrentBatchStopwatch.Restart();
                            }

                            continue;
                        }

                        anyDocumentsSentInCurrentIteration = true;
                        writer.WriteStartObject();

                        writer.WritePropertyName(docsContext.GetLazyStringForFieldWithCaching(TypeSegment));
                        writer.WriteValue(BlittableJsonToken.String, docsContext.GetLazyStringForFieldWithCaching(DataSegment));
                        writer.WriteComma();
                        writer.WritePropertyName(docsContext.GetLazyStringForFieldWithCaching(DataSegment));
                        result.Doc.EnsureMetadata();

                        if (result.Exception != null)
                        {
                            var metadata = result.Doc.Data[Client.Constants.Documents.Metadata.Key];
                            writer.WriteValue(BlittableJsonToken.StartObject,
                                              docsContext.ReadObject(new DynamicJsonValue
                            {
                                [Client.Constants.Documents.Metadata.Key] = metadata
                            }, result.Doc.Id)
                                              );
                            writer.WriteComma();
                            writer.WritePropertyName(docsContext.GetLazyStringForFieldWithCaching(ExceptionSegment));
                            writer.WriteValue(BlittableJsonToken.String, docsContext.GetLazyStringForFieldWithCaching(result.Exception.ToString()));
                        }
                        else
                        {
                            writer.WriteDocument(docsContext, result.Doc, metadataOnly: false);
                        }

                        writer.WriteEndObject();
                        docsToFlush++;

                        // perform flush for current batch after 1000ms of running or 1 MB
                        if (_buffer.Length > Constants.Size.Megabyte ||
                            sendingCurrentBatchStopwatch.ElapsedMilliseconds > 1000)
                        {
                            if (docsToFlush > 0)
                            {
                                await FlushDocsToClient(writer, docsToFlush);

                                docsToFlush = 0;
                                sendingCurrentBatchStopwatch.Restart();
                            }
                            else
                            {
                                await SendHeartBeat();
                            }
                        }
                    }
                }

                if (anyDocumentsSentInCurrentIteration)
                {
                    docsContext.Write(writer, new DynamicJsonValue
                    {
                        [nameof(SubscriptionConnectionServerMessage.Type)] = nameof(SubscriptionConnectionServerMessage.MessageType.EndOfBatch)
                    });

                    await FlushDocsToClient(writer, docsToFlush, true);

                    if (_logger.IsInfoEnabled)
                    {
                        _logger.Info(
                            $"Finished sending a batch with {docsToFlush} documents for subscription {Options.SubscriptionName}");
                    }
                }
            }
            return(anyDocumentsSentInCurrentIteration);
        }
Ejemplo n.º 9
0
        private unsafe void WriteDocument(BlittableJsonTextWriter writer, JsonOperationContext context, Document document, HashSet <string> propertiesPreviewToSend, HashSet <string> fullPropertiesToSend)
        {
            writer.WriteStartObject();

            document.Data.TryGet(Constants.Documents.Metadata.Key, out BlittableJsonReaderObject metadata);

            bool first = true;

            var arrayStubsJson  = new DynamicJsonValue();
            var objectStubsJson = new DynamicJsonValue();
            var trimmedValue    = new HashSet <LazyStringValue>();

            var prop = new BlittableJsonReaderObject.PropertyDetails();

            using (var buffers = document.Data.GetPropertiesByInsertionOrder())
            {
                for (int i = 0; i < buffers.Size; i++)
                {
                    document.Data.GetPropertyByIndex(buffers.Properties[i], ref prop);
                    var sendFull = fullPropertiesToSend.Contains(prop.Name);
                    if (sendFull || propertiesPreviewToSend.Contains(prop.Name))
                    {
                        var strategy = sendFull ? ValueWriteStrategy.Passthrough : FindWriteStrategy(prop.Token & BlittableJsonReaderBase.TypesMask, prop.Value);

                        if (strategy == ValueWriteStrategy.Passthrough || strategy == ValueWriteStrategy.Trim)
                        {
                            if (first == false)
                            {
                                writer.WriteComma();
                            }
                            first = false;
                        }

                        switch (strategy)
                        {
                        case ValueWriteStrategy.Passthrough:
                            writer.WritePropertyName(prop.Name);
                            writer.WriteValue(prop.Token & BlittableJsonReaderBase.TypesMask, prop.Value);
                            break;

                        case ValueWriteStrategy.SubstituteWithArrayStub:
                            arrayStubsJson[prop.Name] = ((BlittableJsonReaderArray)prop.Value).Length;
                            break;

                        case ValueWriteStrategy.SubstituteWithObjectStub:
                            objectStubsJson[prop.Name] = ((BlittableJsonReaderObject)prop.Value).Count;
                            break;

                        case ValueWriteStrategy.Trim:
                            writer.WritePropertyName(prop.Name);
                            WriteTrimmedValue(writer, prop.Token & BlittableJsonReaderBase.TypesMask, prop.Value);
                            trimmedValue.Add(prop.Name);
                            break;
                        }
                    }
                }
            }
            if (first == false)
            {
                writer.WriteComma();
            }

            var extraMetadataProperties = new DynamicJsonValue(metadata)
            {
                [ObjectStubsKey]  = objectStubsJson,
                [ArrayStubsKey]   = arrayStubsJson,
                [TrimmedValueKey] = new DynamicJsonArray(trimmedValue)
            };

            if (metadata != null)
            {
                metadata.Modifications = extraMetadataProperties;

                if (document.Flags.Contain(DocumentFlags.HasCounters) || document.Flags.Contain(DocumentFlags.HasAttachments) || document.Flags.Contain(DocumentFlags.HasTimeSeries))
                {
                    metadata.Modifications.Remove(Constants.Documents.Metadata.Counters);
                    metadata.Modifications.Remove(Constants.Documents.Metadata.Attachments);
                    metadata.Modifications.Remove(Constants.Documents.Metadata.TimeSeries);
                }

                using (var old = metadata)
                {
                    metadata = context.ReadObject(metadata, document.Id);
                }
            }
            else
            {
                metadata = context.ReadObject(extraMetadataProperties, document.Id);
            }

            writer.WriteMetadata(document, metadata);
            writer.WriteEndObject();
        }