Example #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));
                }
            }
Example #2
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);
        }
        /// <summary>
        /// Finds all the sentinel values in a field map.
        /// Additionally, this validates that no sentinels exist in arrays (even nested).
        /// </summary>
        /// <param name="fields">The field map to find sentinels within.</param>
        /// <returns>The sentinel fields in the field map: both the value and the corresponding field path.</returns>
        private static List <SentinelField> FindSentinels(IDictionary <string, Value> fields)
        {
            List <SentinelField> result = new List <SentinelField>();

            FindSentinelsRecursively(fields, FieldPath.Empty);
            return(result);

            void FindSentinelsRecursively(IDictionary <string, Value> currentFields, FieldPath currentParentPath)
            {
                foreach (var pair in currentFields)
                {
                    Value        value        = pair.Value;
                    string       key          = pair.Key;
                    SentinelKind sentinelKind = SentinelValue.GetKind(value);
                    if (sentinelKind != SentinelKind.None)
                    {
                        result.Add(new SentinelField(currentParentPath.Append(key), value));
                    }
                    else if (value.MapValue != null)
                    {
                        FindSentinelsRecursively(value.MapValue.Fields, currentParentPath.Append(pair.Key));
                    }
                    else if (value.ArrayValue != null)
                    {
                        ValidateNoSentinelValues(value.ArrayValue.Values);
                    }
                }
            }

            void ValidateNoSentinelValues(IEnumerable <Value> values)
            {
                foreach (var value in values)
                {
                    if (SentinelValue.GetKind(value) != SentinelKind.None)
                    {
                        // We don't know what parameter name to use here
                        throw new ArgumentException("Sentinel values must not appear directly or indirectly within array values");
                    }
                    else if (value.MapValue != null)
                    {
                        ValidateNoSentinelValues(value.MapValue.Fields.Values);
                    }
                    else if (value.ArrayValue != null)
                    {
                        ValidateNoSentinelValues(value.ArrayValue.Values);
                    }
                }
            }
        }
        /// <summary>
        /// Finds all the sentinel values in a field map, adding them to lists based on their type.
        /// Additionally, this validates that no sentinels exist in arrays (even nested).
        /// </summary>
        /// <param name="fields">The field map</param>
        /// <param name="parentPath">The path of this map within the document. (So FieldPath.Empty for a top-level call.)</param>
        /// <param name="serverTimestamps">The list to add any discovered server timestamp sentinels to</param>
        /// <param name="deletes">The list to add any discovered delete sentinels to</param>
        private static void FindSentinels(IDictionary <string, Value> fields, FieldPath parentPath, List <FieldPath> serverTimestamps, List <FieldPath> deletes)
        {
            foreach (var pair in fields)
            {
                Value  value = pair.Value;
                string key   = pair.Key;
                SentinelValue.SentinelKind sentinelKind = SentinelValue.GetKind(value);
                if (sentinelKind == SentinelValue.SentinelKind.ServerTimestamp)
                {
                    serverTimestamps.Add(parentPath.Append(key));
                }
                else if (sentinelKind == SentinelValue.SentinelKind.Delete)
                {
                    deletes.Add(parentPath.Append(key));
                }
                else if (value.MapValue != null)
                {
                    FindSentinels(value.MapValue.Fields, parentPath.Append(pair.Key), serverTimestamps, deletes);
                }
                else if (value.ArrayValue != null)
                {
                    ValidateNoSentinelValues(value.ArrayValue.Values);
                }
            }

            void ValidateNoSentinelValues(IEnumerable <Value> values)
            {
                foreach (var value in values)
                {
                    if (SentinelValue.GetKind(value) != SentinelValue.SentinelKind.None)
                    {
                        // We don't know what parameter name to use here
                        throw new ArgumentException("Sentinel values must not appear directly or indirectly within array values");
                    }
                    else if (value.MapValue != null)
                    {
                        ValidateNoSentinelValues(value.MapValue.Fields.Values);
                    }
                    else if (value.ArrayValue != null)
                    {
                        ValidateNoSentinelValues(value.ArrayValue.Values);
                    }
                }
            }
        }