diff --git a/Documentation/Books/AQL/Functions/Miscellaneous.mdpp b/Documentation/Books/AQL/Functions/Miscellaneous.mdpp index cef4da9ac8..fa2139fce5 100644 --- a/Documentation/Books/AQL/Functions/Miscellaneous.mdpp +++ b/Documentation/Books/AQL/Functions/Miscellaneous.mdpp @@ -16,6 +16,19 @@ AQL offers the following functions to let the user control the flow of operation Finally, AQL supports the following functions that do not belong to any of the other function categories: +- *HASH(value)*: Calculates a hash value for *value*. *value* is not required to be a + string, but can have any data type. The calculated hash value will take the data type + of *value* into account, so for example the number *1* and the string *"1"* will have + different hash values. For arrays the hash values will be creared if the arrays contain + exactly the same values (including value types) in the same order. For objects the same + hash values will be created if the objects have exactly the same attribute names and + values (including value types). The order in which attributes appear inside objects + is not important for hashing. + The hash value returned by this function is a number. The hash algorithm is not guaranteed + to remain the same in future versions of ArangoDB. The hash values should therefore be + used only for temporary calculations, e.g. to compare if two documents are the same, or + for grouping values in queries. + - *COLLECTIONS()*: Returns an array of collections. Each collection is returned as a document with attributes *name* and *_id* diff --git a/Documentation/Books/AQL/Functions/TypeCast.mdpp b/Documentation/Books/AQL/Functions/TypeCast.mdpp index 08fc57a7a6..f9feb11fac 100644 --- a/Documentation/Books/AQL/Functions/TypeCast.mdpp +++ b/Documentation/Books/AQL/Functions/TypeCast.mdpp @@ -50,7 +50,7 @@ not not 1 // true converted to the number *0*. - An object / document is converted to the number *0*. - An unary plus will also try to cast to a number, but `TO_NUMBER()` is the preferred way: + A unary plus will also cast to a number, but `TO_NUMBER()` is the preferred way: ```js +'5' // 5 +[8] // 8 @@ -122,3 +122,6 @@ The following type check functions are available: in a date function. This includes partial dates such as *2015* or *2015-10* and strings containing invalid dates such as *2015-02-31*. The function will return false for all non-string values, even if some of them may be usable in date functions. + +- *TYPENAME(value)*: Returns the data type name of *value*. The data type name can + be either *null*, *bool*, *number*, *string*, *array* or *object*. diff --git a/arangod/Aql/AqlValue.h b/arangod/Aql/AqlValue.h index 06db297aeb..8384ad2007 100644 --- a/arangod/Aql/AqlValue.h +++ b/arangod/Aql/AqlValue.h @@ -130,6 +130,34 @@ struct AqlValue final { setType(AqlValueType::VPACK_INLINE); } + // construct from char* and length + AqlValue(char const* value, size_t length) { + if (length == 0) { + // empty string + _data.internal[0] = 0x40; + setType(AqlValueType::VPACK_INLINE); + return; + } + if (length < sizeof(_data.internal) - 1) { + // short string... can store it inline + _data.internal[0] = 0x40 + length; + memcpy(_data.internal + 1, value, length); + setType(AqlValueType::VPACK_INLINE); + } else if (length <= 126) { + // short string... cannot store inline, but we don't need to + // create a full-features Builder object here + _data.buffer = new arangodb::velocypack::Buffer(length + 1); + _data.buffer->push_back(0x40 + length); + _data.buffer->append(value, length); + setType(AqlValueType::VPACK_MANAGED); + } else { + // long string + VPackBuilder builder; + builder.add(VPackValue(value)); + initFromSlice(builder.slice()); + } + } + // construct from std::string explicit AqlValue(std::string const& value) { if (value.empty()) { diff --git a/arangod/Aql/Executor.cpp b/arangod/Aql/Executor.cpp index 208ea3c033..6bb82738ae 100644 --- a/arangod/Aql/Executor.cpp +++ b/arangod/Aql/Executor.cpp @@ -120,6 +120,8 @@ std::unordered_map const Executor::FunctionNames{ false, true, true, &Functions::IsObject)}, {"IS_DATESTRING", Function("IS_DATESTRING", "AQL_IS_DATESTRING", ".", true, true, false, true, true)}, + {"TYPENAME", Function("TYPENAME", "AQL_TYPENAME", ".", true, + true, false, true, true)}, // type cast functions {"TO_NUMBER", Function("TO_NUMBER", "AQL_TO_NUMBER", ".", true, true, false, @@ -173,6 +175,8 @@ std::unordered_map const Executor::FunctionNames{ &Functions::Md5)}, {"SHA1", Function("SHA1", "AQL_SHA1", "s", true, true, false, true, true, &Functions::Sha1)}, + {"HASH", Function("HASH", "AQL_HASH", ".", true, true, false, true, true, + &Functions::Hash)}, {"RANDOM_TOKEN", Function("RANDOM_TOKEN", "AQL_RANDOM_TOKEN", "n", false, false, true, true, true)}, @@ -482,9 +486,6 @@ std::unordered_map const Executor::FunctionNames{ /// an array / object literal "big" and pulling it out of the expression size_t const Executor::DefaultLiteralSizeThreshold = 32; -/// @brief maxmium number of array members created from range accesses -int64_t const Executor::MaxRangeAccessArraySize = 1024 * 1024 * 32; - /// @brief creates an executor Executor::Executor(int64_t literalSizeThreshold) : _buffer(nullptr), diff --git a/arangod/Aql/Executor.h b/arangod/Aql/Executor.h index 6f4c2dc7bd..f922c1f2ba 100644 --- a/arangod/Aql/Executor.h +++ b/arangod/Aql/Executor.h @@ -175,8 +175,6 @@ class Executor { /// an array / object literal "big" and pulling it out of the expression static size_t const DefaultLiteralSizeThreshold; - /// @brief maxmium number of array members created from range accesses - static int64_t const MaxRangeAccessArraySize; }; } } diff --git a/arangod/Aql/Functions.cpp b/arangod/Aql/Functions.cpp index 67eb1097b5..206ae0997c 100644 --- a/arangod/Aql/Functions.cpp +++ b/arangod/Aql/Functions.cpp @@ -884,7 +884,7 @@ AqlValue Functions::IsNull(arangodb::aql::Query* query, arangodb::AqlTransaction* trx, VPackFunctionParameters const& parameters) { AqlValue a = ExtractFunctionParameterValue(trx, parameters, 0); - return AqlValue(arangodb::basics::VelocyPackHelper::BooleanValue(a.isNull(true))); + return AqlValue(a.isNull(true)); } /// @brief function IS_BOOL @@ -892,7 +892,7 @@ AqlValue Functions::IsBool(arangodb::aql::Query* query, arangodb::AqlTransaction* trx, VPackFunctionParameters const& parameters) { AqlValue a = ExtractFunctionParameterValue(trx, parameters, 0); - return AqlValue(arangodb::basics::VelocyPackHelper::BooleanValue(a.isBoolean())); + return AqlValue(a.isBoolean()); } /// @brief function IS_NUMBER @@ -900,7 +900,7 @@ AqlValue Functions::IsNumber(arangodb::aql::Query* query, arangodb::AqlTransaction* trx, VPackFunctionParameters const& parameters) { AqlValue a = ExtractFunctionParameterValue(trx, parameters, 0); - return AqlValue(arangodb::basics::VelocyPackHelper::BooleanValue(a.isNumber())); + return AqlValue(a.isNumber()); } /// @brief function IS_STRING @@ -908,7 +908,7 @@ AqlValue Functions::IsString(arangodb::aql::Query* query, arangodb::AqlTransaction* trx, VPackFunctionParameters const& parameters) { AqlValue a = ExtractFunctionParameterValue(trx, parameters, 0); - return AqlValue(arangodb::basics::VelocyPackHelper::BooleanValue(a.isString())); + return AqlValue(a.isString()); } /// @brief function IS_ARRAY @@ -916,7 +916,7 @@ AqlValue Functions::IsArray(arangodb::aql::Query* query, arangodb::AqlTransaction* trx, VPackFunctionParameters const& parameters) { AqlValue a = ExtractFunctionParameterValue(trx, parameters, 0); - return AqlValue(arangodb::basics::VelocyPackHelper::BooleanValue(a.isArray())); + return AqlValue(a.isArray()); } /// @brief function IS_OBJECT @@ -924,7 +924,31 @@ AqlValue Functions::IsObject(arangodb::aql::Query* query, arangodb::AqlTransaction* trx, VPackFunctionParameters const& parameters) { AqlValue a = ExtractFunctionParameterValue(trx, parameters, 0); - return AqlValue(arangodb::basics::VelocyPackHelper::BooleanValue(a.isObject())); + return AqlValue(a.isObject()); +} + +/// @brief function TYPENAME +AqlValue Functions::Typename(arangodb::aql::Query* query, + arangodb::AqlTransaction* trx, + VPackFunctionParameters const& parameters) { + AqlValue value = ExtractFunctionParameterValue(trx, parameters, 0); + + if (value.isObject()) { + return AqlValue(TRI_CHAR_LENGTH_PAIR("object")); + } + if (value.isArray()) { + return AqlValue(TRI_CHAR_LENGTH_PAIR("array")); + } + if (value.isString()) { + return AqlValue(TRI_CHAR_LENGTH_PAIR("string")); + } + if (value.isNumber()) { + return AqlValue(TRI_CHAR_LENGTH_PAIR("number")); + } + if (value.isBoolean()) { + return AqlValue(TRI_CHAR_LENGTH_PAIR("bool")); + } + return AqlValue(TRI_CHAR_LENGTH_PAIR("null")); } /// @brief function TO_NUMBER @@ -968,7 +992,7 @@ AqlValue Functions::ToBool(arangodb::aql::Query* query, arangodb::AqlTransaction* trx, VPackFunctionParameters const& parameters) { AqlValue a = ExtractFunctionParameterValue(trx, parameters, 0); - return AqlValue(arangodb::basics::VelocyPackHelper::BooleanValue(a.toBoolean())); + return AqlValue(a.toBoolean()); } /// @brief function TO_ARRAY @@ -1639,9 +1663,7 @@ AqlValue Functions::Md5(arangodb::aql::Query* query, arangodb::rest::SslInterface::sslHEX(hash, 16, p, length); - TransactionBuilderLeaser builder(trx); - builder->add(VPackValue(std::string(hex, 32))); - return AqlValue(builder.get()); + return AqlValue(std::string(hex, 32)); } /// @brief function SHA1 @@ -1669,8 +1691,21 @@ AqlValue Functions::Sha1(arangodb::aql::Query* query, arangodb::rest::SslInterface::sslHEX(hash, 20, p, length); + return AqlValue(std::string(hex, 40)); +} + +/// @brief function HASH +AqlValue Functions::Hash(arangodb::aql::Query* query, + arangodb::AqlTransaction* trx, + VPackFunctionParameters const& parameters) { + AqlValue value = ExtractFunctionParameterValue(trx, parameters, 0); + + // throw away the top bytes so the hash value can safely be used + // without precision loss when storing in JavaScript etc. + uint64_t hash = value.hash(trx) & 0x0007ffffffffffffULL; + TransactionBuilderLeaser builder(trx); - builder->add(VPackValue(std::string(hex, 40))); + builder->add(VPackValue(hash)); return AqlValue(builder.get()); } diff --git a/arangod/Aql/Functions.h b/arangod/Aql/Functions.h index 54ec24f14b..f6b48e09dc 100644 --- a/arangod/Aql/Functions.h +++ b/arangod/Aql/Functions.h @@ -53,168 +53,172 @@ struct Functions { static AqlValue IsNull(arangodb::aql::Query*, arangodb::AqlTransaction*, VPackFunctionParameters const&); static AqlValue IsBool(arangodb::aql::Query*, arangodb::AqlTransaction*, - VPackFunctionParameters const&); + VPackFunctionParameters const&); static AqlValue IsNumber(arangodb::aql::Query*, arangodb::AqlTransaction*, - VPackFunctionParameters const&); + VPackFunctionParameters const&); static AqlValue IsString(arangodb::aql::Query*, arangodb::AqlTransaction*, - VPackFunctionParameters const&); + VPackFunctionParameters const&); static AqlValue IsArray(arangodb::aql::Query*, arangodb::AqlTransaction*, - VPackFunctionParameters const&); + VPackFunctionParameters const&); static AqlValue IsObject(arangodb::aql::Query*, arangodb::AqlTransaction*, - VPackFunctionParameters const&); + VPackFunctionParameters const&); + static AqlValue Typename(arangodb::aql::Query*, arangodb::AqlTransaction*, + VPackFunctionParameters const&); static AqlValue ToNumber(arangodb::aql::Query*, arangodb::AqlTransaction*, - VPackFunctionParameters const&); + VPackFunctionParameters const&); static AqlValue ToString(arangodb::aql::Query*, arangodb::AqlTransaction*, - VPackFunctionParameters const&); + VPackFunctionParameters const&); static AqlValue ToBool(arangodb::aql::Query*, arangodb::AqlTransaction*, - VPackFunctionParameters const&); + VPackFunctionParameters const&); static AqlValue ToArray(arangodb::aql::Query*, arangodb::AqlTransaction*, - VPackFunctionParameters const&); + VPackFunctionParameters const&); static AqlValue Length(arangodb::aql::Query*, arangodb::AqlTransaction*, - VPackFunctionParameters const&); + VPackFunctionParameters const&); static AqlValue First(arangodb::aql::Query*, arangodb::AqlTransaction*, - VPackFunctionParameters const&); + VPackFunctionParameters const&); static AqlValue Last(arangodb::aql::Query*, arangodb::AqlTransaction*, - VPackFunctionParameters const&); + VPackFunctionParameters const&); static AqlValue Nth(arangodb::aql::Query*, arangodb::AqlTransaction*, - VPackFunctionParameters const&); + VPackFunctionParameters const&); static AqlValue Concat(arangodb::aql::Query*, arangodb::AqlTransaction*, - VPackFunctionParameters const&); + VPackFunctionParameters const&); static AqlValue Like(arangodb::aql::Query*, arangodb::AqlTransaction*, - VPackFunctionParameters const&); + VPackFunctionParameters const&); static AqlValue Passthru(arangodb::aql::Query*, arangodb::AqlTransaction*, - VPackFunctionParameters const&); + VPackFunctionParameters const&); static AqlValue Unset(arangodb::aql::Query*, arangodb::AqlTransaction*, - VPackFunctionParameters const&); + VPackFunctionParameters const&); static AqlValue UnsetRecursive(arangodb::aql::Query*, - arangodb::AqlTransaction*, - VPackFunctionParameters const&); + arangodb::AqlTransaction*, + VPackFunctionParameters const&); static AqlValue Keep(arangodb::aql::Query*, arangodb::AqlTransaction*, - VPackFunctionParameters const&); + VPackFunctionParameters const&); static AqlValue Merge(arangodb::aql::Query*, arangodb::AqlTransaction*, - VPackFunctionParameters const&); + VPackFunctionParameters const&); static AqlValue MergeRecursive(arangodb::aql::Query*, - arangodb::AqlTransaction*, - VPackFunctionParameters const&); + arangodb::AqlTransaction*, + VPackFunctionParameters const&); static AqlValue Has(arangodb::aql::Query*, arangodb::AqlTransaction*, - VPackFunctionParameters const&); + VPackFunctionParameters const&); static AqlValue Attributes(arangodb::aql::Query*, arangodb::AqlTransaction*, - VPackFunctionParameters const&); + VPackFunctionParameters const&); static AqlValue Values(arangodb::aql::Query*, arangodb::AqlTransaction*, - VPackFunctionParameters const&); + VPackFunctionParameters const&); static AqlValue Min(arangodb::aql::Query*, arangodb::AqlTransaction*, - VPackFunctionParameters const&); + VPackFunctionParameters const&); static AqlValue Max(arangodb::aql::Query*, arangodb::AqlTransaction*, - VPackFunctionParameters const&); + VPackFunctionParameters const&); static AqlValue Sum(arangodb::aql::Query*, arangodb::AqlTransaction*, - VPackFunctionParameters const&); + VPackFunctionParameters const&); static AqlValue Average(arangodb::aql::Query*, arangodb::AqlTransaction*, - VPackFunctionParameters const&); + VPackFunctionParameters const&); static AqlValue Md5(arangodb::aql::Query*, arangodb::AqlTransaction*, - VPackFunctionParameters const&); + VPackFunctionParameters const&); static AqlValue Sha1(arangodb::aql::Query*, arangodb::AqlTransaction*, - VPackFunctionParameters const&); + VPackFunctionParameters const&); + static AqlValue Hash(arangodb::aql::Query*, arangodb::AqlTransaction*, + VPackFunctionParameters const&); static AqlValue Unique(arangodb::aql::Query*, arangodb::AqlTransaction*, - VPackFunctionParameters const&); + VPackFunctionParameters const&); static AqlValue SortedUnique(arangodb::aql::Query*, - arangodb::AqlTransaction*, - VPackFunctionParameters const&); - static AqlValue Union(arangodb::aql::Query*, arangodb::AqlTransaction*, - VPackFunctionParameters const&); - static AqlValue UnionDistinct(arangodb::aql::Query*, - arangodb::AqlTransaction*, - VPackFunctionParameters const&); - static AqlValue Intersection(arangodb::aql::Query*, - arangodb::AqlTransaction*, - VPackFunctionParameters const&); - static AqlValue Neighbors(arangodb::aql::Query*, arangodb::AqlTransaction*, - VPackFunctionParameters const&); - static AqlValue Near(arangodb::aql::Query*, arangodb::AqlTransaction*, - VPackFunctionParameters const&); - static AqlValue Within(arangodb::aql::Query*, arangodb::AqlTransaction*, - VPackFunctionParameters const&); - static AqlValue Flatten(arangodb::aql::Query*, arangodb::AqlTransaction*, - VPackFunctionParameters const&); - static AqlValue Zip(arangodb::aql::Query*, arangodb::AqlTransaction*, - VPackFunctionParameters const&); - static AqlValue ParseIdentifier(arangodb::aql::Query*, - arangodb::AqlTransaction*, - VPackFunctionParameters const&); - static AqlValue Minus(arangodb::aql::Query*, arangodb::AqlTransaction*, - VPackFunctionParameters const&); - static AqlValue Document(arangodb::aql::Query*, arangodb::AqlTransaction*, - VPackFunctionParameters const&); - static AqlValue Edges(arangodb::aql::Query*, arangodb::AqlTransaction*, - VPackFunctionParameters const&); - static AqlValue Round(arangodb::aql::Query*, arangodb::AqlTransaction*, - VPackFunctionParameters const&); - static AqlValue Abs(arangodb::aql::Query*, arangodb::AqlTransaction*, - VPackFunctionParameters const&); - static AqlValue Ceil(arangodb::aql::Query*, arangodb::AqlTransaction*, - VPackFunctionParameters const&); - static AqlValue Floor(arangodb::aql::Query*, arangodb::AqlTransaction*, - VPackFunctionParameters const&); - static AqlValue Sqrt(arangodb::aql::Query*, arangodb::AqlTransaction*, - VPackFunctionParameters const&); - static AqlValue Pow(arangodb::aql::Query*, arangodb::AqlTransaction*, - VPackFunctionParameters const&); - static AqlValue Rand(arangodb::aql::Query*, arangodb::AqlTransaction*, - VPackFunctionParameters const&); - static AqlValue FirstDocument(arangodb::aql::Query*, - arangodb::AqlTransaction*, - VPackFunctionParameters const&); - static AqlValue FirstList(arangodb::aql::Query*, arangodb::AqlTransaction*, - VPackFunctionParameters const&); - static AqlValue Push(arangodb::aql::Query*, arangodb::AqlTransaction*, - VPackFunctionParameters const&); - static AqlValue Pop(arangodb::aql::Query*, arangodb::AqlTransaction*, - VPackFunctionParameters const&); - static AqlValue Append(arangodb::aql::Query*, arangodb::AqlTransaction*, - VPackFunctionParameters const&); - static AqlValue Unshift(arangodb::aql::Query*, arangodb::AqlTransaction*, - VPackFunctionParameters const&); - static AqlValue Shift(arangodb::aql::Query*, arangodb::AqlTransaction*, - VPackFunctionParameters const&); - static AqlValue RemoveValue(arangodb::aql::Query*, arangodb::AqlTransaction*, + arangodb::AqlTransaction*, VPackFunctionParameters const&); - static AqlValue RemoveValues(arangodb::aql::Query*, + static AqlValue Union(arangodb::aql::Query*, arangodb::AqlTransaction*, + VPackFunctionParameters const&); + static AqlValue UnionDistinct(arangodb::aql::Query*, arangodb::AqlTransaction*, VPackFunctionParameters const&); - static AqlValue RemoveNth(arangodb::aql::Query*, arangodb::AqlTransaction*, - VPackFunctionParameters const&); - static AqlValue NotNull(arangodb::aql::Query*, arangodb::AqlTransaction*, - VPackFunctionParameters const&); - static AqlValue CurrentDatabase(arangodb::aql::Query*, - arangodb::AqlTransaction*, - VPackFunctionParameters const&); - static AqlValue CollectionCount(arangodb::aql::Query*, - arangodb::AqlTransaction*, - VPackFunctionParameters const&); - static AqlValue VarianceSample(arangodb::aql::Query*, + static AqlValue Intersection(arangodb::aql::Query*, + arangodb::AqlTransaction*, + VPackFunctionParameters const&); + static AqlValue Neighbors(arangodb::aql::Query*, arangodb::AqlTransaction*, + VPackFunctionParameters const&); + static AqlValue Near(arangodb::aql::Query*, arangodb::AqlTransaction*, + VPackFunctionParameters const&); + static AqlValue Within(arangodb::aql::Query*, arangodb::AqlTransaction*, + VPackFunctionParameters const&); + static AqlValue Flatten(arangodb::aql::Query*, arangodb::AqlTransaction*, + VPackFunctionParameters const&); + static AqlValue Zip(arangodb::aql::Query*, arangodb::AqlTransaction*, + VPackFunctionParameters const&); + static AqlValue ParseIdentifier(arangodb::aql::Query*, arangodb::AqlTransaction*, VPackFunctionParameters const&); - static AqlValue VariancePopulation(arangodb::aql::Query*, - arangodb::AqlTransaction*, - VPackFunctionParameters const&); - static AqlValue StdDevSample(arangodb::aql::Query*, + static AqlValue Minus(arangodb::aql::Query*, arangodb::AqlTransaction*, + VPackFunctionParameters const&); + static AqlValue Document(arangodb::aql::Query*, arangodb::AqlTransaction*, + VPackFunctionParameters const&); + static AqlValue Edges(arangodb::aql::Query*, arangodb::AqlTransaction*, + VPackFunctionParameters const&); + static AqlValue Round(arangodb::aql::Query*, arangodb::AqlTransaction*, + VPackFunctionParameters const&); + static AqlValue Abs(arangodb::aql::Query*, arangodb::AqlTransaction*, + VPackFunctionParameters const&); + static AqlValue Ceil(arangodb::aql::Query*, arangodb::AqlTransaction*, + VPackFunctionParameters const&); + static AqlValue Floor(arangodb::aql::Query*, arangodb::AqlTransaction*, + VPackFunctionParameters const&); + static AqlValue Sqrt(arangodb::aql::Query*, arangodb::AqlTransaction*, + VPackFunctionParameters const&); + static AqlValue Pow(arangodb::aql::Query*, arangodb::AqlTransaction*, + VPackFunctionParameters const&); + static AqlValue Rand(arangodb::aql::Query*, arangodb::AqlTransaction*, + VPackFunctionParameters const&); + static AqlValue FirstDocument(arangodb::aql::Query*, arangodb::AqlTransaction*, VPackFunctionParameters const&); - static AqlValue StdDevPopulation(arangodb::aql::Query*, - arangodb::AqlTransaction*, - VPackFunctionParameters const&); - static AqlValue Median(arangodb::aql::Query*, arangodb::AqlTransaction*, - VPackFunctionParameters const&); - static AqlValue Percentile(arangodb::aql::Query*, arangodb::AqlTransaction*, - VPackFunctionParameters const&); - static AqlValue Range(arangodb::aql::Query*, arangodb::AqlTransaction*, + static AqlValue FirstList(arangodb::aql::Query*, arangodb::AqlTransaction*, + VPackFunctionParameters const&); + static AqlValue Push(arangodb::aql::Query*, arangodb::AqlTransaction*, + VPackFunctionParameters const&); + static AqlValue Pop(arangodb::aql::Query*, arangodb::AqlTransaction*, + VPackFunctionParameters const&); + static AqlValue Append(arangodb::aql::Query*, arangodb::AqlTransaction*, VPackFunctionParameters const&); - static AqlValue Position(arangodb::aql::Query*, arangodb::AqlTransaction*, + static AqlValue Unshift(arangodb::aql::Query*, arangodb::AqlTransaction*, + VPackFunctionParameters const&); + static AqlValue Shift(arangodb::aql::Query*, arangodb::AqlTransaction*, + VPackFunctionParameters const&); + static AqlValue RemoveValue(arangodb::aql::Query*, arangodb::AqlTransaction*, + VPackFunctionParameters const&); + static AqlValue RemoveValues(arangodb::aql::Query*, + arangodb::AqlTransaction*, + VPackFunctionParameters const&); + static AqlValue RemoveNth(arangodb::aql::Query*, arangodb::AqlTransaction*, VPackFunctionParameters const&); - static AqlValue Fulltext(arangodb::aql::Query*, arangodb::AqlTransaction*, - VPackFunctionParameters const&); - static AqlValue IsSameCollection(arangodb::aql::Query*, + static AqlValue NotNull(arangodb::aql::Query*, arangodb::AqlTransaction*, + VPackFunctionParameters const&); + static AqlValue CurrentDatabase(arangodb::aql::Query*, + arangodb::AqlTransaction*, + VPackFunctionParameters const&); + static AqlValue CollectionCount(arangodb::aql::Query*, + arangodb::AqlTransaction*, + VPackFunctionParameters const&); + static AqlValue VarianceSample(arangodb::aql::Query*, + arangodb::AqlTransaction*, + VPackFunctionParameters const&); + static AqlValue VariancePopulation(arangodb::aql::Query*, + arangodb::AqlTransaction*, + VPackFunctionParameters const&); + static AqlValue StdDevSample(arangodb::aql::Query*, + arangodb::AqlTransaction*, + VPackFunctionParameters const&); + static AqlValue StdDevPopulation(arangodb::aql::Query*, arangodb::AqlTransaction*, VPackFunctionParameters const&); + static AqlValue Median(arangodb::aql::Query*, arangodb::AqlTransaction*, + VPackFunctionParameters const&); + static AqlValue Percentile(arangodb::aql::Query*, arangodb::AqlTransaction*, + VPackFunctionParameters const&); + static AqlValue Range(arangodb::aql::Query*, arangodb::AqlTransaction*, + VPackFunctionParameters const&); + static AqlValue Position(arangodb::aql::Query*, arangodb::AqlTransaction*, + VPackFunctionParameters const&); + static AqlValue Fulltext(arangodb::aql::Query*, arangodb::AqlTransaction*, + VPackFunctionParameters const&); + static AqlValue IsSameCollection(arangodb::aql::Query*, + arangodb::AqlTransaction*, + VPackFunctionParameters const&); }; } } diff --git a/arangod/RestServer/arangod_libarangod_a-ArangoServer.o-61d1e2e3 b/arangod/RestServer/arangod_libarangod_a-ArangoServer.o-61d1e2e3 deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/arangod/V8Server/v8-vocbase.cpp b/arangod/V8Server/v8-vocbase.cpp index 971316bf49..79a80c7cad 100644 --- a/arangod/V8Server/v8-vocbase.cpp +++ b/arangod/V8Server/v8-vocbase.cpp @@ -2447,6 +2447,34 @@ static void JS_QuerySleepAql(v8::FunctionCallbackInfo const& args) { TRI_V8_TRY_CATCH_END } +//////////////////////////////////////////////////////////////////////////////// +/// @brief hashes a V8 object +//////////////////////////////////////////////////////////////////////////////// + +static void JS_ObjectHash(v8::FunctionCallbackInfo const& args) { + TRI_V8_TRY_CATCH_BEGIN(isolate); + v8::HandleScope scope(isolate); + + // extract arguments + if (args.Length() != 1) { + TRI_V8_THROW_EXCEPTION_USAGE("hash()"); + } + + VPackBuilder builder; + int res = TRI_V8ToVPack(isolate, builder, args[0], false); + + if (res != TRI_ERROR_NO_ERROR) { + TRI_V8_THROW_EXCEPTION(res); + } + + // throw away the top bytes so the hash value can safely be used + // without precision loss when storing in JavaScript etc. + uint64_t hash = builder.slice().normalizedHash() & 0x0007ffffffffffffULL; + + TRI_V8_RETURN(v8::Number::New(isolate, static_cast(hash))); + TRI_V8_TRY_CATCH_END +} + //////////////////////////////////////////////////////////////////////////////// /// @brief wraps a TRI_vocbase_t //////////////////////////////////////////////////////////////////////////////// @@ -3556,6 +3584,10 @@ void TRI_InitV8VocBridge(v8::Isolate* isolate, v8::Handle context, TRI_AddGlobalFunctionVocbase( isolate, context, TRI_V8_ASCII_STRING("AQL_QUERY_CACHE_INVALIDATE"), JS_QueryCacheInvalidateAql, true); + + TRI_AddGlobalFunctionVocbase(isolate, context, + TRI_V8_ASCII_STRING("OBJECT_HASH"), + JS_ObjectHash, true); TRI_AddGlobalFunctionVocbase( isolate, context, TRI_V8_ASCII_STRING("THROW_COLLECTION_NOT_LOADED"), diff --git a/js/apps/system/_admin/aardvark/APP/frontend/src/mode-aql.js b/js/apps/system/_admin/aardvark/APP/frontend/src/mode-aql.js index 830edfdc79..9a50eceec2 100644 --- a/js/apps/system/_admin/aardvark/APP/frontend/src/mode-aql.js +++ b/js/apps/system/_admin/aardvark/APP/frontend/src/mode-aql.js @@ -92,7 +92,7 @@ var AqlHighlightRules = function() { ); var builtinFunctions = ( - "(to_bool|to_number|to_string|to_list|is_null|is_bool|is_number|is_string|is_list|is_document|" + + "(to_bool|to_number|to_string|to_list|is_null|is_bool|is_number|is_string|is_list|is_document|typename|" + "concat|concat_separator|char_length|lower|upper|substring|left|right|trim|reverse|contains|" + "like|floor|ceil|round|abs|rand|sqrt|pow|length|min|max|average|sum|median|variance_population|" + "variance_sample|first|last|unique|matches|merge|merge_recursive|has|attributes|values|unset|unset_recursive|keep|" + @@ -107,7 +107,7 @@ var AqlHighlightRules = function() { "date_add|date_subtract|date_diff|date_compare|date_format|fail|passthru|sleep|not_null|" + "first_list|first_document|parse_identifier|current_user|current_database|" + "collections|document|union|union_distinct|intersection|flatten|is_same_collection|" + - "ltrim|rtrim|find_first|find_last|split|substitute|md5|sha1|random_token|AQL_LAST_ENTRY)" + "ltrim|rtrim|find_first|find_last|split|substitute|md5|sha1|hash|random_token|AQL_LAST_ENTRY)" ); var keywordMapper = this.createKeywordMapper({ diff --git a/js/server/modules/@arangodb/aql.js b/js/server/modules/@arangodb/aql.js index 329b580815..9046ab4f4a 100644 --- a/js/server/modules/@arangodb/aql.js +++ b/js/server/modules/@arangodb/aql.js @@ -1,6 +1,6 @@ /*jshint strict: false, unused: false, bitwise: false, esnext: true */ /*global COMPARE_STRING, AQL_TO_BOOL, AQL_TO_NUMBER, AQL_TO_STRING, AQL_WARNING, AQL_QUERY_SLEEP */ -/*global CPP_SHORTEST_PATH, CPP_NEIGHBORS, Set */ +/*global CPP_SHORTEST_PATH, CPP_NEIGHBORS, Set, OBJECT_HASH */ //////////////////////////////////////////////////////////////////////////////// /// @brief Aql, internal query functions @@ -2519,6 +2519,39 @@ function AQL_SHA1 (value) { return INTERNAL.sha1(AQL_TO_STRING(value)); } +//////////////////////////////////////////////////////////////////////////////// +/// @brief generates a hash value for an object +//////////////////////////////////////////////////////////////////////////////// + +function AQL_HASH (value) { + 'use strict'; + + return OBJECT_HASH(value); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief returns the typename for an object +//////////////////////////////////////////////////////////////////////////////// + +function AQL_TYPENAME (value) { + 'use strict'; + + switch (TYPEWEIGHT(value)) { + case TYPEWEIGHT_BOOL: + return "bool"; + case TYPEWEIGHT_NUMBER: + return "number"; + case TYPEWEIGHT_STRING: + return "string"; + case TYPEWEIGHT_ARRAY: + return "array"; + case TYPEWEIGHT_OBJECT: + return "object"; + } + + return "null"; +} + //////////////////////////////////////////////////////////////////////////////// /// @brief generates a random token of the specified length //////////////////////////////////////////////////////////////////////////////// @@ -8133,6 +8166,8 @@ exports.AQL_SPLIT = AQL_SPLIT; exports.AQL_SUBSTITUTE = AQL_SUBSTITUTE; exports.AQL_MD5 = AQL_MD5; exports.AQL_SHA1 = AQL_SHA1; +exports.AQL_HASH = AQL_HASH; +exports.AQL_TYPENAME = AQL_TYPENAME; exports.AQL_RANDOM_TOKEN = AQL_RANDOM_TOKEN; exports.AQL_FIND_FIRST = AQL_FIND_FIRST; exports.AQL_FIND_LAST = AQL_FIND_LAST; diff --git a/js/server/tests/aql/aql-functions-string.js b/js/server/tests/aql/aql-functions-string.js index 288df95447..39feb229dd 100644 --- a/js/server/tests/aql/aql-functions-string.js +++ b/js/server/tests/aql/aql-functions-string.js @@ -1,5 +1,5 @@ /*jshint globalstrict:false, strict:false, maxlen:5000 */ -/*global assertEqual, assertTrue */ +/*global assertEqual, assertNotEqual, assertTrue */ //////////////////////////////////////////////////////////////////////////////// /// @brief tests for query language, functions @@ -1323,6 +1323,101 @@ function ahuacatlStringFunctionsTestSuite () { } }, +//////////////////////////////////////////////////////////////////////////////// +/// @brief test hash function +//////////////////////////////////////////////////////////////////////////////// + + testHash : function () { + var buildQuery = function (nr, input) { + switch (nr) { + case 0: + return `RETURN HASH(${input})`; + case 1: + return `RETURN NOOPT(HASH(${input}))`; + case 2: + return `RETURN NOOPT(V8(HASH(${input})))`; + default: + assertTrue(false, "Undefined state"); + } + }; + var i; + for (i = 0; i < 3; ++i) { + assertQueryError(errors.ERROR_QUERY_FUNCTION_ARGUMENT_NUMBER_MISMATCH.code, buildQuery(i, "")); + assertQueryError(errors.ERROR_QUERY_FUNCTION_ARGUMENT_NUMBER_MISMATCH.code, buildQuery(i, "1, 2")); + assertQueryError(errors.ERROR_QUERY_FUNCTION_ARGUMENT_NUMBER_MISMATCH.code, buildQuery(i, "1, 2, 3")); + assertEqual([ 675717317264138 ], getQueryResults(buildQuery(i, "null"))); + assertEqual([ 1217335385489389 ], getQueryResults(buildQuery(i, "false"))); + assertEqual([ 57801618404459 ], getQueryResults(buildQuery(i, "true"))); + assertEqual([ 2964198978643 ], getQueryResults(buildQuery(i, "1 / 0"))); + assertEqual([ 2964198978643 ], getQueryResults(buildQuery(i, "0"))); + assertEqual([ 2964198978643 ], getQueryResults(buildQuery(i, "0.0"))); + assertEqual([ 464020872367562 ], getQueryResults(buildQuery(i, "0.00001"))); + assertEqual([ 652971229830707 ], getQueryResults(buildQuery(i, "1"))); + assertEqual([ 510129580600084 ], getQueryResults(buildQuery(i, "-1"))); + assertEqual([ 24372339383975 ], getQueryResults(buildQuery(i, "-10.5"))); + assertEqual([ 1041198105137773 ], getQueryResults(buildQuery(i, "123452532322453"))); + assertEqual([ 876255539722551 ], getQueryResults(buildQuery(i, "123452532322454"))); + assertEqual([ 1277486662998285 ], getQueryResults(buildQuery(i, "123452532322454.434"))); + assertEqual([ 210539478145939 ], getQueryResults(buildQuery(i, "-123452532322454"))); + assertEqual([ 261745517313272 ], getQueryResults(buildQuery(i, "-9999999999999.999"))); + assertEqual([ 441814588996558 ], getQueryResults(buildQuery(i, "''"))); + assertEqual([ 1112732548475941 ], getQueryResults(buildQuery(i, "' '"))); + assertEqual([ 246233608921999 ], getQueryResults(buildQuery(i, "' '"))); + assertEqual([ 1542381651001813 ], getQueryResults(buildQuery(i, "'a'"))); + assertEqual([ 843602980995939 ], getQueryResults(buildQuery(i, "'A'"))); + assertEqual([ 1618092585478118 ], getQueryResults(buildQuery(i, "' a'"))); + assertEqual([ 725364078947946 ], getQueryResults(buildQuery(i, "' A'"))); + assertEqual([ 736233736371291 ], getQueryResults(buildQuery(i, "' foobar'"))); + assertEqual([ 360657200843601 ], getQueryResults(buildQuery(i, "'this is a string test. please ignore.'"))); + assertEqual([ 828085160327326 ], getQueryResults(buildQuery(i, "'this is a string test. please Ignore.'"))); + assertEqual([ 2072438876063292 ], getQueryResults(buildQuery(i, "'a string is a string is a string of course. even longer strings can be hashed. isn\\'t this fantastic? let\\'s see if we can cross the short-string bounds with it...'"))); + assertEqual([ 181227890622943 ], getQueryResults(buildQuery(i, "[]"))); + assertEqual([ 346113245898278 ], getQueryResults(buildQuery(i, "[0]"))); + assertEqual([ 785599515440277 ], getQueryResults(buildQuery(i, "[1]"))); + assertEqual([ 1295855700045140 ], getQueryResults(buildQuery(i, "[1,2]"))); + assertEqual([ 1295855700045140 ], getQueryResults(buildQuery(i, "1..2"))); + assertEqual([ 1255602544875390 ], getQueryResults(buildQuery(i, "[2,1]"))); + assertEqual([ 1255602544875390 ], getQueryResults(buildQuery(i, "2..1"))); + assertEqual([ 1625466870434085 ], getQueryResults(buildQuery(i, "[1,2,3]"))); + assertEqual([ 1625466870434085 ], getQueryResults(buildQuery(i, "1..3"))); + assertEqual([ 1657598895986170 ], getQueryResults(buildQuery(i, "[1,2,3,4]"))); + assertEqual([ 1657598895986170 ], getQueryResults(buildQuery(i, "1..4"))); + assertEqual([ 1580543009747638 ], getQueryResults(buildQuery(i, "[1,2,4,3]"))); + assertEqual([ 157821093310761 ], getQueryResults(buildQuery(i, "[1,2,3,2]"))); + assertEqual([ 1032992608692014 ], getQueryResults(buildQuery(i, "[1,2,3,2,1]"))); + assertEqual([ 2051766968908771 ], getQueryResults(buildQuery(i, "1..1000"))); + assertEqual([ 1954991255293719 ], getQueryResults(buildQuery(i, "{}"))); + assertEqual([ 1270059518310386 ], getQueryResults(buildQuery(i, "{a:1}"))); + assertEqual([ 1462532781001381 ], getQueryResults(buildQuery(i, "{a:2}"))); + assertEqual([ 1872109801523384 ], getQueryResults(buildQuery(i, "{a:1,b:1}"))); + assertEqual([ 599770551193312 ], getQueryResults(buildQuery(i, "{a:1,b:2}"))); + assertEqual([ 1872109801523384 ], getQueryResults(buildQuery(i, "{b:1,a:1}"))); + assertEqual([ 599770551193312 ], getQueryResults(buildQuery(i, "{b:2,a:1}"))); + assertEqual([ 876136767628139 ], getQueryResults(buildQuery(i, "{b:1,a:2}"))); + assertEqual([ 876136767628139 ], getQueryResults(buildQuery(i, "{a:2,b:1}"))); + assertEqual([ 92631926086363 ], getQueryResults(buildQuery(i, "{a:2,b:'1'}"))); + assertEqual([ 2054068497715740 ], getQueryResults(buildQuery(i, "{a:2,b:null}"))); + assertEqual([ 550542031834779 ], getQueryResults(buildQuery(i, "{A:1,B:2}"))); + assertEqual([ 2125993103279620 ], getQueryResults(buildQuery(i, "{a:'A',b:'B'}"))); + assertEqual([ 878459260153284 ], getQueryResults(buildQuery(i, "{a:'a',b:'b'}"))); + assertEqual([ 1454594333033579 ], getQueryResults(buildQuery(i, "{a:['a'],b:['b']}"))); + assertEqual([ 296899533959594 ], getQueryResults(buildQuery(i, "{a:1,b:-1}"))); + assertEqual([ 944398530367049 ], getQueryResults(buildQuery(i, "{_id:'foo',_key:'bar',_rev:'baz'}"))); + } + + for (i = 0; i < 3; ++i) { + // order does not matter + assertEqual(getQueryResults(buildQuery(i, "{a:1,b:2}")), getQueryResults(buildQuery(i, "{b:2,a:1}"))); + assertNotEqual(getQueryResults(buildQuery(i, "{a:1,b:2}")), getQueryResults(buildQuery(i, "{a:2,b:1}"))); + // order matters + assertNotEqual(getQueryResults(buildQuery(i, "[1,2,3]")), getQueryResults(buildQuery(i, "[3,2,1]"))); + // arrays and ranges + assertEqual(getQueryResults(buildQuery(i, "[1,2,3]")), getQueryResults(buildQuery(i, "1..3"))); + // arrays and subqueries + assertEqual(getQueryResults(buildQuery(i, "[1,2,3]")), getQueryResults(buildQuery(i, "FOR i IN [1,2,3] RETURN i"))); + } + }, + //////////////////////////////////////////////////////////////////////////////// /// @brief test md5 function //////////////////////////////////////////////////////////////////////////////// diff --git a/js/server/tests/aql/aql-functions-types-cxx.js b/js/server/tests/aql/aql-functions-types-cxx.js index a40d5365a1..39cd716b43 100644 --- a/js/server/tests/aql/aql-functions-types-cxx.js +++ b/js/server/tests/aql/aql-functions-types-cxx.js @@ -41,6 +41,47 @@ var assertQueryError = helper.assertQueryError; function ahuacatlTypesFunctionsTestSuite () { return { +//////////////////////////////////////////////////////////////////////////////// +/// @brief test typename function +//////////////////////////////////////////////////////////////////////////////// + + testTypename : function () { + assertEqual([ "null" ], getQueryResults("RETURN NOOPT(TYPENAME(null))")); + assertEqual([ "bool" ], getQueryResults("RETURN NOOPT(TYPENAME(false))")); + assertEqual([ "bool" ], getQueryResults("RETURN NOOPT(TYPENAME(true))")); + assertEqual([ "number" ], getQueryResults("RETURN NOOPT(TYPENAME(0))")); + assertEqual([ "number" ], getQueryResults("RETURN NOOPT(TYPENAME(1))")); + assertEqual([ "number" ], getQueryResults("RETURN NOOPT(TYPENAME(-99999))")); + assertEqual([ "number" ], getQueryResults("RETURN NOOPT(TYPENAME(0.005))")); + assertEqual([ "number" ], getQueryResults("RETURN NOOPT(TYPENAME(1334540.005))")); + assertEqual([ "number" ], getQueryResults("RETURN NOOPT(TYPENAME(3e32))")); + assertEqual([ "array" ], getQueryResults("RETURN NOOPT(TYPENAME(1..2))")); + assertEqual([ "array" ], getQueryResults("RETURN NOOPT(TYPENAME(99..0))")); + assertEqual([ "array" ], getQueryResults("RETURN NOOPT(TYPENAME(FOR i IN 1..10 RETURN i))")); + assertEqual([ "array" ], getQueryResults("RETURN NOOPT(TYPENAME([ ]))")); + assertEqual([ "array" ], getQueryResults("RETURN NOOPT(TYPENAME([ 'foo ' ]))")); + assertEqual([ "object" ], getQueryResults("RETURN NOOPT(TYPENAME({ }))")); + assertEqual([ "object" ], getQueryResults("RETURN NOOPT(TYPENAME({ 'foo': 'bar' }))")); + assertEqual([ "string" ], getQueryResults("RETURN NOOPT(TYPENAME('foo'))")); + assertEqual([ "string" ], getQueryResults("RETURN NOOPT(TYPENAME(''))")); + assertEqual([ "string" ], getQueryResults("RETURN NOOPT(TYPENAME(' '))")); + assertEqual([ "string" ], getQueryResults("RETURN NOOPT(TYPENAME('0'))")); + assertEqual([ "string" ], getQueryResults("RETURN NOOPT(TYPENAME('1'))")); + assertEqual([ "string" ], getQueryResults("RETURN NOOPT(TYPENAME('true'))")); + assertEqual([ "string" ], getQueryResults("RETURN NOOPT(TYPENAME('false'))")); + assertEqual([ "number", "number" ], getQueryResults("FOR i IN 1..2 RETURN NOOPT(TYPENAME(i))")); + assertEqual([ "string", "string", "string", "number" ], getQueryResults("FOR i IN [ 'foo', 'bar', 'baz', 42 ] RETURN NOOPT(TYPENAME(i))")); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test typename function, invalid arguments +//////////////////////////////////////////////////////////////////////////////// + + testTypenameInvalid : function () { + assertQueryError(errors.ERROR_QUERY_FUNCTION_ARGUMENT_NUMBER_MISMATCH.code, "RETURN NOOPT(TYPENAME())"); + assertQueryError(errors.ERROR_QUERY_FUNCTION_ARGUMENT_NUMBER_MISMATCH.code, "RETURN NOOPT(TYPENAME('a', 'b'))"); + }, + //////////////////////////////////////////////////////////////////////////////// /// @brief test is_string function //////////////////////////////////////////////////////////////////////////////// diff --git a/js/server/tests/aql/aql-functions-types.js b/js/server/tests/aql/aql-functions-types.js index 8f4779aa82..b690b97137 100644 --- a/js/server/tests/aql/aql-functions-types.js +++ b/js/server/tests/aql/aql-functions-types.js @@ -65,6 +65,47 @@ function ahuacatlTypesFunctionsTestSuite () { db._drop(vn); }, +//////////////////////////////////////////////////////////////////////////////// +/// @brief test typename function +//////////////////////////////////////////////////////////////////////////////// + + testTypename : function () { + assertEqual([ "null" ], getQueryResults("RETURN TYPENAME(null)")); + assertEqual([ "bool" ], getQueryResults("RETURN TYPENAME(false)")); + assertEqual([ "bool" ], getQueryResults("RETURN TYPENAME(true)")); + assertEqual([ "number" ], getQueryResults("RETURN TYPENAME(0)")); + assertEqual([ "number" ], getQueryResults("RETURN TYPENAME(1)")); + assertEqual([ "number" ], getQueryResults("RETURN TYPENAME(-99999)")); + assertEqual([ "number" ], getQueryResults("RETURN TYPENAME(0.005)")); + assertEqual([ "number" ], getQueryResults("RETURN TYPENAME(1334540.005)")); + assertEqual([ "number" ], getQueryResults("RETURN TYPENAME(3e32)")); + assertEqual([ "array" ], getQueryResults("RETURN TYPENAME(1..2)")); + assertEqual([ "array" ], getQueryResults("RETURN TYPENAME(99..0)")); + assertEqual([ "array" ], getQueryResults("RETURN TYPENAME(FOR i IN 1..10 RETURN i)")); + assertEqual([ "array" ], getQueryResults("RETURN TYPENAME([ ])")); + assertEqual([ "array" ], getQueryResults("RETURN TYPENAME([ 'foo ' ])")); + assertEqual([ "object" ], getQueryResults("RETURN TYPENAME({ })")); + assertEqual([ "object" ], getQueryResults("RETURN TYPENAME({ 'foo': 'bar' })")); + assertEqual([ "string" ], getQueryResults("RETURN TYPENAME('foo')")); + assertEqual([ "string" ], getQueryResults("RETURN TYPENAME('')")); + assertEqual([ "string" ], getQueryResults("RETURN TYPENAME(' ')")); + assertEqual([ "string" ], getQueryResults("RETURN TYPENAME('0')")); + assertEqual([ "string" ], getQueryResults("RETURN TYPENAME('1')")); + assertEqual([ "string" ], getQueryResults("RETURN TYPENAME('true')")); + assertEqual([ "string" ], getQueryResults("RETURN TYPENAME('false')")); + assertEqual([ "number", "number" ], getQueryResults("FOR i IN 1..2 RETURN TYPENAME(i)")); + assertEqual([ "string", "string", "string", "number" ], getQueryResults("FOR i IN [ 'foo', 'bar', 'baz', 42 ] RETURN TYPENAME(i)")); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test typename function, invalid arguments +//////////////////////////////////////////////////////////////////////////////// + + testTypenameInvalid : function () { + assertQueryError(errors.ERROR_QUERY_FUNCTION_ARGUMENT_NUMBER_MISMATCH.code, "RETURN TYPENAME()"); + assertQueryError(errors.ERROR_QUERY_FUNCTION_ARGUMENT_NUMBER_MISMATCH.code, "RETURN TYPENAME('a', 'b')"); + }, + //////////////////////////////////////////////////////////////////////////////// /// @brief test is_string function //////////////////////////////////////////////////////////////////////////////// diff --git a/js/server/tests/aql/aql-queries-collection.js b/js/server/tests/aql/aql-queries-collection.js index edac9503d4..72cd9ae2c4 100644 --- a/js/server/tests/aql/aql-queries-collection.js +++ b/js/server/tests/aql/aql-queries-collection.js @@ -854,6 +854,47 @@ function ahuacatlQueryCollectionTestSuite () { var expected = [ [ "riding", "skating", "swimming", null, "swimming" ] ]; var actual = getQueryResults("FOR u in " + users.name() + " FILTER HAS(u, 'hobbies') RETURN [ u.hobbies[0], u.hobbies[1], u.hobbies[2], u.hobbies[3], u.hobbies[-1] ]"); assertEqual(expected, actual); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test hashing the documents +//////////////////////////////////////////////////////////////////////////////// + + testHashes : function () { + var expected = [ + 1089385960271542, + 1549718497690202, + 560358553578052, + 1642812332573238, + 435359225373270, + 408295336378511, + 1897938296916959, + 713554280722917, + 369118524423277, + 1548261017980441, + 541971611858512, + 1804578553815548, + 976161958841139, + 259242346857309, + 671328068995257, + 2154779012539123, + 1413254027041724, + 854175714643442, + 178243611496212, + 791314881371364 + ]; + var actual = getQueryResults("FOR u in " + users.name() + " SORT u.id RETURN HASH(UNSET(u, ['_key', '_rev', '_id']))"); + assertEqual(expected, actual); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test hashing the documents +//////////////////////////////////////////////////////////////////////////////// + + testHashSubquery : function () { + var expected = [ 1866655274639415 ]; + var actual = getQueryResults("RETURN HASH(FOR u in " + users.name() + " SORT u.id RETURN UNSET(u, ['_key', '_rev', '_id']))"); + assertEqual(expected, actual); } };