示例#1
0
            internal static InternalFilter Create(FieldPath fieldPath, FieldOp op, object value)
            {
                GaxPreconditions.CheckNotNull(fieldPath, nameof(fieldPath));
                var unaryOperator = GetUnaryOperator(value);

                if (unaryOperator != UnaryOp.Unspecified)
                {
                    if (op == FieldOp.Equal)
                    {
                        return(new InternalFilter(fieldPath, (int)unaryOperator, null));
                    }
                    else
                    {
                        throw new ArgumentException(nameof(value), "null and NaN values can only be used with the Equal operator");
                    }
                }
                else
                {
                    var convertedValue = ValueSerializer.Serialize(value);
                    if (SentinelValue.GetKind(convertedValue) != SentinelValue.SentinelKind.None)
                    {
                        throw new ArgumentException(nameof(value), "Sentinel values cannot be specified in filters");
                    }
                    return(new InternalFilter(fieldPath, (int)op, convertedValue));
                }
            }
示例#2
0
            internal static InternalFilter Create(FieldPath fieldPath, QueryOperator op, object value)
            {
                GaxPreconditions.CheckNotNull(fieldPath, nameof(fieldPath));
                FieldOp queryOp       = GetOperator(op);
                var     unaryOperator = GetUnaryOperator(value);

                if (unaryOperator != UnaryOp.Unspecified)
                {
                    if (queryOp == FieldOp.Equal)
                    {
                        return(new InternalFilter(fieldPath, (int)unaryOperator, null));
                    }
                    else
                    {
                        throw new ArgumentException(nameof(value), "null and NaN values can only be used with the Equal operator");
                    }
                }
                else
                {
                    var convertedValue = ValueSerializer.Serialize(value);
                    if (convertedValue.IsDeleteSentinel() || convertedValue.IsServerTimestampSentinel())
                    {
                        throw new ArgumentException(nameof(value), "Sentinel values cannot be specified in filters");
                    }
                    return(new InternalFilter(fieldPath, (int)queryOp, convertedValue));
                }
            }
示例#3
0
        /// <summary>
        /// Add a filter for the given field path.
        /// </summary>
        /// <remarks>
        /// This call adds additional filters to any previously-specified ones.
        /// </remarks>
        /// <param name="fieldPath">The field path to filter on. Must not be null.</param>
        /// <param name="op">The filter operator.</param>
        /// <param name="value">The value to compare in the filter.</param>
        /// <returns>A new query based on the current one, but with the additional specified filter applied.</returns>
        public Query Where(FieldPath fieldPath, QueryOperator op, object value)
        {
            FieldOp queryOp       = GetOperator(op);
            var     unaryOperator = GetUnaryOperator(value);
            Filter  filter;

            if (unaryOperator != UnaryFilter.Types.Operator.Unspecified)
            {
                if (queryOp == FieldOp.Equal)
                {
                    filter = new Filter {
                        UnaryFilter = new UnaryFilter {
                            Field = fieldPath.ToFieldReference(), Op = unaryOperator
                        }
                    };
                }
                else
                {
                    throw new ArgumentException(nameof(value), "null and NaN values can only be used with the Equal operator");
                }
            }
            else
            {
                var convertedValue = ValueSerializer.Serialize(value);
                filter = new Filter {
                    FieldFilter = new FieldFilter {
                        Field = fieldPath.ToFieldReference(), Op = queryOp, Value = convertedValue
                    }
                };
            }
            return(WithFilter(filter));
        }
示例#4
0
        private Cursor CreateCursor(object[] fieldValues, bool before)
        {
            GaxPreconditions.CheckNotNull(fieldValues, nameof(fieldValues));
            GaxPreconditions.CheckArgument(fieldValues.Length != 0, nameof(fieldValues), "Cannot specify an empty set of values for a start/end query cursor.");
            GaxPreconditions.CheckArgument(
                fieldValues.Length <= _orderings.Count,
                nameof(fieldValues),
                "Too many cursor values specified. The specified values must match the ordering constraints of the query. {0} specified for a query with {1} ordering constraints.",
                fieldValues.Length, _orderings.Count);

            var cursor = new Cursor {
                Before = before
            };

            for (int i = 0; i < fieldValues.Length; i++)
            {
                object value = fieldValues[i];
                // The DocumentId field path is handled differently to other fields. We accept a string (relative path) or
                // a DocumentReference (absolute path that must be a descendant of this collection).
                if (Equals(_orderings[i].Field, FieldPath.DocumentId))
                {
                    switch (fieldValues[i])
                    {
                    case string relativePath:
                        // Note: this assumes querying over a single collection at the moment.
                        // Convert to a DocumentReference for the cursor
                        PathUtilities.ValidateId(relativePath, nameof(fieldValues));
                        value = Collection.Document(relativePath);
                        break;

                    case DocumentReference absoluteRef:
                        // Just validate that the given document is a direct child of the parent collection.
                        GaxPreconditions.CheckArgument(absoluteRef.Parent.Equals(Collection), nameof(fieldValues),
                                                       "A DocumentReference cursor value for a document ID must be a descendant of the collection of the query");
                        break;

                    default:
                        throw new ArgumentException($"A cursor value for a document ID must be a string (relative path) or a DocumentReference", nameof(fieldValues));
                    }
                }
                var convertedValue = ValueSerializer.Serialize(value);
                if (SentinelValue.GetKind(convertedValue) != SentinelValue.SentinelKind.None)
                {
                    throw new ArgumentException("Snapshot ordering contained a sentinel value");
                }
                cursor.Values.Add(convertedValue);
            }

            return(cursor);
        }
示例#5
0
        internal static SentinelValue ForArrayValue(SentinelKind sentinelKind, object[] values)
        {
            GaxPreconditions.CheckNotNull(values, nameof(values));
            ArrayValue array = ValueSerializer.Serialize(values).ArrayValue;

            // This is just checking that the simple approach we've taken in the previous line
            // really did what we expect.
            GaxPreconditions.CheckState(array != null, "Input wasn't serialized as an array");
            AugmentedValue augmented = new AugmentedValue {
                Kind = sentinelKind, Array = array
            };

            return(new SentinelValue(augmented));
        }
示例#6
0
        internal static SentinelValue ForArrayValue(SerializationContext context, SentinelKind sentinelKind, object[] values)
        {
            GaxPreconditions.CheckNotNull(values, nameof(values));
            ArrayValue array = ValueSerializer.Serialize(context, values).ArrayValue;

            // This is just checking that the simple approach we've taken in the previous line
            // really did what we expect.
            GaxPreconditions.CheckState(array != null, "Input wasn't serialized as an array");
            GaxPreconditions.CheckState(!array.Values.Any(FindNestedSentinels), "Sentinel values cannot be nested in array union/remove values.");
            AugmentedValue augmented = new AugmentedValue {
                Kind = sentinelKind, Array = array
            };

            return(new SentinelValue(augmented));
        }
        /// <summary>
        /// Adds an update operation that updates just the specified fields paths in the document, with the corresponding values.
        /// </summary>
        /// <param name="documentReference">A document reference indicating the path of the document to update. Must not be null.</param>
        /// <param name="updates">The updates to perform on the document, keyed by the field path to update. Fields not present in this dictionary are not updated. Must not be null or empty.</param>
        /// <param name="precondition">Optional precondition for updating the document. May be null, which is equivalent to <see cref="Precondition.MustExist"/>.</param>
        /// <returns>This batch, for the purposes of method chaining.</returns>
        public WriteBatch Update(DocumentReference documentReference, IDictionary <FieldPath, object> updates, Precondition precondition = null)
        {
            GaxPreconditions.CheckNotNull(documentReference, nameof(documentReference));
            GaxPreconditions.CheckNotNull(updates, nameof(updates));
            GaxPreconditions.CheckArgument(updates.Count != 0, nameof(updates), "Empty set of updates specified");
            GaxPreconditions.CheckArgument(precondition?.Proto.Exists != true, nameof(precondition), "Cannot specify a must-exist precondition for update");

            var serializedUpdates = updates.ToDictionary(pair => pair.Key, pair => ValueSerializer.Serialize(pair.Value));
            var expanded          = ExpandObject(serializedUpdates);


            var serverTimestamps = new List <FieldPath>();
            var deletes          = new List <FieldPath>();

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

            // This effectively validates that a delete wasn't part of a map. It could still be a multi-segment field path, but it can't be within something else.
            GaxPreconditions.CheckArgument(deletes.All(fp => updates.ContainsKey(fp)), nameof(updates), "Deletes cannot be nested within update calls");
            RemoveSentinels(expanded, deletes);
            RemoveSentinels(expanded, serverTimestamps);
            AddUpdateWrites(documentReference, expanded, updates.Keys.ToList(), precondition ?? Precondition.MustExist, serverTimestamps, false);
            return(this);
        }
示例#8
0
        /// <summary>
        /// Adds an update operation that updates just the specified fields paths in the document, with the corresponding values.
        /// </summary>
        /// <param name="documentReference">A document reference indicating the path of the document to update. Must not be null.</param>
        /// <param name="updates">The updates to perform on the document, keyed by the field path to update. Fields not present in this dictionary are not updated. Must not be null or empty.</param>
        /// <param name="precondition">Optional precondition for updating the document. May be null, which is equivalent to <see cref="Precondition.MustExist"/>.</param>
        /// <returns>This batch, for the purposes of method chaining.</returns>
        public WriteBatch Update(DocumentReference documentReference, IDictionary <FieldPath, object> updates, Precondition precondition = null)
        {
            GaxPreconditions.CheckNotNull(documentReference, nameof(documentReference));
            GaxPreconditions.CheckNotNull(updates, nameof(updates));
            GaxPreconditions.CheckArgument(updates.Count != 0, nameof(updates), "Empty set of updates specified");
            GaxPreconditions.CheckArgument(precondition?.Exists != true, nameof(precondition), "Cannot specify a must-exist precondition for update");

            var serializedUpdates = updates.ToDictionary(pair => pair.Key, pair => ValueSerializer.Serialize(documentReference.Database.SerializationContext, pair.Value));
            var expanded          = ExpandObject(serializedUpdates);


            var sentinels = FindSentinels(expanded);

            // This effectively validates that a delete wasn't part of a map. It could still be a multi-segment field path, but it can't be within something else.
            var deletePaths = sentinels.Where(sf => sf.IsDelete).Select(sf => sf.FieldPath);

            GaxPreconditions.CheckArgument(deletePaths.All(fp => updates.ContainsKey(fp)), nameof(updates), "Deletes cannot be nested within update calls");
            RemoveSentinels(expanded, sentinels);

            var nonDeletes = sentinels.Where(sf => !sf.IsDelete).ToList();

            AddUpdateWrite(documentReference, expanded, updates.Keys.ToList(), precondition ?? Precondition.MustExist, sentinelFields: nonDeletes);
            return(this);
        }
示例#9
0
        private Cursor CreateCursorFromSnapshot(DocumentSnapshot snapshot, bool before, out IReadOnlyList <InternalOrdering> newOrderings)
        {
            GaxPreconditions.CheckArgument(Equals(snapshot.Reference.Parent, Collection),
                                           nameof(snapshot), "Snapshot was from incorrect collection");

            GaxPreconditions.CheckNotNull(snapshot, nameof(snapshot));
            var cursor = new Cursor {
                Before = before
            };
            bool hasDocumentId = false;

            // We may or may not need to add some orderings; this is communicated through the out parameter.
            newOrderings = _orderings;
            // Only used when we need to add orderings; set newOrderings to this at the same time.
            List <InternalOrdering> modifiedOrderings = null;

            if (_orderings.Count == 0 && _filters != null)
            {
                // If no explicit ordering is specified, use the first inequality to define an implicit order.
                foreach (var filter in _filters)
                {
                    if (!filter.IsEqualityFilter())
                    {
                        modifiedOrderings = new List <InternalOrdering>(newOrderings)
                        {
                            new InternalOrdering(filter.Field, Direction.Ascending)
                        };
                        newOrderings = modifiedOrderings;
                    }
                }
            }
            else
            {
                hasDocumentId = _orderings.Any(order => Equals(order.Field, FieldPath.DocumentId));
            }

            if (!hasDocumentId)
            {
                // Add implicit sorting by name, using the last specified direction.
                Direction lastDirection = _orderings.Count == 0 ? Direction.Ascending : _orderings.Last().Direction;

                // Clone iff this is the first new ordering.
                if (modifiedOrderings == null)
                {
                    modifiedOrderings = new List <InternalOrdering>(newOrderings);
                    newOrderings      = modifiedOrderings;
                }
                modifiedOrderings.Add(new InternalOrdering(FieldPath.DocumentId, lastDirection));
            }

            foreach (var ordering in newOrderings)
            {
                var field = ordering.Field;
                var value = Equals(field, FieldPath.DocumentId) ? ValueSerializer.Serialize(snapshot.Reference) : snapshot.ExtractValue(field);
                if (value == null)
                {
                    throw new ArgumentException($"Snapshot does not contain field {field}", nameof(snapshot));
                }
                cursor.Values.Add(ValueSerializer.Serialize(value));
            }
            return(cursor);
        }