mirror of https://gitee.com/bigwinds/arangodb
3167 lines
99 KiB
C++
3167 lines
99 KiB
C++
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief V8-vocbase queries
|
|
///
|
|
/// @file
|
|
///
|
|
/// DISCLAIMER
|
|
///
|
|
/// Copyright 2004-2013 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 Dr. Frank Celler
|
|
/// @author Copyright 2011-2013, triAGENS GmbH, Cologne, Germany
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "v8-query.h"
|
|
|
|
#include "BasicsC/logging.h"
|
|
#include "BasicsC/random.h"
|
|
#include "BasicsC/string-buffer.h"
|
|
#include "GeoIndex/geo-index.h"
|
|
#include "HashIndex/hash-index.h"
|
|
#include "FulltextIndex/fulltext-index.h"
|
|
#include "FulltextIndex/fulltext-result.h"
|
|
#include "FulltextIndex/fulltext-query.h"
|
|
#include "SkipLists/skiplistIndex.h"
|
|
#include "Utils/Barrier.h"
|
|
#include "Utils/CollectionNameResolver.h"
|
|
#include "Utils/EmbeddableTransaction.h"
|
|
#include "Utils/SingleCollectionReadOnlyTransaction.h"
|
|
#include "Utils/V8TransactionContext.h"
|
|
#include "V8/v8-globals.h"
|
|
#include "V8/v8-conv.h"
|
|
#include "V8/v8-utils.h"
|
|
#include "V8Server/v8-vocbase.h"
|
|
#include "VocBase/edge-collection.h"
|
|
|
|
using namespace std;
|
|
using namespace triagens::basics;
|
|
using namespace triagens::arango;
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- private defines
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @addtogroup VocBase
|
|
/// @{
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief shortcut for read-only transaction class type
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#define ReadTransactionType SingleCollectionReadOnlyTransaction<EmbeddableTransaction<V8TransactionContext> >
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief shortcut to wrap a shaped-json object in a read-only transaction
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#define WRAP_SHAPED_JSON(...) TRI_WrapShapedJson<ReadTransactionType>(__VA_ARGS__)
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief free barrier if set
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#define FREE_BARRIER(barrier) \
|
|
if (barrier != 0) { \
|
|
TRI_FreeBarrier(barrier); \
|
|
barrier = 0; \
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @}
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- HELPER FUNCTIONS
|
|
// -----------------------------------------------------------------------------
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- private types
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @addtogroup VocBase
|
|
/// @{
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief geo coordinate container, also containing the distance
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
typedef struct {
|
|
double _distance;
|
|
void const* _data;
|
|
}
|
|
geo_coordinate_distance_t;
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief query types
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
typedef enum {
|
|
QUERY_EXAMPLE,
|
|
QUERY_CONDITION
|
|
}
|
|
query_t;
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @}
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- private functions
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @addtogroup VocBase
|
|
/// @{
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief return an empty result set
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static v8::Handle<v8::Value> EmptyResult () {
|
|
v8::HandleScope scope;
|
|
|
|
v8::Handle<v8::Object> result = v8::Object::New();
|
|
result->Set(v8::String::New("documents"), v8::Array::New());
|
|
result->Set(v8::String::New("total"), v8::Number::New(0));
|
|
result->Set(v8::String::New("count"), v8::Number::New(0));
|
|
|
|
return scope.Close(result);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief extracts skip and limit
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void ExtractSkipAndLimit (v8::Arguments const& argv,
|
|
size_t pos,
|
|
TRI_voc_ssize_t& skip,
|
|
TRI_voc_size_t& limit) {
|
|
|
|
skip = TRI_QRY_NO_SKIP;
|
|
limit = TRI_QRY_NO_LIMIT;
|
|
|
|
if (pos < (size_t) argv.Length() && ! argv[pos]->IsNull() && ! argv[pos]->IsUndefined()) {
|
|
skip = (TRI_voc_size_t) TRI_ObjectToDouble(argv[pos]);
|
|
}
|
|
|
|
if (pos + 1 < (size_t) argv.Length() && ! argv[pos + 1]->IsNull() && ! argv[pos + 1]->IsUndefined()) {
|
|
limit = (TRI_voc_ssize_t) TRI_ObjectToDouble(argv[pos + 1]);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief calculates slice
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void CalculateSkipLimitSlice (size_t length,
|
|
TRI_voc_ssize_t skip,
|
|
TRI_voc_size_t limit,
|
|
size_t& s,
|
|
size_t& e) {
|
|
s = 0;
|
|
e = length;
|
|
|
|
// skip from the beginning
|
|
if (0 < skip) {
|
|
s = (size_t) skip;
|
|
|
|
if (e < s) {
|
|
s = (size_t) e;
|
|
}
|
|
}
|
|
|
|
// skip from the end
|
|
else if (skip < 0) {
|
|
skip = -skip;
|
|
|
|
if ((size_t) skip < e) {
|
|
s = e - skip;
|
|
}
|
|
}
|
|
|
|
// apply limit
|
|
if (s + limit < e) {
|
|
int64_t sum = (int64_t) s + (int64_t) limit;
|
|
if (sum < (int64_t) e) {
|
|
if (sum >= (int64_t) TRI_QRY_NO_LIMIT) {
|
|
e = TRI_QRY_NO_LIMIT;
|
|
}
|
|
else {
|
|
e = (size_t) sum;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief cleans up the example object
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void CleanupExampleObject (TRI_memory_zone_t* zone,
|
|
size_t n,
|
|
TRI_shape_pid_t* pids,
|
|
TRI_shaped_json_t** values) {
|
|
|
|
// clean shaped json objects
|
|
for (size_t j = 0; j < n; ++j) {
|
|
TRI_FreeShapedJson(zone, values[j]);
|
|
}
|
|
|
|
TRI_Free(TRI_UNKNOWN_MEM_ZONE, values);
|
|
|
|
if (pids != 0) {
|
|
TRI_Free(TRI_UNKNOWN_MEM_ZONE, pids);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief sets up the example object
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static int SetupExampleObject (v8::Handle<v8::Object> const& example,
|
|
TRI_shaper_t* shaper,
|
|
size_t& n,
|
|
TRI_shape_pid_t*& pids,
|
|
TRI_shaped_json_t**& values,
|
|
v8::Handle<v8::Object>* err) {
|
|
|
|
// get own properties of example
|
|
v8::Handle<v8::Array> names = example->GetOwnPropertyNames();
|
|
n = names->Length();
|
|
|
|
// setup storage
|
|
pids = (TRI_shape_pid_t*) TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, n * sizeof(TRI_shape_pid_t), false);
|
|
|
|
if (pids == 0) {
|
|
// out of memory
|
|
*err = TRI_CreateErrorObject(TRI_ERROR_OUT_OF_MEMORY);
|
|
|
|
return TRI_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
values = (TRI_shaped_json_t**) TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, n * sizeof(TRI_shaped_json_t*), false);
|
|
|
|
if (values == 0) {
|
|
// out of memory
|
|
TRI_Free(TRI_UNKNOWN_MEM_ZONE, pids);
|
|
pids = 0;
|
|
*err = TRI_CreateErrorObject(TRI_ERROR_OUT_OF_MEMORY);
|
|
|
|
return TRI_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
// convert
|
|
for (size_t i = 0; i < n; ++i) {
|
|
v8::Handle<v8::Value> key = names->Get(i);
|
|
v8::Handle<v8::Value> val = example->Get(key);
|
|
|
|
TRI_Utf8ValueNFC keyStr(TRI_UNKNOWN_MEM_ZONE, key);
|
|
|
|
if (*keyStr != 0) {
|
|
pids[i] = shaper->lookupAttributePathByName(shaper, *keyStr);
|
|
values[i] = TRI_ShapedJsonV8Object(val, shaper, false, false);
|
|
|
|
if (pids[i] == 0 || values[i] == 0) {
|
|
// no attribute path found. this means the result will be empty
|
|
CleanupExampleObject(shaper->_memoryZone, i, pids, values);
|
|
return TRI_RESULT_ELEMENT_NOT_FOUND;
|
|
}
|
|
}
|
|
|
|
if (*keyStr == 0 || pids[i] == 0 || values[i] == 0) {
|
|
CleanupExampleObject(shaper->_memoryZone, i, pids, values);
|
|
|
|
if (*keyStr == 0) {
|
|
*err = TRI_CreateErrorObject(TRI_ERROR_BAD_PARAMETER,
|
|
"cannot convert attribute path to UTF8");
|
|
}
|
|
else {
|
|
*err = TRI_CreateErrorObject(TRI_ERROR_BAD_PARAMETER,
|
|
"cannot convert value to JSON");
|
|
}
|
|
return TRI_ERROR_BAD_PARAMETER;
|
|
}
|
|
}
|
|
|
|
return TRI_ERROR_NO_ERROR;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief sets up the skiplist operator for a skiplist condition query
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static TRI_index_operator_t* SetupConditionsSkiplist (TRI_index_t* idx,
|
|
TRI_shaper_t* shaper,
|
|
v8::Handle<v8::Object> conditions) {
|
|
TRI_index_operator_t* lastOperator = 0;
|
|
size_t numEq = 0;
|
|
size_t lastNonEq = 0;
|
|
|
|
TRI_json_t* parameters = TRI_CreateListJson(TRI_UNKNOWN_MEM_ZONE);
|
|
|
|
if (parameters == 0) {
|
|
return 0;
|
|
}
|
|
|
|
// iterate over all index fields
|
|
for (size_t i = 1; i <= idx->_fields._length; ++i) {
|
|
v8::Handle<v8::String> key = v8::String::New(idx->_fields._buffer[i - 1]);
|
|
|
|
if (! conditions->HasOwnProperty(key)) {
|
|
break;
|
|
}
|
|
v8::Handle<v8::Value> fieldConditions = conditions->Get(key);
|
|
|
|
if (! fieldConditions->IsArray()) {
|
|
// wrong data type for field conditions
|
|
break;
|
|
}
|
|
|
|
// iterator over all conditions
|
|
v8::Handle<v8::Array> values = v8::Handle<v8::Array>::Cast(fieldConditions);
|
|
for (uint32_t j = 0; j < values->Length(); ++j) {
|
|
v8::Handle<v8::Value> fieldCondition = values->Get(j);
|
|
|
|
if (!fieldCondition->IsArray()) {
|
|
// wrong data type for single condition
|
|
goto MEM_ERROR;
|
|
}
|
|
|
|
v8::Handle<v8::Array> condition = v8::Handle<v8::Array>::Cast(fieldCondition);
|
|
|
|
if (condition->Length() != 2) {
|
|
// wrong number of values in single condition
|
|
goto MEM_ERROR;
|
|
}
|
|
|
|
v8::Handle<v8::Value> op = condition->Get(0);
|
|
v8::Handle<v8::Value> value = condition->Get(1);
|
|
|
|
if (!op->IsString()) {
|
|
// wrong operator type
|
|
goto MEM_ERROR;
|
|
}
|
|
|
|
TRI_json_t* json = TRI_ObjectToJson(value);
|
|
|
|
if (json == 0) {
|
|
goto MEM_ERROR;
|
|
}
|
|
|
|
std::string opValue = TRI_ObjectToString(op);
|
|
if (opValue == "==") {
|
|
// equality comparison
|
|
|
|
if (lastNonEq > 0) {
|
|
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json);
|
|
goto MEM_ERROR;
|
|
}
|
|
|
|
TRI_PushBack3ListJson(TRI_UNKNOWN_MEM_ZONE, parameters, json);
|
|
// creation of equality operator is deferred until it is finally needed
|
|
++numEq;
|
|
break;
|
|
}
|
|
else {
|
|
if (lastNonEq > 0 && lastNonEq != i) {
|
|
// if we already had a range condition and a previous field, we cannot continue
|
|
// because the skiplist interface does not support such queries
|
|
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json);
|
|
goto MEM_ERROR;
|
|
}
|
|
|
|
TRI_index_operator_type_e opType;
|
|
if (opValue == ">") {
|
|
opType = TRI_GT_INDEX_OPERATOR;
|
|
}
|
|
else if (opValue == ">=") {
|
|
opType = TRI_GE_INDEX_OPERATOR;
|
|
}
|
|
else if (opValue == "<") {
|
|
opType = TRI_LT_INDEX_OPERATOR;
|
|
}
|
|
else if (opValue == "<=") {
|
|
opType = TRI_LE_INDEX_OPERATOR;
|
|
}
|
|
else {
|
|
// wrong operator type
|
|
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json);
|
|
goto MEM_ERROR;
|
|
}
|
|
|
|
lastNonEq = i;
|
|
|
|
TRI_json_t* cloned = TRI_CopyJson(TRI_UNKNOWN_MEM_ZONE, parameters);
|
|
|
|
if (cloned == 0) {
|
|
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json);
|
|
goto MEM_ERROR;
|
|
}
|
|
|
|
TRI_PushBack3ListJson(TRI_UNKNOWN_MEM_ZONE, cloned, json);
|
|
|
|
if (numEq) {
|
|
// create equality operator if one is in queue
|
|
TRI_json_t* clonedParams = TRI_CopyJson(TRI_UNKNOWN_MEM_ZONE, parameters);
|
|
|
|
if (clonedParams == 0) {
|
|
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, cloned);
|
|
goto MEM_ERROR;
|
|
}
|
|
lastOperator = TRI_CreateIndexOperator(TRI_EQ_INDEX_OPERATOR, NULL, NULL, clonedParams, shaper, NULL, clonedParams->_value._objects._length, NULL);
|
|
numEq = 0;
|
|
}
|
|
|
|
TRI_index_operator_t* current;
|
|
|
|
// create the operator for the current condition
|
|
current = TRI_CreateIndexOperator(opType, NULL, NULL, cloned, shaper, NULL, cloned->_value._objects._length, NULL);
|
|
|
|
if (current == 0) {
|
|
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, cloned);
|
|
goto MEM_ERROR;
|
|
}
|
|
|
|
if (lastOperator == 0) {
|
|
lastOperator = current;
|
|
}
|
|
else {
|
|
// merge the current operator with previous operators using logical AND
|
|
TRI_index_operator_t* newOperator = TRI_CreateIndexOperator(TRI_AND_INDEX_OPERATOR, lastOperator, current, NULL, shaper, NULL, 2, NULL);
|
|
|
|
if (newOperator == 0) {
|
|
TRI_FreeIndexOperator(current);
|
|
goto MEM_ERROR;
|
|
}
|
|
else {
|
|
lastOperator = newOperator;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if (numEq) {
|
|
// create equality operator if one is in queue
|
|
assert(lastOperator == 0);
|
|
assert(lastNonEq == 0);
|
|
|
|
TRI_json_t* clonedParams = TRI_CopyJson(TRI_UNKNOWN_MEM_ZONE, parameters);
|
|
|
|
if (clonedParams == 0) {
|
|
goto MEM_ERROR;
|
|
}
|
|
lastOperator = TRI_CreateIndexOperator(TRI_EQ_INDEX_OPERATOR, NULL, NULL, clonedParams, shaper, NULL, clonedParams->_value._objects._length, NULL);
|
|
}
|
|
|
|
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, parameters);
|
|
|
|
return lastOperator;
|
|
|
|
MEM_ERROR:
|
|
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, parameters);
|
|
|
|
if (lastOperator == 0) {
|
|
TRI_FreeIndexOperator(lastOperator);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief sets up the bitarray operator for a bitarray condition query
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
static TRI_json_t* SetupBitarrayAttributeValuesHelper (TRI_index_t* idx, v8::Handle<v8::Object> attributeValues) {
|
|
TRI_json_t* parameters = TRI_CreateListJson(TRI_UNKNOWN_MEM_ZONE);
|
|
|
|
|
|
// ........................................................................
|
|
// No memory, no problem
|
|
// ........................................................................
|
|
|
|
if (parameters == 0) {
|
|
return 0;
|
|
}
|
|
|
|
|
|
// ........................................................................
|
|
// Client mucked something up?
|
|
// ........................................................................
|
|
|
|
if (! attributeValues->IsObject()) {
|
|
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, parameters);
|
|
return 0;
|
|
}
|
|
|
|
|
|
// ........................................................................
|
|
// Observe that the client can have sent any number of parameters which
|
|
// do not match the list of attributes defined in the index.
|
|
// These parameters are IGNORED -- no error is reported.
|
|
// ........................................................................
|
|
|
|
for (size_t i = 0; i < idx->_fields._length; ++i) {
|
|
v8::Handle<v8::String> key = v8::String::New(idx->_fields._buffer[i]);
|
|
TRI_json_t* json;
|
|
|
|
// ......................................................................
|
|
// The client may have sent values for all of the Attributes or for
|
|
// a subset of them. If the value for an Attribute is missing, then we
|
|
// assume that the client wishes to IGNORE the value of that Attribute.
|
|
// In the later case, we add the json object 'TRI_JSON_UNUSED' to
|
|
// indicate that this attribute is to be ignored. Notice that it is
|
|
// possible to ignore all the attributes defined as part of the index.
|
|
// ......................................................................
|
|
|
|
|
|
if (attributeValues->HasOwnProperty(key)) {
|
|
|
|
// ....................................................................
|
|
// for this index attribute, there is such an attribute given as a
|
|
// as a parameter by the client -- determine the value (or values)
|
|
// of this attribute parameter and store it for later use in the
|
|
// lookup
|
|
// ....................................................................
|
|
|
|
v8::Handle<v8::Value> value = attributeValues->Get(key);
|
|
json = TRI_ObjectToJson(value);
|
|
|
|
if (json == 0) {
|
|
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, parameters);
|
|
return 0;
|
|
}
|
|
|
|
|
|
// ....................................................................
|
|
// special case: if client sent {"x":[],...}, then we wrap this up
|
|
// as {"x":[ [] ],...}.
|
|
// ....................................................................
|
|
|
|
if (json->_type == TRI_JSON_LIST) {
|
|
if (json->_value._objects._length == 0) {
|
|
TRI_json_t emptyList;
|
|
emptyList._type = TRI_JSON_LIST;
|
|
TRI_InitVector(&(emptyList._value._objects), TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_json_t));
|
|
TRI_PushBack2ListJson(json, &emptyList);
|
|
}
|
|
}
|
|
}
|
|
|
|
else {
|
|
// ....................................................................
|
|
// for this index attribute we can not locate it in the list of parameters
|
|
// sent to us by the client. Assign it an 'unused' (perhaps should be
|
|
// renamed to 'unknown' or 'undefined').
|
|
// ....................................................................
|
|
json = (TRI_json_t*) TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_json_t), true);
|
|
|
|
if (json == 0) {
|
|
// OOM
|
|
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, parameters);
|
|
return 0;
|
|
}
|
|
|
|
json->_type = TRI_JSON_UNUSED;
|
|
}
|
|
|
|
|
|
// ......................................................................
|
|
// Check and ensure we have a json object defined before we store it.
|
|
// ......................................................................
|
|
|
|
assert(json != 0);
|
|
|
|
// ......................................................................
|
|
// store it in an list json object -- eventually wil be stored as part
|
|
// of the index operator.
|
|
// ......................................................................
|
|
|
|
TRI_PushBack3ListJson(TRI_UNKNOWN_MEM_ZONE, parameters, json);
|
|
}
|
|
|
|
|
|
return parameters;
|
|
}
|
|
|
|
|
|
static TRI_index_operator_t* SetupConditionsBitarrayHelper (TRI_index_t* idx,
|
|
TRI_shaper_t* shaper,
|
|
v8::Handle<v8::Object> condition) {
|
|
|
|
v8::Handle<v8::Value> value;
|
|
TRI_index_operator_type_e operatorType;
|
|
TRI_index_operator_t* indexOperator = 0;
|
|
|
|
|
|
// ........................................................................
|
|
// 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("&"));
|
|
}
|
|
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"));
|
|
}
|
|
// ........................................................................
|
|
// 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;
|
|
}
|
|
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;
|
|
}
|
|
// ........................................................................
|
|
// 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;
|
|
}
|
|
else if (condition->HasOwnProperty(v8::String::New("not"))) {
|
|
value = condition->Get(v8::String::New("not"));
|
|
operatorType = TRI_NOT_INDEX_OPERATOR;
|
|
}
|
|
// ........................................................................
|
|
// 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;
|
|
}
|
|
else if (condition->HasOwnProperty(v8::String::New("="))) {
|
|
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, act upon it
|
|
// may require recursion
|
|
// ........................................................................
|
|
|
|
switch (operatorType) {
|
|
|
|
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<v8::Array> andValues = v8::Handle<v8::Array>::Cast(value);
|
|
|
|
|
|
// ....................................................................
|
|
// Check the length of the array to ensure that it is exactly 2
|
|
// ....................................................................
|
|
|
|
if (andValues->Length() != 2) {
|
|
return 0;
|
|
}
|
|
|
|
v8::Handle<v8::Value> leftValue = andValues->Get(0);
|
|
v8::Handle<v8::Value> rightValue = andValues->Get(1);
|
|
|
|
if (!leftValue->IsObject() || !rightValue->IsObject()) {
|
|
return 0;
|
|
}
|
|
|
|
v8::Handle<v8::Object> leftObject = v8::Handle<v8::Object>::Cast(leftValue);
|
|
v8::Handle<v8::Object> rightObject = v8::Handle<v8::Object>::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);
|
|
|
|
if (leftOp == 0 || rightOp == 0) {
|
|
TRI_FreeIndexOperator(leftOp);
|
|
TRI_FreeIndexOperator(rightOp);
|
|
return 0;
|
|
}
|
|
|
|
indexOperator = TRI_CreateIndexOperator(operatorType, leftOp, rightOp, NULL, shaper, NULL, 0, NULL);
|
|
break;
|
|
}
|
|
|
|
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<v8::Object> leftObject = v8::Handle<v8::Object>::Cast(value);
|
|
|
|
|
|
// ....................................................................
|
|
// recurse the left and only operator
|
|
// ....................................................................
|
|
TRI_index_operator_t* leftOp = SetupConditionsBitarrayHelper(idx, shaper, leftObject);
|
|
|
|
if (leftOp == 0) {
|
|
return 0;
|
|
}
|
|
|
|
indexOperator = TRI_CreateIndexOperator(operatorType, leftOp, NULL, NULL, shaper, NULL, 0, NULL);
|
|
break;
|
|
}
|
|
|
|
case TRI_EQ_INDEX_OPERATOR:
|
|
case TRI_NE_INDEX_OPERATOR:
|
|
case TRI_LE_INDEX_OPERATOR:
|
|
case TRI_LT_INDEX_OPERATOR:
|
|
case TRI_GE_INDEX_OPERATOR:
|
|
case TRI_GT_INDEX_OPERATOR: {
|
|
|
|
v8::Handle<v8::Object> leftObject = v8::Handle<v8::Object>::Cast(value);
|
|
TRI_json_t* parameters = SetupBitarrayAttributeValuesHelper(idx, leftObject);
|
|
|
|
if (parameters == 0) {
|
|
return 0;
|
|
}
|
|
indexOperator = TRI_CreateIndexOperator(operatorType, NULL, NULL, parameters, shaper, NULL, parameters->_value._objects._length, NULL);
|
|
break;
|
|
}
|
|
|
|
|
|
default: {
|
|
return 0;
|
|
}
|
|
|
|
} // end of switch (operatorType)
|
|
|
|
return indexOperator;
|
|
}
|
|
|
|
|
|
static TRI_index_operator_t* SetupConditionsBitarray (TRI_index_t* idx,
|
|
TRI_shaper_t* shaper,
|
|
v8::Handle<v8::Object> condition) {
|
|
TRI_index_operator_t* indexOperator = SetupConditionsBitarrayHelper(idx, shaper, condition);
|
|
return indexOperator;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief sets up the skiplist operator for a skiplist example query
|
|
///
|
|
/// this will set up a JSON container with the example values as a list
|
|
/// at the end, one skiplist equality operator is created for the entire list
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static TRI_index_operator_t* SetupExampleSkiplist (TRI_index_t* idx,
|
|
TRI_shaper_t* shaper,
|
|
v8::Handle<v8::Object> example) {
|
|
TRI_json_t* parameters = TRI_CreateListJson(TRI_UNKNOWN_MEM_ZONE);
|
|
|
|
if (parameters == 0) {
|
|
return 0;
|
|
}
|
|
|
|
for (size_t i = 0; i < idx->_fields._length; ++i) {
|
|
v8::Handle<v8::String> key = v8::String::New(idx->_fields._buffer[i]);
|
|
|
|
if (! example->HasOwnProperty(key)) {
|
|
break;
|
|
}
|
|
|
|
v8::Handle<v8::Value> value = example->Get(key);
|
|
|
|
TRI_json_t* json = TRI_ObjectToJson(value);
|
|
|
|
if (json == 0) {
|
|
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, parameters);
|
|
|
|
return 0;
|
|
}
|
|
|
|
TRI_PushBack3ListJson(TRI_UNKNOWN_MEM_ZONE, parameters, json);
|
|
}
|
|
|
|
if (parameters->_value._objects._length > 0) {
|
|
// example means equality comparisons only
|
|
return TRI_CreateIndexOperator(TRI_EQ_INDEX_OPERATOR, NULL, NULL, parameters, shaper, NULL, parameters->_value._objects._length, NULL);
|
|
}
|
|
|
|
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, parameters);
|
|
return 0;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief creates an index operator for a bitarray example query
|
|
///
|
|
/// this will set up a JSON container with the example values as a list
|
|
/// at the end, one skiplist equality operator is created for the entire list
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static TRI_index_operator_t* SetupExampleBitarray (TRI_index_t* idx, TRI_shaper_t* shaper, v8::Handle<v8::Object> example) {
|
|
TRI_json_t* parameters = SetupBitarrayAttributeValuesHelper(idx, example);
|
|
|
|
if (parameters == 0) {
|
|
return 0;
|
|
}
|
|
|
|
// for an example query, we can only assume equality operator is required.
|
|
return TRI_CreateIndexOperator(TRI_EQ_INDEX_OPERATOR, NULL, NULL, parameters, shaper, NULL, parameters->_value._objects._length, NULL);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief destroys the example object for a hash index
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void DestroySearchValue (TRI_memory_zone_t* zone,
|
|
TRI_index_search_value_t& value) {
|
|
size_t n;
|
|
|
|
n = value._length;
|
|
|
|
for (size_t j = 0; j < n; ++j) {
|
|
TRI_DestroyShapedJson(zone, &value._values[j]);
|
|
}
|
|
|
|
TRI_Free(TRI_CORE_MEM_ZONE, value._values);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief sets up the example object for a hash index
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static int SetupSearchValue (TRI_vector_t const* paths,
|
|
v8::Handle<v8::Object> example,
|
|
TRI_shaper_t* shaper,
|
|
TRI_index_search_value_t& result,
|
|
v8::Handle<v8::Object>* err) {
|
|
size_t n;
|
|
|
|
// extract attribute paths
|
|
n = paths->_length;
|
|
|
|
// setup storage
|
|
result._length = n;
|
|
result._values = (TRI_shaped_json_t*) TRI_Allocate(TRI_CORE_MEM_ZONE,
|
|
n * sizeof(TRI_shaped_json_t),
|
|
true);
|
|
|
|
// convert
|
|
for (size_t i = 0; i < n; ++i) {
|
|
TRI_shape_pid_t pid = * (TRI_shape_pid_t*) TRI_AtVector(paths, i);
|
|
|
|
assert(pid != 0);
|
|
char const* name = TRI_AttributeNameShapePid(shaper, pid);
|
|
|
|
if (name == NULL) {
|
|
DestroySearchValue(shaper->_memoryZone, result);
|
|
*err = TRI_CreateErrorObject(TRI_ERROR_INTERNAL, "shaper failed");
|
|
return TRI_ERROR_BAD_PARAMETER;
|
|
}
|
|
|
|
v8::Handle<v8::String> key = v8::String::New(name);
|
|
int res;
|
|
|
|
if (example->HasOwnProperty(key)) {
|
|
v8::Handle<v8::Value> val = example->Get(key);
|
|
|
|
res = TRI_FillShapedJsonV8Object(val, &result._values[i], shaper, false, false);
|
|
}
|
|
else {
|
|
res = TRI_FillShapedJsonV8Object(v8::Null(), &result._values[i], shaper, false, false);
|
|
}
|
|
|
|
if (res != TRI_ERROR_NO_ERROR) {
|
|
DestroySearchValue(shaper->_memoryZone, result);
|
|
|
|
if (res != TRI_RESULT_ELEMENT_NOT_FOUND) {
|
|
*err = TRI_CreateErrorObject(res, "cannot convert value to JSON");
|
|
}
|
|
return res;
|
|
}
|
|
}
|
|
|
|
return TRI_ERROR_NO_ERROR;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief execute a skiplist query (by condition or by example)
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static v8::Handle<v8::Value> ExecuteSkiplistQuery (v8::Arguments const& argv,
|
|
std::string const& signature,
|
|
const query_t type) {
|
|
v8::HandleScope scope;
|
|
|
|
// expecting index, example, skip, and limit
|
|
if (argv.Length() < 2) {
|
|
TRI_V8_EXCEPTION_USAGE(scope, signature.c_str());
|
|
}
|
|
|
|
if (! argv[1]->IsObject()) {
|
|
std::string msg;
|
|
|
|
if (type == QUERY_EXAMPLE) {
|
|
msg = "<example> must be an object";
|
|
}
|
|
else {
|
|
msg = "<conditions> must be an object";
|
|
}
|
|
|
|
TRI_V8_TYPE_ERROR(scope, msg.c_str());
|
|
}
|
|
|
|
|
|
TRI_vocbase_col_t const* col;
|
|
col = TRI_UnwrapClass<TRI_vocbase_col_t>(argv.Holder(), TRI_GetVocBaseColType());
|
|
|
|
if (col == 0) {
|
|
TRI_V8_EXCEPTION_INTERNAL(scope, "cannot extract collection");
|
|
}
|
|
|
|
TRI_SHARDING_COLLECTION_NOT_YET_IMPLEMENTED(scope, col);
|
|
|
|
CollectionNameResolver resolver(col->_vocbase);
|
|
ReadTransactionType trx(col->_vocbase, resolver, col->_cid);
|
|
int res = trx.begin();
|
|
|
|
if (res != TRI_ERROR_NO_ERROR) {
|
|
TRI_V8_EXCEPTION(scope, res);
|
|
}
|
|
|
|
v8::Handle<v8::Object> err;
|
|
|
|
TRI_primary_collection_t* primary = trx.primaryCollection();
|
|
TRI_shaper_t* shaper = primary->_shaper;
|
|
|
|
// extract skip and limit
|
|
TRI_voc_ssize_t skip;
|
|
TRI_voc_size_t limit;
|
|
|
|
ExtractSkipAndLimit(argv, 2, skip, limit);
|
|
|
|
// setup result
|
|
v8::Handle<v8::Object> result = v8::Object::New();
|
|
|
|
v8::Handle<v8::Array> documents = v8::Array::New();
|
|
result->Set(v8::String::New("documents"), documents);
|
|
|
|
// .............................................................................
|
|
// inside a read transaction
|
|
// .............................................................................
|
|
|
|
trx.lockRead();
|
|
|
|
|
|
// extract the index
|
|
TRI_index_t* idx = TRI_LookupIndexByHandle(col, argv[0], false, &err);
|
|
|
|
if (idx == 0) {
|
|
return scope.Close(v8::ThrowException(err));
|
|
}
|
|
|
|
if (idx->_type != TRI_IDX_TYPE_SKIPLIST_INDEX) {
|
|
TRI_V8_EXCEPTION(scope, TRI_ERROR_ARANGO_NO_INDEX);
|
|
}
|
|
|
|
TRI_index_operator_t* skiplistOperator;
|
|
v8::Handle<v8::Object> values = argv[1]->ToObject();
|
|
|
|
if (type == QUERY_EXAMPLE) {
|
|
skiplistOperator = SetupExampleSkiplist(idx, shaper, values);
|
|
}
|
|
else {
|
|
skiplistOperator = SetupConditionsSkiplist(idx, shaper, values);
|
|
}
|
|
|
|
if (skiplistOperator == 0) {
|
|
TRI_V8_EXCEPTION(scope, TRI_ERROR_BAD_PARAMETER);
|
|
}
|
|
|
|
TRI_skiplist_iterator_t* skiplistIterator = TRI_LookupSkiplistIndex(idx, skiplistOperator);
|
|
|
|
if (skiplistIterator == 0) {
|
|
int res = TRI_errno();
|
|
if (res == TRI_RESULT_ELEMENT_NOT_FOUND) {
|
|
return scope.Close(EmptyResult());
|
|
}
|
|
|
|
TRI_V8_EXCEPTION(scope, TRI_ERROR_ARANGO_NO_INDEX);
|
|
}
|
|
|
|
TRI_voc_ssize_t total = 0;
|
|
TRI_voc_size_t count = 0;
|
|
bool error = false;
|
|
|
|
TRI_barrier_t* barrier = TRI_CreateBarrierElement(&primary->_barrierList);
|
|
|
|
if (barrier == 0) {
|
|
TRI_FreeSkiplistIterator(skiplistIterator);
|
|
TRI_V8_EXCEPTION_MEMORY(scope);
|
|
}
|
|
|
|
assert(barrier != 0);
|
|
|
|
while (true) {
|
|
TRI_skiplist_index_element_t* indexElement = skiplistIterator->_next(skiplistIterator);
|
|
|
|
if (indexElement == NULL) {
|
|
break;
|
|
}
|
|
|
|
++total;
|
|
|
|
if (total > skip && count < limit) {
|
|
v8::Handle<v8::Value> doc = WRAP_SHAPED_JSON(trx, col->_cid, (TRI_doc_mptr_t const*) indexElement->_document, barrier);
|
|
|
|
if (doc.IsEmpty()) {
|
|
error = true;
|
|
break;
|
|
}
|
|
else {
|
|
documents->Set(count, doc);
|
|
++count;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
trx.finish(res);
|
|
|
|
// .............................................................................
|
|
// outside a write transaction
|
|
// .............................................................................
|
|
|
|
// free data allocated by skiplist index result
|
|
TRI_FreeSkiplistIterator(skiplistIterator);
|
|
|
|
result->Set(v8::String::New("total"), v8::Number::New((double) total));
|
|
result->Set(v8::String::New("count"), v8::Number::New(count));
|
|
|
|
if (error) {
|
|
FREE_BARRIER(barrier);
|
|
TRI_V8_EXCEPTION_MEMORY(scope);
|
|
}
|
|
|
|
if (count == 0) {
|
|
FREE_BARRIER(barrier);
|
|
}
|
|
|
|
return scope.Close(result);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief execute a bitarray index query (by condition or by example)
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// Example of a filter associated with an interator
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static bool BitarrayFilterExample (TRI_index_iterator_t* indexIterator) {
|
|
TRI_doc_mptr_t* indexElement;
|
|
TRI_bitarray_index_t* baIndex;
|
|
|
|
indexElement = (TRI_doc_mptr_t*) indexIterator->_next(indexIterator);
|
|
|
|
if (indexElement == NULL) {
|
|
return false;
|
|
}
|
|
|
|
baIndex = (TRI_bitarray_index_t*) indexIterator->_index;
|
|
|
|
if (baIndex == NULL) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static v8::Handle<v8::Value> ExecuteBitarrayQuery (v8::Arguments const& argv,
|
|
std::string const& signature,
|
|
const query_t type) {
|
|
v8::HandleScope scope;
|
|
|
|
// ...........................................................................
|
|
// Check the parameters, expecting index, example, skip, and limit
|
|
// e.g. ("110597/962565", {"x":1}, null, null)
|
|
// ...........................................................................
|
|
|
|
if (argv.Length() < 2) {
|
|
TRI_V8_EXCEPTION_USAGE(scope, signature.c_str());
|
|
}
|
|
|
|
|
|
// ...........................................................................
|
|
// Check that the second parameter is an associative array (json object)
|
|
// ...........................................................................
|
|
|
|
if (! argv[1]->IsObject()) {
|
|
std::string msg;
|
|
|
|
if (type == QUERY_EXAMPLE) {
|
|
msg = "<example> must be an object";
|
|
}
|
|
else {
|
|
msg = "<conditions> must be an object";
|
|
}
|
|
|
|
TRI_V8_EXCEPTION_PARAMETER(scope, msg);
|
|
}
|
|
|
|
|
|
// .............................................................................
|
|
// extract skip and limit
|
|
// .............................................................................
|
|
|
|
TRI_voc_ssize_t skip;
|
|
TRI_voc_size_t limit;
|
|
ExtractSkipAndLimit(argv, 2, skip, limit);
|
|
|
|
|
|
TRI_vocbase_col_t const* col;
|
|
col = TRI_UnwrapClass<TRI_vocbase_col_t>(argv.Holder(), TRI_GetVocBaseColType());
|
|
|
|
if (col == 0) {
|
|
TRI_V8_EXCEPTION_INTERNAL(scope, "cannot extract collection");
|
|
}
|
|
|
|
TRI_SHARDING_COLLECTION_NOT_YET_IMPLEMENTED(scope, col);
|
|
|
|
CollectionNameResolver resolver(col->_vocbase);
|
|
ReadTransactionType trx(col->_vocbase, resolver, col->_cid);
|
|
|
|
int res = trx.begin();
|
|
|
|
if (res != TRI_ERROR_NO_ERROR) {
|
|
TRI_V8_EXCEPTION(scope, res);
|
|
}
|
|
|
|
TRI_primary_collection_t* primary = trx.primaryCollection();
|
|
TRI_shaper_t* shaper = primary->_shaper;
|
|
|
|
// .............................................................................
|
|
// Create the json object result which stores documents located
|
|
// .............................................................................
|
|
|
|
v8::Handle<v8::Object> result = v8::Object::New();
|
|
|
|
// .............................................................................
|
|
// Create the array to store documents located
|
|
// .............................................................................
|
|
|
|
v8::Handle<v8::Array> documents = v8::Array::New();
|
|
result->Set(v8::String::New("documents"), documents);
|
|
|
|
|
|
// .............................................................................
|
|
// inside a read transaction
|
|
// .............................................................................
|
|
|
|
trx.lockRead();
|
|
|
|
// .............................................................................
|
|
// extract the index
|
|
// .............................................................................
|
|
|
|
v8::Handle<v8::Object> err;
|
|
TRI_index_t* idx = TRI_LookupIndexByHandle(col, argv[0], false, &err);
|
|
|
|
if (idx == 0) {
|
|
return scope.Close(v8::ThrowException(err));
|
|
}
|
|
|
|
if (idx->_type != TRI_IDX_TYPE_BITARRAY_INDEX) {
|
|
TRI_V8_EXCEPTION(scope, TRI_ERROR_ARANGO_NO_INDEX);
|
|
}
|
|
|
|
|
|
TRI_index_operator_t* indexOperator;
|
|
v8::Handle<v8::Object> values = argv[1]->ToObject();
|
|
if (type == QUERY_EXAMPLE) {
|
|
indexOperator = SetupExampleBitarray(idx, shaper, values);
|
|
}
|
|
else {
|
|
indexOperator = SetupConditionsBitarray(idx, shaper, values);
|
|
}
|
|
|
|
if (indexOperator == 0) { // something wrong
|
|
TRI_V8_EXCEPTION(scope, TRI_ERROR_BAD_PARAMETER);
|
|
}
|
|
|
|
// .............................................................................
|
|
// attempt to locate the documents
|
|
// .............................................................................
|
|
|
|
TRI_index_iterator_t* indexIterator = TRI_LookupBitarrayIndex(idx, indexOperator, BitarrayFilterExample);
|
|
|
|
// .............................................................................
|
|
// Take care of the case where the index iterator is returned as NULL -- may
|
|
// occur when some catastrophic error occurs.
|
|
// .............................................................................
|
|
|
|
TRI_voc_ssize_t total = 0;
|
|
TRI_voc_size_t count = 0;
|
|
bool error = false;
|
|
|
|
TRI_barrier_t* barrier = TRI_CreateBarrierElement(&primary->_barrierList);
|
|
|
|
if (barrier == 0) {
|
|
if (indexIterator != NULL) {
|
|
TRI_FreeIndexIterator(indexIterator);
|
|
}
|
|
TRI_V8_EXCEPTION_MEMORY(scope);
|
|
}
|
|
|
|
assert(barrier != 0);
|
|
|
|
if (indexIterator != NULL) {
|
|
while (true) {
|
|
TRI_doc_mptr_t* data = (TRI_doc_mptr_t*) indexIterator->_next(indexIterator);
|
|
|
|
if (data == NULL) {
|
|
break;
|
|
}
|
|
|
|
++total;
|
|
|
|
if (total > skip && count < limit) {
|
|
v8::Handle<v8::Value> doc = WRAP_SHAPED_JSON(trx, col->_cid, data, barrier);
|
|
|
|
if (doc.IsEmpty()) {
|
|
error = true;
|
|
break;
|
|
}
|
|
else {
|
|
documents->Set(count, doc);
|
|
++count;
|
|
}
|
|
}
|
|
}
|
|
|
|
// free data allocated by index result
|
|
TRI_FreeIndexIterator(indexIterator);
|
|
}
|
|
|
|
else {
|
|
LOG_WARNING("index iterator returned with a NULL value in ExecuteBitarrayQuery");
|
|
// return an empty list
|
|
}
|
|
|
|
trx.finish(res);
|
|
|
|
// .............................................................................
|
|
// outside a write transaction
|
|
// .............................................................................
|
|
|
|
|
|
result->Set(v8::String::New("total"), v8::Number::New((double) total));
|
|
result->Set(v8::String::New("count"), v8::Number::New(count));
|
|
|
|
if (error) {
|
|
FREE_BARRIER(barrier);
|
|
TRI_V8_EXCEPTION_MEMORY(scope);
|
|
}
|
|
|
|
if (count == 0) {
|
|
FREE_BARRIER(barrier);
|
|
}
|
|
|
|
return scope.Close(result);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief sorts geo coordinates
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static int CompareGeoCoordinateDistance (geo_coordinate_distance_t* left,
|
|
geo_coordinate_distance_t* right) {
|
|
if (left->_distance < right->_distance) {
|
|
return -1;
|
|
}
|
|
else if (left->_distance > right->_distance) {
|
|
return 1;
|
|
}
|
|
else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
#define FSRT_INSTANCE SortGeo
|
|
#define FSRT_NAME SortGeoCoordinates
|
|
#define FSRT_NAM2 SortGeoCoordinatesTmp
|
|
#define FSRT_TYPE geo_coordinate_distance_t
|
|
|
|
#define FSRT_COMP(l,r,s) CompareGeoCoordinateDistance(l,r)
|
|
|
|
uint32_t SortGeoFSRT_Rand = 0;
|
|
|
|
static uint32_t SortGeoRandomGenerator (void) {
|
|
return (SortGeoFSRT_Rand = SortGeoFSRT_Rand * 31415 + 27818);
|
|
}
|
|
|
|
#define FSRT__RAND ((fs_b) + FSRT__UNIT * (SortGeoRandomGenerator() % FSRT__DIST(fs_e,fs_b,FSRT__SIZE)))
|
|
|
|
#include "BasicsC/fsrt.inc"
|
|
|
|
#undef FSRT__RAND
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief creates a geo result
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static int StoreGeoResult (ReadTransactionType& trx,
|
|
TRI_vocbase_col_t const* collection,
|
|
GeoCoordinates* cors,
|
|
v8::Handle<v8::Array>& documents,
|
|
v8::Handle<v8::Array>& distances) {
|
|
GeoCoordinate* end;
|
|
GeoCoordinate* ptr;
|
|
double* dtr;
|
|
geo_coordinate_distance_t* gnd;
|
|
geo_coordinate_distance_t* gtr;
|
|
geo_coordinate_distance_t* tmp;
|
|
size_t n;
|
|
uint32_t i;
|
|
TRI_barrier_t* barrier;
|
|
|
|
// sort the result
|
|
n = cors->length;
|
|
|
|
if (n == 0) {
|
|
GeoIndex_CoordinatesFree(cors);
|
|
return TRI_ERROR_NO_ERROR;
|
|
}
|
|
|
|
gtr = (tmp = (geo_coordinate_distance_t*) TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(geo_coordinate_distance_t) * n, false));
|
|
if (gtr == 0) {
|
|
GeoIndex_CoordinatesFree(cors);
|
|
|
|
return TRI_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
gnd = tmp + n;
|
|
|
|
ptr = cors->coordinates;
|
|
end = cors->coordinates + n;
|
|
|
|
dtr = cors->distances;
|
|
|
|
for (; ptr < end; ++ptr, ++dtr, ++gtr) {
|
|
gtr->_distance = *dtr;
|
|
gtr->_data = ptr->data;
|
|
}
|
|
|
|
GeoIndex_CoordinatesFree(cors);
|
|
|
|
SortGeoCoordinates(tmp, gnd);
|
|
|
|
barrier = TRI_CreateBarrierElement(&((TRI_primary_collection_t*) collection->_collection)->_barrierList);
|
|
|
|
if (barrier == 0) {
|
|
TRI_Free(TRI_UNKNOWN_MEM_ZONE, tmp);
|
|
|
|
return TRI_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
// copy the documents
|
|
bool error = false;
|
|
for (gtr = tmp, i = 0; gtr < gnd; ++gtr, ++i) {
|
|
v8::Handle<v8::Value> doc = WRAP_SHAPED_JSON(trx, collection->_cid, (TRI_doc_mptr_t const*) gtr->_data, barrier);
|
|
|
|
if (doc.IsEmpty()) {
|
|
error = true;
|
|
break;
|
|
}
|
|
|
|
documents->Set(i, doc);
|
|
distances->Set(i, v8::Number::New(gtr->_distance));
|
|
}
|
|
|
|
TRI_Free(TRI_UNKNOWN_MEM_ZONE, tmp);
|
|
|
|
if (error) {
|
|
FREE_BARRIER(barrier);
|
|
return TRI_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
return TRI_ERROR_NO_ERROR;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @}
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- QUERY FUNCTIONS
|
|
// -----------------------------------------------------------------------------
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- private functions
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @addtogroup VocBase
|
|
/// @{
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief looks up edges for given direction
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static v8::Handle<v8::Value> EdgesQuery (TRI_edge_direction_e direction,
|
|
v8::Arguments const& argv) {
|
|
v8::HandleScope scope;
|
|
|
|
TRI_vocbase_col_t const* col;
|
|
col = TRI_UnwrapClass<TRI_vocbase_col_t>(argv.Holder(), TRI_GetVocBaseColType());
|
|
|
|
if (col == 0) {
|
|
TRI_V8_EXCEPTION_INTERNAL(scope, "cannot extract collection");
|
|
}
|
|
|
|
TRI_SHARDING_COLLECTION_NOT_YET_IMPLEMENTED(scope, col);
|
|
|
|
if (col->_type != TRI_COL_TYPE_EDGE) {
|
|
TRI_V8_EXCEPTION(scope, TRI_ERROR_ARANGO_COLLECTION_TYPE_INVALID);
|
|
}
|
|
|
|
CollectionNameResolver resolver(col->_vocbase);
|
|
ReadTransactionType trx(col->_vocbase, resolver, col->_cid);
|
|
|
|
int res = trx.begin();
|
|
|
|
if (res != TRI_ERROR_NO_ERROR) {
|
|
TRI_V8_EXCEPTION(scope, res);
|
|
}
|
|
|
|
TRI_primary_collection_t* primary = trx.primaryCollection();
|
|
|
|
// first and only argument schould be a list of document idenfifier
|
|
if (argv.Length() != 1) {
|
|
switch (direction) {
|
|
case TRI_EDGE_IN:
|
|
TRI_V8_EXCEPTION_USAGE(scope, "inEdges(<vertices>)");
|
|
|
|
case TRI_EDGE_OUT:
|
|
TRI_V8_EXCEPTION_USAGE(scope, "outEdges(<vertices>)");
|
|
|
|
case TRI_EDGE_ANY:
|
|
default: {
|
|
TRI_V8_EXCEPTION_USAGE(scope, "edges(<vertices>)");
|
|
}
|
|
}
|
|
}
|
|
|
|
// setup result
|
|
v8::Handle<v8::Array> documents = v8::Array::New();
|
|
|
|
// .............................................................................
|
|
// inside a read transaction
|
|
// .............................................................................
|
|
|
|
trx.lockRead();
|
|
|
|
uint32_t count = 0;
|
|
bool error = false;
|
|
|
|
TRI_barrier_t* barrier = TRI_CreateBarrierElement(&primary->_barrierList);
|
|
|
|
if (barrier == 0) {
|
|
TRI_V8_EXCEPTION_MEMORY(scope);
|
|
}
|
|
|
|
// argument is a list of vertices
|
|
if (argv[0]->IsArray()) {
|
|
v8::Handle<v8::Array> vertices = v8::Handle<v8::Array>::Cast(argv[0]);
|
|
const uint32_t len = vertices->Length();
|
|
|
|
for (uint32_t i = 0; i < len; ++i) {
|
|
TRI_vector_pointer_t edges;
|
|
TRI_voc_cid_t cid;
|
|
TRI_voc_key_t key = 0;
|
|
|
|
res = TRI_ParseVertex(resolver, cid, key, vertices->Get(i), true);
|
|
|
|
if (res != TRI_ERROR_NO_ERROR) {
|
|
// error is just ignored
|
|
continue;
|
|
}
|
|
|
|
edges = TRI_LookupEdgesDocumentCollection((TRI_document_collection_t*) primary, direction, cid, key);
|
|
|
|
if (key != 0) {
|
|
TRI_FreeString(TRI_CORE_MEM_ZONE, key);
|
|
}
|
|
|
|
for (size_t j = 0; j < edges._length; ++j) {
|
|
v8::Handle<v8::Value> doc = WRAP_SHAPED_JSON(trx, col->_cid, (TRI_doc_mptr_t const*) edges._buffer[j], barrier);
|
|
|
|
if (doc.IsEmpty()) {
|
|
// error
|
|
error = true;
|
|
break;
|
|
}
|
|
else {
|
|
documents->Set(count, doc);
|
|
++count;
|
|
}
|
|
|
|
}
|
|
|
|
TRI_DestroyVectorPointer(&edges);
|
|
|
|
if (error) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// argument is a single vertex
|
|
else {
|
|
TRI_vector_pointer_t edges;
|
|
|
|
TRI_voc_key_t key = 0;
|
|
TRI_voc_cid_t cid;
|
|
|
|
res = TRI_ParseVertex(resolver, cid, key, argv[0], true);
|
|
|
|
if (res != TRI_ERROR_NO_ERROR) {
|
|
FREE_BARRIER(barrier);
|
|
TRI_V8_EXCEPTION(scope, res);
|
|
}
|
|
|
|
edges = TRI_LookupEdgesDocumentCollection((TRI_document_collection_t*) primary, direction, cid, key);
|
|
|
|
if (key != 0) {
|
|
TRI_FreeString(TRI_CORE_MEM_ZONE, key);
|
|
}
|
|
|
|
for (size_t j = 0; j < edges._length; ++j) {
|
|
v8::Handle<v8::Value> doc = WRAP_SHAPED_JSON(trx, col->_cid, (TRI_doc_mptr_t const*) edges._buffer[j], barrier);
|
|
|
|
if (doc.IsEmpty()) {
|
|
error = true;
|
|
break;
|
|
}
|
|
else {
|
|
documents->Set(count, doc);
|
|
++count;
|
|
}
|
|
}
|
|
|
|
TRI_DestroyVectorPointer(&edges);
|
|
}
|
|
|
|
trx.finish(res);
|
|
|
|
// .............................................................................
|
|
// outside a read transaction
|
|
// .............................................................................
|
|
|
|
if (error) {
|
|
FREE_BARRIER(barrier);
|
|
TRI_V8_EXCEPTION_MEMORY(scope);
|
|
}
|
|
|
|
if (count == 0) {
|
|
FREE_BARRIER(barrier);
|
|
}
|
|
|
|
return scope.Close(documents);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @}
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- javascript functions
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @addtogroup VocBase
|
|
/// @{
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief selects all documents from a collection
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static v8::Handle<v8::Value> JS_AllQuery (v8::Arguments const& argv) {
|
|
v8::HandleScope scope;
|
|
|
|
// expecting two arguments
|
|
if (argv.Length() != 2) {
|
|
TRI_V8_EXCEPTION_USAGE(scope, "ALL(<skip>, <limit>)");
|
|
}
|
|
|
|
TRI_vocbase_col_t const* col;
|
|
col = TRI_UnwrapClass<TRI_vocbase_col_t>(argv.Holder(), TRI_GetVocBaseColType());
|
|
|
|
if (col == 0) {
|
|
TRI_V8_EXCEPTION_INTERNAL(scope, "cannot extract collection");
|
|
}
|
|
|
|
TRI_SHARDING_COLLECTION_NOT_YET_IMPLEMENTED(scope, col);
|
|
|
|
// extract skip and limit
|
|
TRI_voc_ssize_t skip;
|
|
TRI_voc_size_t limit;
|
|
ExtractSkipAndLimit(argv, 0, skip, limit);
|
|
|
|
TRI_barrier_t* barrier = 0;
|
|
uint32_t total = 0;
|
|
vector<TRI_doc_mptr_t> docs;
|
|
|
|
CollectionNameResolver resolver(col->_vocbase);
|
|
ReadTransactionType trx(col->_vocbase, resolver, col->_cid);
|
|
|
|
int res = trx.begin();
|
|
|
|
if (res != TRI_ERROR_NO_ERROR) {
|
|
TRI_V8_EXCEPTION(scope, res);
|
|
}
|
|
|
|
res = trx.read(docs, &barrier, skip, limit, &total);
|
|
res = trx.finish(res);
|
|
|
|
if (res != TRI_ERROR_NO_ERROR) {
|
|
FREE_BARRIER(barrier);
|
|
TRI_V8_EXCEPTION(scope, res);
|
|
}
|
|
|
|
const size_t n = docs.size();
|
|
uint32_t count = 0;
|
|
|
|
if (n > 0) {
|
|
TRI_ASSERT_MAINTAINER(barrier != 0);
|
|
}
|
|
|
|
// setup result
|
|
v8::Handle<v8::Object> result = v8::Object::New();
|
|
v8::Handle<v8::Array> documents = v8::Array::New(n);
|
|
// reserve full capacity in one go
|
|
result->Set(v8::String::New("documents"), documents);
|
|
|
|
for (size_t i = 0; i < n; ++i) {
|
|
v8::Handle<v8::Value> doc = WRAP_SHAPED_JSON(trx, col->_cid, &docs[i], barrier);
|
|
|
|
if (doc.IsEmpty()) {
|
|
FREE_BARRIER(barrier);
|
|
TRI_V8_EXCEPTION_MEMORY(scope);
|
|
}
|
|
else {
|
|
documents->Set(count++, doc);
|
|
}
|
|
}
|
|
|
|
result->Set(v8::String::New("total"), v8::Number::New(total));
|
|
result->Set(v8::String::New("count"), v8::Number::New(count));
|
|
|
|
if (count == 0) {
|
|
FREE_BARRIER(barrier);
|
|
}
|
|
|
|
return scope.Close(result);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief selects documents from a collection, using an offset into the
|
|
/// primary index. this can be used for incremental access
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static v8::Handle<v8::Value> JS_OffsetQuery (v8::Arguments const& argv) {
|
|
v8::HandleScope scope;
|
|
|
|
// expecting two arguments
|
|
if (argv.Length() != 4) {
|
|
TRI_V8_EXCEPTION_USAGE(scope, "OFFSET(<internalSkip>, <batchSize>, <skip>, <limit>)");
|
|
}
|
|
|
|
TRI_vocbase_col_t const* col;
|
|
col = TRI_UnwrapClass<TRI_vocbase_col_t>(argv.Holder(), TRI_GetVocBaseColType());
|
|
|
|
if (col == 0) {
|
|
TRI_V8_EXCEPTION_INTERNAL(scope, "cannot extract collection");
|
|
}
|
|
|
|
TRI_SHARDING_COLLECTION_NOT_YET_IMPLEMENTED(scope, col);
|
|
|
|
TRI_voc_size_t internalSkip = (TRI_voc_size_t) TRI_ObjectToDouble(argv[0]);
|
|
TRI_voc_size_t batchSize = (TRI_voc_size_t) TRI_ObjectToDouble(argv[1]);
|
|
|
|
// extract skip and limit
|
|
TRI_voc_ssize_t skip;
|
|
TRI_voc_size_t limit;
|
|
ExtractSkipAndLimit(argv, 2, skip, limit);
|
|
|
|
TRI_barrier_t* barrier = 0;
|
|
uint32_t total = 0;
|
|
vector<TRI_doc_mptr_t> docs;
|
|
|
|
CollectionNameResolver resolver(col->_vocbase);
|
|
ReadTransactionType trx(col->_vocbase, resolver, col->_cid);
|
|
|
|
int res = trx.begin();
|
|
|
|
if (res != TRI_ERROR_NO_ERROR) {
|
|
TRI_V8_EXCEPTION(scope, res);
|
|
}
|
|
|
|
res = trx.readOffset(docs, &barrier, internalSkip, batchSize, skip, &total);
|
|
res = trx.finish(res);
|
|
|
|
if (res != TRI_ERROR_NO_ERROR) {
|
|
FREE_BARRIER(barrier);
|
|
TRI_V8_EXCEPTION(scope, res);
|
|
}
|
|
|
|
const size_t n = docs.size();
|
|
uint32_t count = 0;
|
|
|
|
if (n > 0) {
|
|
TRI_ASSERT_MAINTAINER(barrier != 0);
|
|
}
|
|
|
|
// setup result
|
|
v8::Handle<v8::Object> result = v8::Object::New();
|
|
v8::Handle<v8::Array> documents = v8::Array::New(n);
|
|
// reserve full capacity in one go
|
|
result->Set(v8::String::New("documents"), documents);
|
|
|
|
for (size_t i = 0; i < n; ++i) {
|
|
v8::Handle<v8::Value> document = WRAP_SHAPED_JSON(trx, col->_cid, &docs[i], barrier);
|
|
|
|
if (document.IsEmpty()) {
|
|
FREE_BARRIER(barrier);
|
|
TRI_V8_EXCEPTION_MEMORY(scope);
|
|
}
|
|
else {
|
|
documents->Set(count++, document);
|
|
}
|
|
}
|
|
|
|
result->Set(v8::String::New("total"), v8::Number::New(total));
|
|
result->Set(v8::String::New("count"), v8::Number::New(count));
|
|
result->Set(v8::String::New("skip"), v8::Number::New(internalSkip));
|
|
|
|
if (count == 0) {
|
|
FREE_BARRIER(barrier);
|
|
}
|
|
|
|
return scope.Close(result);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief selects a random document
|
|
///
|
|
/// @FUN{@FA{collection}.any()}
|
|
///
|
|
/// The @FN{any} method returns a random document from the collection. It returns
|
|
/// @LIT{null} if the collection is empty.
|
|
///
|
|
/// @EXAMPLES
|
|
///
|
|
/// @code
|
|
/// arangod> db.example.any()
|
|
/// { "_id" : "example/222716379559", "_rev" : "222716379559", "Hello" : "World" }
|
|
/// @endcode
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static v8::Handle<v8::Value> JS_AnyQuery (v8::Arguments const& argv) {
|
|
v8::HandleScope scope;
|
|
|
|
TRI_vocbase_col_t const* col;
|
|
col = TRI_UnwrapClass<TRI_vocbase_col_t>(argv.Holder(), TRI_GetVocBaseColType());
|
|
|
|
if (col == 0) {
|
|
TRI_V8_EXCEPTION_INTERNAL(scope, "cannot extract collection");
|
|
}
|
|
|
|
TRI_barrier_t* barrier = 0;
|
|
TRI_doc_mptr_t document;
|
|
document._data = 0;
|
|
document._key = 0;
|
|
|
|
CollectionNameResolver resolver(col->_vocbase);
|
|
ReadTransactionType trx(col->_vocbase, resolver, col->_cid);
|
|
|
|
int res = trx.begin();
|
|
|
|
if (res != TRI_ERROR_NO_ERROR) {
|
|
TRI_V8_EXCEPTION(scope, res);
|
|
}
|
|
|
|
res = trx.readRandom(&document, &barrier);
|
|
res = trx.finish(res);
|
|
|
|
if (res != TRI_ERROR_NO_ERROR) {
|
|
FREE_BARRIER(barrier);
|
|
TRI_V8_EXCEPTION(scope, res);
|
|
}
|
|
|
|
if (document._data == 0 || document._key == 0) {
|
|
FREE_BARRIER(barrier);
|
|
|
|
return scope.Close(v8::Null());
|
|
}
|
|
|
|
v8::Handle<v8::Value> doc = WRAP_SHAPED_JSON(trx, col->_cid, &document, barrier);
|
|
|
|
if (doc.IsEmpty()) {
|
|
FREE_BARRIER(barrier);
|
|
TRI_V8_EXCEPTION_MEMORY(scope);
|
|
}
|
|
|
|
return scope.Close(doc);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief selects documents by example (not using any index)
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static v8::Handle<v8::Value> JS_ByExampleQuery (v8::Arguments const& argv) {
|
|
v8::HandleScope scope;
|
|
|
|
// expecting example, skip, limit
|
|
if (argv.Length() < 1) {
|
|
TRI_V8_EXCEPTION_USAGE(scope, "BY_EXAMPLE(<example>, <skip>, <limit>)");
|
|
}
|
|
|
|
// extract the example
|
|
if (! argv[0]->IsObject()) {
|
|
TRI_V8_TYPE_ERROR(scope, "<example> must be an object");
|
|
}
|
|
|
|
|
|
TRI_vocbase_col_t const* col;
|
|
col = TRI_UnwrapClass<TRI_vocbase_col_t>(argv.Holder(), TRI_GetVocBaseColType());
|
|
|
|
if (col == 0) {
|
|
TRI_V8_EXCEPTION_INTERNAL(scope, "cannot extract collection");
|
|
}
|
|
|
|
TRI_SHARDING_COLLECTION_NOT_YET_IMPLEMENTED(scope, col);
|
|
|
|
CollectionNameResolver resolver(col->_vocbase);
|
|
ReadTransactionType trx(col->_vocbase, resolver, col->_cid);
|
|
|
|
int res = trx.begin();
|
|
|
|
if (res != TRI_ERROR_NO_ERROR) {
|
|
TRI_V8_EXCEPTION(scope, res);
|
|
}
|
|
|
|
TRI_primary_collection_t* primary = trx.primaryCollection();
|
|
TRI_shaper_t* shaper = primary->_shaper;
|
|
|
|
v8::Handle<v8::Object> example = argv[0]->ToObject();
|
|
|
|
// extract skip and limit
|
|
TRI_voc_ssize_t skip;
|
|
TRI_voc_size_t limit;
|
|
ExtractSkipAndLimit(argv, 1, skip, limit);
|
|
|
|
// extract sub-documents
|
|
TRI_shape_pid_t* pids;
|
|
TRI_shaped_json_t** values = 0;
|
|
size_t n;
|
|
|
|
v8::Handle<v8::Object> err;
|
|
res = SetupExampleObject(example, shaper, n, pids, values, &err);
|
|
|
|
if (res == TRI_RESULT_ELEMENT_NOT_FOUND) {
|
|
// empty result
|
|
return scope.Close(EmptyResult());
|
|
}
|
|
|
|
if (res != TRI_ERROR_NO_ERROR) {
|
|
return scope.Close(v8::ThrowException(err));
|
|
}
|
|
|
|
// setup result
|
|
v8::Handle<v8::Object> result = v8::Object::New();
|
|
v8::Handle<v8::Array> documents = v8::Array::New();
|
|
result->Set(v8::String::New("documents"), documents);
|
|
|
|
// .............................................................................
|
|
// inside a read transaction
|
|
// .............................................................................
|
|
|
|
trx.lockRead();
|
|
|
|
// find documents by example
|
|
TRI_vector_t filtered = TRI_SelectByExample(trx.trxCollection(), n, pids, values);
|
|
|
|
// convert to list of shaped jsons
|
|
size_t total = filtered._length;
|
|
size_t count = 0;
|
|
bool error = false;
|
|
|
|
TRI_barrier_t* barrier = 0;
|
|
|
|
if (total > 0) {
|
|
barrier = TRI_CreateBarrierElement(&primary->_barrierList);
|
|
|
|
if (barrier == 0) {
|
|
TRI_DestroyVector(&filtered);
|
|
TRI_V8_EXCEPTION_MEMORY(scope);
|
|
}
|
|
|
|
assert(barrier != 0);
|
|
|
|
size_t s, e;
|
|
CalculateSkipLimitSlice(filtered._length, skip, limit, s, e);
|
|
|
|
for (size_t j = s; j < e; ++j) {
|
|
TRI_doc_mptr_t* mptr = (TRI_doc_mptr_t*) TRI_AtVector(&filtered, j);
|
|
|
|
v8::Handle<v8::Value> doc = WRAP_SHAPED_JSON(trx, col->_cid, mptr, barrier);
|
|
|
|
if (doc.IsEmpty()) {
|
|
error = true;
|
|
break;
|
|
}
|
|
else {
|
|
documents->Set(count++, doc);
|
|
}
|
|
}
|
|
}
|
|
|
|
TRI_DestroyVector(&filtered);
|
|
|
|
trx.finish(res);
|
|
|
|
// .............................................................................
|
|
// outside a write transaction
|
|
// .............................................................................
|
|
|
|
result->Set(v8::String::New("total"), v8::Integer::New(total));
|
|
result->Set(v8::String::New("count"), v8::Integer::New(count));
|
|
|
|
CleanupExampleObject(shaper->_memoryZone, n, pids, values);
|
|
|
|
if (error) {
|
|
FREE_BARRIER(barrier);
|
|
TRI_V8_EXCEPTION_MEMORY(scope);
|
|
}
|
|
|
|
if (count == 0) {
|
|
FREE_BARRIER(barrier);
|
|
}
|
|
|
|
return scope.Close(result);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief selects documents by example using a hash index
|
|
///
|
|
/// It is the callers responsibility to acquire and free the required locks
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static v8::Handle<v8::Value> ByExampleHashIndexQuery (ReadTransactionType& trx,
|
|
TRI_vocbase_col_t const* collection,
|
|
v8::Handle<v8::Object>* err,
|
|
v8::Arguments const& argv) {
|
|
v8::HandleScope scope;
|
|
|
|
// expecting index, example, skip, and limit
|
|
if (argv.Length() < 2) {
|
|
TRI_V8_EXCEPTION_USAGE(scope, "EXAMPLE_HASH(<index>, <example>, <skip>, <limit>)");
|
|
}
|
|
|
|
// extract the example
|
|
if (! argv[1]->IsObject()) {
|
|
TRI_V8_TYPE_ERROR(scope, "<example> must be an object");
|
|
}
|
|
|
|
v8::Handle<v8::Object> example = argv[1]->ToObject();
|
|
|
|
// extract skip and limit
|
|
TRI_voc_ssize_t skip;
|
|
TRI_voc_size_t limit;
|
|
|
|
ExtractSkipAndLimit(argv, 2, skip, limit);
|
|
|
|
// setup result
|
|
v8::Handle<v8::Object> result = v8::Object::New();
|
|
|
|
v8::Handle<v8::Array> documents = v8::Array::New();
|
|
result->Set(v8::String::New("documents"), documents);
|
|
|
|
// extract the index
|
|
TRI_index_t* idx = TRI_LookupIndexByHandle(collection, argv[0], false, err);
|
|
|
|
if (idx == 0) {
|
|
return scope.Close(v8::ThrowException(*err));
|
|
}
|
|
|
|
if (idx->_type != TRI_IDX_TYPE_HASH_INDEX) {
|
|
TRI_V8_EXCEPTION(scope, TRI_ERROR_ARANGO_NO_INDEX);
|
|
}
|
|
|
|
TRI_hash_index_t* hashIndex = (TRI_hash_index_t*) idx;
|
|
|
|
// convert the example (index is locked by lockRead)
|
|
TRI_index_search_value_t searchValue;
|
|
|
|
TRI_primary_collection_t* primary = trx.primaryCollection();
|
|
TRI_shaper_t* shaper = primary->_shaper;
|
|
int res = SetupSearchValue(&hashIndex->_paths, example, shaper, searchValue, err);
|
|
|
|
if (res != TRI_ERROR_NO_ERROR) {
|
|
if (res == TRI_RESULT_ELEMENT_NOT_FOUND) {
|
|
return scope.Close(EmptyResult());
|
|
}
|
|
|
|
return scope.Close(v8::ThrowException(*err));
|
|
}
|
|
|
|
// find the matches
|
|
TRI_index_result_t list = TRI_LookupHashIndex(idx, &searchValue);
|
|
DestroySearchValue(shaper->_memoryZone, searchValue);
|
|
|
|
// convert result
|
|
size_t total = list._length;
|
|
size_t count = 0;
|
|
bool error = false;
|
|
|
|
TRI_barrier_t* barrier = 0;
|
|
|
|
if (0 < total) {
|
|
barrier = TRI_CreateBarrierElement(&primary->_barrierList);
|
|
|
|
if (barrier == 0) {
|
|
TRI_DestroyIndexResult(&list);
|
|
TRI_V8_EXCEPTION_MEMORY(scope);
|
|
}
|
|
|
|
size_t s, e;
|
|
CalculateSkipLimitSlice(total, skip, limit, s, e);
|
|
|
|
for (size_t i = s; i < e; ++i) {
|
|
v8::Handle<v8::Value> doc = WRAP_SHAPED_JSON(trx, collection->_cid, list._documents[i], barrier);
|
|
|
|
if (doc.IsEmpty()) {
|
|
error = true;
|
|
break;
|
|
}
|
|
else {
|
|
documents->Set(count++, doc);
|
|
}
|
|
}
|
|
}
|
|
|
|
// free data allocated by hash index result
|
|
TRI_DestroyIndexResult(&list);
|
|
|
|
result->Set(v8::String::New("total"), v8::Number::New((double) total));
|
|
result->Set(v8::String::New("count"), v8::Number::New(count));
|
|
|
|
if (error) {
|
|
FREE_BARRIER(barrier);
|
|
TRI_V8_EXCEPTION_MEMORY(scope);
|
|
}
|
|
|
|
if (count == 0) {
|
|
FREE_BARRIER(barrier);
|
|
}
|
|
|
|
return scope.Close(result);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief selects documents by example using a hash index
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static v8::Handle<v8::Value> JS_ByExampleHashIndex (v8::Arguments const& argv) {
|
|
v8::HandleScope scope;
|
|
|
|
TRI_vocbase_col_t const* col;
|
|
col = TRI_UnwrapClass<TRI_vocbase_col_t>(argv.Holder(), TRI_GetVocBaseColType());
|
|
|
|
if (col == 0) {
|
|
TRI_V8_EXCEPTION_INTERNAL(scope, "cannot extract collection");
|
|
}
|
|
|
|
TRI_SHARDING_COLLECTION_NOT_YET_IMPLEMENTED(scope, col);
|
|
|
|
CollectionNameResolver resolver(col->_vocbase);
|
|
ReadTransactionType trx(col->_vocbase, resolver, col->_cid);
|
|
|
|
int res = trx.begin();
|
|
|
|
if (res != TRI_ERROR_NO_ERROR) {
|
|
TRI_V8_EXCEPTION(scope, res);
|
|
}
|
|
|
|
v8::Handle<v8::Object> err;
|
|
|
|
// .............................................................................
|
|
// inside a read transaction
|
|
// .............................................................................
|
|
|
|
trx.lockRead();
|
|
|
|
v8::Handle<v8::Value> result = ByExampleHashIndexQuery(trx, col, &err, argv);
|
|
|
|
trx.finish(res);
|
|
|
|
// .............................................................................
|
|
// outside a write transaction
|
|
// .............................................................................
|
|
|
|
return scope.Close(result);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief selects documents by condition using a skiplist index
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static v8::Handle<v8::Value> JS_ByConditionSkiplist (v8::Arguments const& argv) {
|
|
std::string const signature("BY_CONDITION_SKIPLIST(<index>, <conditions>, <skip>, <limit>)");
|
|
|
|
return ExecuteSkiplistQuery(argv, signature, QUERY_CONDITION);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief selects documents by example using a skiplist index
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static v8::Handle<v8::Value> JS_ByExampleSkiplist (v8::Arguments const& argv) {
|
|
std::string const signature("BY_EXAMPLE_SKIPLIST(<index>, <example>, <skip>, <limit>)");
|
|
|
|
return ExecuteSkiplistQuery(argv, signature, QUERY_EXAMPLE);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief selects documents by example using a bitarray index
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static v8::Handle<v8::Value> JS_ByExampleBitarray (v8::Arguments const& argv) {
|
|
std::string const signature("BY_EXAMPLE_BITARRAY(<index>, <example>, <skip>, <limit>)");
|
|
|
|
return ExecuteBitarrayQuery(argv, signature, QUERY_EXAMPLE);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief selects documents by condition using a bitarray index
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static v8::Handle<v8::Value> JS_ByConditionBitarray (v8::Arguments const& argv) {
|
|
std::string const signature("BY_CONDITION_BITARRAY(<index>, <conditions>, <skip>, <limit>)");
|
|
|
|
return ExecuteBitarrayQuery(argv, signature, QUERY_CONDITION);
|
|
}
|
|
|
|
typedef struct collection_checksum_s {
|
|
TRI_string_buffer_t _buffer;
|
|
CollectionNameResolver* _resolver;
|
|
uint32_t _checksum;
|
|
}
|
|
collection_checksum_t;
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief callback for checksum calculation, WR = with _rid, WD = with data
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
template<bool WR, bool WD> static bool ChecksumCalculator (TRI_doc_mptr_t const* mptr,
|
|
TRI_primary_collection_t* primary,
|
|
void* data) {
|
|
TRI_df_marker_t const* marker = static_cast<TRI_df_marker_t const*>(mptr->_data);
|
|
collection_checksum_t* helper = static_cast<collection_checksum_t*>(data);
|
|
uint32_t localCrc;
|
|
|
|
if (marker->_type == TRI_DOC_MARKER_KEY_DOCUMENT) {
|
|
localCrc = TRI_Crc32HashString(mptr->_key);
|
|
if (WR) {
|
|
localCrc += TRI_Crc32HashPointer(&mptr->_rid, sizeof(TRI_voc_rid_t));
|
|
}
|
|
}
|
|
else if (marker->_type == TRI_DOC_MARKER_KEY_EDGE) {
|
|
TRI_doc_edge_key_marker_t const* e = (TRI_doc_edge_key_marker_t const*) marker;
|
|
|
|
// must convert _rid, _fromCid, _toCid into strings for portability
|
|
localCrc = TRI_Crc32HashString(mptr->_key);
|
|
if (WR) {
|
|
localCrc += TRI_Crc32HashPointer(&mptr->_rid, sizeof(TRI_voc_rid_t));
|
|
}
|
|
#ifndef TRI_ENABLE_CLUSTER
|
|
const string extra = helper->_resolver->getCollectionName(e->_toCid) + TRI_DOCUMENT_HANDLE_SEPARATOR_CHR + string(((char*) marker) + e->_offsetToKey) +
|
|
helper->_resolver->getCollectionName(e->_fromCid) + TRI_DOCUMENT_HANDLE_SEPARATOR_CHR + string(((char*) marker) + e->_offsetFromKey);
|
|
#else
|
|
const string extra = helper->_resolver->getCollectionNameCluster(e->_toCid) + TRI_DOCUMENT_HANDLE_SEPARATOR_CHR + string(((char*) marker) + e->_offsetToKey) +
|
|
helper->_resolver->getCollectionNameCluster(e->_fromCid) + TRI_DOCUMENT_HANDLE_SEPARATOR_CHR + string(((char*) marker) + e->_offsetFromKey);
|
|
|
|
#endif
|
|
localCrc += TRI_Crc32HashPointer(extra.c_str(), extra.size());
|
|
}
|
|
else {
|
|
return true;
|
|
}
|
|
|
|
if (WD) {
|
|
// with data
|
|
TRI_doc_document_key_marker_t const* d = (TRI_doc_document_key_marker_t const*) marker;
|
|
|
|
TRI_shaped_json_t shaped;
|
|
TRI_EXTRACT_SHAPED_JSON_MARKER(shaped, d);
|
|
|
|
TRI_StringifyArrayShapedJson(primary->_shaper, &helper->_buffer, &shaped, false);
|
|
localCrc += TRI_Crc32HashPointer(TRI_BeginStringBuffer(&helper->_buffer), TRI_LengthStringBuffer(&helper->_buffer));
|
|
TRI_ResetStringBuffer(&helper->_buffer);
|
|
}
|
|
|
|
helper->_checksum += localCrc;
|
|
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief calculates a checksum for the data in a collection
|
|
///
|
|
/// @FUN{@FA{collection}.checksum(@FA{withRevisions}, @FA{withData})}
|
|
///
|
|
/// The @FN{checksum} operation calculates a CRC32 checksum of the keys
|
|
/// contained in collection @FA{collection}.
|
|
///
|
|
/// If the optional argument @FA{withRevisions} is set to @LIT{true}, then the
|
|
/// revision ids of the documents are also included in the checksumming.
|
|
///
|
|
/// If the optional argument @FA{withData} is set to @LIT{true}, then the
|
|
/// actual document data is also checksummed. Including the document data in
|
|
/// checksumming will make the calculation slower, but is more accurate.
|
|
///
|
|
/// Note: this method is not available in a cluster.
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static v8::Handle<v8::Value> JS_ChecksumCollection (v8::Arguments const& argv) {
|
|
v8::HandleScope scope;
|
|
|
|
#ifdef TRI_ENABLE_CLUSTER
|
|
if (ServerState::instance()->isCoordinator()) {
|
|
// renaming a collection in a cluster is unsupported
|
|
TRI_V8_EXCEPTION(scope, TRI_ERROR_CLUSTER_UNSUPPORTED);
|
|
}
|
|
#endif
|
|
|
|
TRI_vocbase_col_t const* col;
|
|
col = TRI_UnwrapClass<TRI_vocbase_col_t>(argv.Holder(), TRI_GetVocBaseColType());
|
|
|
|
if (col == 0) {
|
|
TRI_V8_EXCEPTION_INTERNAL(scope, "cannot extract collection");
|
|
}
|
|
|
|
TRI_SHARDING_COLLECTION_NOT_YET_IMPLEMENTED(scope, col);
|
|
|
|
bool withRevisions = false;
|
|
if (argv.Length() > 0) {
|
|
withRevisions = TRI_ObjectToBoolean(argv[0]);
|
|
}
|
|
|
|
bool withData = false;
|
|
if (argv.Length() > 1) {
|
|
withData = TRI_ObjectToBoolean(argv[1]);
|
|
}
|
|
|
|
CollectionNameResolver resolver(col->_vocbase);
|
|
ReadTransactionType trx(col->_vocbase, resolver, col->_cid);
|
|
|
|
int res = trx.begin();
|
|
|
|
if (res != TRI_ERROR_NO_ERROR) {
|
|
TRI_V8_EXCEPTION(scope, res);
|
|
}
|
|
|
|
TRI_primary_collection_t* primary = trx.primaryCollection();
|
|
|
|
Barrier barrier(primary);
|
|
|
|
collection_checksum_t helper;
|
|
helper._checksum = 0;
|
|
helper._resolver = &resolver;
|
|
|
|
// .............................................................................
|
|
// inside a read transaction
|
|
// .............................................................................
|
|
|
|
trx.lockRead();
|
|
// get last tick
|
|
const string rid = StringUtils::itoa(primary->base._info._revision);
|
|
|
|
if (withData) {
|
|
TRI_InitStringBuffer(&helper._buffer, TRI_CORE_MEM_ZONE);
|
|
|
|
if (withRevisions) {
|
|
TRI_DocumentIteratorPrimaryCollection(primary, &helper, &ChecksumCalculator<true, true>);
|
|
}
|
|
else {
|
|
TRI_DocumentIteratorPrimaryCollection(primary, &helper, &ChecksumCalculator<false, true>);
|
|
}
|
|
|
|
TRI_DestroyStringBuffer(&helper._buffer);
|
|
}
|
|
else {
|
|
if (withRevisions) {
|
|
TRI_DocumentIteratorPrimaryCollection(primary, &helper, &ChecksumCalculator<true, false>);
|
|
}
|
|
else {
|
|
TRI_DocumentIteratorPrimaryCollection(primary, &helper, &ChecksumCalculator<false, false>);
|
|
}
|
|
}
|
|
|
|
trx.finish(res);
|
|
|
|
// .............................................................................
|
|
// outside a write transaction
|
|
// .............................................................................
|
|
|
|
v8::Handle<v8::Object> result = v8::Object::New();
|
|
result->Set(v8::String::New("checksum"), v8::Number::New(helper._checksum));
|
|
result->Set(v8::String::New("revision"), v8::String::New(rid.c_str(), rid.size()));
|
|
|
|
return scope.Close(result);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief selects all edges for a set of vertices
|
|
///
|
|
/// @FUN{@FA{edge-collection}.edges(@FA{vertex})}
|
|
///
|
|
/// The @FN{edges} operator finds all edges starting from (outbound) or ending
|
|
/// in (inbound) @FA{vertex}.
|
|
///
|
|
/// @FUN{@FA{edge-collection}.edges(@FA{vertices})}
|
|
///
|
|
/// The @FN{edges} operator finds all edges starting from (outbound) or ending
|
|
/// in (inbound) a document from @FA{vertices}, which must a list of documents
|
|
/// or document handles.
|
|
///
|
|
/// @EXAMPLES
|
|
///
|
|
/// @verbinclude shell-edge-edges
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static v8::Handle<v8::Value> JS_EdgesQuery (v8::Arguments const& argv) {
|
|
return EdgesQuery(TRI_EDGE_ANY, argv);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief selects all inbound edges
|
|
///
|
|
/// @FUN{@FA{edge-collection}.inEdges(@FA{vertex})}
|
|
///
|
|
/// The @FN{edges} operator finds all edges ending in (inbound) @FA{vertex}.
|
|
///
|
|
/// @FUN{@FA{edge-collection}.inEdges(@FA{vertices})}
|
|
///
|
|
/// The @FN{edges} operator finds all edges ending in (inbound) a document from
|
|
/// @FA{vertices}, which must a list of documents or document handles.
|
|
///
|
|
/// @EXAMPLES
|
|
///
|
|
/// @verbinclude shell-edge-in-edges
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static v8::Handle<v8::Value> JS_InEdgesQuery (v8::Arguments const& argv) {
|
|
return EdgesQuery(TRI_EDGE_IN, argv);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief selects the n first documents in the collection
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static v8::Handle<v8::Value> JS_FirstQuery (v8::Arguments const& argv) {
|
|
v8::HandleScope scope;
|
|
|
|
if (argv.Length() > 1) {
|
|
TRI_V8_EXCEPTION_USAGE(scope, "FIRST(<count>)");
|
|
}
|
|
|
|
int64_t count = 1;
|
|
bool returnList = false;
|
|
|
|
// if argument is supplied, we'll return a list - otherwise we simply return the first doc
|
|
if (argv.Length() == 1) {
|
|
if (! argv[0]->IsUndefined()) {
|
|
count = TRI_ObjectToInt64(argv[0]);
|
|
returnList = true;
|
|
}
|
|
}
|
|
|
|
if (count < 1) {
|
|
TRI_V8_EXCEPTION_PARAMETER(scope, "invalid value for <count>");
|
|
}
|
|
|
|
TRI_vocbase_col_t const* col;
|
|
col = TRI_UnwrapClass<TRI_vocbase_col_t>(argv.Holder(), TRI_GetVocBaseColType());
|
|
|
|
if (col == 0) {
|
|
TRI_V8_EXCEPTION_INTERNAL(scope, "cannot extract collection");
|
|
}
|
|
|
|
TRI_barrier_t* barrier = 0;
|
|
|
|
CollectionNameResolver resolver(col->_vocbase);
|
|
SingleCollectionReadOnlyTransaction<EmbeddableTransaction<V8TransactionContext> > trx(col->_vocbase, resolver, col->_cid);
|
|
|
|
int res = trx.begin();
|
|
|
|
if (res != TRI_ERROR_NO_ERROR) {
|
|
TRI_V8_EXCEPTION(scope, res);
|
|
}
|
|
|
|
barrier = TRI_CreateBarrierElement(&trx.primaryCollection()->_barrierList);
|
|
|
|
if (barrier == 0) {
|
|
TRI_V8_EXCEPTION_MEMORY(scope);
|
|
}
|
|
|
|
vector<TRI_doc_mptr_t const*> documents;
|
|
res = trx.readPositional(documents, 0, count);
|
|
|
|
const size_t n = documents.size();
|
|
|
|
assert(barrier != 0);
|
|
|
|
if (n == 0) {
|
|
FREE_BARRIER(barrier);
|
|
}
|
|
|
|
if (returnList) {
|
|
v8::Handle<v8::Array> result = v8::Array::New(n);
|
|
|
|
uint32_t j = 0;
|
|
|
|
for (size_t i = 0; i < n; ++i) {
|
|
v8::Handle<v8::Value> doc = WRAP_SHAPED_JSON(trx, col->_cid, documents[i], barrier);
|
|
|
|
if (doc.IsEmpty()) {
|
|
// error
|
|
trx.finish(res);
|
|
|
|
FREE_BARRIER(barrier);
|
|
TRI_V8_EXCEPTION_MEMORY(scope);
|
|
}
|
|
|
|
result->Set(j++, doc);
|
|
}
|
|
|
|
trx.finish(res);
|
|
|
|
return scope.Close(result);
|
|
}
|
|
else {
|
|
if (n == 0) {
|
|
trx.finish(res);
|
|
|
|
return scope.Close(v8::Null());
|
|
}
|
|
|
|
v8::Handle<v8::Value> result = WRAP_SHAPED_JSON(trx, col->_cid, documents[0], barrier);
|
|
|
|
trx.finish(res);
|
|
|
|
if (result.IsEmpty()) {
|
|
FREE_BARRIER(barrier);
|
|
TRI_V8_EXCEPTION_MEMORY(scope);
|
|
}
|
|
|
|
return scope.Close(result);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief queries the fulltext index
|
|
///
|
|
/// the caller must ensure all relevant locks are acquired and freed
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static v8::Handle<v8::Value> FulltextQuery (ReadTransactionType& trx,
|
|
TRI_vocbase_col_t const* collection,
|
|
v8::Handle<v8::Object>* err,
|
|
v8::Arguments const& argv) {
|
|
v8::HandleScope scope;
|
|
|
|
// expect: FULLTEXT(<index-handle>, <query>)
|
|
if (argv.Length() != 2) {
|
|
TRI_V8_EXCEPTION_USAGE(scope, "FULLTEXT(<index-handle>, <query>)");
|
|
}
|
|
|
|
// extract the index
|
|
TRI_index_t* idx = TRI_LookupIndexByHandle(collection, argv[0], false, err);
|
|
|
|
if (idx == 0) {
|
|
return scope.Close(v8::ThrowException(*err));
|
|
}
|
|
|
|
if (idx->_type != TRI_IDX_TYPE_FULLTEXT_INDEX) {
|
|
TRI_V8_EXCEPTION(scope, TRI_ERROR_ARANGO_NO_INDEX);
|
|
}
|
|
|
|
const string queryString = TRI_ObjectToString(argv[1]);
|
|
bool isSubstringQuery = false;
|
|
|
|
TRI_fulltext_query_t* query = TRI_CreateQueryFulltextIndex(TRI_FULLTEXT_SEARCH_MAX_WORDS);
|
|
|
|
if (! query) {
|
|
TRI_V8_EXCEPTION_MEMORY(scope);
|
|
}
|
|
|
|
int res = TRI_ParseQueryFulltextIndex(query, queryString.c_str(), &isSubstringQuery);
|
|
|
|
if (res != TRI_ERROR_NO_ERROR) {
|
|
TRI_FreeQueryFulltextIndex(query);
|
|
|
|
TRI_V8_EXCEPTION(scope, res);
|
|
}
|
|
|
|
TRI_fulltext_index_t* fulltextIndex = (TRI_fulltext_index_t*) idx;
|
|
|
|
if (isSubstringQuery && ! fulltextIndex->_indexSubstrings) {
|
|
TRI_FreeQueryFulltextIndex(query);
|
|
|
|
TRI_V8_EXCEPTION(scope, TRI_ERROR_NOT_IMPLEMENTED);
|
|
}
|
|
|
|
TRI_fulltext_result_t* queryResult = TRI_QueryFulltextIndex(fulltextIndex->_fulltextIndex, query);
|
|
|
|
if (! queryResult) {
|
|
TRI_V8_EXCEPTION_INTERNAL(scope, "internal error in fulltext index query");
|
|
}
|
|
|
|
TRI_barrier_t* barrier = 0;
|
|
|
|
if (queryResult->_numDocuments > 0) {
|
|
barrier = TRI_CreateBarrierElement(&((TRI_primary_collection_t*) collection->_collection)->_barrierList);
|
|
}
|
|
|
|
// setup result
|
|
v8::Handle<v8::Object> result = v8::Object::New();
|
|
|
|
v8::Handle<v8::Array> documents = v8::Array::New();
|
|
result->Set(v8::String::New("documents"), documents);
|
|
|
|
bool error = false;
|
|
|
|
for (uint32_t i = 0; i < queryResult->_numDocuments; ++i) {
|
|
v8::Handle<v8::Value> doc = WRAP_SHAPED_JSON(trx, collection->_cid, (TRI_doc_mptr_t const*) queryResult->_documents[i], barrier);
|
|
|
|
if (doc.IsEmpty()) {
|
|
error = true;
|
|
break;
|
|
}
|
|
|
|
documents->Set(i, doc);
|
|
}
|
|
|
|
TRI_FreeResultFulltextIndex(queryResult);
|
|
|
|
if (error) {
|
|
FREE_BARRIER(barrier);
|
|
TRI_V8_EXCEPTION_MEMORY(scope);
|
|
}
|
|
|
|
return scope.Close(result);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief queries the fulltext index
|
|
///
|
|
/// @FUN{@FA{collection}.FULLTEXT(@FA{index-handle}, @FA{query})}
|
|
///
|
|
/// The @FN{FULLTEXT} operator performs a fulltext search using the specified
|
|
/// index and the specified @FA{query}.
|
|
///
|
|
/// @FA{query} must contain a comma-separated list of words to look for.
|
|
/// Each word can optionally be prefixed with one of the following command
|
|
/// literals:
|
|
/// - @LIT{prefix}: perform a prefix-search for the word following
|
|
/// - @LIT{substring}: perform substring-matching for the word following. This
|
|
/// option is only supported for fulltext indexes that have been created with
|
|
/// the @LIT{indexSubstrings} option
|
|
/// - @LIT{complete}: only match the complete following word (this is the default)
|
|
///
|
|
/// @EXAMPLES
|
|
///
|
|
/// @verbinclude shell-simple-fulltext
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static v8::Handle<v8::Value> JS_FulltextQuery (v8::Arguments const& argv) {
|
|
v8::HandleScope scope;
|
|
|
|
TRI_vocbase_col_t const* col;
|
|
col = TRI_UnwrapClass<TRI_vocbase_col_t>(argv.Holder(), TRI_GetVocBaseColType());
|
|
|
|
if (col == 0) {
|
|
TRI_V8_EXCEPTION_INTERNAL(scope, "cannot extract collection");
|
|
}
|
|
|
|
TRI_SHARDING_COLLECTION_NOT_YET_IMPLEMENTED(scope, col);
|
|
|
|
CollectionNameResolver resolver(col->_vocbase);
|
|
ReadTransactionType trx(col->_vocbase, resolver, col->_cid);
|
|
|
|
int res = trx.begin();
|
|
|
|
if (res != TRI_ERROR_NO_ERROR) {
|
|
TRI_V8_EXCEPTION(scope, res);
|
|
}
|
|
|
|
v8::Handle<v8::Object> err;
|
|
|
|
// .............................................................................
|
|
// inside a read transaction
|
|
// .............................................................................
|
|
|
|
trx.lockRead();
|
|
|
|
v8::Handle<v8::Value> result = FulltextQuery(trx, col, &err, argv);
|
|
|
|
trx.finish(res);
|
|
|
|
// .............................................................................
|
|
// outside a write transaction
|
|
// .............................................................................
|
|
|
|
return scope.Close(result);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief selects the n last documents in the collection
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static v8::Handle<v8::Value> JS_LastQuery (v8::Arguments const& argv) {
|
|
v8::HandleScope scope;
|
|
|
|
if (argv.Length() > 1) {
|
|
TRI_V8_EXCEPTION_USAGE(scope, "LAST(<count>)");
|
|
}
|
|
|
|
int64_t count = 1;
|
|
bool returnList = false;
|
|
|
|
// if argument is supplied, we'll return a list - otherwise we simply return the last doc
|
|
if (argv.Length() == 1) {
|
|
if (! argv[0]->IsUndefined()) {
|
|
count = TRI_ObjectToInt64(argv[0]);
|
|
returnList = true;
|
|
}
|
|
}
|
|
|
|
if (count < 1) {
|
|
TRI_V8_EXCEPTION_PARAMETER(scope, "invalid value for <count>");
|
|
}
|
|
|
|
TRI_vocbase_col_t const* col;
|
|
col = TRI_UnwrapClass<TRI_vocbase_col_t>(argv.Holder(), TRI_GetVocBaseColType());
|
|
|
|
if (col == 0) {
|
|
TRI_V8_EXCEPTION_INTERNAL(scope, "cannot extract collection");
|
|
}
|
|
|
|
TRI_SHARDING_COLLECTION_NOT_YET_IMPLEMENTED(scope, col);
|
|
|
|
TRI_barrier_t* barrier = 0;
|
|
|
|
CollectionNameResolver resolver(col->_vocbase);
|
|
SingleCollectionReadOnlyTransaction<EmbeddableTransaction<V8TransactionContext> > trx(col->_vocbase, resolver, col->_cid);
|
|
|
|
int res = trx.begin();
|
|
|
|
if (res != TRI_ERROR_NO_ERROR) {
|
|
TRI_V8_EXCEPTION(scope, res);
|
|
}
|
|
|
|
barrier = TRI_CreateBarrierElement(&trx.primaryCollection()->_barrierList);
|
|
|
|
if (barrier == 0) {
|
|
TRI_V8_EXCEPTION_MEMORY(scope);
|
|
}
|
|
|
|
vector<TRI_doc_mptr_t const*> documents;
|
|
res = trx.readPositional(documents, -1, count);
|
|
|
|
const size_t n = documents.size();
|
|
|
|
assert(barrier != 0);
|
|
|
|
if (n == 0) {
|
|
FREE_BARRIER(barrier);
|
|
}
|
|
|
|
if (returnList) {
|
|
v8::Handle<v8::Array> result = v8::Array::New(n);
|
|
|
|
uint32_t j = 0;
|
|
|
|
for (size_t i = 0; i < n; ++i) {
|
|
v8::Handle<v8::Value> doc = WRAP_SHAPED_JSON(trx, col->_cid, documents[i], barrier);
|
|
|
|
if (doc.IsEmpty()) {
|
|
// error
|
|
trx.finish(res);
|
|
|
|
FREE_BARRIER(barrier);
|
|
TRI_V8_EXCEPTION_MEMORY(scope);
|
|
}
|
|
|
|
result->Set(j++, doc);
|
|
}
|
|
|
|
trx.finish(res);
|
|
|
|
return scope.Close(result);
|
|
}
|
|
else {
|
|
if (n == 0) {
|
|
trx.finish(res);
|
|
|
|
return scope.Close(v8::Null());
|
|
}
|
|
|
|
v8::Handle<v8::Value> result = WRAP_SHAPED_JSON(trx, col->_cid, documents[0], barrier);
|
|
|
|
trx.finish(res);
|
|
|
|
if (result.IsEmpty()) {
|
|
FREE_BARRIER(barrier);
|
|
TRI_V8_EXCEPTION_MEMORY(scope);
|
|
}
|
|
|
|
return scope.Close(result);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief selects points near a given coordinate
|
|
///
|
|
/// the caller must ensure all relevant locks are acquired and freed
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static v8::Handle<v8::Value> NearQuery (ReadTransactionType& trx,
|
|
TRI_vocbase_col_t const* collection,
|
|
v8::Handle<v8::Object>* err,
|
|
v8::Arguments const& argv) {
|
|
v8::HandleScope scope;
|
|
|
|
// expect: NEAR(<index-id>, <latitude>, <longitude>, <limit>)
|
|
if (argv.Length() != 4) {
|
|
TRI_V8_EXCEPTION_USAGE(scope, "NEAR(<index-handle>, <latitude>, <longitude>, <limit>)");
|
|
}
|
|
|
|
// extract the index
|
|
TRI_index_t* idx = TRI_LookupIndexByHandle(collection, argv[0], false, err);
|
|
|
|
if (idx == 0) {
|
|
return scope.Close(v8::ThrowException(*err));
|
|
}
|
|
|
|
if (idx->_type != TRI_IDX_TYPE_GEO1_INDEX &&
|
|
idx->_type != TRI_IDX_TYPE_GEO2_INDEX) {
|
|
TRI_V8_EXCEPTION(scope, TRI_ERROR_ARANGO_NO_INDEX);
|
|
}
|
|
|
|
// extract latitude and longitude
|
|
double latitude = TRI_ObjectToDouble(argv[1]);
|
|
double longitude = TRI_ObjectToDouble(argv[2]);
|
|
|
|
// extract the limit
|
|
TRI_voc_ssize_t limit = (TRI_voc_ssize_t) TRI_ObjectToDouble(argv[3]);
|
|
|
|
// setup result
|
|
v8::Handle<v8::Object> result = v8::Object::New();
|
|
|
|
v8::Handle<v8::Array> documents = v8::Array::New();
|
|
result->Set(v8::String::New("documents"), documents);
|
|
|
|
v8::Handle<v8::Array> distances = v8::Array::New();
|
|
result->Set(v8::String::New("distances"), distances);
|
|
|
|
GeoCoordinates* cors = TRI_NearestGeoIndex(idx, latitude, longitude, limit);
|
|
|
|
if (cors != 0) {
|
|
int res = StoreGeoResult(trx, collection, cors, documents, distances);
|
|
|
|
if (res != TRI_ERROR_NO_ERROR) {
|
|
TRI_V8_EXCEPTION(scope, res);
|
|
}
|
|
}
|
|
|
|
return scope.Close(result);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief selects points near a given coordinate
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static v8::Handle<v8::Value> JS_NearQuery (v8::Arguments const& argv) {
|
|
v8::HandleScope scope;
|
|
|
|
TRI_vocbase_col_t const* col;
|
|
col = TRI_UnwrapClass<TRI_vocbase_col_t>(argv.Holder(), TRI_GetVocBaseColType());
|
|
|
|
if (col == 0) {
|
|
TRI_V8_EXCEPTION_INTERNAL(scope, "cannot extract collection");
|
|
}
|
|
|
|
TRI_SHARDING_COLLECTION_NOT_YET_IMPLEMENTED(scope, col);
|
|
|
|
CollectionNameResolver resolver(col->_vocbase);
|
|
ReadTransactionType trx(col->_vocbase, resolver, col->_cid);
|
|
|
|
int res = trx.begin();
|
|
|
|
if (res != TRI_ERROR_NO_ERROR) {
|
|
TRI_V8_EXCEPTION(scope, res);
|
|
}
|
|
|
|
v8::Handle<v8::Object> err;
|
|
|
|
// .............................................................................
|
|
// inside a read transaction
|
|
// .............................................................................
|
|
|
|
trx.lockRead();
|
|
|
|
v8::Handle<v8::Value> result = NearQuery(trx, col, &err, argv);
|
|
|
|
trx.finish(res);
|
|
|
|
// .............................................................................
|
|
// outside a write transaction
|
|
// .............................................................................
|
|
|
|
return scope.Close(result);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief selects all outbound edges
|
|
///
|
|
/// @FUN{@FA{edge-collection}.outEdges(@FA{vertex})}
|
|
///
|
|
/// The @FN{edges} operator finds all edges starting from (outbound)
|
|
/// @FA{vertices}.
|
|
///
|
|
/// @FUN{@FA{edge-collection}.outEdges(@FA{vertices})}
|
|
///
|
|
/// The @FN{edges} operator finds all edges starting from (outbound) a document
|
|
/// from @FA{vertices}, which must a list of documents or document handles.
|
|
///
|
|
/// @EXAMPLES
|
|
///
|
|
/// @verbinclude shell-edge-out-edges
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static v8::Handle<v8::Value> JS_OutEdgesQuery (v8::Arguments const& argv) {
|
|
return EdgesQuery(TRI_EDGE_OUT, argv);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief selects points within a given radius
|
|
///
|
|
/// the caller must ensure all relevant locks are acquired and freed
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static v8::Handle<v8::Value> WithinQuery (ReadTransactionType& trx,
|
|
TRI_vocbase_col_t const* collection,
|
|
v8::Handle<v8::Object>* err,
|
|
v8::Arguments const& argv) {
|
|
v8::HandleScope scope;
|
|
|
|
// expect: WITHIN(<index-handle>, <latitude>, <longitude>, <radius>)
|
|
if (argv.Length() != 4) {
|
|
TRI_V8_EXCEPTION_USAGE(scope, "WITHIN(<index-handle>, <latitude>, <longitude>, <radius>)");
|
|
}
|
|
|
|
// extract the index
|
|
TRI_index_t* idx = TRI_LookupIndexByHandle(collection, argv[0], false, err);
|
|
|
|
if (idx == 0) {
|
|
return scope.Close(v8::ThrowException(*err));
|
|
}
|
|
|
|
if (idx->_type != TRI_IDX_TYPE_GEO1_INDEX &&
|
|
idx->_type != TRI_IDX_TYPE_GEO2_INDEX) {
|
|
TRI_V8_EXCEPTION(scope, TRI_ERROR_ARANGO_NO_INDEX);
|
|
}
|
|
|
|
// extract latitude and longitude
|
|
double latitude = TRI_ObjectToDouble(argv[1]);
|
|
double longitude = TRI_ObjectToDouble(argv[2]);
|
|
|
|
// extract the radius
|
|
double radius = TRI_ObjectToDouble(argv[3]);
|
|
|
|
// setup result
|
|
v8::Handle<v8::Object> result = v8::Object::New();
|
|
|
|
v8::Handle<v8::Array> documents = v8::Array::New();
|
|
result->Set(v8::String::New("documents"), documents);
|
|
|
|
v8::Handle<v8::Array> distances = v8::Array::New();
|
|
result->Set(v8::String::New("distances"), distances);
|
|
|
|
GeoCoordinates* cors = TRI_WithinGeoIndex(idx, latitude, longitude, radius);
|
|
|
|
if (cors != 0) {
|
|
int res = StoreGeoResult(trx, collection, cors, documents, distances);
|
|
|
|
if (res != TRI_ERROR_NO_ERROR) {
|
|
TRI_V8_EXCEPTION(scope, res);
|
|
}
|
|
}
|
|
|
|
return scope.Close(result);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief selects points within a given radius
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static v8::Handle<v8::Value> JS_WithinQuery (v8::Arguments const& argv) {
|
|
v8::HandleScope scope;
|
|
|
|
TRI_vocbase_col_t const* col;
|
|
col = TRI_UnwrapClass<TRI_vocbase_col_t>(argv.Holder(), TRI_GetVocBaseColType());
|
|
|
|
if (col == 0) {
|
|
TRI_V8_EXCEPTION_INTERNAL(scope, "cannot extract collection");
|
|
}
|
|
|
|
TRI_SHARDING_COLLECTION_NOT_YET_IMPLEMENTED(scope, col);
|
|
|
|
CollectionNameResolver resolver(col->_vocbase);
|
|
ReadTransactionType trx(col->_vocbase, resolver, col->_cid);
|
|
|
|
int res = trx.begin();
|
|
|
|
if (res != TRI_ERROR_NO_ERROR) {
|
|
TRI_V8_EXCEPTION(scope, res);
|
|
}
|
|
|
|
v8::Handle<v8::Object> err;
|
|
|
|
// .............................................................................
|
|
// inside a read transaction
|
|
// .............................................................................
|
|
|
|
trx.lockRead();
|
|
|
|
v8::Handle<v8::Value> result = WithinQuery(trx, col, &err, argv);
|
|
|
|
trx.finish(res);
|
|
|
|
// .............................................................................
|
|
// outside a write transaction
|
|
// .............................................................................
|
|
|
|
return scope.Close(result);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @}
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- MODULE
|
|
// -----------------------------------------------------------------------------
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- public functions
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @addtogroup VocBase
|
|
/// @{
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief creates the query functions
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void TRI_InitV8Queries (v8::Handle<v8::Context> context) {
|
|
v8::HandleScope scope;
|
|
|
|
v8::Handle<v8::ObjectTemplate> rt;
|
|
|
|
v8::Isolate* isolate = v8::Isolate::GetCurrent();
|
|
TRI_v8_global_t* v8g = (TRI_v8_global_t*) isolate->GetData();
|
|
|
|
assert(v8g != 0);
|
|
|
|
// .............................................................................
|
|
// generate the TRI_vocbase_col_t template
|
|
// .............................................................................
|
|
|
|
rt = v8g->VocbaseColTempl;
|
|
|
|
TRI_AddMethodVocbase(rt, "ALL", JS_AllQuery, true);
|
|
TRI_AddMethodVocbase(rt, "ANY", JS_AnyQuery, true);
|
|
TRI_AddMethodVocbase(rt, "BY_CONDITION_BITARRAY", JS_ByConditionBitarray, true);
|
|
TRI_AddMethodVocbase(rt, "BY_CONDITION_SKIPLIST", JS_ByConditionSkiplist, true);
|
|
TRI_AddMethodVocbase(rt, "BY_EXAMPLE", JS_ByExampleQuery, true);
|
|
TRI_AddMethodVocbase(rt, "BY_EXAMPLE_BITARRAY", JS_ByExampleBitarray, true);
|
|
TRI_AddMethodVocbase(rt, "BY_EXAMPLE_HASH", JS_ByExampleHashIndex, true);
|
|
TRI_AddMethodVocbase(rt, "BY_EXAMPLE_SKIPLIST", JS_ByExampleSkiplist, true);
|
|
TRI_AddMethodVocbase(rt, "checksum", JS_ChecksumCollection);
|
|
TRI_AddMethodVocbase(rt, "EDGES", JS_EdgesQuery, true);
|
|
TRI_AddMethodVocbase(rt, "FIRST", JS_FirstQuery, true);
|
|
TRI_AddMethodVocbase(rt, "FULLTEXT", JS_FulltextQuery, true);
|
|
TRI_AddMethodVocbase(rt, "INEDGES", JS_InEdgesQuery, true);
|
|
TRI_AddMethodVocbase(rt, "LAST", JS_LastQuery, true);
|
|
TRI_AddMethodVocbase(rt, "NEAR", JS_NearQuery, true);
|
|
|
|
// internal method. not intended to be used by end-users
|
|
TRI_AddMethodVocbase(rt, "OFFSET", JS_OffsetQuery, true);
|
|
|
|
TRI_AddMethodVocbase(rt, "OUTEDGES", JS_OutEdgesQuery, true);
|
|
TRI_AddMethodVocbase(rt, "WITHIN", JS_WithinQuery);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @}
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Local Variables:
|
|
// mode: outline-minor
|
|
// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|/// @page\\|// --SECTION--\\|/// @\\}"
|
|
// End:
|