示例#1
0
        /// <summary>
        /// Asynchronously sets data in the document, either replacing it completely or merging fields.
        /// </summary>
        /// <param name="documentData">The data to store in the document. Must not be null.</param>
        /// <param name="options">The options to use when updating the document. May be null, which is equivalent to <see cref="SetOptions.Overwrite"/>.</param>
        /// <param name="cancellationToken">A cancellation token to monitor for the asynchronous operation.</param>
        /// <returns>The write result of the server operation.</returns>
        public async Task <WriteResult> SetAsync(object documentData, SetOptions options = null, CancellationToken cancellationToken = default)
        {
            var batch = Database.StartBatch();

            batch.Set(this, documentData, options);
            var results = await batch.CommitAsync(cancellationToken).ConfigureAwait(false);

            return(results[0]);
        }
示例#2
0
 /// <summary>
 /// Adds an operation to set a document's data in this transaction.
 /// </summary>
 /// <param name="documentReference">The document in which to set the data. Must not be null.</param>
 /// <param name="documentData">The data for the document. Must not be null.</param>
 /// <param name="options">The options to use when updating the document. May be null, which is equivalent to <see cref="SetOptions.Overwrite"/>.</param>
 public void Set(DocumentReference documentReference, object documentData, SetOptions options = null)
 {
     // Preconditions are validated by WriteBatch.
     _writes.Set(documentReference, documentData, options);
 }
        /// <summary>
        /// Adds an operation that sets data in a document, either replacing it completely or merging fields.
        /// </summary>
        /// <param name="documentReference">A document reference indicating the path of the document to update. Must not be null.</param>
        /// <param name="documentData">The data to store in the document. Must not be null.</param>
        /// <param name="options">The options to use when setting data in the document. May be null, which is equivalent to <see cref="SetOptions.Overwrite"/>.</param>
        /// <returns>This batch, for the purposes of method chaining.</returns>
        public WriteBatch Set(DocumentReference documentReference, object documentData, SetOptions options = null)
        {
            GaxPreconditions.CheckNotNull(documentReference, nameof(documentReference));
            GaxPreconditions.CheckNotNull(documentData, nameof(documentData));

            var fields = ValueSerializer.SerializeMap(documentData);

            options = options ?? SetOptions.Overwrite;
            var sentinels  = FindSentinels(fields);
            var deletes    = sentinels.Where(sf => sf.IsDelete).ToList();
            var nonDeletes = sentinels.Where(sf => !sf.IsDelete).ToList();

            bool forceWrite = false;
            IDictionary <FieldPath, Value> updates;
            IReadOnlyList <FieldPath>      updatePaths;

            if (options.Merge)
            {
                var mask = options.FieldMask;
                if (mask.Count == 0)
                {
                    // Merge all:
                    // - If the data is empty, we force a write
                    // - Deletes are allowed anywhere
                    // - All timestamps converted to transforms
                    // - Each top-level entry becomes a FieldPath
                    forceWrite = fields.Count == 0;
                    RemoveSentinels(fields, nonDeletes);
                    // Work out the update paths after removing server timestamps but before removing deletes,
                    // so that we correctly perform the deletes.
                    updatePaths = ExtractDocumentMask(fields);
                    RemoveSentinels(fields, deletes);
                    updates = fields.ToDictionary(pair => new FieldPath(pair.Key), pair => pair.Value);
                }
                else
                {
                    // Merge specific:
                    // - Deletes must be in the mask
                    // - Only timestamps in the mask are converted to transforms
                    // - Apply the field mask to get the updates
                    GaxPreconditions.CheckArgument(deletes.All(sf => mask.Contains(sf.FieldPath)), nameof(documentData), "Delete cannot appear in an unmerged field");
                    nonDeletes = nonDeletes.Where(sf => mask.Any(fp => fp.IsPrefixOf(sf.FieldPath))).ToList();
                    RemoveSentinels(fields, deletes);
                    RemoveSentinels(fields, nonDeletes);
                    updates = ApplyFieldMask(fields, mask);
                    // Every field path in the mask must either refer to a now-removed sentinel, or a remaining value.
                    // Sentinels are permitted to be in the mask in a nested fashion rather than directly, e.g. a mask of "parent" with a sentinel of "parent.child.timestamp" is fine.
                    GaxPreconditions.CheckArgument(
                        mask.All(p => updates.ContainsKey(p) ||
                                 deletes.Any(sf => p.IsPrefixOf(sf.FieldPath)) ||
                                 nonDeletes.Any(sf => p.IsPrefixOf(sf.FieldPath))),
                        nameof(documentData), "All paths specified for merging must appear in the data.");
                    updatePaths = mask;
                }
            }
            else
            {
                // Overwrite:
                // - No deletes allowed
                // - All timestamps converted to transforms
                // - Each top-level entry becomes a FieldPath
                GaxPreconditions.CheckArgument(deletes.Count == 0, nameof(documentData), "Delete cannot appear in document data when overwriting");
                RemoveSentinels(fields, nonDeletes);
                updates     = fields.ToDictionary(pair => new FieldPath(pair.Key), pair => pair.Value);
                updatePaths = s_emptyFieldPathList;
                forceWrite  = true;
            }

            AddUpdateWrites(documentReference, ExpandObject(updates), updatePaths, null, nonDeletes, forceWrite, options.Merge);
            return(this);
        }
        /// <summary>
        /// Adds an operation that sets data in a document, either replacing it completely or merging fields.
        /// </summary>
        /// <param name="documentReference">A document reference indicating the path of the document to update. Must not be null.</param>
        /// <param name="documentData">The data to store in the document. Must not be null.</param>
        /// <param name="options">The options to use when setting data in the document. May be null, which is equivalent to <see cref="SetOptions.Overwrite"/>.</param>
        /// <returns>This batch, for the purposes of method chaining.</returns>
        public WriteBatch Set(DocumentReference documentReference, object documentData, SetOptions options = null)
        {
            GaxPreconditions.CheckNotNull(documentReference, nameof(documentReference));
            GaxPreconditions.CheckNotNull(documentData, nameof(documentData));

            var fields = ValueSerializer.SerializeMap(documentData);

            options = options ?? SetOptions.Overwrite;
            var serverTimestamps = new List <FieldPath>();
            var deletes          = new List <FieldPath>();

            FindSentinels(fields, FieldPath.Empty, serverTimestamps, deletes);

            bool forceWrite = false;
            IDictionary <FieldPath, Value> updates;
            IReadOnlyList <FieldPath>      updatePaths;

            if (options.Merge)
            {
                var mask = options.FieldMask;
                if (mask.Count == 0)
                {
                    // Merge all:
                    // - Empty data is not allowed
                    // - Deletes are allowed anywhere
                    // - All timestamps converted to transforms
                    // - Each top-level entry becomes a FieldPath
                    GaxPreconditions.CheckArgument(fields.Count != 0, nameof(documentData), "{0} cannot be specified with empty data", nameof(SetOptions.MergeAll));
                    RemoveSentinels(fields, serverTimestamps);
                    // Work out the update paths after removing server timestamps but before removing deletes,
                    // so that we correctly perform the deletes.
                    updatePaths = ExtractDocumentMask(fields);
                    RemoveSentinels(fields, deletes);
                    updates = fields.ToDictionary(pair => new FieldPath(pair.Key), pair => pair.Value);
                }
                else
                {
                    // Merge specific:
                    // - Deletes must be in the mask
                    // - Only timestamps in the mask are converted to transforms
                    // - Apply the field mask to get the updates
                    GaxPreconditions.CheckArgument(deletes.All(p => mask.Contains(p)), nameof(documentData), "Delete cannot appear in an unmerged field");
                    serverTimestamps = serverTimestamps.Intersect(mask).ToList();
                    RemoveSentinels(fields, deletes);
                    RemoveSentinels(fields, serverTimestamps);
                    updates = ApplyFieldMask(fields, mask);
                    // Every field path in the mask must either refer to a now-removed sentinel, or a remaining value.
                    GaxPreconditions.CheckArgument(mask.All(p => updates.ContainsKey(p) || deletes.Contains(p) || serverTimestamps.Contains(p)),
                                                   nameof(documentData), "All paths specified for merging must appear in the data.");
                    updatePaths = mask;
                }
            }
            else
            {
                // Overwrite:
                // - No deletes allowed
                // - All timestamps converted to transforms
                // - Each top-level entry becomes a FieldPath
                GaxPreconditions.CheckArgument(deletes.Count == 0, nameof(documentData), "Delete cannot appear in document data when overwriting");
                RemoveSentinels(fields, serverTimestamps);
                updates     = fields.ToDictionary(pair => new FieldPath(pair.Key), pair => pair.Value);
                updatePaths = s_emptyFieldPathList;
                forceWrite  = true;
            }

            AddUpdateWrites(documentReference, ExpandObject(updates), updatePaths, null, serverTimestamps, forceWrite);
            return(this);
        }