diff --git a/CHANGELOG b/CHANGELOG index 03794f086d..0976b35278 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,8 @@ v1.2.alpha (XXXX-XX-XX) ----------------------- +* added AQL function MATCHES() to check a document against a list of examples + * added documentation and tests for db.collection.removeByExample * added --progress option for arangoimp. This will show the percentage of the input diff --git a/arangod/Ahuacatl/ahuacatl-functions.c b/arangod/Ahuacatl/ahuacatl-functions.c index b9e09d2665..cc44b76d00 100644 --- a/arangod/Ahuacatl/ahuacatl-functions.c +++ b/arangod/Ahuacatl/ahuacatl-functions.c @@ -615,7 +615,7 @@ TRI_associative_pointer_t* TRI_InitialiseFunctionsAql (void) { REGISTER_FUNCTION("MERGE", "MERGE", true, false, "a,a|+", NULL); REGISTER_FUNCTION("MERGE_RECURSIVE", "MERGE_RECURSIVE", true, false, "a,a|+", NULL); REGISTER_FUNCTION("DOCUMENT", "DOCUMENT", false, false, "h,sl", NULL); - REGISTER_FUNCTION("MATCHES", "MATCHES", true, false, ".,l", NULL); + REGISTER_FUNCTION("MATCHES", "MATCHES", true, false, ".,l|b", NULL); // geo functions REGISTER_FUNCTION("NEAR", "GEO_NEAR", false, false, "h,n,n,n|s", NULL); diff --git a/arangod/Documentation/aql.dox b/arangod/Documentation/aql.dox index a32611ddd6..ec9ad0ae2f 100644 --- a/arangod/Documentation/aql.dox +++ b/arangod/Documentation/aql.dox @@ -854,6 +854,32 @@ /// /// AQL supports the following functions to operate on document values: /// +/// - @FN{MATCHES(@FA{document}\, @FA{examples}\, @FA{return-index})}: compares the document +/// @FA{document} against each example document provided in the list @FA{examples}. +/// If @FA{document} matches one of the examples, @LIT{true} is returned, and if there is +/// no match @LIT{false} will be returned. The default return value type can be changed by +/// passing @LIT{true} as the third function parameter @FA{return-index}. Setting this +/// flag will return the index of the example that matched (starting at offset 0), or +/// @LIT{\-1} if there was no match. +/// +/// The comparisons will be started with the first example. All attributes of the example +/// will be compared against the attributes of @FA{document}. If all attributes match, the +/// comparison stops and the result is returned. If there is a mismatch, the function will +/// continue the comparison with the next example until there are no more examples left. +/// +/// The @FA{examples} must be a list of 0..n example documents, with any number of attributes +/// each. +/// Example usage: +/// @code +/// RETURN MATCHES({ "test" : 1 }, [ +/// { "test" : 1, "foo" : "bar" }, +/// { "foo" : 1 }, +/// { "test : 1 } +/// ], true) +/// @endcode +/// This will return @LIT{2}, because the third example matches, and because the +/// @LIT{return-index} flag is set to @LIT{true}. +/// /// - @FN{MERGE(@FA{document1}\, @FA{document2}\, ... @FA{documentn})}: merges the documents /// in @FA{document1} to @FA{documentn} into a single document. If document attribute /// keys are ambiguous, the merged result will contain the values of the documents diff --git a/js/server/ahuacatl.js b/js/server/ahuacatl.js index 6a92af387e..ae55aece3a 100644 --- a/js/server/ahuacatl.js +++ b/js/server/ahuacatl.js @@ -2581,7 +2581,8 @@ function AHUACATL_MERGE_RECURSIVE () { //////////////////////////////////////////////////////////////////////////////// /// @brief compare an object against a list of examples and return whether the -/// object matches at least one of the examples +/// object matches any of the examples. returns the example index or a bool, +/// depending on the value of the control flag (3rd) parameter //////////////////////////////////////////////////////////////////////////////// function AHUACATL_MATCHES () { @@ -2596,6 +2597,8 @@ function AHUACATL_MATCHES () { examples = [ examples ]; } + var returnIndex = arguments[2] || false; + for (var i = 0; i < examples.length; ++i) { var example = examples[i]; var result = true; @@ -2615,11 +2618,12 @@ function AHUACATL_MATCHES () { } if (result) { - return true; + // 3rd parameter determines whether we return the index or a bool flag + return (returnIndex ? i : true); } } - return false; + return (returnIndex ? -1 : false); } //////////////////////////////////////////////////////////////////////////////// diff --git a/js/server/tests/ahuacatl-functions.js b/js/server/tests/ahuacatl-functions.js index 167d505ef0..00cefef40d 100644 --- a/js/server/tests/ahuacatl-functions.js +++ b/js/server/tests/ahuacatl-functions.js @@ -1680,6 +1680,111 @@ function ahuacatlFunctionsTestSuite () { assertEqual(expected, actual); }, +//////////////////////////////////////////////////////////////////////////////// +/// @brief test matches +//////////////////////////////////////////////////////////////////////////////// + + testMatches : function () { + var tests = [ + { + doc: { test1: 1, test2: 2 }, + examples: [ ], + flag: true, + expected: [ -1 ] + }, + { + doc: { test1: 1, test2: 2 }, + examples: [ ], + flag: false, + expected: [ false ] + }, + { + doc: { test1: 1, test2: 2 }, + examples: [ ], + flag: null, + expected: [ false ] + }, + { + doc: { test1: 1, test2: 2 }, + examples: [ { test1: 1, test2: 1 } ], + flag: true, + expected: [ -1 ] + }, + { + doc: { test1: 1, test2: 2 }, + examples: [ { test1: 1, test2: 1 } ], + flag: false, + expected: [ false ] + }, + { + doc: { test1: 1, test2: 2 }, + examples: [ { test1: 1, test2: 2 } ], + flag: true, + expected: [ 0 ] + }, + { + doc: { test1: 1, test2: 2 }, + examples: [ { test1: 1, test2: 2 } ], + flag: false, + expected: [ true ] + }, + { + doc: { test1: 1, test2: 2 }, + examples: [ { test1: 1, test2: 3 }, { test1: 1, test2: 2 } ], + flag: true, + expected: [ 1 ] + }, + { + doc: { test1: 1, test2: 2 }, + examples: [ { test1: 1, test2: 3 }, { test1: 1, test2: 2 } ], + flag: false, + expected: [ true ] + }, + { + doc: { test1: 1, test2: 2 }, + examples: [ { test1: 1, test2: 3 }, { test1: 1, test2: 2 } ], + flag: null, + expected: [ true ] + }, + { + doc: { test1: 1, test2: 2 }, + examples: [ { } ], + flag: true, + expected: [ 0 ] + }, + { + doc: { test1: 1, test2: 2 }, + examples: [ { } ], + flag: false, + expected: [ true ] + }, + { + doc: { test1: 1, test2: 2 }, + examples: [ { fox: true }, { fox: false}, { test1: "something" }, { test99: 1, test2: 2 }, { test1: "1", test2: "2" } ], + flag: true, + expected: [ -1 ] + }, + { + doc: { test1: 1, test2: 2 }, + examples: [ { fox: true }, { fox: false}, { test1: "something" }, { test99: 1, test2: 2 }, { test1: "1", test2: "2" }, { } ], + flag: true, + expected: [ 5 ] + } + ] + + tests.forEach(function (data) { + var query = "RETURN MATCHES(" + JSON.stringify(data.doc) + ", " + JSON.stringify(data.examples); + if (data.flag != null) { + query += ", " + JSON.stringify(data.flag) + ")"; + } + else { + query += ")"; + } + var actual = getQueryResults(query, true); + assertEqual(data.expected, actual); + }); + }, + //////////////////////////////////////////////////////////////////////////////// /// @brief test to_bool ////////////////////////////////////////////////////////////////////////////////