private Number64 GetObfuscatedNumber(Number64 value) { Number64 obfuscatedNumber; // Leave NaN, Infinity, numbers in epsilon range, and small integers unchanged if (value.IsInfinity || (value.IsInteger && (Number64.ToLong(value) == long.MinValue)) || (value.IsInteger && (Math.Abs(Number64.ToLong(value)) < 100)) || (value.IsDouble && (Math.Abs(Number64.ToDouble(value)) < 100) && ((long)Number64.ToDouble(value) == Number64.ToDouble(value))) || (value.IsDouble && (Math.Abs(Number64.ToDouble(value)) <= Double.Epsilon))) { obfuscatedNumber = value; } else { if (!this.obfuscatedNumbers.TryGetValue(value, out obfuscatedNumber)) { double doubleValue = Number64.ToDouble(value); int sequenceNumber = ++this.numberSequenceNumber; double log10 = Math.Floor(Math.Log10(Math.Abs(doubleValue))); double adjustedSequence = Math.Pow(10, log10) * sequenceNumber / 1e4; obfuscatedNumber = Math.Round(doubleValue, 2) + adjustedSequence; this.obfuscatedNumbers.Add(value, obfuscatedNumber); } } return(obfuscatedNumber); }
public static double ToDouble(this CosmosElement element) { if (!(element is CosmosNumber cosmosNumber)) { throw new ArgumentException($"Expected cosmos number: {element}."); } return(Number64.ToDouble(cosmosNumber.Value)); }
/// <summary> /// Writes a number to the internal buffer. /// </summary> /// <param name="value">The value of the number to write.</param> public override void WriteNumberValue(Number64 value) { if (value.IsInteger) { this.WriteIntegerInternal(Number64.ToLong(value)); } else { this.WriteDoubleInternal(Number64.ToDouble(value)); } }
public override void WriteNumber64Value(Number64 value) { if (value.IsInteger) { this.writer.WriteValue(Number64.ToLong(value)); } else { this.writer.WriteValue(Number64.ToDouble(value)); } }
/// <inheritdoc /> public override void WriteNumberValue(Number64 value) { if (value.IsInteger) { this.WriteIntegerInternal(Number64.ToLong(value)); } else { this.WriteDoubleInternal(Number64.ToDouble(value)); } this.bufferedContexts.Peek().Count++; }
private static CosmosElement PerformUnaryNumberOperation( Func <double, double> unaryOperation, CosmosElement operand) { if (!(operand is CosmosNumber operandAsNumber)) { return(Undefined); } double result = unaryOperation(Number64.ToDouble(operandAsNumber.Value)); return(CosmosNumber64.Create(result)); }
/// <summary> /// Reads the next number token but returns null at the end of an array. /// </summary> /// <returns>The next number token but returns null at the end of an array.</returns> private double?ReadNumberValue() { this.Read(); if (this.jsonReader.CurrentTokenType == JsonTokenType.EndArray) { return(null); } Number64 value = this.jsonReader.GetNumberValue(); double doubleValue = Number64.ToDouble(value); return(doubleValue); }
public bool Visit(CosmosNumber cosmosNumber) { if (cosmosNumber.Value.IsInteger) { return(false); } double value = Number64.ToDouble(cosmosNumber.Value); return (double.IsInfinity(value) || double.IsNaN(value) || double.IsNegativeInfinity(value) || double.IsPositiveInfinity(value)); }
public override double?AsFloatingPoint() { double?value; if (this.IsFloatingPoint) { value = Number64.ToDouble(this.GetValue()); } else { value = null; } return(value); }
/// <summary> /// Reads the next number token but returns null at the end of an array. /// </summary> /// <returns>The next number token but returns null at the end of an array.</returns> private double?ReadNumberValue() { this.Read(); double?value; if (this.jsonReader.CurrentTokenType == JsonTokenType.EndArray) { value = null; } else { value = Number64.ToDouble(this.jsonReader.GetNumberValue()); } return(value); }
/// <summary> /// Adds a local sum to the global sum. /// </summary> /// <param name="localSum">The local sum.</param> public void Aggregate(CosmosElement localSum) { // If someone tried to add an undefined just set the globalSum to NaN and it will stay that way for the duration of the aggregation. if (localSum == null) { this.globalSum = double.NaN; } else { if (!(localSum is CosmosNumber cosmosNumber)) { throw new ArgumentException("localSum must be a number."); } this.globalSum += Number64.ToDouble(cosmosNumber.Value); } }
private static CosmosElement PerformBinaryNumberOperation( Func <double, double, double> operation, CosmosElement left, CosmosElement right) { if (!(left is CosmosNumber leftAsNumber)) { return(Undefined); } if (!(right is CosmosNumber rightAsNumber)) { return(Undefined); } double result = operation(Number64.ToDouble(leftAsNumber.Value), Number64.ToDouble(rightAsNumber.Value)); return(CosmosNumber64.Create(result)); }
public static SqlNumberLiteral Create(Number64 number64) { SqlNumberLiteral sqlNumberLiteral; if (number64.IsDouble) { if (!SqlNumberLiteral.FrequentDoubles.TryGetValue(Number64.ToDouble(number64), out sqlNumberLiteral)) { sqlNumberLiteral = new SqlNumberLiteral(number64); } } else { if (!SqlNumberLiteral.FrequentLongs.TryGetValue(Number64.ToLong(number64), out sqlNumberLiteral)) { sqlNumberLiteral = new SqlNumberLiteral(number64); } } return(sqlNumberLiteral); }
/// <summary> /// Initializes a new instance of the AverageInfo class. /// </summary> public static TryCatch <AverageInfo> TryCreateFromCosmosElement(CosmosElement cosmosElement) { if (cosmosElement == null) { throw new ArgumentNullException($"{nameof(cosmosElement)} must not be null."); } if (!(cosmosElement is CosmosObject cosmosObject)) { return(TryCatch <AverageInfo> .FromException( new ArgumentException($"{nameof(cosmosElement)} must be an object."))); } double?sum; if (cosmosObject.TryGetValue(SumName, out CosmosElement sumPropertyValue)) { if (!(sumPropertyValue is CosmosNumber cosmosSum)) { return(TryCatch <AverageInfo> .FromException( new ArgumentException($"value for the {SumName} field was not a number"))); } sum = Number64.ToDouble(cosmosSum.Value); } else { sum = null; } if (!cosmosObject.TryGetValue(CountName, out CosmosNumber cosmosCount)) { return(TryCatch <AverageInfo> .FromException( new ArgumentException($"value for the {CountName} field was not a number"))); } long count = Number64.ToLong(cosmosCount.Value); return(TryCatch <AverageInfo> .FromResult(new AverageInfo(sum, count))); }
public override Output ExecuteTest(Input input) { CosmosElement value = input.PartitionKeyValue; PartitionKeyHash partitionKeyHashV1; PartitionKeyHash partitionKeyHashV2; switch (value) { case null: partitionKeyHashV1 = PartitionKeyHash.V1.HashUndefined(); partitionKeyHashV2 = PartitionKeyHash.V2.HashUndefined(); break; case CosmosNull cosmosNull: partitionKeyHashV1 = PartitionKeyHash.V1.HashNull(); partitionKeyHashV2 = PartitionKeyHash.V2.HashNull(); break; case CosmosBoolean cosmosBoolean: partitionKeyHashV1 = PartitionKeyHash.V1.Hash(cosmosBoolean.Value); partitionKeyHashV2 = PartitionKeyHash.V2.Hash(cosmosBoolean.Value); break; case CosmosString cosmosString: partitionKeyHashV1 = PartitionKeyHash.V1.Hash(cosmosString.Value); partitionKeyHashV2 = PartitionKeyHash.V2.Hash(cosmosString.Value); break; case CosmosNumber cosmosNumber: partitionKeyHashV1 = PartitionKeyHash.V1.Hash(Number64.ToDouble(cosmosNumber.Value)); partitionKeyHashV2 = PartitionKeyHash.V2.Hash(Number64.ToDouble(cosmosNumber.Value)); break; default: throw new ArgumentOutOfRangeException($"Unknown {nameof(CosmosElement)} type: {value.GetType()}."); } return(new Output(partitionKeyHashV1, partitionKeyHashV2)); }
/// <summary> /// Adds a local sum to the global sum. /// </summary> /// <param name="localSum">The local sum.</param> public void Aggregate(CosmosElement localSum) { if (double.IsNaN(this.globalSum)) { // Global sum is undefined and nothing is going to change that. return; } // If someone tried to add an undefined just set the globalSum to NaN and it will stay that way for the duration of the aggregation. if (localSum == null) { this.globalSum = double.NaN; return; } if (!(localSum is CosmosNumber cosmosNumber)) { throw new ArgumentException("localSum must be a number."); } this.globalSum += Number64.ToDouble(cosmosNumber.Value); }
public static TryCatch <IAggregator> TryCreate(CosmosElement requestContinuationToken) { double partialSum; if (requestContinuationToken != null) { if (!(requestContinuationToken is CosmosNumber cosmosNumber)) { return(TryCatch <IAggregator> .FromException( new MalformedContinuationTokenException($"Malformed {nameof(SumAggregator)} continuation token: {requestContinuationToken}"))); } partialSum = Number64.ToDouble(cosmosNumber.Value); } else { partialSum = 0.0; } return(TryCatch <IAggregator> .FromResult( new SumAggregator(partialSum))); }
public static TryCatch <IAggregator> TryCreate(CosmosElement requestContinuationToken) { double partialSum; if (requestContinuationToken != null) { if (requestContinuationToken is CosmosNumber cosmosNumber) { partialSum = Number64.ToDouble(cosmosNumber.Value); } else if (requestContinuationToken is CosmosString cosmosString) { if (!double.TryParse( cosmosString.Value, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out partialSum)) { return(TryCatch <IAggregator> .FromException( new MalformedContinuationTokenException( $"Malformed {nameof(SumAggregator)} continuation token: {requestContinuationToken}"))); } } else { return(TryCatch <IAggregator> .FromException( new MalformedContinuationTokenException( $"Malformed {nameof(SumAggregator)} continuation token: {requestContinuationToken}"))); } } else { partialSum = 0.0; } return(TryCatch <IAggregator> .FromResult( new SumAggregator(partialSum))); }
public override void Visit(SqlNumberLiteral sqlNumberLiteral) { // We have to use InvariantCulture due to number formatting. // "1234.1234" is correct while "1234,1234" is incorrect. if (sqlNumberLiteral.Value.IsDouble) { string literalString = sqlNumberLiteral.Value.ToString(CultureInfo.InvariantCulture); double literalValue = 0.0; if (!sqlNumberLiteral.Value.IsNaN && !sqlNumberLiteral.Value.IsInfinity && (!double.TryParse(literalString, NumberStyles.Number, CultureInfo.InvariantCulture, out literalValue) || !Number64.ToDouble(sqlNumberLiteral.Value).Equals(literalValue))) { literalString = sqlNumberLiteral.Value.ToString("G17", CultureInfo.InvariantCulture); } this.writer.Write(literalString); } else { this.writer.Write(sqlNumberLiteral.Value.ToString(CultureInfo.InvariantCulture)); } }
public async Task TestAggregateFunctionsAsync() { AggregateTestArgs args = new AggregateTestArgs() { NumberOfDocsWithSamePartitionKey = 37, NumberOfDocumentsDifferentPartitionKey = 43, PartitionKey = "key", UniquePartitionKey = "uniquePartitionKey", Field = "field", Values = new object[] { false, true, "abc", "cdfg", "opqrs", "ttttttt", "xyz" }, }; List <string> documents = new List <string>(args.NumberOfDocumentsDifferentPartitionKey + args.NumberOfDocsWithSamePartitionKey); foreach (object val in args.Values) { Document doc; doc = new Document(); doc.SetPropertyValue(args.PartitionKey, val); doc.SetPropertyValue("id", Guid.NewGuid().ToString()); documents.Add(doc.ToString()); } for (int i = 0; i < args.NumberOfDocsWithSamePartitionKey; ++i) { Document doc = new Document(); doc.SetPropertyValue(args.PartitionKey, args.UniquePartitionKey); documents.Add(doc.ToString()); } Random random = new Random(); for (int i = 0; i < args.NumberOfDocumentsDifferentPartitionKey; ++i) { Document doc = new Document(); doc.SetPropertyValue(args.PartitionKey, random.NextDouble()); documents.Add(doc.ToString()); } await this.CreateIngestQueryDeleteAsync <AggregateTestArgs>( ConnectionModes.Direct | ConnectionModes.Gateway, CollectionTypes.SinglePartition | CollectionTypes.MultiPartition, documents, ImplementationAsync, args, "/" + args.PartitionKey); async Task ImplementationAsync( Container container, IReadOnlyList <CosmosObject> inputDocuments, AggregateTestArgs aggregateTestArgs) { IReadOnlyList <CosmosObject> documentsWherePkIsANumber = inputDocuments .Where(doc => { return(double.TryParse( doc[aggregateTestArgs.PartitionKey].ToString(), out double result)); }) .ToList(); double numberSum = documentsWherePkIsANumber .Sum(doc => { if (!doc.TryGetValue(aggregateTestArgs.PartitionKey, out CosmosNumber number)) { Assert.Fail("Failed to get partition key from document"); } return(Number64.ToDouble(number.Value)); }); double count = documentsWherePkIsANumber.Count(); AggregateQueryArguments[] aggregateQueryArgumentsList = new AggregateQueryArguments[] { new AggregateQueryArguments() { AggregateOperator = "AVG", ExpectedValue = CosmosNumber64.Create(numberSum / count), Predicate = $"IS_NUMBER(r.{aggregateTestArgs.PartitionKey})", }, new AggregateQueryArguments() { AggregateOperator = "AVG", ExpectedValue = null, Predicate = "true", }, new AggregateQueryArguments() { AggregateOperator = "COUNT", ExpectedValue = CosmosNumber64.Create(documents.Count()), Predicate = "true", }, new AggregateQueryArguments() { AggregateOperator = "MAX", ExpectedValue = CosmosString.Create("xyz"), Predicate = "true", }, new AggregateQueryArguments() { AggregateOperator = "MIN", ExpectedValue = CosmosBoolean.Create(false), Predicate = "true", }, new AggregateQueryArguments() { AggregateOperator = "SUM", ExpectedValue = CosmosNumber64.Create(numberSum), Predicate = $"IS_NUMBER(r.{aggregateTestArgs.PartitionKey})", }, new AggregateQueryArguments() { AggregateOperator = "SUM", ExpectedValue = null, Predicate = $"true", }, }; foreach (int maxDoP in new[] { 0, 10 }) { foreach (AggregateQueryArguments argument in aggregateQueryArgumentsList) { string[] queryFormats = new[] { "SELECT VALUE {0}(r.{1}) FROM r WHERE {2}", "SELECT VALUE {0}(r.{1}) FROM r WHERE {2} ORDER BY r.{1}" }; foreach (string queryFormat in queryFormats) { string query = string.Format( CultureInfo.InvariantCulture, queryFormat, argument.AggregateOperator, aggregateTestArgs.PartitionKey, argument.Predicate); string message = string.Format( CultureInfo.InvariantCulture, "query: {0}, data: {1}", query, JsonConvert.SerializeObject(argument)); List <CosmosElement> items = await QueryTestsBase.RunQueryAsync( container, query, new QueryRequestOptions() { MaxConcurrency = maxDoP, }); if (argument.ExpectedValue == null) { Assert.AreEqual(0, items.Count, message); } else { Assert.AreEqual(1, items.Count, message); CosmosElement expected = argument.ExpectedValue; CosmosElement actual = items.Single(); if ((expected is CosmosNumber expectedNumber) && (actual is CosmosNumber actualNumber)) { Assert.AreEqual(Number64.ToDouble(expectedNumber.Value), Number64.ToDouble(actualNumber.Value), .01); }
public override SqlScalarExpression Visit(SqlFunctionCallScalarExpression sqlFunctionCallScalarExpression) { SqlScalarExpression rewrittenExpression; // If the function call is an aggregate just evaluate the aggregate first and return that if ( !sqlFunctionCallScalarExpression.IsUdf && Enum.TryParse(value: sqlFunctionCallScalarExpression.Name.Value, ignoreCase: true, result: out Aggregate aggregate)) { IReadOnlyList <SqlScalarExpression> arguments = sqlFunctionCallScalarExpression.Arguments; if (arguments.Count != 1) { throw new ArgumentException("Aggregates only accept one argument."); } IEnumerable <CosmosElement> results = this.dataSource .Select((element) => arguments[0].Accept( ScalarExpressionEvaluator.Singleton, element)); // If aggregates are pushed to the index, then we only get back defined results results = results.Where((result) => result != Undefined); CosmosElement aggregationResult; switch (aggregate) { case Aggregate.Min: case Aggregate.Max: if (results.Count() == 0) { aggregationResult = Undefined; } else if (results.Any(result => !Utils.IsPrimitive(result))) { aggregationResult = Undefined; } else { aggregationResult = results.First(); foreach (CosmosElement result in results) { // First compare the types int comparison = result.CompareTo(aggregationResult); if (aggregate == Aggregate.Min) { if (comparison < 0) { aggregationResult = result; } } else if (aggregate == Aggregate.Max) { if (comparison > 0) { aggregationResult = result; } } else { throw new InvalidOperationException("Should not get here"); } } } break; case Aggregate.Avg: case Aggregate.Sum: CosmosNumber sum = CosmosNumber64.Create(0); double count = 0; foreach (CosmosElement result in results) { if ((result is CosmosNumber resultAsNumber) && (sum != Undefined)) { sum = CosmosNumber64.Create(Number64.ToDouble(sum.Value) + Number64.ToDouble(resultAsNumber.Value)); count++; } else { sum = Undefined; } }
/// <summary> /// Reads the next token from the reader. /// </summary> /// <returns>True if a token was read, else false.</returns> public override bool Read() { bool read = this.jsonReader.Read(); if (!read) { this.SetToken(JsonToken.None); return(false); } JsonTokenType jsonTokenType = this.jsonReader.CurrentTokenType; JsonToken newtonsoftToken; object value; switch (jsonTokenType) { case JsonTokenType.BeginArray: newtonsoftToken = JsonToken.StartArray; value = CosmosDBToNewtonsoftReader.Null; break; case JsonTokenType.EndArray: newtonsoftToken = JsonToken.EndArray; value = CosmosDBToNewtonsoftReader.Null; break; case JsonTokenType.BeginObject: newtonsoftToken = JsonToken.StartObject; value = CosmosDBToNewtonsoftReader.Null; break; case JsonTokenType.EndObject: newtonsoftToken = JsonToken.EndObject; value = CosmosDBToNewtonsoftReader.Null; break; case JsonTokenType.String: newtonsoftToken = JsonToken.String; value = this.jsonReader.GetStringValue(); break; case JsonTokenType.Number: Number64 number64Value = this.jsonReader.GetNumberValue(); if (number64Value.IsInteger) { value = Number64.ToLong(number64Value); newtonsoftToken = JsonToken.Integer; } else { value = Number64.ToDouble(number64Value); newtonsoftToken = JsonToken.Float; } break; case JsonTokenType.True: newtonsoftToken = JsonToken.Boolean; value = CosmosDBToNewtonsoftReader.True; break; case JsonTokenType.False: newtonsoftToken = JsonToken.Boolean; value = CosmosDBToNewtonsoftReader.False; break; case JsonTokenType.Null: newtonsoftToken = JsonToken.Null; value = CosmosDBToNewtonsoftReader.Null; break; case JsonTokenType.FieldName: newtonsoftToken = JsonToken.PropertyName; value = this.jsonReader.GetStringValue(); break; case JsonTokenType.Int8: newtonsoftToken = JsonToken.Integer; value = this.jsonReader.GetInt8Value(); break; case JsonTokenType.Int16: newtonsoftToken = JsonToken.Integer; value = this.jsonReader.GetInt16Value(); break; case JsonTokenType.Int32: newtonsoftToken = JsonToken.Integer; value = this.jsonReader.GetInt32Value(); break; case JsonTokenType.Int64: newtonsoftToken = JsonToken.Integer; value = this.jsonReader.GetInt64Value(); break; case JsonTokenType.UInt32: newtonsoftToken = JsonToken.Integer; value = this.jsonReader.GetUInt32Value(); break; case JsonTokenType.Float32: newtonsoftToken = JsonToken.Float; value = this.jsonReader.GetFloat32Value(); break; case JsonTokenType.Float64: newtonsoftToken = JsonToken.Float; value = this.jsonReader.GetFloat64Value(); break; case JsonTokenType.Guid: newtonsoftToken = JsonToken.String; value = this.jsonReader.GetGuidValue().ToString(); break; case JsonTokenType.Binary: newtonsoftToken = JsonToken.Bytes; value = this.jsonReader.GetBinaryValue().ToArray(); break; default: throw new ArgumentException($"Unexpected jsonTokenType: {jsonTokenType}"); } this.SetToken(newtonsoftToken, value); return(read); }
public override SqlObject Visit(SqlNumberLiteral sqlNumberLiteral) { return(SqlNumberLiteral.Create( Number64.ToDouble( this.GetObfuscatedNumber(sqlNumberLiteral.Value)))); }
/// <summary> /// Compares to objects and returns their partial sort relationship. /// </summary> /// <param name="element1">The first element to compare.</param> /// <param name="element2">The second element to compare.</param> /// <returns> /// Less than zero if obj1 comes before obj2 in the sort order. /// Zero if obj1 and obj2 are interchangeable in the sort order. /// Greater than zero if obj2 comes before obj1 in the sort order. /// </returns> public int Compare(CosmosElement element1, CosmosElement element2) { if (object.ReferenceEquals(element1, element2)) { return(0); } if (object.ReferenceEquals(element1, MinValueItem.Singleton)) { return(-1); } if (object.ReferenceEquals(element2, MinValueItem.Singleton)) { return(1); } if (object.ReferenceEquals(element1, MaxValueItem.Singleton)) { return(1); } if (object.ReferenceEquals(element2, MaxValueItem.Singleton)) { return(-1); } if (element1 == Undefined) { return(-1); } if (element2 == Undefined) { return(1); } CosmosElementType type1 = element1.Type; int cmp = CompareTypes(element1, element2); if (cmp == 0) { // If they are the same type then you need to break the tie. switch (type1) { case CosmosElementType.Boolean: cmp = Comparer <bool> .Default.Compare( (element1 as CosmosBoolean).Value, (element2 as CosmosBoolean).Value); break; case CosmosElementType.Null: // All nulls are the same. cmp = 0; break; case CosmosElementType.Number: CosmosNumber number1 = element1 as CosmosNumber; CosmosNumber number2 = element2 as CosmosNumber; if (number1.NumberType == CosmosNumberType.Number64) { // Both are Number64, so compare as Number64 cmp = number1.Value.CompareTo(number2.Value); } else { // We have a number with precision if (number2.Value.IsInteger) { // compare as longs, since all types have an implicit conversion with full fidelity. long integer1 = Number64.ToLong(number1.Value); long integer2 = Number64.ToLong(number2.Value); cmp = Comparer <long> .Default.Compare(integer1, integer2); } else { // compare as doubles, since all types have an implicit conversion with full fidelity. double double1 = Number64.ToDouble(number1.Value); double double2 = Number64.ToDouble(number2.Value); cmp = Comparer <double> .Default.Compare(double1, double2); } } break; case CosmosElementType.String: CosmosString string1 = element1 as CosmosString; CosmosString string2 = element2 as CosmosString; cmp = string.CompareOrdinal( string1.Value, string2.Value); break; case CosmosElementType.Guid: CosmosGuid guid1 = element1 as CosmosGuid; CosmosGuid guid2 = element2 as CosmosGuid; cmp = guid1.Value.CompareTo(guid2.Value); break; case CosmosElementType.Binary: CosmosBinary binary1 = element1 as CosmosBinary; CosmosBinary binary2 = element2 as CosmosBinary; cmp = ItemComparer.CompareTo(binary1, binary2); break; case CosmosElementType.Array: case CosmosElementType.Object: { UInt128 hash1 = DistinctHash.GetHash(element1); UInt128 hash2 = DistinctHash.GetHash(element2); return(hash1.CompareTo(hash2)); } default: throw new ArgumentException($"Unknown: {nameof(CosmosElementType)}: {type1}"); } } return(cmp); }
public override double?AsFloatingPoint() { return(Number64.ToDouble(this.number)); }