コード例 #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 FieldTransform ToFieldTransform()
            {
                var transform = new FieldTransform {
                    FieldPath = FieldPath.EncodedPath
                };

                switch (Kind)
                {
                case SentinelKind.ServerTimestamp:
                    transform.SetToServerValue = ServerValue.RequestTime;
                    break;

                case SentinelKind.ArrayRemove:
                    transform.RemoveAllFromArray = SentinelValue.GetArrayValue(Value);
                    break;

                case SentinelKind.ArrayUnion:
                    transform.AppendMissingElements = SentinelValue.GetArrayValue(Value);
                    break;

                case SentinelKind.NumericIncrement:
                    transform.Increment = SentinelValue.GetIncrement(Value);
                    break;

                default:
                    throw new InvalidOperationException($"Cannot convert sentinel value of kind {Kind} to field transform");
                }
                return(transform);
            }
コード例 #3
0
            internal AttributedProperty(PropertyInfo property, FirestorePropertyAttribute attribute)
            {
                _propertyInfo = property;
                FirestoreName = attribute.Name ?? property.Name;
                SentinelValue = SentinelValue.FromPropertyAttributes(property);

                // Note that the error messages in here don't use nameof, as we don't have an overload of CheckState accepting three
                // format arguments.
                // TODO: Put that in GAX and use nameof.
                string typeName = property.DeclaringType.FullName;

                GaxPreconditions.CheckState(property.GetIndexParameters().Length == 0,
                                            "{0}.{1} is an indexer, and should not be decorated with FirestorePropertyAttribute.",
                                            typeName, property.Name);

                // Annoyingly, we can't easily check whether the property is static - we have to check the individual methods.
                var getMethod = property.GetGetMethod(nonPublic: true);
                var setMethod = property.GetSetMethod(nonPublic: true);

                GaxPreconditions.CheckState(getMethod == null || !getMethod.IsStatic,
                                            "{0}.{1} is static, and should not be decorated with FirestorePropertyAttribute.",
                                            typeName, property.Name);
                GaxPreconditions.CheckState(setMethod == null || !setMethod.IsStatic,
                                            "{0}.{1} is static, and should not be decorated with FirestorePropertyAttribute.",
                                            typeName, property.Name);
            }
コード例 #4
0
        internal static Value ToProtoValue(this SentinelValue value)
        {
            switch (value)
            {
            case SentinelValue.ServerTimestamp:
                return(ServerTimestampSentinel);

            case SentinelValue.Delete:
                return(DeleteSentinel);

            default:
                throw new ArgumentException($"Unable to convert {nameof(SentinelValue)} value {value}");
            }
        }
コード例 #5
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);
        }
コード例 #6
0
        /// <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);
                    }
                }
            }
        }
コード例 #7
0
        /// <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);
                    }
                }
            }
        }
コード例 #8
0
        private static Dictionary <string, Value> ConvertFirestoreAttributedType(object value)
        {
            var typeInfo = value.GetType().GetTypeInfo();
            var ret      = new Dictionary <string, Value>();

            // TODO(optimization): We almost certainly want to cache this.
            foreach (var property in typeInfo.DeclaredProperties.Where(p => p.CanRead && p.GetGetMethod().IsPublic&& !p.GetGetMethod().IsStatic))
            {
                var attribute = property.GetCustomAttribute <FirestorePropertyAttribute>();
                if (attribute != null)
                {
                    var   sentinel   = SentinelValue.FromPropertyAttributes(property);
                    Value protoValue = sentinel == null?Serialize(property.GetValue(value)) : sentinel.ToProtoValue();

                    ret[attribute.Name ?? property.Name] = protoValue;
                }
            }
            return(ret);
        }
コード例 #9
0
 /// <summary>
 /// Creates a sentinel value to indicate the removal of the given values with an array.
 /// </summary>
 /// <param name="values">The values to include in the resulting sentinel value. Must not be null.</param>
 /// <returns>A sentinel value representing an array removal.</returns>
 public static object ArrayRemove(params object[] values) =>
 SentinelValue.ForArrayValue(SentinelKind.ArrayRemove, values);
コード例 #10
0
 /// <summary>
 /// Creates a sentinel value to indicate the union of the given values with an array.
 /// </summary>
 /// <param name="values">The values to include in the resulting sentinel value. Must not be null.</param>
 /// <returns>A sentinel value representing an array union.</returns>
 public static object ArrayUnion(params object[] values) =>
 SentinelValue.ForArrayValue(SentinelKind.ArrayUnion, values);
コード例 #11
0
 /// <summary>
 /// Creates a sentinel value to indicate the removal of the given values with an array. This over
 /// </summary>
 /// <param name="database">Database to check for custom serialization.</param>
 /// <param name="values">The values to include in the resulting sentinel value. Must not be null.</param>
 /// <returns>A sentinel value representing an array removal.</returns>
 public static object ArrayRemove(FirestoreDb database, params object[] values) =>
 SentinelValue.ForArrayValue(GaxPreconditions.CheckNotNull(database, nameof(database)).SerializationContext, SentinelKind.ArrayRemove, values);
コード例 #12
0
 /// <summary>
 /// Creates a sentinel value to indicate the removal of the given values with an array.
 /// This overload assumes that any custom serializers are configured via attributes. Use the overload
 /// accepting a <see cref="FirestoreDb" /> if you need to use database-registered custom serializers.
 /// </summary>
 /// <param name="values">The values to include in the resulting sentinel value. Must not be null.</param>
 /// <returns>A sentinel value representing an array removal.</returns>
 public static object ArrayRemove(params object[] values) =>
 SentinelValue.ForArrayValue(SerializationContext.Default, SentinelKind.ArrayRemove, values);
コード例 #13
0
 /// <summary>
 /// Creates a sentinel value to indicate an increment by the given value.
 /// </summary>
 /// <remarks>
 /// <para>
 /// If the current value is an integer or a double, both the current and the given value will be
 /// interpreted as doubles and all arithmetic will follow IEEE 754 semantics.Otherwise, the
 /// transformation will set the field to the given value.
 /// </para>
 /// </remarks>
 /// <param name="amount">The amount to increment the field by.</param>
 /// <returns>A sentinel value representing a field increment.</returns>
 public static object Increment(double amount) =>
 SentinelValue.ForIncrement(new Value {
     DoubleValue = amount
 });
コード例 #14
0
 /// <summary>
 /// Creates a sentinel value to indicate an increment by the given value.
 /// </summary>
 /// <remarks>
 /// <para>
 /// If the current field value is an integer, possible integer overflows are resolved to
 /// <see cref="long.MaxValue"/> or <see cref="long.MinValue"/>. If the current field value
 /// is a double, both values will be interpreted as doubles and the arithmetic will follow IEEE 754 semantics.
 /// </para>
 /// <para>
 /// If the current field is not an integer or double, or if the field does not yet exist, the
 /// transformation will set the field to the given value.
 /// </para>
 /// </remarks>
 /// <param name="amount">The amount to increment the field by.</param>
 /// <returns>A sentinel value representing a field increment.</returns>
 public static object Increment(long amount) =>
 SentinelValue.ForIncrement(new Value {
     IntegerValue = amount
 });