mirror of https://gitee.com/bigwinds/arangodb
allow AQL to use indexes in additional cases
This commit is contained in:
parent
75d62962a6
commit
5da08eb283
|
@ -1618,6 +1618,7 @@ UNITTESTS_SERVER = $(addprefix --javascript.unit-tests ,$(SHELL_SERVER))
|
|||
################################################################################
|
||||
SHELL_SERVER_AHUACATL = @top_srcdir@/js/server/tests/ahuacatl-ranges.js \
|
||||
@top_srcdir@/js/server/tests/ahuacatl-queries-optimiser.js \
|
||||
@top_srcdir@/js/server/tests/ahuacatl-queries-optimiser-ref.js \
|
||||
@top_srcdir@/js/server/tests/ahuacatl-escaping.js \
|
||||
@top_srcdir@/js/server/tests/ahuacatl-functions.js \
|
||||
@top_srcdir@/js/server/tests/ahuacatl-variables.js \
|
||||
|
|
|
@ -242,6 +242,7 @@ unittests-shell-server:
|
|||
|
||||
SHELL_SERVER_AHUACATL = @top_srcdir@/js/server/tests/ahuacatl-ranges.js \
|
||||
@top_srcdir@/js/server/tests/ahuacatl-queries-optimiser.js \
|
||||
@top_srcdir@/js/server/tests/ahuacatl-queries-optimiser-ref.js \
|
||||
@top_srcdir@/js/server/tests/ahuacatl-escaping.js \
|
||||
@top_srcdir@/js/server/tests/ahuacatl-functions.js \
|
||||
@top_srcdir@/js/server/tests/ahuacatl-variables.js \
|
||||
|
|
|
@ -2032,22 +2032,31 @@ static TRI_vector_pointer_t* ProcessNode (TRI_aql_context_t* const context,
|
|||
node->_type == TRI_AQL_NODE_OPERATOR_BINARY_IN) {
|
||||
TRI_aql_node_t* lhs = TRI_AQL_NODE_MEMBER(node, 0);
|
||||
TRI_aql_node_t* rhs = TRI_AQL_NODE_MEMBER(node, 1);
|
||||
TRI_vector_pointer_t* previous;
|
||||
TRI_aql_attribute_name_t* field;
|
||||
TRI_aql_node_t* node1;
|
||||
TRI_aql_node_t* node2;
|
||||
TRI_aql_node_type_e operator;
|
||||
bool useBoth;
|
||||
|
||||
if (node->_type == TRI_AQL_NODE_OPERATOR_BINARY_IN && rhs->_type != TRI_AQL_NODE_LIST) {
|
||||
// in operator is special. if right operand is not a list, we must abort here
|
||||
return NULL;
|
||||
}
|
||||
|
||||
useBoth = false;
|
||||
|
||||
if ((lhs->_type == TRI_AQL_NODE_REFERENCE || lhs->_type == TRI_AQL_NODE_ATTRIBUTE_ACCESS) &&
|
||||
(TRI_IsConstantValueNodeAql(rhs) || rhs->_type == TRI_AQL_NODE_REFERENCE || rhs->_type == TRI_AQL_NODE_ATTRIBUTE_ACCESS)) {
|
||||
// collection.attribute|reference operator const value|reference|attribute access
|
||||
node1 = lhs;
|
||||
node2 = rhs;
|
||||
operator = node->_type;
|
||||
|
||||
if (rhs->_type == TRI_AQL_NODE_REFERENCE || rhs->_type == TRI_AQL_NODE_ATTRIBUTE_ACCESS) {
|
||||
// expression of type reference|attribute access operator reference|attribute access
|
||||
useBoth = true;
|
||||
}
|
||||
}
|
||||
else if ((rhs->_type == TRI_AQL_NODE_REFERENCE || rhs->_type == TRI_AQL_NODE_ATTRIBUTE_ACCESS) &&
|
||||
(TRI_IsConstantValueNodeAql(lhs) || lhs->_type == TRI_AQL_NODE_REFERENCE || lhs->_type == TRI_AQL_NODE_ATTRIBUTE_ACCESS)) {
|
||||
|
@ -2056,6 +2065,11 @@ static TRI_vector_pointer_t* ProcessNode (TRI_aql_context_t* const context,
|
|||
node2 = lhs;
|
||||
operator = TRI_ReverseOperatorRelationalAql(node->_type);
|
||||
assert(operator != TRI_AQL_NODE_NOP);
|
||||
|
||||
if (lhs->_type == TRI_AQL_NODE_REFERENCE || lhs->_type == TRI_AQL_NODE_ATTRIBUTE_ACCESS) {
|
||||
// expression of type reference|attribute access operator reference|attribute access
|
||||
useBoth = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return NULL;
|
||||
|
@ -2070,6 +2084,10 @@ static TRI_vector_pointer_t* ProcessNode (TRI_aql_context_t* const context,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
previous = NULL;
|
||||
|
||||
again:
|
||||
// we'll get back here for expressions of type a.x == b.y (where both sides are references)
|
||||
field = GetAttributeName(context, node1);
|
||||
|
||||
if (field) {
|
||||
|
@ -2082,7 +2100,7 @@ static TRI_vector_pointer_t* ProcessNode (TRI_aql_context_t* const context,
|
|||
result = MergeVectors(context,
|
||||
TRI_AQL_NODE_OPERATOR_BINARY_AND,
|
||||
Vectorize(context, attributeAccess),
|
||||
NULL,
|
||||
previous,
|
||||
inheritedRestrictions);
|
||||
|
||||
if (result == NULL) {
|
||||
|
@ -2090,7 +2108,7 @@ static TRI_vector_pointer_t* ProcessNode (TRI_aql_context_t* const context,
|
|||
}
|
||||
else {
|
||||
if (TRI_ContainsImpossibleAql(result)) {
|
||||
// inject a dummy false == true node into the true if the condition is always false
|
||||
// inject a dummy false == true node into the tree if the condition is always false
|
||||
node->_type = TRI_AQL_NODE_OPERATOR_BINARY_EQ;
|
||||
node->_members._buffer[0] = TRI_CreateNodeValueBoolAql(context, false);
|
||||
node->_members._buffer[1] = TRI_CreateNodeValueBoolAql(context, true);
|
||||
|
@ -2100,6 +2118,24 @@ static TRI_vector_pointer_t* ProcessNode (TRI_aql_context_t* const context,
|
|||
}
|
||||
}
|
||||
|
||||
if (useBoth) {
|
||||
// in this situation, we have an expression of type a.x == b.y
|
||||
// we'll have to process both sides of the expression
|
||||
TRI_aql_node_t* tempNode;
|
||||
|
||||
// swap node1 and node2
|
||||
tempNode = node1;
|
||||
node1 = node2;
|
||||
node2 = tempNode;
|
||||
|
||||
operator = TRI_ReverseOperatorRelationalAql(node->_type);
|
||||
|
||||
// and try again
|
||||
previous = result;
|
||||
useBoth = false;
|
||||
goto again;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,7 +60,7 @@ static void LogIndexString (const char* const what,
|
|||
TRI_AppendStringStringBuffer(buffer, idx->_fields._buffer[i]);
|
||||
}
|
||||
|
||||
LOG_TRACE("%s %s index (%s) for '%s'",
|
||||
LOG_DEBUG("%s %s index (%s) for '%s'",
|
||||
what,
|
||||
TRI_TypeNameIndex(idx),
|
||||
buffer->_buffer,
|
||||
|
@ -290,7 +290,7 @@ TRI_aql_index_t* TRI_DetermineIndexAql (TRI_aql_context_t* const context,
|
|||
// these indexes are valid candidates
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
LogIndexString("checking", idx, collectionName);
|
||||
|
||||
TRI_ClearVectorPointer(&matches);
|
||||
|
|
|
@ -0,0 +1,229 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief tests for query language, reference optimiser
|
||||
///
|
||||
/// @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 triAGENS GmbH, Cologne, Germany
|
||||
///
|
||||
/// @author Jan Steemann
|
||||
/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
var jsunity = require("jsunity");
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief test suite
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function ahuacatlQueryOptimiserRefTestSuite () {
|
||||
var users = null;
|
||||
var cn = "UnitTestsAhuacatlOptimiserRef";
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief execute a given query
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function executeQuery (query) {
|
||||
var cursor = AHUACATL_RUN(query, undefined);
|
||||
if (cursor instanceof ArangoError) {
|
||||
print(query, cursor.errorMessage);
|
||||
}
|
||||
assertFalse(cursor instanceof ArangoError);
|
||||
return cursor;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief execute a given query and return the results as an array
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function getQueryResults (query, isFlat) {
|
||||
var result = executeQuery(query).getRows();
|
||||
var results = [ ];
|
||||
|
||||
for (var i in result) {
|
||||
if (!result.hasOwnProperty(i)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var row = result[i];
|
||||
if (isFlat) {
|
||||
results.push(row);
|
||||
}
|
||||
else {
|
||||
var keys = [ ];
|
||||
for (var k in row) {
|
||||
if (row.hasOwnProperty(k) && k != '_id' && k != '_rev') {
|
||||
keys.push(k);
|
||||
}
|
||||
}
|
||||
|
||||
keys.sort();
|
||||
var resultRow = { };
|
||||
for (var k in keys) {
|
||||
if (keys.hasOwnProperty(k)) {
|
||||
resultRow[keys[k]] = row[keys[k]];
|
||||
}
|
||||
}
|
||||
results.push(resultRow);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief set up
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
setUp : function () {
|
||||
users = internal.db._create(cn);
|
||||
users.save({ "id" : 100, "name" : "John", "age" : 37, "active" : true, "gender" : "m" });
|
||||
users.save({ "id" : 101, "name" : "Fred", "age" : 36, "active" : true, "gender" : "m" });
|
||||
users.save({ "id" : 102, "name" : "Jacob", "age" : 35, "active" : false, "gender" : "m" });
|
||||
users.save({ "id" : 103, "name" : "Ethan", "age" : 34, "active" : false, "gender" : "m" });
|
||||
users.save({ "id" : 104, "name" : "Michael", "age" : 33, "active" : true, "gender" : "m" });
|
||||
users.save({ "id" : 105, "name" : "Alexander", "age" : 32, "active" : true, "gender" : "m" });
|
||||
users.save({ "id" : 106, "name" : "Daniel", "age" : 31, "active" : true, "gender" : "m" });
|
||||
users.save({ "id" : 107, "name" : "Anthony", "age" : 30, "active" : true, "gender" : "m" });
|
||||
users.save({ "id" : 108, "name" : "Jim", "age" : 29, "active" : true, "gender" : "m" });
|
||||
users.save({ "id" : 109, "name" : "Diego", "age" : 28, "active" : true, "gender" : "m" });
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief tear down
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
tearDown : function () {
|
||||
internal.db._drop(cn);
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief check a ref access without any indexes
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testRefAccess1 : function () {
|
||||
var expected = [ { "name" : "John" }, { "name" : "Fred" }, { "name" : "Jacob" }, { "name" : "Ethan" }, { "name" : "Michael" }, { "name" : "Alexander" }, { "name" : "Daniel" }, { "name" : "Anthony" }, { "name" : "Jim"} , { "name" : "Diego" } ];
|
||||
var actual = getQueryResults("FOR u1 IN " + cn + " FOR u2 IN " + cn + " FILTER u1.name == u2.name SORT u1.id RETURN { \"name\" : u1.name }", true);
|
||||
|
||||
assertEqual(expected, actual);
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief check a ref access without any indexes (reverted expression)
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testRefAccess2 : function () {
|
||||
var expected = [ { "name" : "John" }, { "name" : "Fred" }, { "name" : "Jacob" }, { "name" : "Ethan" }, { "name" : "Michael" }, { "name" : "Alexander" }, { "name" : "Daniel" }, { "name" : "Anthony" }, { "name" : "Jim"} , { "name" : "Diego" } ];
|
||||
var actual = getQueryResults("FOR u1 IN " + cn + " FOR u2 IN " + cn + " FILTER u2.name == u1.name SORT u1.id RETURN { \"name\" : u1.name }", true);
|
||||
|
||||
assertEqual(expected, actual);
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief check a ref access with _id filter
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testRefAccessId1 : function () {
|
||||
var expected = [ { "name" : "John" }, { "name" : "Fred" }, { "name" : "Jacob" }, { "name" : "Ethan" }, { "name" : "Michael" }, { "name" : "Alexander" }, { "name" : "Daniel" }, { "name" : "Anthony" }, { "name" : "Jim"} , { "name" : "Diego" } ];
|
||||
var actual = getQueryResults("FOR u1 IN " + cn + " FOR u2 IN " + cn + " FILTER u1._id == u2._id SORT u1.id RETURN { \"name\" : u1.name }", true);
|
||||
|
||||
assertEqual(expected, actual);
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief check a ref access with _id filter (reverted expression)
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testRefAccessId2 : function () {
|
||||
var expected = [ { "name" : "John" }, { "name" : "Fred" }, { "name" : "Jacob" }, { "name" : "Ethan" }, { "name" : "Michael" }, { "name" : "Alexander" }, { "name" : "Daniel" }, { "name" : "Anthony" }, { "name" : "Jim"} , { "name" : "Diego" } ];
|
||||
var actual = getQueryResults("FOR u1 IN " + cn + " FOR u2 IN " + cn + " FILTER u2._id == u1._id SORT u1.id RETURN { \"name\" : u1.name }", true);
|
||||
|
||||
assertEqual(expected, actual);
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief check a ref access with index
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testRefAccessIndex1 : function () {
|
||||
users.ensureHashIndex("name");
|
||||
|
||||
var expected = [ { "name" : "John" }, { "name" : "Fred" }, { "name" : "Jacob" }, { "name" : "Ethan" }, { "name" : "Michael" }, { "name" : "Alexander" }, { "name" : "Daniel" }, { "name" : "Anthony" }, { "name" : "Jim"} , { "name" : "Diego" } ];
|
||||
var actual = getQueryResults("FOR u1 IN " + cn + " FOR u2 IN " + cn + " FILTER u1.name == u2.name SORT u1.id RETURN { \"name\" : u1.name }", true);
|
||||
|
||||
assertEqual(expected, actual);
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief check a ref access with index (reverted expression)
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testRefAccessIndex2 : function () {
|
||||
users.ensureHashIndex("name");
|
||||
|
||||
var expected = [ { "name" : "John" }, { "name" : "Fred" }, { "name" : "Jacob" }, { "name" : "Ethan" }, { "name" : "Michael" }, { "name" : "Alexander" }, { "name" : "Daniel" }, { "name" : "Anthony" }, { "name" : "Jim"} , { "name" : "Diego" } ];
|
||||
var actual = getQueryResults("FOR u1 IN " + cn + " FOR u2 IN " + cn + " FILTER u2.name == u1.name SORT u1.id RETURN { \"name\" : u1.name }", true);
|
||||
|
||||
assertEqual(expected, actual);
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief check a ref access with index (multiple filters)
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testRefAccessIndex3 : function () {
|
||||
users.ensureHashIndex("name");
|
||||
|
||||
var expected = [ { "name" : "John" }, { "name" : "Fred" }, { "name" : "Jacob" }, { "name" : "Ethan" }, { "name" : "Michael" }, { "name" : "Alexander" }, { "name" : "Daniel" }, { "name" : "Anthony" }, { "name" : "Jim"} , { "name" : "Diego" } ];
|
||||
var actual = getQueryResults("FOR u1 IN " + cn + " FOR u2 IN " + cn + " FILTER u2.name == u1.name && u1.name == u2.name SORT u1.id RETURN { \"name\" : u1.name }", true);
|
||||
|
||||
assertEqual(expected, actual);
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief check a ref access with index (multiple filters)
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testRefAccessIndex4 : function () {
|
||||
users.ensureHashIndex("name");
|
||||
|
||||
var expected = [ { "name" : "John" }, { "name" : "Fred" }, { "name" : "Jacob" }, { "name" : "Ethan" }, { "name" : "Michael" }, { "name" : "Alexander" }, { "name" : "Daniel" }, { "name" : "Anthony" }, { "name" : "Jim"} , { "name" : "Diego" } ];
|
||||
var actual = getQueryResults("FOR u1 IN " + cn + " FOR u2 IN " + cn + " FILTER u2.name == u1.name && u1.name == u2.name && u2.name == u1.name && u1._id == u2._id SORT u1.id RETURN { \"name\" : u1.name }", true);
|
||||
|
||||
assertEqual(expected, actual);
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief executes the test suite
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
jsunity.run(ahuacatlQueryOptimiserRefTestSuite);
|
||||
|
||||
return jsunity.done();
|
||||
|
||||
// Local Variables:
|
||||
// mode: outline-minor
|
||||
// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)"
|
||||
// End:
|
Loading…
Reference in New Issue