From 8847d875803285d319a07ce09fb1ada352d3353f Mon Sep 17 00:00:00 2001 From: Oreste Panaia Date: Mon, 10 Sep 2012 20:42:14 +0800 Subject: [PATCH] added limited AQL support for bit indexes --- .../api-index-create-new-bitarray | 35 ++++ arangod/Ahuacatl/ahuacatl-codegen.c | 88 +++++++++- arangod/Ahuacatl/ahuacatl-context.c | 1 + arangod/Ahuacatl/ahuacatl-index.c | 122 +++++++++++-- arangod/Ahuacatl/ahuacatl-optimiser.c | 4 +- arangod/GeoIndex/GeoIndex.c | 45 +++-- arangod/GeoIndex/GeoIndex.h | 2 +- arangod/V8Server/v8-query.cpp | 165 ++++++++++++++---- arangod/V8Server/v8-vocbase.cpp | 4 +- arangod/VocBase/index.c | 75 ++++++-- js/client/client.js | 26 +++ js/client/js-client.h | 26 +++ js/server/ahuacatl.js | 34 ++++ js/server/js-ahuacatl.h | 34 ++++ 14 files changed, 572 insertions(+), 89 deletions(-) create mode 100755 Documentation/Examples.ArangoDB/api-index-create-new-bitarray mode change 100644 => 100755 arangod/Ahuacatl/ahuacatl-codegen.c mode change 100644 => 100755 arangod/Ahuacatl/ahuacatl-context.c mode change 100644 => 100755 arangod/Ahuacatl/ahuacatl-index.c mode change 100644 => 100755 arangod/Ahuacatl/ahuacatl-optimiser.c mode change 100644 => 100755 arangod/VocBase/index.c mode change 100644 => 100755 js/client/client.js mode change 100644 => 100755 js/server/ahuacatl.js diff --git a/Documentation/Examples.ArangoDB/api-index-create-new-bitarray b/Documentation/Examples.ArangoDB/api-index-create-new-bitarray new file mode 100755 index 0000000000..8b4dceb30a --- /dev/null +++ b/Documentation/Examples.ArangoDB/api-index-create-new-bitarray @@ -0,0 +1,35 @@ +> curl --data @- -X POST --dump - http://localhost:8529/_api/index?collection=109061392 +{ "type" : "skiplist", "unique" : false, "fields" : [ "x", [0,1,[]], "y", ["a","b",[]] ] } + +HTTP/1.1 201 Created +content-type: application/json + +{ + "fields": [ + [ + "x", + [ + 0, + 1, + [ + ] + ] + ], + [ + "y", + [ + "a", + "b", + [ + ] + ] + ] + ], + "id": "109061392/173166777", + "type": "bitarray", + "isNewlyCreated": true, + "unique": false, + "undefined": false, + "code": 201, + "error": false +} diff --git a/arangod/Ahuacatl/ahuacatl-codegen.c b/arangod/Ahuacatl/ahuacatl-codegen.c old mode 100644 new mode 100755 index 7053cf98fb..06bd2e22b9 --- a/arangod/Ahuacatl/ahuacatl-codegen.c +++ b/arangod/Ahuacatl/ahuacatl-codegen.c @@ -1088,6 +1088,88 @@ static void GenerateSkiplistAccess (TRI_aql_codegen_js_t* const generator, ScopeOutput(generator, " })"); } + +//////////////////////////////////////////////////////////////////////////////// +/// @brief generate code for bitarray access +//////////////////////////////////////////////////////////////////////////////// + +static void GenerateBitarrayAccess (TRI_aql_codegen_js_t* const generator, + const TRI_aql_index_t* const idx, + const TRI_aql_collection_t* const collection, + const char* const collectionName) { + size_t i, n; + + n = idx->_fieldAccesses->_length; + assert(n >= 1); + + + if (n == 1) { + // peek at first element and check if it is a list access + TRI_aql_field_access_t* fieldAccess = (TRI_aql_field_access_t*) TRI_AtVectorPointer(idx->_fieldAccesses, 0); + + if (fieldAccess->_type == TRI_AQL_ACCESS_LIST) { + assert(false); + ScopeOutput(generator, "AHUACATL_GET_DOCUMENTS_BITARRAY_LIST('"); + ScopeOutput(generator, collectionName); + ScopeOutput(generator, "', "); + ScopeOutputIndexId(generator, collection, idx); + ScopeOutput(generator, ", "); + ScopeOutputQuoted2(generator, fieldAccess->_fullName + fieldAccess->_variableNameLength + 1); + ScopeOutput(generator, ", "); + ScopeOutputJson(generator, fieldAccess->_value._value); + ScopeOutput(generator, ")"); + return; + } + // fall through to other access types + } + + ScopeOutput(generator, "AHUACATL_GET_DOCUMENTS_BITARRAY('"); + ScopeOutput(generator, collectionName); + ScopeOutput(generator, "', "); + ScopeOutputIndexId(generator, collection, idx); + ScopeOutput(generator, ", { \"==\" : {"); // only support the equality operator for now + + + // .................................................................................. + // Construct the javascript object which will eventually be used to generate + // the index operator. The object is in the form: {"==": {"x":0}} + // .................................................................................. + + for (i = 0; i < n; ++i) { + TRI_aql_field_access_t* fieldAccess = (TRI_aql_field_access_t*) TRI_AtVectorPointer(idx->_fieldAccesses, i); + + // ................................................................................ + // Only implement equality operator for now + // ................................................................................ + + ScopeOutputQuoted2(generator, fieldAccess->_fullName + fieldAccess->_variableNameLength + 1); + ScopeOutput(generator, " : "); + + switch (fieldAccess->_type) { + + case TRI_AQL_ACCESS_EXACT: { + ScopeOutputJson(generator, fieldAccess->_value._value); + break; + } + + case TRI_AQL_ACCESS_REFERENCE: { + ProcessAttributeAccess(generator, fieldAccess->_value._reference._ref._node); + } + + default: { + assert(false); + } + } + + if (i < (n-1)) { + ScopeOutput(generator, ", "); + } + + } + + ScopeOutput(generator, "} })"); +} + //////////////////////////////////////////////////////////////////////////////// /// @brief generate code for a reference (the name of a variable) //////////////////////////////////////////////////////////////////////////////// @@ -1287,7 +1369,6 @@ static void ProcessCollectionHinted (TRI_aql_codegen_js_t* const generator, case TRI_IDX_TYPE_GEO2_INDEX: case TRI_IDX_TYPE_PRIORITY_QUEUE_INDEX: case TRI_IDX_TYPE_CAP_CONSTRAINT: - case TRI_IDX_TYPE_BITARRAY_INDEX: // these index types are not yet supported generator->_errorCode = TRI_ERROR_INTERNAL; break; @@ -1303,6 +1384,11 @@ static void ProcessCollectionHinted (TRI_aql_codegen_js_t* const generator, case TRI_IDX_TYPE_SKIPLIST_INDEX: GenerateSkiplistAccess(generator, hint->_index, hint->_collection, collectionName); break; + + case TRI_IDX_TYPE_BITARRAY_INDEX: { + GenerateBitarrayAccess(generator, hint->_index, hint->_collection, collectionName); + break; + } } } diff --git a/arangod/Ahuacatl/ahuacatl-context.c b/arangod/Ahuacatl/ahuacatl-context.c old mode 100644 new mode 100755 index b4a426c13b..a4914b5bbb --- a/arangod/Ahuacatl/ahuacatl-context.c +++ b/arangod/Ahuacatl/ahuacatl-context.c @@ -335,6 +335,7 @@ bool TRI_BindQueryContextAql (TRI_aql_context_t* const context, //////////////////////////////////////////////////////////////////////////////// bool TRI_OptimiseQueryContextAql (TRI_aql_context_t* const context) { + // do some basic optimisations in the AST if (!TRI_OptimiseAql(context)) { // constant folding failed diff --git a/arangod/Ahuacatl/ahuacatl-index.c b/arangod/Ahuacatl/ahuacatl-index.c old mode 100644 new mode 100755 index 048a38ec34..e204623a07 --- a/arangod/Ahuacatl/ahuacatl-index.c +++ b/arangod/Ahuacatl/ahuacatl-index.c @@ -104,11 +104,11 @@ static TRI_aql_index_t* PickIndex (TRI_aql_context_t* const context, TRI_aql_index_t* pickedIndex, const TRI_index_t* const idx, TRI_vector_pointer_t* fieldAccesses) { - bool isBetter; + bool isBetter = false; assert(idx); assert(fieldAccesses); - + if (pickedIndex == NULL) { pickedIndex = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_aql_index_t), false); pickedIndex->_idx = NULL; @@ -121,21 +121,98 @@ static TRI_aql_index_t* PickIndex (TRI_aql_context_t* const context, return NULL; } + + // ........................................................................... + // If we do not have an index yet, then this index will do. As has been said + // before 'any index is better than none' + // ........................................................................... + if (pickedIndex->_idx == NULL) { - // any index is better than none + pickedIndex->_idx = (TRI_index_t*) idx; + pickedIndex->_fieldAccesses = TRI_CopyVectorPointer(TRI_UNKNOWN_MEM_ZONE, fieldAccesses); + return pickedIndex; + } + + + // ........................................................................... + // We have previously selected an index, if it happens to be the primary then + // we stick with it. + // ........................................................................... + + if (pickedIndex->_idx->_type == TRI_IDX_TYPE_PRIMARY_INDEX) { + return pickedIndex; + } + + + // ........................................................................... + // Now go through the various possibilities if we have not located something + // better. + // ........................................................................... + + if ( (isBetter == false) && (idx->_type == TRI_IDX_TYPE_PRIMARY_INDEX) ) { + // ......................................................................... + // If we can used the primary index, then this is better than any other + // index so use it. + // ......................................................................... + isBetter = true; + } + + + if ( (isBetter == false) && (idx->_type == TRI_IDX_TYPE_HASH_INDEX) ) { + // ......................................................................... + // If the index type is a hash index, use this -- but only if we have NOT + // located something better BEFORE. + // ......................................................................... isBetter = true; } - else { - isBetter = idx->_type == TRI_IDX_TYPE_PRIMARY_INDEX || // primary index is better than any others - (idx->_type == TRI_IDX_TYPE_HASH_INDEX && pickedIndex->_idx->_type == TRI_IDX_TYPE_SKIPLIST_INDEX) || // hash is better than skiplist - (idx->_unique && !pickedIndex->_idx->_unique) || // unique indexes are better than non-unique ones - (fieldAccesses->_length < pickedIndex->_fieldAccesses->_length && idx->_unique) || // shorter indexes are better if unique - (fieldAccesses->_length > pickedIndex->_fieldAccesses->_length && !idx->_unique); // longer indexes are better if non-unique - - // if we have already picked the primary index, we won't overwrite it with any other index - isBetter &= (pickedIndex->_idx->_type != TRI_IDX_TYPE_PRIMARY_INDEX); + + + if ( (isBetter == false) && (idx->_type == TRI_IDX_TYPE_SKIPLIST_INDEX) && + (pickedIndex->_idx->_type != TRI_IDX_TYPE_HASH_INDEX) ) { + // ......................................................................... + // If the index type is a skiplist index, use this -- but only if we have NOT + // located something better BEFORE. + // ......................................................................... + isBetter = true; + } + + + if ( (isBetter == false) && (idx->_type == TRI_IDX_TYPE_BITARRAY_INDEX) && + (pickedIndex->_idx->_type != TRI_IDX_TYPE_HASH_INDEX) && + (pickedIndex->_idx->_type != TRI_IDX_TYPE_SKIPLIST_INDEX) ) { + // ......................................................................... + // If the index type is a bitarray index, use this -- but only if we have NOT + // located something better BEFORE. + // ......................................................................... + isBetter = true; + } + + + if ( (isBetter == false) && (idx->_unique == true) && (pickedIndex->_idx->_unique == false) ) { + // ......................................................................... + // If the index is a unique one and the picked index is non-unique, then + // replace it with the unique overriding the preferences above. E.g. if + // we have a non-unique hash index (which we have chosen) and now we are + // testing a unique skiplist, replace it with the skiplist. + // ......................................................................... + isBetter = true; } + + if ( (isBetter == false) && + (fieldAccesses->_length < pickedIndex->_fieldAccesses->_length ) && + (idx->_unique == true) ) { + isBetter = true; + } + + + if ( (isBetter == false) && + (fieldAccesses->_length > pickedIndex->_fieldAccesses->_length ) && + (idx->_unique == false) ) { + isBetter = true; + } + + if (isBetter) { if (pickedIndex->_fieldAccesses != NULL) { TRI_FreeVectorPointer(TRI_UNKNOWN_MEM_ZONE, pickedIndex->_fieldAccesses); @@ -189,7 +266,7 @@ TRI_aql_index_t* TRI_DetermineIndexAql (TRI_aql_context_t* const context, assert(context); assert(collectionName); assert(candidates); - + n = availableIndexes->_length; for (i = 0; i < n; ++i) { TRI_index_t* idx = (TRI_index_t*) availableIndexes->_buffer[i]; @@ -207,12 +284,12 @@ TRI_aql_index_t* TRI_DetermineIndexAql (TRI_aql_context_t* const context, case TRI_IDX_TYPE_GEO2_INDEX: case TRI_IDX_TYPE_PRIORITY_QUEUE_INDEX: case TRI_IDX_TYPE_CAP_CONSTRAINT: - case TRI_IDX_TYPE_BITARRAY_INDEX: // ignore all these index types for now continue; case TRI_IDX_TYPE_PRIMARY_INDEX: case TRI_IDX_TYPE_HASH_INDEX: case TRI_IDX_TYPE_SKIPLIST_INDEX: + case TRI_IDX_TYPE_BITARRAY_INDEX: // these indexes are valid candidates break; } @@ -239,6 +316,7 @@ TRI_aql_index_t* TRI_DetermineIndexAql (TRI_aql_context_t* const context, for (k = 0; k < candidates->_length; ++k) { TRI_aql_field_access_t* candidate = (TRI_aql_field_access_t*) TRI_AtVectorPointer(candidates, k); + if (candidate->_type == TRI_AQL_ACCESS_IMPOSSIBLE || candidate->_type == TRI_AQL_ACCESS_ALL) { // wrong index type, doesn't help us at all @@ -260,6 +338,7 @@ TRI_aql_index_t* TRI_DetermineIndexAql (TRI_aql_context_t* const context, TRI_PushBackVectorPointer(&matches, candidate); } + else if (idx->_type == TRI_IDX_TYPE_HASH_INDEX) { if (!IsExactCandidate(candidate)) { // wrong access type for hash index @@ -273,6 +352,21 @@ TRI_aql_index_t* TRI_DetermineIndexAql (TRI_aql_context_t* const context, TRI_PushBackVectorPointer(&matches, candidate); } + + else if (idx->_type == TRI_IDX_TYPE_BITARRAY_INDEX) { + if (!IsExactCandidate(candidate)) { + // wrong access type for hash index + continue; + } + + if (candidate->_type == TRI_AQL_ACCESS_LIST) { + // we found a list, but the index covers multiple attributes. that means we cannot use list access + continue; + } + + TRI_PushBackVectorPointer(&matches, candidate); + } + else if (idx->_type == TRI_IDX_TYPE_SKIPLIST_INDEX) { bool candidateIsExact; diff --git a/arangod/Ahuacatl/ahuacatl-optimiser.c b/arangod/Ahuacatl/ahuacatl-optimiser.c old mode 100644 new mode 100755 index 49437aa67f..3b4af674e8 --- a/arangod/Ahuacatl/ahuacatl-optimiser.c +++ b/arangod/Ahuacatl/ahuacatl-optimiser.c @@ -85,7 +85,7 @@ static void AttachCollectionHint (TRI_aql_context_t* const context, collectionName = TRI_AQL_NODE_STRING(nameNode); assert(collectionName); - + hint = (TRI_aql_collection_hint_t*) TRI_AQL_NODE_DATA(node); if (hint == NULL) { @@ -93,7 +93,7 @@ static void AttachCollectionHint (TRI_aql_context_t* const context, return; } - + if (hint->_ranges == NULL) { // no ranges found to be used as indexes diff --git a/arangod/GeoIndex/GeoIndex.c b/arangod/GeoIndex/GeoIndex.c index da62a403f8..d87a2b5118 100755 --- a/arangod/GeoIndex/GeoIndex.c +++ b/arangod/GeoIndex/GeoIndex.c @@ -96,17 +96,16 @@ typedef struct /* "points" lists the slotid of the points. This is */ /* only used for a leaf pot. */ /* =================================================== */ -typedef struct -{ - int LorLeaf; - int RorPoints; - GeoString middle; - GeoFix maxdist[GeoIndexFIXEDPOINTS]; - GeoString start; - GeoString end; - int level; - int points[GeoIndexPOTSIZE]; -} GeoPot; +typedef struct { + int LorLeaf; + int RorPoints; + GeoString middle; + GeoFix maxdist[GeoIndexFIXEDPOINTS]; + GeoString start; + GeoString end; + int level; + int points[GeoIndexPOTSIZE]; +} GeoPot; /* =================================================== */ /* GeoIx structure */ /* This is the REAL GeoIndex structure - the one in */ @@ -125,14 +124,13 @@ typedef struct /* There is no provision at present for the index to */ /* get smaller when the majority of points are deleted */ /* =================================================== */ -typedef struct -{ - GeoIndexFixed fixed; /* fixed point data */ - int potct; /* pots allocated */ - int slotct; /* slots allocated */ - GeoPot * pots; /* the pots themselves */ - GeoCoordinate * gc; /* the slots themselves */ -} GeoIx; +typedef struct { + GeoIndexFixed fixed; /* fixed point data */ + int potct; /* pots allocated */ + int slotct; /* slots allocated */ + GeoPot * pots; /* the pots themselves */ + GeoCoordinate * gc; /* the slots themselves */ +} GeoIx; /* =================================================== */ /* GeoDetailedPoint structure */ /* The routine GeoMkDetail is given a point - really */ @@ -369,15 +367,14 @@ int GeoIndexNewPot(GeoIx * gix) /* GeoString values of real (latitude, longitude) */ /* points */ /* =================================================== */ -GeoIndex * GeoIndex_new(void) -{ +GeoIndex * GeoIndex_new(void) { GeoIx * gix; int i,j; double lat, lon, x, y, z; gix = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(GeoIx), false); - if(gix == NULL) { + if (gix == NULL) { return (GeoIndex *) gix; } @@ -576,8 +573,7 @@ GeoIndex * GeoIndex_new(void) /* objects that may have been pointed to by the user's */ /* data pointers are (of course) not freed by this call*/ /* =================================================== */ -void GeoIndex_free(GeoIndex * gi) -{ +void GeoIndex_free(GeoIndex * gi) { GeoIx * gix; if (gi == NULL) { @@ -2275,6 +2271,7 @@ int GeoIndex_INDEXVALID(GeoIndex * gi) //////////////////////////////////////////////////////////////////////////////// int GeoIndex_assignMethod(void* methodHandle, TRI_index_method_assignment_type_e methodType) { + switch (methodType) { case TRI_INDEX_METHOD_ASSIGNMENT_FREE : { diff --git a/arangod/GeoIndex/GeoIndex.h b/arangod/GeoIndex/GeoIndex.h index 6f289f69e0..ce0306dcdc 100755 --- a/arangod/GeoIndex/GeoIndex.h +++ b/arangod/GeoIndex/GeoIndex.h @@ -102,7 +102,7 @@ int GeoIndex_assignMethod (void*, TRI_index_method_assignment_type_e); // Allows one or more call back functions to be assigned /////////////////////////////////////////////////////////////////////////////////////// -int GeoIndexIndex_assignMethod (void*, TRI_index_method_assignment_type_e); +int GeoIndex_assignMethod (void*, TRI_index_method_assignment_type_e); GeoIndex * GeoIndex_new(void); void GeoIndex_free(GeoIndex * gi); diff --git a/arangod/V8Server/v8-query.cpp b/arangod/V8Server/v8-query.cpp index 6dfed530d3..363301108c 100755 --- a/arangod/V8Server/v8-query.cpp +++ b/arangod/V8Server/v8-query.cpp @@ -519,54 +519,51 @@ static TRI_index_operator_t* SetupConditionsBitarrayHelper (TRI_index_t* idx, // Check the various operator conditions // ........................................................................ + + // ........................................................................ + // Check for an 'AND' condition. The following are acceptable: '&', '&&' 'and' + // ........................................................................ if (condition->HasOwnProperty(v8::String::New("&"))) { + operatorType = TRI_AND_INDEX_OPERATOR; value = condition->Get(v8::String::New("&")); - operatorType = TRI_AND_INDEX_OPERATOR; - if (!value->IsArray()) { - // wrong data type for AND condition -- we require [leftOperation,rightOperation] - return 0; - } } - if (condition->HasOwnProperty(v8::String::New("and"))) { + else if (condition->HasOwnProperty(v8::String::New("&&"))) { + operatorType = TRI_AND_INDEX_OPERATOR; + value = condition->Get(v8::String::New("&&")); + } + else if (condition->HasOwnProperty(v8::String::New("and"))) { + operatorType = TRI_AND_INDEX_OPERATOR; value = condition->Get(v8::String::New("and")); - operatorType = TRI_AND_INDEX_OPERATOR; - if (!value->IsArray()) { - // wrong data type for AND condition -- we require [leftOperation,rightOperation] - return 0; - } - } + } + // ........................................................................ + // Check for an 'OR' condition. The following are acceptable: '|', '||' 'or' + // ........................................................................ else if (condition->HasOwnProperty(v8::String::New("|"))) { value = condition->Get(v8::String::New("|")); operatorType = TRI_OR_INDEX_OPERATOR; - if (!value->IsArray()) { - // wrong data type for OR condition -- we require [leftOperation,rightOperation] - return 0; - } + } + else if (condition->HasOwnProperty(v8::String::New("||"))) { + value = condition->Get(v8::String::New("||")); + operatorType = TRI_OR_INDEX_OPERATOR; } else if (condition->HasOwnProperty(v8::String::New("or"))) { value = condition->Get(v8::String::New("or")); operatorType = TRI_OR_INDEX_OPERATOR; - if (!value->IsArray()) { - // wrong data type for OR condition -- we require [leftOperation,rightOperation] - return 0; - } } + // ........................................................................ + // Check for an 'NOT' condition. The following are acceptable: '!', 'not' + // ........................................................................ else if (condition->HasOwnProperty(v8::String::New("!"))) { value = condition->Get(v8::String::New("!")); operatorType = TRI_NOT_INDEX_OPERATOR; - if (!value->IsObject()) { - // wrong data type for NOT condition -- we require {condition...} ] - return 0; - } } else if (condition->HasOwnProperty(v8::String::New("not"))) { - value = condition->Get(v8::String::New("!")); + value = condition->Get(v8::String::New("not")); operatorType = TRI_NOT_INDEX_OPERATOR; - if (!value->IsObject()) { - // wrong data type for NOT condition -- we require {condition...} ] - return 0; - } } + // ........................................................................ + // Check for an 'EQUAL' condition. The following are acceptable: '=', '==', 'eq' + // ........................................................................ else if (condition->HasOwnProperty(v8::String::New("=="))) { value = condition->Get(v8::String::New("==")); operatorType = TRI_EQ_INDEX_OPERATOR; @@ -575,33 +572,79 @@ static TRI_index_operator_t* SetupConditionsBitarrayHelper (TRI_index_t* idx, value = condition->Get(v8::String::New("=")); operatorType = TRI_EQ_INDEX_OPERATOR; } + else if (condition->HasOwnProperty(v8::String::New("eq"))) { + value = condition->Get(v8::String::New("eq")); + operatorType = TRI_EQ_INDEX_OPERATOR; + } + // ........................................................................ + // Check for an 'NOT EQUAL' condition. The following are acceptable: '!=', '<>, 'ne' + // ........................................................................ else if (condition->HasOwnProperty(v8::String::New("!="))) { value = condition->Get(v8::String::New("!=")); operatorType = TRI_NE_INDEX_OPERATOR; } + else if (condition->HasOwnProperty(v8::String::New("<>"))) { + value = condition->Get(v8::String::New("<>")); + operatorType = TRI_NE_INDEX_OPERATOR; + } + else if (condition->HasOwnProperty(v8::String::New("ne"))) { + value = condition->Get(v8::String::New("ne")); + operatorType = TRI_NE_INDEX_OPERATOR; + } + // ........................................................................ + // Check for an 'LESS THAN OR EQUAL' condition. The following are acceptable: '<=', 'le' + // ........................................................................ else if (condition->HasOwnProperty(v8::String::New("<="))) { value = condition->Get(v8::String::New("<=")); operatorType = TRI_LE_INDEX_OPERATOR; } + else if (condition->HasOwnProperty(v8::String::New("le"))) { + value = condition->Get(v8::String::New("le")); + operatorType = TRI_LE_INDEX_OPERATOR; + } + // ........................................................................ + // Check for an 'LESS THAN ' condition. The following are acceptable: '<', 'lt' + // ........................................................................ else if (condition->HasOwnProperty(v8::String::New("<"))) { value = condition->Get(v8::String::New("<")); operatorType = TRI_LT_INDEX_OPERATOR; } + else if (condition->HasOwnProperty(v8::String::New("lt"))) { + value = condition->Get(v8::String::New("lt")); + operatorType = TRI_LT_INDEX_OPERATOR; + } + // ........................................................................ + // Check for an 'GREATER THAN OR EQUAL' condition. The following are acceptable: '>=', 'ge' + // ........................................................................ else if (condition->HasOwnProperty(v8::String::New(">="))) { value = condition->Get(v8::String::New(">=")); operatorType = TRI_GE_INDEX_OPERATOR; } + else if (condition->HasOwnProperty(v8::String::New("ge"))) { + value = condition->Get(v8::String::New("ge")); + operatorType = TRI_GE_INDEX_OPERATOR; + } + // ........................................................................ + // Check for an 'GREATER THAN ' condition. The following are acceptable: '>', 'gt' + // ........................................................................ else if (condition->HasOwnProperty(v8::String::New(">"))) { value = condition->Get(v8::String::New(">")); operatorType = TRI_GT_INDEX_OPERATOR; } + else if (condition->HasOwnProperty(v8::String::New("gt"))) { + value = condition->Get(v8::String::New("gt")); + operatorType = TRI_GT_INDEX_OPERATOR; + } + // ........................................................................ + // We received an invalid condition. Most likely we are really expressing + // a condition {"x":1} which should be BY_EXAMPLE rather than BY_CONDITION + // ........................................................................ else { // invalid operator index condition return 0; } - - + // ........................................................................ - // Since we have a valid condition condition, act upon it + // Since we have a valid condition, act upon it // may require recursion // ........................................................................ @@ -609,7 +652,41 @@ static TRI_index_operator_t* SetupConditionsBitarrayHelper (TRI_index_t* idx, case TRI_AND_INDEX_OPERATOR: case TRI_OR_INDEX_OPERATOR: { + + // .................................................................... + // For both the 'AND' and 'OR' index operators, we require an array + // with 2 elements for the value of the condition object. E.g. we + // expect: {"&": [{"x":0},{"x":1}]} <-- this is a special "and" call + // see the ensureBitarray doc for + // more information. + // More common is + // expect: {"or": [{"x":0},{"x":1}]} <-- which means return all docs + // where attribute "x" has the + // value of 0 or 1. + // To have "x" = 0 or "x" = 1 or "x" = 2 we expect: + // {"or":[{"x":0},{"or":[{"x":1},{"x":2}]}]} or any valid iteration + // of this. TODO: shortcut this with the "list" index operator + // .................................................................... + + // .................................................................... + // wrong data type for this condition -- we require [leftOperation,rightOperation] + // .................................................................... + + if (!value->IsArray()) { + return 0; + } + v8::Handle andValues = v8::Handle::Cast(value); + + + // .................................................................... + // Check the length of the array to ensure that it is exactly 2 + // .................................................................... + + if (andValues->Length() != 2) { + return 0; + } + v8::Handle leftValue = andValues->Get(0); v8::Handle rightValue = andValues->Get(1); @@ -620,6 +697,11 @@ static TRI_index_operator_t* SetupConditionsBitarrayHelper (TRI_index_t* idx, v8::Handle leftObject = v8::Handle::Cast(leftValue); v8::Handle rightObject = v8::Handle::Cast(rightValue); + + // .................................................................... + // recurse the left and right operators + // .................................................................... + TRI_index_operator_t* leftOp = SetupConditionsBitarrayHelper(idx, shaper, leftObject); TRI_index_operator_t* rightOp = SetupConditionsBitarrayHelper(idx, shaper, rightObject); @@ -634,7 +716,22 @@ static TRI_index_operator_t* SetupConditionsBitarrayHelper (TRI_index_t* idx, } case TRI_NOT_INDEX_OPERATOR: { + + // .................................................................... + // wrong data type for this condition -- we require {...} which becomes + // the left object for not operator. + // .................................................................... + + if (!value->IsObject()) { + return 0; + } + v8::Handle leftObject = v8::Handle::Cast(value); + + + // .................................................................... + // recurse the left and only operator + // .................................................................... TRI_index_operator_t* leftOp = SetupConditionsBitarrayHelper(idx, shaper, leftObject); if (leftOp == 0) { @@ -651,6 +748,7 @@ static TRI_index_operator_t* SetupConditionsBitarrayHelper (TRI_index_t* idx, case TRI_LT_INDEX_OPERATOR: case TRI_GE_INDEX_OPERATOR: case TRI_GT_INDEX_OPERATOR: { + v8::Handle leftObject = v8::Handle::Cast(value); TRI_json_t* parameters = SetupBitarrayAttributeValuesHelper(idx, leftObject); @@ -667,7 +765,7 @@ static TRI_index_operator_t* SetupConditionsBitarrayHelper (TRI_index_t* idx, } } // end of switch (operatorType) - + return indexOperator; } @@ -1166,6 +1264,7 @@ static v8::Handle ExecuteBitarrayQuery (v8::Arguments const& argv, st else { LOG_WARNING("index iterator returned with a NULL value in ExecuteBitarrayQuery"); + // return an empty list } collection->_collection->endRead(collection->_collection); diff --git a/arangod/V8Server/v8-vocbase.cpp b/arangod/V8Server/v8-vocbase.cpp index 971464c222..451b779a5d 100755 --- a/arangod/V8Server/v8-vocbase.cpp +++ b/arangod/V8Server/v8-vocbase.cpp @@ -1338,6 +1338,7 @@ static v8::Handle ExecuteQueryNativeAhuacatl (TRI_aql_context_t* cons // bind values // optimise // lock + if (!TRI_ValidateQueryContextAql(context) || !TRI_BindQueryContextAql(context, parameters) || !TRI_LockQueryContextAql(context) || @@ -2088,7 +2089,6 @@ static v8::Handle JS_RunAhuacatl (v8::Arguments const& argv) { } const string queryString = TRI_ObjectToString(queryArg); - // return number of total records in cursor? bool doCount = false; // maximum number of results to return at once @@ -2781,7 +2781,7 @@ static v8::Handle EnsureBitarray (v8::Arguments const& argv, bool sup if ( (argv.Length() < 2) || (argv.Length() % 2 != 0) ) { LOG_WARNING("bitarray index creation failed -- invalid parameters (require key_1,values_1,...,key_n,values_n)"); TRI_ReleaseCollection(collection); - return scope.Close(v8::ThrowException(TRI_CreateErrorObject(TRI_ERROR_ILLEGAL_OPTION, "usage: ensureBitarray(, , ...)"))); + return scope.Close(v8::ThrowException(TRI_CreateErrorObject(TRI_ERROR_ILLEGAL_OPTION, "usage: ensureBitarray(path 1, , , , ...)"))); } diff --git a/arangod/VocBase/index.c b/arangod/VocBase/index.c old mode 100644 new mode 100755 index ea8f1ff177..4017afa771 --- a/arangod/VocBase/index.c +++ b/arangod/VocBase/index.c @@ -1017,7 +1017,8 @@ TRI_index_t* TRI_CreateGeo1Index (struct TRI_doc_collection_s* collection, bool ignoreNull) { TRI_geo_index_t* geo; char* ln; - + int result; + geo = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_geo_index_t), false); if (geo == NULL) { @@ -1047,13 +1048,32 @@ TRI_index_t* TRI_CreateGeo1Index (struct TRI_doc_collection_s* collection, geo->_constraint = constraint; geo->_geoIndex = GeoIndex_new(); - + + if (geo->_geoIndex == NULL) { // oops out of memory? + LOG_WARNING("geo index creation failed -- internal error when creating new goe index structure"); + TRI_DestroyVectorString(&geo->base._fields); + TRI_Free(TRI_UNKNOWN_MEM_ZONE, geo); + return NULL; + } + geo->_variant = geoJson ? INDEX_GEO_COMBINED_LAT_LON : INDEX_GEO_COMBINED_LON_LAT; geo->_location = location; geo->_latitude = 0; geo->_longitude = 0; geo->_geoJson = geoJson; - + + result = GeoIndex_assignMethod(&(geo->base.indexQuery), TRI_INDEX_METHOD_ASSIGNMENT_QUERY); + result = result || GeoIndex_assignMethod(&(geo->base.indexQueryFree), TRI_INDEX_METHOD_ASSIGNMENT_FREE); + result = result || GeoIndex_assignMethod(&(geo->base.indexQueryResult), TRI_INDEX_METHOD_ASSIGNMENT_RESULT); + + if (result != TRI_ERROR_NO_ERROR) { + TRI_DestroyVectorString(&geo->base._fields); + GeoIndex_free(geo->_geoIndex); + TRI_Free(TRI_UNKNOWN_MEM_ZONE, geo); + LOG_WARNING("geo index creation failed -- internal error when assigning function calls"); + return NULL; + } + return &geo->base; } @@ -1071,7 +1091,8 @@ TRI_index_t* TRI_CreateGeo2Index (struct TRI_doc_collection_s* collection, TRI_geo_index_t* geo; char* lat; char* lon; - + int result; + geo = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_geo_index_t), false); if (geo == NULL) { @@ -1103,11 +1124,30 @@ TRI_index_t* TRI_CreateGeo2Index (struct TRI_doc_collection_s* collection, geo->_constraint = constraint; geo->_geoIndex = GeoIndex_new(); + if (geo->_geoIndex == NULL) { // oops out of memory? + LOG_WARNING("geo index creation failed -- internal error when creating new goe index structure"); + TRI_DestroyVectorString(&geo->base._fields); + TRI_Free(TRI_UNKNOWN_MEM_ZONE, geo); + return NULL; + } + geo->_variant = INDEX_GEO_INDIVIDUAL_LAT_LON; geo->_location = 0; geo->_latitude = latitude; geo->_longitude = longitude; + result = GeoIndex_assignMethod(&(geo->base.indexQuery), TRI_INDEX_METHOD_ASSIGNMENT_QUERY); + result = result || GeoIndex_assignMethod(&(geo->base.indexQueryFree), TRI_INDEX_METHOD_ASSIGNMENT_FREE); + result = result || GeoIndex_assignMethod(&(geo->base.indexQueryResult), TRI_INDEX_METHOD_ASSIGNMENT_RESULT); + + if (result != TRI_ERROR_NO_ERROR) { + TRI_DestroyVectorString(&geo->base._fields); + GeoIndex_free(geo->_geoIndex); + TRI_Free(TRI_UNKNOWN_MEM_ZONE, geo); + LOG_WARNING("geo index creation failed -- internal error when assigning function calls"); + return NULL; + } + return &geo->base; } @@ -1732,7 +1772,7 @@ TRI_index_t* TRI_CreateHashIndex (struct TRI_doc_collection_s* collection, hashIndex->_hashIndex = MultiHashIndex_new(); } - if (hashIndex->_hashIndex == NULL) { + if (hashIndex->_hashIndex == NULL) { // oops out of memory? TRI_DestroyVector(&hashIndex->_paths); TRI_DestroyVectorString(&hashIndex->base._fields); TRI_Free(TRI_UNKNOWN_MEM_ZONE, hashIndex); @@ -1750,7 +1790,8 @@ TRI_index_t* TRI_CreateHashIndex (struct TRI_doc_collection_s* collection, if (result != TRI_ERROR_NO_ERROR) { TRI_DestroyVector(&hashIndex->_paths); TRI_DestroyVectorString(&hashIndex->base._fields); - TRI_FreeBitarrayIndex(&hashIndex->base); + HashIndex_free(hashIndex->_hashIndex); + TRI_Free(TRI_UNKNOWN_MEM_ZONE, hashIndex); LOG_WARNING("hash index creation failed -- internal error when assigning function calls"); return NULL; } @@ -3484,6 +3525,15 @@ TRI_index_t* TRI_CreateSkiplistIndex (struct TRI_doc_collection_s* collection, skiplistIndex->_skiplistIndex = MultiSkiplistIndex_new(); } + if (skiplistIndex->_skiplistIndex == NULL) { + TRI_DestroyVector(&skiplistIndex->_paths); + TRI_DestroyVectorString(&skiplistIndex->base._fields); + TRI_Free(TRI_UNKNOWN_MEM_ZONE, skiplistIndex); + LOG_WARNING("skiplist index creation failed -- internal error when creating skiplist structure"); + return NULL; + } + + // ........................................................................... // Assign the function calls used by the query engine // ........................................................................... @@ -3495,7 +3545,8 @@ TRI_index_t* TRI_CreateSkiplistIndex (struct TRI_doc_collection_s* collection, if (result != TRI_ERROR_NO_ERROR) { TRI_DestroyVector(&skiplistIndex->_paths); TRI_DestroyVectorString(&skiplistIndex->base._fields); - TRI_FreeBitarrayIndex(&skiplistIndex->base); + SkiplistIndex_free(skiplistIndex->_skiplistIndex); + TRI_Free(TRI_UNKNOWN_MEM_ZONE, skiplistIndex); LOG_WARNING("skiplist index creation failed -- internal error when assigning function calls"); return NULL; } @@ -4384,7 +4435,6 @@ TRI_index_t* TRI_CreateBitarrayIndex (struct TRI_doc_collection_s* collection, createContext = NULL; - // ........................................................................... // Check that the attributes have not been repeated // ........................................................................... @@ -4403,7 +4453,7 @@ TRI_index_t* TRI_CreateBitarrayIndex (struct TRI_doc_collection_s* collection, if (value == NULL) { TRI_DestroyVector(&baIndex->_paths); TRI_DestroyVector(&baIndex->_values); - TRI_FreeBitarrayIndex(&baIndex->base); + TRI_Free(TRI_UNKNOWN_MEM_ZONE,baIndex); LOG_WARNING("bitarray index creation failed -- list of values for index undefined"); return NULL; } @@ -4428,7 +4478,7 @@ TRI_index_t* TRI_CreateBitarrayIndex (struct TRI_doc_collection_s* collection, if (cardinality > 64) { TRI_DestroyVector(&baIndex->_paths); TRI_DestroyVector(&baIndex->_values); - TRI_FreeBitarrayIndex(&baIndex->base); + TRI_Free(TRI_UNKNOWN_MEM_ZONE,baIndex); LOG_WARNING("bitarray index creation failed -- more than 64 possible values"); return NULL; } @@ -4437,7 +4487,7 @@ TRI_index_t* TRI_CreateBitarrayIndex (struct TRI_doc_collection_s* collection, if (cardinality < 1 ) { TRI_DestroyVector(&baIndex->_paths); TRI_DestroyVector(&baIndex->_values); - TRI_FreeBitarrayIndex(&baIndex->base); + TRI_Free(TRI_UNKNOWN_MEM_ZONE,baIndex); LOG_WARNING("bitarray index creation failed -- no index values defined"); return NULL; } @@ -4455,7 +4505,7 @@ TRI_index_t* TRI_CreateBitarrayIndex (struct TRI_doc_collection_s* collection, if (result != TRI_ERROR_NO_ERROR) { TRI_DestroyVector(&baIndex->_paths); TRI_DestroyVector(&baIndex->_values); - TRI_FreeBitarrayIndex(&baIndex->base); + TRI_Free(TRI_UNKNOWN_MEM_ZONE,baIndex); LOG_WARNING("bitarray index creation failed -- internal error when assigning function calls"); return NULL; } @@ -4471,6 +4521,7 @@ TRI_index_t* TRI_CreateBitarrayIndex (struct TRI_doc_collection_s* collection, TRI_DestroyVector(&baIndex->_paths); TRI_DestroyVector(&baIndex->_values); TRI_FreeBitarrayIndex(&baIndex->base); + TRI_Free(TRI_UNKNOWN_MEM_ZONE,baIndex); LOG_WARNING("bitarray index creation failed -- your guess as good as mine"); return NULL; } diff --git a/js/client/client.js b/js/client/client.js old mode 100644 new mode 100755 index 8b22dc1743..d9829a8685 --- a/js/client/client.js +++ b/js/client/client.js @@ -1356,6 +1356,32 @@ function ArangoCollection (database, data) { return true; }; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief adds a bitarray index +//////////////////////////////////////////////////////////////////////////////// + + ArangoCollection.prototype.ensureBitarray = function () { + var i; + var body; + var fields = []; + + for (i = 0; i < arguments.length; ++i) { + fields.push(arguments[i]); + } + + body = { type : "bitarray", unique : false, fields : fields }; + + var requestResult = this._database._connection.POST( + "/_api/index?collection=" + encodeURIComponent(this._id), + JSON.stringify(body)); + + client.checkRequestResult(requestResult); + + return requestResult; + }; + + //////////////////////////////////////////////////////////////////////////////// /// @brief adds a cap constraint //////////////////////////////////////////////////////////////////////////////// diff --git a/js/client/js-client.h b/js/client/js-client.h index b8a9863a41..0845cc7ee4 100644 --- a/js/client/js-client.h +++ b/js/client/js-client.h @@ -1357,6 +1357,32 @@ static string JS_client_client = " return true;\n" " };\n" "\n" + " \n" + "////////////////////////////////////////////////////////////////////////////////\n" + "/// @brief adds a bitarray index\n" + "////////////////////////////////////////////////////////////////////////////////\n" + "\n" + " ArangoCollection.prototype.ensureBitarray = function () {\n" + " var i;\n" + " var body;\n" + " var fields = [];\n" + "\n" + " for (i = 0; i < arguments.length; ++i) {\n" + " fields.push(arguments[i]);\n" + " }\n" + "\n" + " body = { type : \"bitarray\", unique : false, fields : fields };\n" + "\n" + " var requestResult = this._database._connection.POST(\n" + " \"/_api/index?collection=\" + encodeURIComponent(this._id),\n" + " JSON.stringify(body));\n" + "\n" + " client.checkRequestResult(requestResult);\n" + "\n" + " return requestResult;\n" + " };\n" + "\n" + " \n" "////////////////////////////////////////////////////////////////////////////////\n" "/// @brief adds a cap constraint\n" "////////////////////////////////////////////////////////////////////////////////\n" diff --git a/js/server/ahuacatl.js b/js/server/ahuacatl.js old mode 100644 new mode 100755 index 7070e727f6..343c1c0a61 --- a/js/server/ahuacatl.js +++ b/js/server/ahuacatl.js @@ -455,6 +455,40 @@ function AHUACATL_GET_DOCUMENTS_HASH_LIST (collection, idx, attribute, values) { return result; } +//////////////////////////////////////////////////////////////////////////////// +/// @brief get documents from the specified collection using a bitarray +//////////////////////////////////////////////////////////////////////////////// + +function AHUACATL_GET_DOCUMENTS_BITARRAY (collection, idx, example) { + return internal.db[collection].BY_CONDITION_BITARRAY(idx, example).documents; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief get documents from the specified collection using a bitarray +/// (multiple index values) TODO: replace by 'IN index operator' +//////////////////////////////////////////////////////////////////////////////// + +function AHUACATL_GET_DOCUMENTS_BITARRAY_LIST (collection, idx, attribute, values) { + var result = [ ]; + + for (var i in values) { + var value = values[i]; + var example = { }; + + example[attribute] = value; + + var documents = internal.db[collection].BY_EXAMPLE_BITARRAY(idx, example).documents; + for (var j in documents) { + result.push(documents[j]); + } + } + + return result; +} + + + + //////////////////////////////////////////////////////////////////////////////// /// @brief get documents from the specified collection using a skiplist //////////////////////////////////////////////////////////////////////////////// diff --git a/js/server/js-ahuacatl.h b/js/server/js-ahuacatl.h index 83a3a92ed9..36f55da426 100644 --- a/js/server/js-ahuacatl.h +++ b/js/server/js-ahuacatl.h @@ -457,6 +457,40 @@ static string JS_server_ahuacatl = "}\n" "\n" "////////////////////////////////////////////////////////////////////////////////\n" + "/// @brief get documents from the specified collection using a bitarray\n" + "////////////////////////////////////////////////////////////////////////////////\n" + "\n" + "function AHUACATL_GET_DOCUMENTS_BITARRAY (collection, idx, example) {\n" + " return internal.db[collection].BY_CONDITION_BITARRAY(idx, example).documents;\n" + "}\n" + "\n" + "////////////////////////////////////////////////////////////////////////////////\n" + "/// @brief get documents from the specified collection using a bitarray\n" + "/// (multiple index values) TODO: replace by 'IN index operator'\n" + "////////////////////////////////////////////////////////////////////////////////\n" + "\n" + "function AHUACATL_GET_DOCUMENTS_BITARRAY_LIST (collection, idx, attribute, values) {\n" + " var result = [ ];\n" + "\n" + " for (var i in values) {\n" + " var value = values[i];\n" + " var example = { };\n" + "\n" + " example[attribute] = value;\n" + "\n" + " var documents = internal.db[collection].BY_EXAMPLE_BITARRAY(idx, example).documents;\n" + " for (var j in documents) {\n" + " result.push(documents[j]);\n" + " }\n" + " }\n" + "\n" + " return result;\n" + "}\n" + "\n" + "\n" + "\n" + "\n" + "////////////////////////////////////////////////////////////////////////////////\n" "/// @brief get documents from the specified collection using a skiplist\n" "////////////////////////////////////////////////////////////////////////////////\n" "\n"