/*jshint globalstrict:false, strict:false, maxlen: 500 */ /*global assertTrue, assertEqual, AQL_EXECUTE, AQL_EXPLAIN */ //////////////////////////////////////////////////////////////////////////////// /// @brief tests for single operation nodes in cluster /// /// @file /// /// DISCLAIMER /// /// Copyright 2010-2012 triagens GmbH, Cologne, Germany /// /// Licensed under the Apache License, Version 2.0 (the "License"); /// you may not use this file except in compliance with the License. /// You may obtain a copy of the License at /// /// http://www.apache.org/licenses/LICENSE-2.0 /// /// Unless required by applicable law or agreed to in writing, software /// distributed under the License is distributed on an "AS IS" BASIS, /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. /// See the License for the specific language governing permissions and /// limitations under the License. /// /// Copyright holder is ArangoDB GmbH, Cologne, Germany /// /// @author Wilfried Goesgens /// @author Copyright 2018, ArangoDB GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// var jsunity = require("jsunity"); var internal = require("internal"); var errors = internal.errors; var db = require("@arangodb").db; var helper = require("@arangodb/aql-helper"); var assertQueryError = helper.assertQueryError; const isCluster = require("@arangodb/cluster").isCluster(); //////////////////////////////////////////////////////////////////////////////// /// @brief test suite //////////////////////////////////////////////////////////////////////////////// function optimizerClusterSingleDocumentTestSuite () { var ruleName = "optimize-cluster-single-document-operations"; // various choices to control the optimizer: var thisRuleEnabled = { optimizer: { rules: [ "+all" ] } }; // we can only work after other rules var thisRuleDisabled = { optimizer: { rules: [ "+all", "-" + ruleName ] } }; var notHereDoc = "notHereDoc"; var yeOldeDoc = "yeOldeDoc"; var cn1 = "UnitTestsCollection"; var c1; var cn2 = "UnitTestsCollectionEmty"; var c2; var cn3 = "UnitTestsCollectionModify"; var c3; var s = function() {}; var setupC1 = function() { db._drop(cn1); c1 = db._create(cn1, { numberOfShards: 5 }); for (var i = 0; i < 20; ++i) { c1.save({ _key: `${i}`, group: "test" + (i % 10), value1: i, value2: i % 5 }); } }; var setupC2 = function() { db._drop(cn2); c2 = db._create(cn2, { numberOfShards: 5 }); c2.save({_key: yeOldeDoc}); }; var setupC3 = function() { db._drop(cn3); c3 = db._create(cn3, { numberOfShards: 5 }); for (var i = 0; i < 20; ++i) { c3.save({ _key: `${i}`, group: "test" + (i % 10), value1: i, value2: i % 5 }); } }; var pruneRevisions = function(obj) { if (typeof obj instanceof Array) { obj.forEach(function (doc) { pruneRevisions(doc);}); } else { if ((obj !== null) && (typeof obj !== "string")) { if (obj instanceof Object) { if (obj.hasOwnProperty('_rev')) { obj._rev = "wedontcare"; } for (var property in obj) { if (!obj.hasOwnProperty(property)) continue; pruneRevisions(obj[property]); } } } } }; var explain = function (result) { return helper.getCompactPlan(result).map(function(node) { return node.type; }); }; var runTestSet = function(sets, expectedRules, expectedNodes) { let count = 0; sets.forEach(function(set) { let queryString = set[0]; let expectRule = expectedRules[set[1]]; let expectNode = expectedNodes[set[2]]; let doFullTest = set[3]; // Do advanced checking let setupFunction = set[4]; // this resets the setup let errorCode = set[5]; // expected error code const queryInfo = "count: " + count + " query info: " + JSON.stringify(set); var result = AQL_EXPLAIN(queryString, { }, thisRuleEnabled); // explain - only assertEqual(expectRule, result.plan.rules, "rules mismatch: " + queryInfo); assertEqual(expectNode, explain(result), "nodes mismatch: " + queryInfo); if (doFullTest) { var r1 = {json: []}, r2 = {json: []}; // run it first without the rule setupFunction(); try { r1 = AQL_EXECUTE(queryString, {}, thisRuleDisabled); assertEqual(0, errorCode, "we have no error in the original, but the tests expects an exception: " + queryInfo); } catch (y) { assertTrue(errorCode.hasOwnProperty('code'), "original plan throws, but we don't expect an exception" + JSON.stringify(y) + queryInfo); assertEqual(y.errorNum, errorCode.code, "match other error code - got: " + JSON.stringify(y) + queryInfo); } // Run it again with our rule setupFunction(); try { r2 = AQL_EXECUTE(queryString, {}, thisRuleEnabled); assertEqual(0, errorCode, "we have no error in our plan, but the tests expects an exception" + queryInfo); } catch (x) { assertTrue(errorCode.hasOwnProperty('code'), "our plan throws, but we don't expect an exception" + JSON.stringify(x) + queryInfo); assertEqual(x.errorNum, errorCode.code, "match our error code" + JSON.stringify(x) + queryInfo); } pruneRevisions(r1); pruneRevisions(r2); assertEqual(r1.json, r2.json, "results of with and without rule differ: " + queryInfo); } count += 1; }); }; return { setUp : function () { setupC1(); setupC2(); setupC3(); }, tearDown : function () { db._drop(cn1); db._drop(cn2); db._drop(cn3); }, //////////////////////////////////////////////////////////////////////////////// /// @brief test plans that should result //////////////////////////////////////////////////////////////////////////////// testRuleFetch : function () { var queries = [ [ "FOR d IN " + cn1 + " FILTER d._key == '1' RETURN d", 0, 0, true, s, 0], [ "FOR d IN " + cn1 + " FILTER '1' == d._key RETURN d", 0, 0, true, s, 0], [ "FOR d IN " + cn1 + " FILTER d.xyz == '1' RETURN d", 1, 1, false, s, 0], [ "FOR d IN " + cn1 + " FILTER d._key == '1' RETURN 123", 2, 2, true, s, 0], [ "FOR d IN " + cn1 + " FILTER d._key == '1' LIMIT 10, 1 RETURN d", 3, 3, false, s, 0], [ "FOR d IN " + cn1 + " FILTER d._key == '1' RETURN d._key", 4, 4, true, s, 0], ]; var expectedRules = [[ "use-indexes", "remove-filter-covered-by-index", "remove-unnecessary-calculations-2", "optimize-cluster-single-document-operations" ], [ "scatter-in-cluster", "distribute-filtercalc-to-cluster", "remove-unnecessary-remote-scatter" ], [ "move-calculations-up", "use-indexes", "remove-filter-covered-by-index", "remove-unnecessary-calculations-2", "optimize-cluster-single-document-operations" ], [ "use-indexes", "remove-filter-covered-by-index", "remove-unnecessary-calculations-2", "scatter-in-cluster", "remove-unnecessary-remote-scatter", "restrict-to-single-shard"], [ "move-calculations-up", "move-filters-up", "move-calculations-up-2", "move-filters-up-2", "use-indexes", "remove-filter-covered-by-index", "remove-unnecessary-calculations-2", "optimize-cluster-single-document-operations" ], ]; var expectedNodes = [ [ "SingletonNode", "SingleRemoteOperationNode", "ReturnNode" ], [ "SingletonNode", "EnumerateCollectionNode", "CalculationNode", "FilterNode", "RemoteNode", "GatherNode", "ReturnNode" ], [ "SingletonNode", "CalculationNode", "SingleRemoteOperationNode", "ReturnNode"], [ "SingletonNode", "IndexNode", "RemoteNode", "GatherNode", "LimitNode", "ReturnNode" ], [ "SingletonNode", "SingleRemoteOperationNode", "CalculationNode", "ReturnNode" ], ]; runTestSet(queries, expectedRules, expectedNodes); }, testRuleInsert : function () { var queries = [ // [ query, expectedRulesField, expectedNodesField, doFullTest, setupFunction, errorCode ] [ `INSERT {_key: '${notHereDoc}', insert1: true} IN ${cn2} OPTIONS {waitForSync: true, ignoreErrors:true}`, 0, 0, true, setupC2, 0 ], [ `INSERT {_key: '${yeOldeDoc}', insert1: true} IN ${cn2} OPTIONS {waitForSync: true, ignoreErrors:true}`, 0, 0, true, setupC2, 0 ], [ `INSERT {_key: '${notHereDoc}', insert1: true} IN ${cn2} OPTIONS {waitForSync: true, overwrite: true} RETURN OLD`, 0, 1, true, setupC2, 0 ], [ `INSERT {_key: '${yeOldeDoc}', insert1: true} IN ${cn2} OPTIONS {waitForSync: true, overwrite: true} RETURN OLD`, 0, 1, true, setupC2, 0 ], [ `INSERT {_key: '${notHereDoc}', insert1: true} IN ${cn2} OPTIONS {waitForSync: true, overwrite: true} RETURN NEW`, 0, 1, true, setupC2, 0 ], [ `INSERT {_key: '${yeOldeDoc}', insert1: true} IN ${cn2} OPTIONS {waitForSync: true, overwrite: true} RETURN NEW`, 0, 1, true, setupC2, 0 ], [ `INSERT {_key: '${notHereDoc}', insert1: true} IN ${cn2} OPTIONS {waitForSync: true, overwrite: false} RETURN NEW`, 0, 1, true, setupC2, 0 ], [ `INSERT {_key: '${yeOldeDoc}', insert1: true} IN ${cn2} OPTIONS {waitForSync: true, overwrite: false} RETURN NEW`, 0, 1, true, setupC2, errors.ERROR_ARANGO_UNIQUE_CONSTRAINT_VIOLATED], [ `INSERT {_key: '${yeOldeDoc}', insert1: true} IN ${cn2} OPTIONS {waitForSync: true, overwrite: true} RETURN [OLD, NEW]`, 1, 2, true, setupC2, 0 ], [ `INSERT {_key: '${yeOldeDoc}', insert1: true} IN ${cn2} OPTIONS {waitForSync: true, overwrite: true} RETURN { old: OLD, new: NEW }`, 1, 2, true, setupC2, 0 ], [ `LET a = { a: 123 } INSERT {_key: '${notHereDoc}', insert1: true} IN ${cn2} OPTIONS {waitForSync: true, overwrite: true} RETURN [OLD, NEW, a]`, 2, 2, true, setupC2, 0 ], [ `LET a = { a: 123 } INSERT {_key: '${yeOldeDoc}', insert1: true} IN ${cn2} OPTIONS {waitForSync: true, overwrite: true} RETURN { old: OLD, new: NEW, a: a }`, 2, 2, true, setupC2, 0 ], [ `LET a = { a: 123 } INSERT {_key: '${notHereDoc}', insert1: true} IN ${cn2} OPTIONS {waitForSync: true, overwrite: true} RETURN a`, 3, 3, true, setupC2, 0 ], [ `INSERT {_key: '${notHereDoc}', insert1: true} IN ${cn2} RETURN NEW`, 0, 1, true, setupC2, 0 ], [ `INSERT {_key: '${notHereDoc}', insert1: true} IN ${cn2} RETURN NEW._key`, 0, 2, true, setupC2, 0 ], ]; var expectedRules = [ [ "remove-data-modification-out-variables", "optimize-cluster-single-document-operations" ], [ "optimize-cluster-single-document-operations" ], [ "move-calculations-up", "remove-unnecessary-calculations", "optimize-cluster-single-document-operations" ], [ "move-calculations-up", "remove-redundant-calculations", "remove-unnecessary-calculations", "move-calculations-up-2", "remove-data-modification-out-variables", "optimize-cluster-single-document-operations" ] ]; var expectedNodes = [ [ "SingletonNode", "CalculationNode", "SingleRemoteOperationNode" ], [ "SingletonNode", "CalculationNode", "SingleRemoteOperationNode", "ReturnNode" ], [ "SingletonNode", "CalculationNode", "SingleRemoteOperationNode", "CalculationNode", "ReturnNode" ], [ "SingletonNode", "CalculationNode", "CalculationNode", "SingleRemoteOperationNode", "ReturnNode" ] ]; runTestSet(queries, expectedRules, expectedNodes); }, testRuleUpdate : function () { var queries = [ [ "UPDATE {_key: '1'} IN " + cn1 + " OPTIONS {}", 0, 0, true, s, 0], [ "UPDATE {_key: '1'} INTO " + cn1 + " OPTIONS {}", 0, 0, true, s, 0], [ "UPDATE {_key: '1'} IN " + cn1 + " OPTIONS {} RETURN OLD", 0, 1, true, s, 0], [ "UPDATE {_key: '1'} INTO " + cn1 + " OPTIONS {} RETURN OLD", 0, 1, true, s, 0], [ "UPDATE {_key: '1'} IN " + cn1 + " OPTIONS {} RETURN NEW", 0, 1, true, s, 0], [ "UPDATE {_key: '1'} INTO " + cn1 + " OPTIONS {} RETURN NEW", 0, 1, true, s, 0], [ "UPDATE {_key: '1'} IN " + cn1 + " OPTIONS {} RETURN [OLD, NEW]", 3, 2, false], [ "UPDATE {_key: '1'} INTO " + cn1 + " OPTIONS {} RETURN [OLD, NEW]", 3, 2, false], [ "UPDATE {_key: '1'} IN " + cn1 + " OPTIONS {} RETURN { old: OLD, new: NEW }", 3, 2, false], [ "UPDATE {_key: '1'} INTO " + cn1 + " OPTIONS {} RETURN { old: OLD, new: NEW }", 3, 2, false], [ "UPDATE {_key: '1', boom: true } INTO " + cn1 + " OPTIONS {} RETURN { old: OLD, new: NEW }", 3, 2, true, setupC1, 0], [ "UPDATE {_key: '1'} WITH {foo: 'bar1a'} IN " + cn1 + " OPTIONS {}", 1, 0, true, s, 0], [ "UPDATE {_key: '1'} WITH {foo: 'bar2a'} IN " + cn1 + " OPTIONS {} RETURN OLD", 1, 1, true, setupC1, 0], [ "UPDATE {_key: '1'} WITH {foo: 'bar3a'} IN " + cn1 + " OPTIONS {} RETURN NEW", 1, 1, true, s, 0], [ "UPDATE {_key: '1'} WITH {foo: 'bar4a'} IN " + cn1 + " OPTIONS {} RETURN [OLD, NEW]", 2, 2, true, setupC1, 0], [ "UPDATE {_key: '1'} WITH {foo: 'bar5a'} IN " + cn1 + " OPTIONS {} RETURN { old: OLD, new: NEW }", 2, 2, true, setupC1, 0], [ "UPDATE {_key: '1'} INTO " + cn1 + " RETURN OLD._key", 0, 2, true, s, 0], [ `FOR doc IN ${cn1} FILTER doc._key == '1' UPDATE doc INTO ${cn1} OPTIONS {} RETURN NEW`, 4, 3, true, s, 0], [ `FOR doc IN ${cn1} FILTER doc._key == '1' UPDATE doc WITH {foo: 'bar'} INTO ${cn1} OPTIONS {} RETURN [OLD, NEW]`, 8, 2, true, setupC1, 0], [ `FOR doc IN ${cn1} FILTER doc._key == '${notHereDoc}' UPDATE doc WITH {foo: 'bar'} INTO ${cn1} OPTIONS {} RETURN [OLD, NEW]`, 8, 2, true, setupC1, 0], [ `LET a = { a: 123 } FOR doc IN ${cn1} FILTER doc._key == '1' UPDATE doc INTO ${cn1} OPTIONS {} RETURN { NEW: NEW, a: a }`, 6, 4, true, s, 0], [ `LET a = { a: 123 } FOR doc IN ${cn1} FILTER doc._key == '1' UPDATE doc WITH {foo: 'bar'} INTO ${cn1} OPTIONS {} RETURN [OLD, NEW, a]`, 9, 2, true, setupC1, 0], [ `LET a = { a: 123 } FOR doc IN ${cn1} FILTER doc._key == '1' UPDATE doc INTO ${cn1} OPTIONS {} RETURN [ NEW, a ]`, 6, 4, true, s, 0], [ `LET a = { a: 123 } FOR doc IN ${cn1} FILTER doc._key == '1' UPDATE doc WITH {foo: 'bar'} INTO ${cn1} OPTIONS {} RETURN [OLD, NEW, a]`, 9, 2, true, setupC1, 0], [ `LET a = { a: 123 } FOR doc IN ${cn1} FILTER doc._key == '1' UPDATE doc WITH {foo: 'bar'} INTO ${cn1} OPTIONS {} RETURN [doc, NEW, a]`, 9, 2, true, setupC1, 0], [ `FOR doc IN ${cn1} FILTER doc._key == '1' UPDATE doc WITH {foo: 'bar'} INTO ${cn1} RETURN OLD._key`, 8, 2, true, setupC1, 0], ]; var expectedRules = [ [ "remove-data-modification-out-variables", "optimize-cluster-single-document-operations" ], [ "move-calculations-up", "move-calculations-up-2", "remove-data-modification-out-variables", "optimize-cluster-single-document-operations" ], [ "move-calculations-up", "move-calculations-up-2", "optimize-cluster-single-document-operations" ], [ "optimize-cluster-single-document-operations" ], [ "remove-data-modification-out-variables", "use-indexes", "remove-filter-covered-by-index", "remove-unnecessary-calculations-2", "optimize-cluster-single-document-operations" ], [ "move-calculations-up", "use-indexes", "remove-filter-covered-by-index", "remove-unnecessary-calculations-2", "optimize-cluster-single-document-operations" ], [ "remove-unnecessary-calculations", "remove-data-modification-out-variables", "use-indexes", "remove-filter-covered-by-index", "remove-unnecessary-calculations-2", "optimize-cluster-single-document-operations" ], [ "move-calculations-up", "remove-unnecessary-calculations", "use-indexes", "remove-filter-covered-by-index", "remove-unnecessary-calculations-2", "optimize-cluster-single-document-operations" ], [ "move-calculations-up", "remove-data-modification-out-variables", "use-indexes", "remove-filter-covered-by-index", "remove-unnecessary-calculations-2", "optimize-cluster-single-document-operations" ], [ "move-calculations-up", "remove-unnecessary-calculations", "remove-data-modification-out-variables", "use-indexes", "remove-filter-covered-by-index", "remove-unnecessary-calculations-2", "optimize-cluster-single-document-operations" ], ]; var expectedNodes = [ [ "SingletonNode", "CalculationNode", "SingleRemoteOperationNode" ], [ "SingletonNode", "CalculationNode", "SingleRemoteOperationNode", "ReturnNode" ], [ "SingletonNode", "CalculationNode", "SingleRemoteOperationNode", "CalculationNode", "ReturnNode" ], [ "SingletonNode", "SingleRemoteOperationNode", "ReturnNode" ], [ "SingletonNode", "SingleRemoteOperationNode", "CalculationNode", "ReturnNode" ] ]; runTestSet(queries, expectedRules, expectedNodes); }, testRuleReplace : function () { var queries = [ [ "REPLACE {_key: '1'} IN " + cn1 + " OPTIONS {}", 0, 0, true, s, 0], [ "REPLACE {_key: '1'} INTO " + cn1 + " OPTIONS {}", 0, 0, true, s, 0], [ "REPLACE {_key: '1'} IN " + cn1 + " OPTIONS {} RETURN OLD", 0, 1, true, s, 0], [ "REPLACE {_key: '1'} INTO " + cn1 + " OPTIONS {} RETURN OLD", 0, 1, true, s, 0], [ "REPLACE {_key: '1'} IN " + cn1 + " OPTIONS {} RETURN NEW", 0, 1, true, s, 0], [ "REPLACE {_key: '1'} INTO " + cn1 + " OPTIONS {} RETURN NEW", 0, 1, true, s, 0], [ "REPLACE {_key: '1'} IN " + cn1 + " OPTIONS {} RETURN [OLD, NEW]", 3, 2, false], [ "REPLACE {_key: '1'} INTO " + cn1 + " OPTIONS {} RETURN [OLD, NEW]", 3, 2, false], [ "REPLACE {_key: '1'} IN " + cn1 + " OPTIONS {} RETURN { old: OLD, new: NEW }", 3, 2, false], [ "REPLACE {_key: '1'} INTO " + cn1 + " OPTIONS {} RETURN { old: OLD, new: NEW }", 3, 2, false], [ "REPLACE {_key: '1'} WITH {foo: 'bar1a'} IN " + cn1 + " OPTIONS {}", 1, 0, true, s, 0], [ "REPLACE {_key: '1'} WITH {foo: 'bar2a'} IN " + cn1 + " OPTIONS {} RETURN OLD", 1, 1, true, setupC1, 0], [ "REPLACE {_key: '1'} WITH {foo: 'bar2a'} IN " + cn1 + " OPTIONS {} RETURN OLD._key", 10, 2, true, setupC1, 0], [ "REPLACE {_key: '1'} WITH {foo: 'bar3a'} IN " + cn1 + " OPTIONS {} RETURN NEW", 1, 1, true, s, 0], [ "REPLACE {_key: '1'} WITH {foo: 'bar4a'} IN " + cn1 + " OPTIONS {} RETURN [OLD, NEW]", 2, 2, true, setupC1, 0], [ "REPLACE {_key: '1', boom: true } IN " + cn1 + " OPTIONS {} RETURN [OLD, NEW]", 3, 2, true, setupC1, 0], [ "REPLACE {_key: '1'} WITH {foo: 'bar5a'} IN " + cn1 + " OPTIONS {} RETURN { old: OLD, new: NEW }", 2, 2, true, setupC1, 0], [ `FOR doc IN ${cn1} FILTER doc._key == '1' REPLACE doc WITH {foo: 'bar'} INTO ${cn1} OPTIONS {} RETURN [OLD, NEW]`, 8, 2, true, setupC1, 0], [ `FOR doc IN ${cn1} FILTER doc._key == '1' REPLACE doc INTO ${cn1} OPTIONS {} RETURN NEW`, 4, 3, true, setupC1, 0], [ `LET a = 123 FOR doc IN ${cn1} FILTER doc._key == '-1' REPLACE doc WITH {foo: 'bar'} INTO ${cn1} OPTIONS {} RETURN [OLD, NEW, a]`, 9, 2, true, setupC1, 0 ], [ `LET a = 123 FOR doc IN ${cn1} FILTER doc._key == '1' REPLACE doc WITH {foo: 'bar'} INTO ${cn1} OPTIONS {} RETURN [OLD, NEW, a]`, 9, 2, true, setupC1, 0], [ `LET a = 123 FOR doc IN ${cn1} FILTER doc._key == '1' REPLACE doc WITH {foo: 'bar'} INTO ${cn1} OPTIONS {} RETURN [doc, NEW, a]`, 9, 2, true, setupC1, 0], [ `LET a = 123 FOR doc IN ${cn1} FILTER doc._key == '1' REPLACE doc INTO ${cn1} OPTIONS {} RETURN [ NEW, a ]`, 7, 4, true, setupC1, 0], [ `FOR doc IN ${cn1} FILTER doc._key == '1' REPLACE doc INTO ${cn1} RETURN OLD._key`, 4, 4, true, setupC1, 0], ]; var expectedRules = [ [ "remove-data-modification-out-variables", "optimize-cluster-single-document-operations" ], [ "move-calculations-up", "move-calculations-up-2", "remove-data-modification-out-variables", "optimize-cluster-single-document-operations" ], [ "move-calculations-up", "move-calculations-up-2", "optimize-cluster-single-document-operations" ], [ "optimize-cluster-single-document-operations" ], [ "remove-data-modification-out-variables", "use-indexes", "remove-filter-covered-by-index", "remove-unnecessary-calculations-2", "optimize-cluster-single-document-operations" ], [ "move-calculations-up", "use-indexes", "remove-filter-covered-by-index", "remove-unnecessary-calculations-2", "optimize-cluster-single-document-operations" ], [ "move-calculations-up", "remove-unnecessary-calculations", "use-indexes", "remove-filter-covered-by-index", "remove-unnecessary-calculations-2", "optimize-cluster-single-document-operations" ], [ "remove-unnecessary-calculations", "remove-data-modification-out-variables", "use-indexes", "remove-filter-covered-by-index", "remove-unnecessary-calculations-2", "optimize-cluster-single-document-operations" ], [ "move-calculations-up", "remove-data-modification-out-variables", "use-indexes", "remove-filter-covered-by-index", "remove-unnecessary-calculations-2", "optimize-cluster-single-document-operations" ], [ "move-calculations-up", "remove-unnecessary-calculations", "remove-data-modification-out-variables", "use-indexes", "remove-filter-covered-by-index", "remove-unnecessary-calculations-2", "optimize-cluster-single-document-operations" ], [ "move-calculations-up", "move-calculations-up-2", "remove-data-modification-out-variables", "optimize-cluster-single-document-operations" ], ]; var expectedNodes = [ [ "SingletonNode", "CalculationNode", "SingleRemoteOperationNode" ], [ "SingletonNode", "CalculationNode", "SingleRemoteOperationNode", "ReturnNode" ], [ "SingletonNode", "CalculationNode", "SingleRemoteOperationNode", "CalculationNode", "ReturnNode"], [ "SingletonNode", "SingleRemoteOperationNode", "ReturnNode" ], [ "SingletonNode", "SingleRemoteOperationNode", "CalculationNode", "ReturnNode" ] ]; runTestSet(queries, expectedRules, expectedNodes); }, testRuleRemove : function () { var queries = [ [ "REMOVE {_key: '1'} IN " + cn1 + " OPTIONS {}", 0, 0, true, setupC1, 0], [ "REMOVE {_key: '2'} INTO " + cn1 + " OPTIONS {}", 0, 0, true, setupC1, 0], [ "REMOVE {_key: '3'} IN " + cn1 + " OPTIONS {} RETURN OLD", 0, 1, true, setupC1, 0], [ "REMOVE {_key: '4'} INTO " + cn1 + " OPTIONS {} RETURN OLD", 0, 1, true, setupC1, 0], [ "REMOVE {_key: '4'} INTO " + cn1 + " RETURN OLD._key", 0, 2, true, setupC1, 0], [ `FOR doc IN ${cn1} FILTER doc._key == '1' REMOVE doc IN ${cn1} RETURN OLD`, 1, 1, true, setupC1, 0], [ `FOR doc IN ${cn1} FILTER doc._key == 'notheredoc' REMOVE doc IN ${cn1} RETURN OLD`, 1, 1, true, s, 0], [ `LET a = 123 FOR doc IN ${cn1} FILTER doc._key == '1' REMOVE doc IN ${cn1} RETURN [OLD, a]`, 2, 2, true, setupC1, 0], [ `LET a = 123 FOR doc IN ${cn1} FILTER doc._key == 'notheredoc' REMOVE doc IN ${cn1} RETURN [OLD, a]`, 2, 2, true, s, 0], [ `LET a = 123 FOR doc IN ${cn1} FILTER doc._key == 'notheredoc' REMOVE doc IN ${cn1} RETURN a`, 3, 3, true, s, 0], [ `LET a = 123 FOR doc IN ${cn1} FILTER doc._key == 'notheredoc' REMOVE doc IN ${cn1} RETURN doc`, 4, 1, true, s, 0], [ `LET a = 123 FOR doc IN ${cn1} FILTER doc._key == 'notheredoc' REMOVE doc IN ${cn1} RETURN OLD`, 2, 1, true, s, 0], [ `LET a = 123 FOR doc IN ${cn1} FILTER doc._key == 'notheredoc' REMOVE doc IN ${cn1} RETURN OLD._key`, 4, 2, true, s, 0], [ `FOR doc IN ${cn1} FILTER doc._key == 'notheredoc' REMOVE doc IN ${cn1} RETURN OLD._key`, 1, 2, true, s, 0], ]; var expectedRules = [ [ "remove-data-modification-out-variables", "optimize-cluster-single-document-operations" ], [ "remove-data-modification-out-variables", "use-indexes", "remove-filter-covered-by-index", "remove-unnecessary-calculations-2", "optimize-cluster-single-document-operations" ], [ "remove-unnecessary-calculations", "remove-data-modification-out-variables", "use-indexes", "remove-filter-covered-by-index", "remove-unnecessary-calculations-2", "optimize-cluster-single-document-operations" ], [ "move-calculations-up", "remove-redundant-calculations", "remove-unnecessary-calculations", "remove-data-modification-out-variables", "use-indexes", "remove-filter-covered-by-index", "remove-unnecessary-calculations-2", "optimize-cluster-single-document-operations" ], [ "remove-unnecessary-calculations", "remove-data-modification-out-variables", "use-indexes", "remove-filter-covered-by-index", "remove-unnecessary-calculations-2", "optimize-cluster-single-document-operations" ], ]; var expectedNodes = [ [ "SingletonNode", "SingleRemoteOperationNode" ], [ "SingletonNode", "SingleRemoteOperationNode", "ReturnNode" ], [ "SingletonNode", "SingleRemoteOperationNode", "CalculationNode", "ReturnNode" ], [ "SingletonNode", "CalculationNode", "SingleRemoteOperationNode", "ReturnNode" ], ]; runTestSet(queries, expectedRules, expectedNodes); } }; } jsunity.run(optimizerClusterSingleDocumentTestSuite); return jsunity.done();