diff --git a/CHANGELOG b/CHANGELOG index 3c86de9984..c454fdef1a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,8 @@ v1.4 ---- +* added AQL INTERSECTION function + * INCOMPATIBLE CHANGE: changed AQL user function namespace resolution operator from `:` to `::` AQL user-defined functions were introduced in ArangoDB 1.3, and the namespace resolution diff --git a/Documentation/UserManual/Aql.md b/Documentation/UserManual/Aql.md index 837d128837..bb76b94c5b 100644 --- a/Documentation/UserManual/Aql.md +++ b/Documentation/UserManual/Aql.md @@ -1091,7 +1091,9 @@ AQL supports the following functions to operate on list values: Calling this function might return the unique elements in any order. - @FN{UNION(@FA{list1, list2, ...})}: returns the union of all lists specified. - The function expects at least two list values as its arguments. + The function expects at least two list values as its arguments. The result is a list + of values in an undefined order. + Note: no duplicates will be removed. In order to remove duplicates, please use the @LIT{UNIQUE} function. @@ -1116,6 +1118,12 @@ AQL supports the following functions to operate on list values: will produce: [ [ 1, 2, 3 ] ] +- @FN{INTERSECTION(@FA{list1, list2, ...})}: returns the intersection of all lists specified. + The function expects at least two list values as its arguments. + The result is a list of values that occur in all arguments. The order of the result list + is undefined and should not be relied on. + Note: duplicates will be removed. + Apart from these functions, AQL also offers several language constructs (e.g. `FOR`, `SORT`, `LIMIT`, `COLLECT`) to operate on lists. diff --git a/arangod/Ahuacatl/ahuacatl-functions.c b/arangod/Ahuacatl/ahuacatl-functions.c index 83802a37cc..bc35a8509b 100644 --- a/arangod/Ahuacatl/ahuacatl-functions.c +++ b/arangod/Ahuacatl/ahuacatl-functions.c @@ -644,6 +644,7 @@ TRI_associative_pointer_t* TRI_InitialiseFunctionsAql (void) { // list functions REGISTER_FUNCTION("UNION", "UNION", true, false, "l,l|+", NULL); + REGISTER_FUNCTION("INTERSECTION", "INTERSECTION", true, false, "l,l|+", NULL); REGISTER_FUNCTION("LENGTH", "LENGTH", true, true, "las", NULL); REGISTER_FUNCTION("MIN", "MIN", true, true, "l", NULL); REGISTER_FUNCTION("MAX", "MAX", true, true, "l", NULL); diff --git a/js/server/modules/org/arangodb/ahuacatl.js b/js/server/modules/org/arangodb/ahuacatl.js index 0c0a9bc403..0058cbe33d 100644 --- a/js/server/modules/org/arangodb/ahuacatl.js +++ b/js/server/modules/org/arangodb/ahuacatl.js @@ -2500,6 +2500,58 @@ function UNION () { return result; } +//////////////////////////////////////////////////////////////////////////////// +/// @brief create the intersection of all arguments +//////////////////////////////////////////////////////////////////////////////// + +function INTERSECTION () { + "use strict"; + + var result = [ ], i, first = true, keys = { }; + + var func = function (value) { + var normalized = NORMALIZE(value); + keys[JSON.stringify(normalized)] = normalized; + }; + + for (i in arguments) { + if (arguments.hasOwnProperty(i)) { + var element = arguments[i]; + + if (TYPEWEIGHT(element) !== TYPEWEIGHT_LIST) { + THROW(INTERNAL.errors.ERROR_QUERY_FUNCTION_ARGUMENT_TYPE_MISMATCH, "INTERSECTION"); + } + + if (first) { + element.forEach(func); + first = false; + } + else { + var j, newKeys = { }; + for (j = 0; j < element.length; ++j) { + var normalized = NORMALIZE(element[j]); + var key = JSON.stringify(normalized); + + if (keys.hasOwnProperty(key)) { + newKeys[key] = normalized; + } + } + keys = newKeys; + newKeys = null; + } + + } + } + + for (i in keys) { + if (keys.hasOwnProperty(i)) { + result.push(keys[i]); + } + } + + return result; +} + //////////////////////////////////////////////////////////////////////////////// /// @brief maximum of all values //////////////////////////////////////////////////////////////////////////////// @@ -3796,6 +3848,7 @@ exports.LAST = LAST; exports.REVERSE = REVERSE; exports.UNIQUE = UNIQUE; exports.UNION = UNION; +exports.INTERSECTION = INTERSECTION; exports.MAX = MAX; exports.MIN = MIN; exports.SUM = SUM; diff --git a/js/server/tests/ahuacatl-functions.js b/js/server/tests/ahuacatl-functions.js index 23059d39f0..44ef0e5057 100644 --- a/js/server/tests/ahuacatl-functions.js +++ b/js/server/tests/ahuacatl-functions.js @@ -1370,6 +1370,106 @@ function ahuacatlFunctionsTestSuite () { assertEqual(expected, actual); }, +//////////////////////////////////////////////////////////////////////////////// +/// @brief test intersect function +//////////////////////////////////////////////////////////////////////////////// + + testIntersection1 : function () { + var expected = [ [ 1, -3 ] ]; + var actual = getQueryResults("RETURN INTERSECTION([ 1, -3 ], [ -3, 1 ])"); + assertEqual(expected, actual); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test intersect function +//////////////////////////////////////////////////////////////////////////////// + + testIntersection2 : function () { + var expected = [ [ ] ]; + var actual = getQueryResults("RETURN INTERSECTION([ ], [ 1 ])"); + assertEqual(expected, actual); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test intersect function +//////////////////////////////////////////////////////////////////////////////// + + testIntersection3 : function () { + var expected = [ [ ] ]; + var actual = getQueryResults("RETURN INTERSECTION([ 1 ], [ ])"); + assertEqual(expected, actual); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test intersect function +//////////////////////////////////////////////////////////////////////////////// + + testIntersection4 : function () { + var expected = [ [ ] ]; + var actual = getQueryResults("RETURN INTERSECTION([ 1 ], [ 2, 3, 1 ], [ 4, 5, 6 ])"); + assertEqual(expected, actual); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test intersect function +//////////////////////////////////////////////////////////////////////////////// + + testIntersection5 : function () { + var expected = [ [ 2, 4 ] ]; + var actual = getQueryResults("RETURN INTERSECTION([ 1, 3, 2, 4 ], [ 2, 3, 1, 4 ], [ 4, 5, 6, 2 ])"); + assertEqual(expected, actual); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test intersect function +//////////////////////////////////////////////////////////////////////////////// + + testIntersection6 : function () { + var expected = [ [ ] ]; + var actual = getQueryResults("RETURN INTERSECTION([ [ 1, 2 ] ], [ 2, 1 ])"); + assertEqual(expected, actual); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test intersect function +//////////////////////////////////////////////////////////////////////////////// + + testIntersection7 : function () { + var expected = [ [ ] ]; + var actual = getQueryResults("RETURN INTERSECTION([ [ 1, 2 ] ], [ 1, 2 ])"); + assertEqual(expected, actual); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test intersect function +//////////////////////////////////////////////////////////////////////////////// + + testIntersection8 : function () { + var expected = [ [ [ 1, 2 ] ] ]; + var actual = getQueryResults("RETURN INTERSECTION([ [ 1, 2 ] ], [ [ 1, 2 ] ])"); + assertEqual(expected, actual); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test intersect function +//////////////////////////////////////////////////////////////////////////////// + + testIntersection9 : function () { + var expected = [ [ { foo: 'test' } ] ]; + var actual = getQueryResults("RETURN INTERSECTION([ { foo: 'bar' }, { foo: 'test' } ], [ { foo: 'test' } ])"); + assertEqual(expected, actual); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test intersect function +//////////////////////////////////////////////////////////////////////////////// + + testIntersection10 : function () { + var expected = [ [ 2, 4, 5 ] ]; + var actual = getQueryResults("RETURN INTERSECTION([ 1, 2, 3, 3, 4, 4, 5, 1 ], [ 2, 4, 5 ])"); + assertEqual(expected, actual); + }, + //////////////////////////////////////////////////////////////////////////////// /// @brief test document function ////////////////////////////////////////////////////////////////////////////////