mirror of https://gitee.com/bigwinds/arangodb
Merge branch 'devel' of github.com:triAGENS/ArangoDB into devel
This commit is contained in:
commit
11233fd57e
|
@ -3560,6 +3560,14 @@ void RemoveBlock::work (std::vector<AqlItemBlock*>& blocks) {
|
|||
0,
|
||||
nullptr,
|
||||
ep->_options.waitForSync);
|
||||
if (ExecutionEngine::isDBServer() &&
|
||||
errorCode == TRI_ERROR_ARANGO_DOCUMENT_NOT_FOUND) {
|
||||
auto* node = static_cast<RemoveNode const*>(getPlanNode());
|
||||
if (node->getOptions().ignoreDocumentNotFound) {
|
||||
// Ignore document not found on the DBserver:
|
||||
errorCode = TRI_ERROR_NO_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleResult(errorCode, ep->_options.ignoreErrors);
|
||||
|
@ -3795,6 +3803,14 @@ void UpdateBlock::work (std::vector<AqlItemBlock*>& blocks) {
|
|||
errorCode = TRI_ERROR_ARANGO_DOCUMENT_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
if (ExecutionEngine::isDBServer() &&
|
||||
errorCode == TRI_ERROR_ARANGO_DOCUMENT_NOT_FOUND) {
|
||||
auto* node = static_cast<UpdateNode const*>(getPlanNode());
|
||||
if (node->getOptions().ignoreDocumentNotFound) {
|
||||
// Ignore document not found on the DBserver:
|
||||
errorCode = TRI_ERROR_NO_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleResult(errorCode, ep->_options.ignoreErrors, &errorMessage);
|
||||
|
@ -3884,6 +3900,16 @@ void ReplaceBlock::work (std::vector<AqlItemBlock*>& blocks) {
|
|||
|
||||
// all exceptions are caught in _trx->update()
|
||||
errorCode = _trx->update(trxCollection, key, 0, &mptr, json.json(), TRI_DOC_UPDATE_LAST_WRITE, 0, nullptr, ep->_options.waitForSync);
|
||||
if (ExecutionEngine::isDBServer() &&
|
||||
errorCode == TRI_ERROR_ARANGO_DOCUMENT_NOT_FOUND) {
|
||||
auto* node = static_cast<ReplaceNode const*>(getPlanNode());
|
||||
if (node->getOptions().ignoreDocumentNotFound) {
|
||||
errorCode = TRI_ERROR_NO_ERROR;
|
||||
}
|
||||
else {
|
||||
errorCode = TRI_ERROR_ARANGO_DOCUMENT_NOT_FOUND_OR_SHARDING_ATTRIBUTES_CHANGED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleResult(errorCode, ep->_options.ignoreErrors);
|
||||
|
|
|
@ -425,13 +425,13 @@ struct CoordinatorInstanciator : public WalkerWorker<ExecutionNode> {
|
|||
// this map allows to find the queries which are the parts of the big
|
||||
// query. There are two cases, the first is for the remote queries on
|
||||
// the DBservers, for these, the key is:
|
||||
// itoa(ID of RemoteNode in original plan) + "_" + shardId
|
||||
// itoa(ID of RemoteNode in original plan) + ":" + shardId
|
||||
// and the value is the
|
||||
// queryId on DBserver
|
||||
// with a * appended, if it is a PART_MAIN query.
|
||||
// The second case is a query, which lives on the coordinator but is not
|
||||
// the main query. For these, we store
|
||||
// itoa(ID of RemoteNode in original plan)
|
||||
// itoa(ID of RemoteNode in original plan) + "/" + <name of vocbase>
|
||||
// and the value is the
|
||||
// queryId used in the QueryRegistry
|
||||
// this is built up when we instanciate the various engines on the
|
||||
|
@ -603,7 +603,7 @@ struct CoordinatorInstanciator : public WalkerWorker<ExecutionNode> {
|
|||
// std::cout << "DB SERVER ANSWERED WITHOUT ERROR: " << res->answer->body() << ", REMOTENODEID: " << info.idOfRemoteNode << " SHARDID:" << res->shardID << ", QUERYID: " << queryId << "\n";
|
||||
std::string theID
|
||||
= triagens::basics::StringUtils::itoa(info.idOfRemoteNode)
|
||||
+ "_" + res->shardID;
|
||||
+ ":" + res->shardID;
|
||||
if (info.part == triagens::aql::PART_MAIN) {
|
||||
queryIds.emplace(theID, queryId+"*");
|
||||
}
|
||||
|
@ -736,7 +736,7 @@ struct CoordinatorInstanciator : public WalkerWorker<ExecutionNode> {
|
|||
for (auto const& shardId : shardIds) {
|
||||
std::string theId
|
||||
= triagens::basics::StringUtils::itoa(remoteNode->id())
|
||||
+ "_" + shardId;
|
||||
+ ":" + shardId;
|
||||
auto it = queryIds.find(theId);
|
||||
if (it == queryIds.end()) {
|
||||
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, "could not find query id in list");
|
||||
|
@ -952,7 +952,8 @@ ExecutionEngine* ExecutionEngine::instanciateFromPlan (QueryRegistry* queryRegis
|
|||
for (auto& q : inst.get()->queryIds) {
|
||||
std::string theId = q.first;
|
||||
std::string queryId = q.second;
|
||||
auto pos = theId.find('_');
|
||||
// std::cout << "queryIds: " << theId << " : " << queryId << std::endl;
|
||||
auto pos = theId.find(':');
|
||||
if (pos != std::string::npos) {
|
||||
// So this is a remote one on a DBserver:
|
||||
if (queryId.back() == '*') { // only the PART_MAIN one!
|
||||
|
@ -963,6 +964,25 @@ ExecutionEngine* ExecutionEngine::instanciateFromPlan (QueryRegistry* queryRegis
|
|||
}
|
||||
}
|
||||
}
|
||||
// Second round, this time we deal with the coordinator pieces
|
||||
// and tell them the lockedShards as well, we need to copy, since
|
||||
// they want to delete independently:
|
||||
for (auto& q : inst.get()->queryIds) {
|
||||
std::string theId = q.first;
|
||||
std::string queryId = q.second;
|
||||
// std::cout << "queryIds: " << theId << " : " << queryId << std::endl;
|
||||
auto pos = theId.find('/');
|
||||
if (pos != std::string::npos) {
|
||||
// std::cout << "Setting lockedShards for query ID "
|
||||
// << queryId << std::endl;
|
||||
QueryId qId = triagens::basics::StringUtils::uint64(queryId);
|
||||
TRI_vocbase_t* vocbase = query->vocbase();
|
||||
Query* q = queryRegistry->open(vocbase, qId);
|
||||
q->engine()->setLockedShards(new std::unordered_set<std::string>(*engine->_lockedShards));
|
||||
queryRegistry->close(vocbase, qId);
|
||||
// std::cout << "Setting lockedShards done." << std::endl;
|
||||
}
|
||||
}
|
||||
// Now lock them all in the right order:
|
||||
for (auto& p : forLocking) {
|
||||
std::string const& shardId = p.first;
|
||||
|
@ -998,7 +1018,7 @@ ExecutionEngine* ExecutionEngine::instanciateFromPlan (QueryRegistry* queryRegis
|
|||
for (auto& q : inst.get()->queryIds) {
|
||||
std::string theId = q.first;
|
||||
std::string queryId = q.second;
|
||||
auto pos = theId.find('_');
|
||||
auto pos = theId.find(':');
|
||||
if (pos != std::string::npos) {
|
||||
// So this is a remote one on a DBserver:
|
||||
std::string shardId = theId.substr(pos+1);
|
||||
|
|
|
@ -239,6 +239,14 @@ namespace triagens {
|
|||
return _lockedShards;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief _lockedShards
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void setLockedShards (std::unordered_set<std::string>* lockedShards) {
|
||||
_lockedShards = lockedShards;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- private variables
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
|
@ -2251,6 +2251,22 @@ namespace triagens {
|
|||
|
||||
double estimateCost (size_t&) const override final;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief getOptions
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ModificationOptions const& getOptions () const {
|
||||
return _options;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief getOptions
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ModificationOptions& getOptions () {
|
||||
return _options;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- protected variables
|
||||
// -----------------------------------------------------------------------------
|
||||
|
@ -2553,6 +2569,8 @@ namespace triagens {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
virtual std::vector<Variable const*> getVariablesUsedHere () const override final {
|
||||
// Please do not change the order here without adjusting the
|
||||
// optimizer rule distributeInCluster as well!
|
||||
std::vector<Variable const*> v;
|
||||
v.push_back(_inDocVariable);
|
||||
|
||||
|
@ -2669,6 +2687,8 @@ namespace triagens {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
virtual std::vector<Variable const*> getVariablesUsedHere () const override final {
|
||||
// Please do not change the order here without adjusting the
|
||||
// optimizer rule distributeInCluster as well!
|
||||
std::vector<Variable const*> v;
|
||||
v.push_back(_inDocVariable);
|
||||
|
||||
|
@ -3087,7 +3107,7 @@ namespace triagens {
|
|||
: ExecutionNode(plan, id),
|
||||
_vocbase(vocbase),
|
||||
_collection(collection),
|
||||
_varId(varId){
|
||||
_varId(varId) {
|
||||
}
|
||||
|
||||
DistributeNode (ExecutionPlan*,
|
||||
|
|
|
@ -37,6 +37,7 @@ ModificationOptions::ModificationOptions (Json const& json) {
|
|||
waitForSync = JsonHelper::getBooleanValue(array.json(), "waitForSync", false);
|
||||
nullMeansRemove = JsonHelper::getBooleanValue(array.json(), "nullMeansRemove", false);
|
||||
mergeObjects = JsonHelper::getBooleanValue(array.json(), "mergeObjects", true);
|
||||
ignoreDocumentNotFound = JsonHelper::getBooleanValue(array.json(), "ignoreDocumentNotFound", false);
|
||||
}
|
||||
|
||||
void ModificationOptions::toJson (triagens::basics::Json& json,
|
||||
|
@ -46,7 +47,8 @@ void ModificationOptions::toJson (triagens::basics::Json& json,
|
|||
("ignoreErrors", Json(ignoreErrors))
|
||||
("waitForSync", Json(waitForSync))
|
||||
("nullMeansRemove", Json(nullMeansRemove))
|
||||
("mergeObjects", Json(mergeObjects));
|
||||
("mergeObjects", Json(mergeObjects))
|
||||
("ignoreDocumentNotFound", Json(ignoreDocumentNotFound));
|
||||
|
||||
json ("modificationFlags", flags);
|
||||
}
|
||||
|
|
|
@ -54,7 +54,8 @@ namespace triagens {
|
|||
: ignoreErrors(false),
|
||||
waitForSync(false),
|
||||
nullMeansRemove(false),
|
||||
mergeObjects(true) {
|
||||
mergeObjects(true),
|
||||
ignoreDocumentNotFound(false) {
|
||||
}
|
||||
|
||||
void toJson (triagens::basics::Json& json, TRI_memory_zone_t* zone) const;
|
||||
|
@ -67,6 +68,7 @@ namespace triagens {
|
|||
bool waitForSync;
|
||||
bool nullMeansRemove;
|
||||
bool mergeObjects;
|
||||
bool ignoreDocumentNotFound;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -2499,6 +2499,12 @@ int triagens::aql::scatterInClusterRule (Optimizer* opt,
|
|||
nodeType == ExecutionNode::REMOVE) {
|
||||
vocbase = static_cast<ModificationNode*>(node)->vocbase();
|
||||
collection = static_cast<ModificationNode*>(node)->collection();
|
||||
if (nodeType == ExecutionNode::REMOVE ||
|
||||
nodeType == ExecutionNode::UPDATE ||
|
||||
nodeType == ExecutionNode::REPLACE) {
|
||||
auto* modNode = static_cast<ModificationNode*>(node);
|
||||
modNode->getOptions().ignoreDocumentNotFound = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
TRI_ASSERT(false);
|
||||
|
@ -2553,7 +2559,7 @@ int triagens::aql::scatterInClusterRule (Optimizer* opt,
|
|||
///
|
||||
/// this rule inserts distribute, remote nodes so operations on sharded
|
||||
/// collections actually work, this differs from scatterInCluster in that every
|
||||
/// incoming row is only set to one shard and not all as in scatterInCluster
|
||||
/// incoming row is only sent to one shard and not all as in scatterInCluster
|
||||
///
|
||||
/// it will change plans in place
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -2571,22 +2577,33 @@ int triagens::aql::distributeInClusterRule (Optimizer* opt,
|
|||
auto const nodeType = node->getType();
|
||||
|
||||
if (nodeType != ExecutionNode::INSERT &&
|
||||
nodeType != ExecutionNode::REMOVE) {
|
||||
nodeType != ExecutionNode::REMOVE &&
|
||||
nodeType != ExecutionNode::REPLACE &&
|
||||
nodeType != ExecutionNode::UPDATE) {
|
||||
opt->addPlan(plan, rule->level, wasModified);
|
||||
return TRI_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
Collection const* collection = static_cast<ModificationNode*>(node)->collection();
|
||||
|
||||
if (nodeType == ExecutionNode::REMOVE) {
|
||||
// check if collection shard keys are only _key
|
||||
std::vector<std::string> shardKeys = collection->shardKeys();
|
||||
if (shardKeys.size() != 1 || shardKeys[0] != TRI_VOC_ATTRIBUTE_KEY) {
|
||||
bool defaultSharding = true;
|
||||
// check if collection shard keys are only _key
|
||||
std::vector<std::string> shardKeys = collection->shardKeys();
|
||||
if (shardKeys.size() != 1 || shardKeys[0] != TRI_VOC_ATTRIBUTE_KEY) {
|
||||
defaultSharding = false;
|
||||
}
|
||||
|
||||
if (nodeType == ExecutionNode::REMOVE ||
|
||||
nodeType == ExecutionNode::UPDATE) {
|
||||
if (! defaultSharding) {
|
||||
// We have to use a ScatterNode.
|
||||
opt->addPlan(plan, rule->level, wasModified);
|
||||
return TRI_ERROR_NO_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
// In the INSERT and REPLACE cases we use a DistributeNode...
|
||||
|
||||
auto deps = node->getDependencies();
|
||||
TRI_ASSERT(deps.size() == 1);
|
||||
|
||||
|
@ -2597,9 +2614,33 @@ int triagens::aql::distributeInClusterRule (Optimizer* opt,
|
|||
TRI_vocbase_t* vocbase = static_cast<ModificationNode*>(node)->vocbase();
|
||||
|
||||
// insert a distribute node
|
||||
TRI_ASSERT(node->getVariablesUsedHere().size() == 1);
|
||||
ExecutionNode* distNode = new DistributeNode(plan, plan->nextId(),
|
||||
vocbase, collection, node->getVariablesUsedHere()[0]->id);
|
||||
ExecutionNode* distNode = nullptr;
|
||||
if (nodeType == ExecutionNode::INSERT ||
|
||||
nodeType == ExecutionNode::REMOVE) {
|
||||
TRI_ASSERT(node->getVariablesUsedHere().size() == 1);
|
||||
distNode = new DistributeNode(plan, plan->nextId(),
|
||||
vocbase, collection, node->getVariablesUsedHere()[0]->id);
|
||||
}
|
||||
else if (nodeType == ExecutionNode::REPLACE) {
|
||||
std::vector<Variable const*> v = node->getVariablesUsedHere();
|
||||
if (defaultSharding) {
|
||||
// We only look into _inKeyVariable
|
||||
distNode = new DistributeNode(plan, plan->nextId(),
|
||||
vocbase, collection, v[1]->id);
|
||||
}
|
||||
else {
|
||||
// We only look into _inDocVariable
|
||||
distNode = new DistributeNode(plan, plan->nextId(),
|
||||
vocbase, collection, v[0]->id);
|
||||
}
|
||||
}
|
||||
else { // if (nodeType == ExecutionNode::UPDATE)
|
||||
std::vector<Variable const*> v = node->getVariablesUsedHere();
|
||||
distNode = new DistributeNode(plan, plan->nextId(),
|
||||
vocbase, collection, v[1]->id);
|
||||
// This is the _inKeyVariable! This works, since we use a ScatterNode
|
||||
// for non-default-sharding attributes.
|
||||
}
|
||||
plan->registerNode(distNode);
|
||||
distNode->addDependency(deps[0]);
|
||||
|
||||
|
|
|
@ -138,6 +138,7 @@ void QueryRegistry::insert (QueryId id,
|
|||
Query* QueryRegistry::open (TRI_vocbase_t* vocbase,
|
||||
QueryId id) {
|
||||
|
||||
// std::cout << "Taking out query with ID " << id << std::endl;
|
||||
WRITE_LOCKER(_lock);
|
||||
|
||||
auto m = _queries.find(vocbase->_name);
|
||||
|
@ -161,6 +162,7 @@ Query* QueryRegistry::open (TRI_vocbase_t* vocbase,
|
|||
// If we had set _makeNolockHeaders, we need to reset it:
|
||||
if (qi->_query->engine()->lockedShards() != nullptr) {
|
||||
if (Transaction::_makeNolockHeaders == nullptr) {
|
||||
// std::cout << "Setting _makeNolockHeaders\n";
|
||||
Transaction::_makeNolockHeaders = qi->_query->engine()->lockedShards();
|
||||
}
|
||||
else {
|
||||
|
@ -177,6 +179,7 @@ Query* QueryRegistry::open (TRI_vocbase_t* vocbase,
|
|||
|
||||
void QueryRegistry::close (TRI_vocbase_t* vocbase, QueryId id, double ttl) {
|
||||
|
||||
// std::cout << "Returning query with ID " << id << std::endl;
|
||||
WRITE_LOCKER(_lock);
|
||||
|
||||
auto m = _queries.find(vocbase->_name);
|
||||
|
@ -200,6 +203,7 @@ void QueryRegistry::close (TRI_vocbase_t* vocbase, QueryId id, double ttl) {
|
|||
// If we have set _makeNolockHeaders, we need to unset it:
|
||||
if (Transaction::_makeNolockHeaders != nullptr) {
|
||||
if (Transaction::_makeNolockHeaders == qi->_query->engine()->lockedShards()) {
|
||||
// std::cout << "Resetting _makeNolockHeaders to nullptr\n";
|
||||
Transaction::_makeNolockHeaders = nullptr;
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -589,7 +589,7 @@ void RestAqlHandler::getInfoQuery (std::string const& operation,
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
triagens::rest::HttpHandler::status_t RestAqlHandler::execute () {
|
||||
// std::cout << "GOT INCOMING REQUEST: " << triagens::rest::HttpRequest::translateMethod(_request->requestType()) << ", " << triagens::arango::ServerState::instance()->getId() << ": " << _request->fullUrl() << ": " << _request->body() << "\n";
|
||||
// std::cout << "GOT INCOMING REQUEST: " << triagens::rest::HttpRequest::translateMethod(_request->requestType()) << ", " << triagens::arango::ServerState::instance()->getId() << ": " << _request->fullUrl() << ": " << _request->body() << "\n\n";
|
||||
|
||||
std::vector<std::string> const& suffix = _request->suffix();
|
||||
|
||||
|
|
|
@ -196,8 +196,12 @@ ClusterCommResult* ClusterComm::asyncRequest (
|
|||
op->serverID = ClusterInfo::instance()->getResponsibleServer(op->shardID);
|
||||
LOG_DEBUG("Responsible server: %s", op->serverID.c_str());
|
||||
if (triagens::arango::Transaction::_makeNolockHeaders != nullptr) {
|
||||
// LOCKING-DEBUG
|
||||
// std::cout << "Found Nolock header\n";
|
||||
auto it = triagens::arango::Transaction::_makeNolockHeaders->find(op->shardID);
|
||||
if (it != triagens::arango::Transaction::_makeNolockHeaders->end()) {
|
||||
// LOCKING-DEBUG
|
||||
// std::cout << "Found our shard\n";
|
||||
(*headerFields)["X-Arango-Nolock"] = op->shardID;
|
||||
}
|
||||
}
|
||||
|
@ -240,6 +244,13 @@ ClusterCommResult* ClusterComm::asyncRequest (
|
|||
op->endTime = timeout == 0.0 ? TRI_microtime()+24*60*60.0
|
||||
: TRI_microtime()+timeout;
|
||||
|
||||
// LOCKING-DEBUG
|
||||
// std::cout << "asyncRequest: sending " << triagens::rest::HttpRequest::translateMethod(reqtype) << " request to DB server '" << op->serverID << ":" << path << "\n" << body << "\n";
|
||||
// for (auto& h : *headerFields) {
|
||||
// std::cout << h.first << ":" << h.second << std::endl;
|
||||
// }
|
||||
// std::cout << std::endl;
|
||||
|
||||
ClusterCommResult* res = new ClusterCommResult();
|
||||
*res = *static_cast<ClusterCommResult*>(op);
|
||||
|
||||
|
@ -311,8 +322,12 @@ ClusterCommResult* ClusterComm::syncRequest (
|
|||
return res;
|
||||
}
|
||||
if (triagens::arango::Transaction::_makeNolockHeaders != nullptr) {
|
||||
// LOCKING-DEBUG
|
||||
// std::cout << "Found Nolock header\n";
|
||||
auto it = triagens::arango::Transaction::_makeNolockHeaders->find(res->shardID);
|
||||
if (it != triagens::arango::Transaction::_makeNolockHeaders->end()) {
|
||||
// LOCKING-DEBUG
|
||||
// std::cout << "Found this shard: " << res->shardID << std::endl;
|
||||
headersCopy["X-Arango-Nolock"] = res->shardID;
|
||||
}
|
||||
}
|
||||
|
@ -355,6 +370,12 @@ ClusterCommResult* ClusterComm::syncRequest (
|
|||
LOG_DEBUG("sending %s request to DB server '%s': %s",
|
||||
triagens::rest::HttpRequest::translateMethod(reqtype).c_str(),
|
||||
res->serverID.c_str(), body.c_str());
|
||||
// LOCKING-DEBUG
|
||||
// std::cout << "syncRequest: sending " << triagens::rest::HttpRequest::translateMethod(reqtype) << " request to DB server '" << res->serverID << ":" << path << "\n" << body << "\n";
|
||||
// for (auto& h : headersCopy) {
|
||||
// std::cout << h.first << ":" << h.second << std::endl;
|
||||
// }
|
||||
// std::cout << std::endl;
|
||||
triagens::httpclient::SimpleHttpClient* client
|
||||
= new triagens::httpclient::SimpleHttpClient(
|
||||
connection->connection,
|
||||
|
|
|
@ -663,6 +663,13 @@ int RestVocbaseBaseHandler::parseDocumentId (CollectionNameResolver const* resol
|
|||
|
||||
void RestVocbaseBaseHandler::prepareExecute () {
|
||||
bool found;
|
||||
// LOCKING-DEBUG
|
||||
// std::cout << "REQ coming in: " << _request->requestType() << ": " <<_request->fullUrl() << std::endl;
|
||||
//std::map<std::string, std::string> h = _request->headers();
|
||||
//for (auto& hh : h) {
|
||||
// std::cout << hh.first << ": " << hh.second << std::endl;
|
||||
//}
|
||||
//std::cout << std::endl;
|
||||
char const* shardId = _request->header("x-arango-nolock", found);
|
||||
if (found) {
|
||||
_nolockHeaderSet = new std::unordered_set<std::string>();
|
||||
|
|
|
@ -861,9 +861,13 @@ static int BeginRead (TRI_document_collection_t* document) {
|
|||
auto it = triagens::arango::Transaction::_makeNolockHeaders->find(collName);
|
||||
if (it != triagens::arango::Transaction::_makeNolockHeaders->end()) {
|
||||
// do not lock by command
|
||||
// LOCKING-DEBUG
|
||||
// std::cout << "BeginRead blocked: " << document->_info._name << std::endl;
|
||||
return TRI_ERROR_NO_ERROR;
|
||||
}
|
||||
}
|
||||
// LOCKING-DEBUG
|
||||
// std::cout << "BeginRead: " << document->_info._name << std::endl;
|
||||
TRI_READ_LOCK_DOCUMENTS_INDEXES_PRIMARY_COLLECTION(document);
|
||||
|
||||
return TRI_ERROR_NO_ERROR;
|
||||
|
@ -879,9 +883,13 @@ static int EndRead (TRI_document_collection_t* document) {
|
|||
auto it = triagens::arango::Transaction::_makeNolockHeaders->find(collName);
|
||||
if (it != triagens::arango::Transaction::_makeNolockHeaders->end()) {
|
||||
// do not lock by command
|
||||
// LOCKING-DEBUG
|
||||
// std::cout << "EndRead blocked: " << document->_info._name << std::endl;
|
||||
return TRI_ERROR_NO_ERROR;
|
||||
}
|
||||
}
|
||||
// LOCKING-DEBUG
|
||||
// std::cout << "EndRead: " << document->_info._name << std::endl;
|
||||
TRI_READ_UNLOCK_DOCUMENTS_INDEXES_PRIMARY_COLLECTION(document);
|
||||
|
||||
return TRI_ERROR_NO_ERROR;
|
||||
|
@ -897,9 +905,13 @@ static int BeginWrite (TRI_document_collection_t* document) {
|
|||
auto it = triagens::arango::Transaction::_makeNolockHeaders->find(collName);
|
||||
if (it != triagens::arango::Transaction::_makeNolockHeaders->end()) {
|
||||
// do not lock by command
|
||||
// LOCKING-DEBUG
|
||||
// std::cout << "BeginWrite blocked: " << document->_info._name << std::endl;
|
||||
return TRI_ERROR_NO_ERROR;
|
||||
}
|
||||
}
|
||||
// LOCKING_DEBUG
|
||||
// std::cout << "BeginWrite: " << document->_info._name << std::endl;
|
||||
TRI_WRITE_LOCK_DOCUMENTS_INDEXES_PRIMARY_COLLECTION(document);
|
||||
|
||||
return TRI_ERROR_NO_ERROR;
|
||||
|
@ -915,9 +927,13 @@ static int EndWrite (TRI_document_collection_t* document) {
|
|||
auto it = triagens::arango::Transaction::_makeNolockHeaders->find(collName);
|
||||
if (it != triagens::arango::Transaction::_makeNolockHeaders->end()) {
|
||||
// do not lock by command
|
||||
// LOCKING-DEBUG
|
||||
// std::cout << "EndWrite blocked: " << document->_info._name << std::endl;
|
||||
return TRI_ERROR_NO_ERROR;
|
||||
}
|
||||
}
|
||||
// LOCKING-DEBUG
|
||||
// std::cout << "EndWrite: " << document->_info._name << std::endl;
|
||||
TRI_WRITE_UNLOCK_DOCUMENTS_INDEXES_PRIMARY_COLLECTION(document);
|
||||
|
||||
return TRI_ERROR_NO_ERROR;
|
||||
|
@ -935,11 +951,15 @@ static int BeginReadTimed (TRI_document_collection_t* document,
|
|||
auto it = triagens::arango::Transaction::_makeNolockHeaders->find(collName);
|
||||
if (it != triagens::arango::Transaction::_makeNolockHeaders->end()) {
|
||||
// do not lock by command
|
||||
// LOCKING-DEBUG
|
||||
// std::cout << "BeginReadTimed blocked: " << document->_info._name << std::endl;
|
||||
return TRI_ERROR_NO_ERROR;
|
||||
}
|
||||
}
|
||||
uint64_t waited = 0;
|
||||
|
||||
// LOCKING-DEBUG
|
||||
// std::cout << "BeginReadTimed: " << document->_info._name << std::endl;
|
||||
while (! TRI_TRY_READ_LOCK_DOCUMENTS_INDEXES_PRIMARY_COLLECTION(document)) {
|
||||
#ifdef _WIN32
|
||||
usleep((unsigned long) sleepPeriod);
|
||||
|
@ -969,11 +989,15 @@ static int BeginWriteTimed (TRI_document_collection_t* document,
|
|||
auto it = triagens::arango::Transaction::_makeNolockHeaders->find(collName);
|
||||
if (it != triagens::arango::Transaction::_makeNolockHeaders->end()) {
|
||||
// do not lock by command
|
||||
// LOCKING-DEBUG
|
||||
// std::cout << "BeginWriteTimed blocked: " << document->_info._name << std::endl;
|
||||
return TRI_ERROR_NO_ERROR;
|
||||
}
|
||||
}
|
||||
uint64_t waited = 0;
|
||||
|
||||
// LOCKING-DEBUG
|
||||
// std::cout << "BeginWriteTimed: " << document->_info._name << std::endl;
|
||||
while (! TRI_TRY_WRITE_LOCK_DOCUMENTS_INDEXES_PRIMARY_COLLECTION(document)) {
|
||||
#ifdef _WIN32
|
||||
usleep((unsigned long) sleepPeriod);
|
||||
|
|
|
@ -348,6 +348,8 @@ static int LockCollection (TRI_transaction_collection_t* trxCollection,
|
|||
auto it = triagens::arango::Transaction::_makeNolockHeaders->find(collName);
|
||||
if (it != triagens::arango::Transaction::_makeNolockHeaders->end()) {
|
||||
// do not lock by command
|
||||
// LOCKING-DEBUG
|
||||
// std::cout << "LockCollection blocked: " << collName << std::endl;
|
||||
return TRI_ERROR_NO_ERROR;
|
||||
}
|
||||
}
|
||||
|
@ -411,6 +413,8 @@ static int UnlockCollection (TRI_transaction_collection_t* trxCollection,
|
|||
auto it = triagens::arango::Transaction::_makeNolockHeaders->find(collName);
|
||||
if (it != triagens::arango::Transaction::_makeNolockHeaders->end()) {
|
||||
// do not lock by command
|
||||
// LOCKING-DEBUG
|
||||
// std::cout << "UnlockCollection blocked: " << collName << std::endl;
|
||||
return TRI_ERROR_NO_ERROR;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -145,6 +145,7 @@
|
|||
"ERROR_CLUSTER_READING_PLAN_AGENCY" : { "code" : 1472, "message" : "error reading Plan in agency" },
|
||||
"ERROR_CLUSTER_COULD_NOT_TRUNCATE_COLLECTION" : { "code" : 1473, "message" : "could not truncate collection" },
|
||||
"ERROR_CLUSTER_AQL_COMMUNICATION" : { "code" : 1474, "message" : "error in cluster internal communication for AQL" },
|
||||
"ERROR_ARANGO_DOCUMENT_NOT_FOUND_OR_SHARDING_ATTRIBUTES_CHANGED" : { "code" : 1475, "message" : "document not found or sharding attributes changed" },
|
||||
"ERROR_QUERY_KILLED" : { "code" : 1500, "message" : "query killed" },
|
||||
"ERROR_QUERY_PARSE" : { "code" : 1501, "message" : "%s" },
|
||||
"ERROR_QUERY_EMPTY" : { "code" : 1502, "message" : "query is empty" },
|
||||
|
|
|
@ -145,6 +145,7 @@
|
|||
"ERROR_CLUSTER_READING_PLAN_AGENCY" : { "code" : 1472, "message" : "error reading Plan in agency" },
|
||||
"ERROR_CLUSTER_COULD_NOT_TRUNCATE_COLLECTION" : { "code" : 1473, "message" : "could not truncate collection" },
|
||||
"ERROR_CLUSTER_AQL_COMMUNICATION" : { "code" : 1474, "message" : "error in cluster internal communication for AQL" },
|
||||
"ERROR_ARANGO_DOCUMENT_NOT_FOUND_OR_SHARDING_ATTRIBUTES_CHANGED" : { "code" : 1475, "message" : "document not found or sharding attributes changed" },
|
||||
"ERROR_QUERY_KILLED" : { "code" : 1500, "message" : "query killed" },
|
||||
"ERROR_QUERY_PARSE" : { "code" : 1501, "message" : "%s" },
|
||||
"ERROR_QUERY_EMPTY" : { "code" : 1502, "message" : "query is empty" },
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
.gitignore
|
||||
node_modules
|
||||
test
|
|
@ -1,7 +0,0 @@
|
|||
language: node_js
|
||||
node_js:
|
||||
- "0.10"
|
||||
services:
|
||||
- redis-server
|
||||
after_script:
|
||||
- npm test
|
|
@ -2,6 +2,55 @@
|
|||
|
||||
The query builder allows constructing complex AQL queries with a pure JavaScript fluid API.
|
||||
|
||||
[](http://opensource.org/licenses/APACHE-2.0) [](https://david-dm.org/arangodb/aqbjs)
|
||||
|
||||
[](https://npmjs.org/package/aqb)
|
||||
|
||||
[](https://travis-ci.org/arangodb/aqbjs) [](https://coveralls.io/r/arangodb/aqbjs?branch=master) [](https://www.codacy.com/public/me_4/aqbjs)
|
||||
|
||||
# Install
|
||||
|
||||
## With NPM
|
||||
|
||||
```sh
|
||||
npm install aqb
|
||||
```
|
||||
|
||||
## With Bower
|
||||
|
||||
```sh
|
||||
bower install aqb
|
||||
```
|
||||
|
||||
## ArangoDB
|
||||
|
||||
As of ArangoDB 1.3, a version of `aqb` comes pre-installed with ArangoDB.
|
||||
|
||||
```js
|
||||
var qb = require('aqb');
|
||||
```
|
||||
|
||||
If you want to use a more recent version of `aqb` in a Foxx app, you can add it to your NPM dependencies as usual.
|
||||
|
||||
## Browser
|
||||
|
||||
This CommonJS module is compatible with [browserify](http://browserify.org).
|
||||
|
||||
If you don't want to use browserify, you can simply use the AMD-compatible [browserify bundle](https://raw.githubusercontent.com/arangodb/aqbjs/master/dist/aqb.min.js) (~30 kB minified, ~6 kB gzipped).
|
||||
|
||||
If you want to use this module in non-ES5 browsers like Microsoft Internet Explorer 8 and earlier, you may need to include [es5-shim](https://www.npmjs.com/package/es5-shim) or a similar ES5 polyfill.
|
||||
|
||||
## From source
|
||||
|
||||
```sh
|
||||
git clone https://github.com/arangodb/aqbjs.git
|
||||
cd aqbjs
|
||||
npm install
|
||||
npm run dist
|
||||
```
|
||||
|
||||
# Example
|
||||
|
||||
```js
|
||||
// in arangosh
|
||||
var db = require('org/arangodb').db;
|
||||
|
@ -9,15 +58,15 @@ var qb = require('aqb');
|
|||
console.log(db._query(qb.for('x').in('1..5').return('x')).toArray()); // [1, 2, 3, 4, 5]
|
||||
```
|
||||
|
||||
## API
|
||||
# API
|
||||
|
||||
### AQL Types
|
||||
## AQL Types
|
||||
|
||||
If raw JavaScript values are passed to AQL statements, they will be wrapped in a matching AQL type automatically.
|
||||
|
||||
JavaScript strings wrapped in quotation marks will be wrapped in AQL strings, all other JavaScript strings will be wrapped as simple references (see below) and throw an *AQLError* if they are not well-formed.
|
||||
|
||||
#### Boolean
|
||||
### Boolean
|
||||
|
||||
Wraps the given value as an AQL Boolean literal.
|
||||
|
||||
|
@ -27,7 +76,7 @@ If the value is truthy, it will be converted to the AQL Boolean *true*, otherwis
|
|||
|
||||
If the value is already an AQL Boolean, its own value will be wrapped instead.
|
||||
|
||||
#### Number
|
||||
### Number
|
||||
|
||||
Wraps the given value as an AQL Number literal.
|
||||
|
||||
|
@ -39,7 +88,7 @@ If the value does not represent a finite number, an *AQLError* will be thrown.
|
|||
|
||||
If the value is already an AQL Number or AQL Integer, its own value will be wrapped instead.
|
||||
|
||||
#### Integer
|
||||
### Integer
|
||||
|
||||
Wraps the given value as an AQL Integer literal.
|
||||
|
||||
|
@ -53,7 +102,7 @@ If the value is already an AQL Number or AQL Integer, its own value will be wrap
|
|||
|
||||
*Alias:* `qb.int_(value)`
|
||||
|
||||
#### String
|
||||
### String
|
||||
|
||||
Wraps the given value as an AQL String literal.
|
||||
|
||||
|
@ -65,7 +114,7 @@ If the value is already an AQL String, its own value will be wrapped instead.
|
|||
|
||||
If the value is an object with a *toAQL* method, the result of calling that method will be wrapped instead.
|
||||
|
||||
#### List
|
||||
### List
|
||||
|
||||
Wraps the given value as an AQL List (Array) literal.
|
||||
|
||||
|
@ -77,7 +126,7 @@ If the value is already an AQL List, its own value will be wrapped instead.
|
|||
|
||||
Any list elements that are not already AQL values will be converted automatically.
|
||||
|
||||
#### Object
|
||||
### Object
|
||||
|
||||
Wraps the given value as an AQL Object literal.
|
||||
|
||||
|
@ -89,7 +138,7 @@ If the value is already an AQL List, its own value will be wrapped instead.
|
|||
|
||||
Any property values that are not already AQL values will be converted automatically.
|
||||
|
||||
#### Simple Reference
|
||||
### Simple Reference
|
||||
|
||||
Wraps a given value in an AQL Simple Reference.
|
||||
|
||||
|
@ -127,9 +176,9 @@ var myUserCollection = applicationContext.collection('users');
|
|||
var users = db._query(qb.for('u').in(myUserCollection).return('u')).toArray();
|
||||
```
|
||||
|
||||
### AQL Expressions
|
||||
## AQL Expressions
|
||||
|
||||
#### Range
|
||||
### Range
|
||||
|
||||
Creates a range expression from the given values.
|
||||
|
||||
|
@ -137,7 +186,7 @@ Creates a range expression from the given values.
|
|||
|
||||
If the values are not already AQL values, they will be converted automatically.
|
||||
|
||||
#### Property Access
|
||||
### Property Access
|
||||
|
||||
Creates a property access expression from the given values.
|
||||
|
||||
|
@ -145,7 +194,7 @@ Creates a property access expression from the given values.
|
|||
|
||||
If the values are not already AQL values, they will be converted automatically.
|
||||
|
||||
#### Raw Expression
|
||||
### Raw Expression
|
||||
|
||||
Wraps a given value in a raw AQL expression.
|
||||
|
||||
|
@ -155,9 +204,9 @@ If the value is already an AQL Raw Expression, its value is wrapped instead.
|
|||
|
||||
**Warning:** Whenever possible, you should use one of the other methods or a combination thereof instead of using a raw expression. Raw expressions allow passing arbitrary strings into your AQL and thus will open you to AQL injection attacks if you are passing in untrusted user input.
|
||||
|
||||
### AQL Operations
|
||||
## AQL Operations
|
||||
|
||||
#### Boolean And
|
||||
### Boolean And
|
||||
|
||||
Creates an "and" operation from the given values.
|
||||
|
||||
|
@ -171,7 +220,7 @@ This function can take any number of arguments.
|
|||
|
||||
`qb.and(a, b, c, d, e, f)` -> `(a && b && c && d && e && f)`
|
||||
|
||||
#### Boolean Or
|
||||
### Boolean Or
|
||||
|
||||
Creates an "or" operation from the given values.
|
||||
|
||||
|
@ -185,7 +234,7 @@ This function can take any number of arguments.
|
|||
|
||||
`qb.or(a, b, c, d, e, f)` -> `(a || b || c || d || e || f)`
|
||||
|
||||
#### Addition
|
||||
### Addition
|
||||
|
||||
Creates an addition operation from the given values.
|
||||
|
||||
|
@ -201,7 +250,7 @@ This function can take any number of arguments.
|
|||
|
||||
`qb.add(a, b, c, d, e, f)` -> `(a + b + c + d + e + f)`
|
||||
|
||||
#### Subtraction
|
||||
### Subtraction
|
||||
|
||||
Creates a subtraction operation from the given values.
|
||||
|
||||
|
@ -217,7 +266,7 @@ This function can take any number of arguments.
|
|||
|
||||
`qb.sub(a, b, c, d, e, f)` -> `(a - b - c - d - e - f)`
|
||||
|
||||
#### Multiplication
|
||||
### Multiplication
|
||||
|
||||
Creates a multiplication operation from the given values.
|
||||
|
||||
|
@ -233,7 +282,7 @@ This function can take any number of arguments.
|
|||
|
||||
`qb.mul(a, b, c, d, e, f)` -> `(a * b * c * d * e * f)`
|
||||
|
||||
#### Division
|
||||
### Division
|
||||
|
||||
Creates a division operation from the given values.
|
||||
|
||||
|
@ -247,7 +296,7 @@ This function can take any number of arguments.
|
|||
|
||||
`qb.div(a, b, c, d, e, f)` -> `(a / b / c / d / e / f)`
|
||||
|
||||
#### Modulus
|
||||
### Modulus
|
||||
|
||||
Creates a modulus operation from the given values.
|
||||
|
||||
|
@ -261,7 +310,7 @@ This function can take any number of arguments.
|
|||
|
||||
`qb.mod(a, b, c, d, e, f)` -> `(a % b % c % d % e % f)`
|
||||
|
||||
#### Equality
|
||||
### Equality
|
||||
|
||||
Creates an equality comparison from the given values.
|
||||
|
||||
|
@ -269,7 +318,7 @@ Creates an equality comparison from the given values.
|
|||
|
||||
If the values are not already AQL values, they will be converted automatically.
|
||||
|
||||
#### Inequality
|
||||
### Inequality
|
||||
|
||||
Creates an inequality comparison from the given values.
|
||||
|
||||
|
@ -277,7 +326,7 @@ Creates an inequality comparison from the given values.
|
|||
|
||||
If the values are not already AQL values, they will be converted automatically.
|
||||
|
||||
#### Greater Than
|
||||
### Greater Than
|
||||
|
||||
Creates a greater-than comparison from the given values.
|
||||
|
||||
|
@ -285,7 +334,7 @@ Creates a greater-than comparison from the given values.
|
|||
|
||||
If the values are not already AQL values, they will be converted automatically.
|
||||
|
||||
#### Greater Than Or Equal To
|
||||
### Greater Than Or Equal To
|
||||
|
||||
Creates a greater-than-or-equal-to comparison from the given values.
|
||||
|
||||
|
@ -293,7 +342,7 @@ Creates a greater-than-or-equal-to comparison from the given values.
|
|||
|
||||
If the values are not already AQL values, they will be converted automatically.
|
||||
|
||||
#### Less Than
|
||||
### Less Than
|
||||
|
||||
Creates a less-than comparison from the given values.
|
||||
|
||||
|
@ -301,7 +350,7 @@ Creates a less-than comparison from the given values.
|
|||
|
||||
If the values are not already AQL values, they will be converted automatically.
|
||||
|
||||
#### Less Than Or Equal To
|
||||
### Less Than Or Equal To
|
||||
|
||||
Creates a less-than-or-equal-to comparison from the given values.
|
||||
|
||||
|
@ -309,7 +358,7 @@ Creates a less-than-or-equal-to comparison from the given values.
|
|||
|
||||
If the values are not already AQL values, they will be converted automatically.
|
||||
|
||||
#### Contains
|
||||
### Contains
|
||||
|
||||
Creates an "in" comparison from the given values.
|
||||
|
||||
|
@ -319,7 +368,7 @@ If the values are not already AQL values, they will be converted automatically.
|
|||
|
||||
*Aliases:* `qb.in_(a, b)`
|
||||
|
||||
#### Negation
|
||||
### Negation
|
||||
|
||||
Creates a negation from the given value.
|
||||
|
||||
|
@ -327,7 +376,7 @@ Creates a negation from the given value.
|
|||
|
||||
If the value is not already an AQL value, it will be converted automatically.
|
||||
|
||||
#### Negative Value
|
||||
### Negative Value
|
||||
|
||||
Creates a negative value expression from the given value.
|
||||
|
||||
|
@ -335,7 +384,7 @@ Creates a negative value expression from the given value.
|
|||
|
||||
If the value is not already an AQL value, it will be converted automatically.
|
||||
|
||||
#### Ternary (if / else)
|
||||
### Ternary (if / else)
|
||||
|
||||
Creates a ternary expression from the given values.
|
||||
|
||||
|
@ -345,7 +394,7 @@ If the values are not already AQL values, they will be converted automatically.
|
|||
|
||||
*Aliases:* `qb.if_(condition, then, otherwise)`
|
||||
|
||||
#### Function Call
|
||||
### Function Call
|
||||
|
||||
Creates a function call for the given name and arguments.
|
||||
|
||||
|
@ -362,7 +411,7 @@ For built-in functions, methods with the relevant function name are already prov
|
|||
* `qb.RANDOM()` -> `RANDOM()`
|
||||
* `qb.FLOOR(qb.div(5, 2))` -> `FLOOR((5 / 2))`
|
||||
|
||||
### AQL Statements
|
||||
## AQL Statements
|
||||
|
||||
In addition to the methods documented above, the query builder provides all methods of *PartialStatement* objects.
|
||||
|
||||
|
@ -375,59 +424,59 @@ qb.for('doc').in('my_collection').return('doc._key').toAQL()
|
|||
// -> FOR doc IN my_collection RETURN doc._key
|
||||
```
|
||||
|
||||
#### FOR expression IN collection
|
||||
### FOR expression IN collection
|
||||
|
||||
`PartialStatement::for(expression).in(collection) : Statement`
|
||||
|
||||
*Alias:* `for_(expression).in_(collection)`
|
||||
|
||||
#### LET varname = expression
|
||||
### LET varname = expression
|
||||
|
||||
`PartialStatement::let(varname, expression) : Statement`
|
||||
|
||||
*Alias:* `let_(varname, expression)`
|
||||
|
||||
#### LET var1 = expr1, var2 = expr2, …, varn = exprn
|
||||
### LET var1 = expr1, var2 = expr2, …, varn = exprn
|
||||
|
||||
`PartialStatement::let(definitions) : Statement`
|
||||
|
||||
*Alias:* `let_(definitions)`
|
||||
|
||||
#### FILTER expression
|
||||
### FILTER expression
|
||||
|
||||
`PartialStatement::filter(expression) : Statement`
|
||||
|
||||
#### COLLECT varname = expression
|
||||
### COLLECT varname = expression
|
||||
|
||||
`PartialStatement::collect(varname, expression) : Statement`
|
||||
|
||||
#### COLLECT varname1 = expression INTO varname2
|
||||
### COLLECT varname1 = expression INTO varname2
|
||||
|
||||
`PartialStatement::collect(varname1, expression).into(varname2) : Statement`
|
||||
|
||||
#### COLLECT var1 = expr1, var2 = expr2, …, varn = exprn
|
||||
### COLLECT var1 = expr1, var2 = expr2, …, varn = exprn
|
||||
|
||||
`PartialStatement::collect(definitions) : Statement`
|
||||
|
||||
#### COLLECT var1 = expr1, var2 = expr2, …, varn = exprn INTO varname
|
||||
### COLLECT var1 = expr1, var2 = expr2, …, varn = exprn INTO varname
|
||||
|
||||
`PartialStatement::collect(definitions).into(varname) : Statement`
|
||||
|
||||
#### SORT args…
|
||||
### SORT args…
|
||||
|
||||
`PartialStatement::sort(args…) : Statement`
|
||||
|
||||
#### LIMIT offset, count
|
||||
### LIMIT offset, count
|
||||
|
||||
`PartialStatement::limit([offset,] count) : Statement`
|
||||
|
||||
#### RETURN expression
|
||||
### RETURN expression
|
||||
|
||||
`PartialStatement::return(expression) : Statement`
|
||||
|
||||
*Alias:* `return_(expression)`
|
||||
|
||||
#### REMOVE expression IN collection
|
||||
### REMOVE expression IN collection
|
||||
|
||||
`PartialStatement::remove(expression).in(collection) : RemoveExpression`
|
||||
|
||||
|
@ -436,11 +485,11 @@ qb.for('doc').in('my_collection').return('doc._key').toAQL()
|
|||
* `remove(expression).in_(collection)`
|
||||
* `remove(expression).into(collection)`
|
||||
|
||||
#### REMOVE … OPTIONS options
|
||||
### REMOVE … OPTIONS options
|
||||
|
||||
`RemoveExpression::options(options) : Statement`
|
||||
|
||||
#### INSERT expression INTO collection
|
||||
### INSERT expression INTO collection
|
||||
|
||||
`PartialStatement::insert(expression).into(collection) : InsertExpression`
|
||||
|
||||
|
@ -449,11 +498,11 @@ qb.for('doc').in('my_collection').return('doc._key').toAQL()
|
|||
* `insert(expression).in(collection)`
|
||||
* `insert(expression).in_(collection)`
|
||||
|
||||
#### INSERT … OPTIONS options
|
||||
### INSERT … OPTIONS options
|
||||
|
||||
`InsertExpression::options(options) : Statement`
|
||||
|
||||
#### UPDATE expression1 WITH expression2 IN collection
|
||||
### UPDATE expression1 WITH expression2 IN collection
|
||||
|
||||
`PartialStatement::update(expression1).with(expression2).in(collection) : UpdateExpression`
|
||||
|
||||
|
@ -465,7 +514,7 @@ qb.for('doc').in('my_collection').return('doc._key').toAQL()
|
|||
* `update(expression1).with_(expression2).in_(collection)`
|
||||
* `update(expression1).with_(expression2).into(collection)`
|
||||
|
||||
#### UPDATE expression IN collection
|
||||
### UPDATE expression IN collection
|
||||
|
||||
`PartialStatement::update(expression).in(collection) : UpdateExpression`
|
||||
|
||||
|
@ -474,11 +523,11 @@ qb.for('doc').in('my_collection').return('doc._key').toAQL()
|
|||
* `update(expression).in_(collection)`
|
||||
* `update(expression).into(collection)`
|
||||
|
||||
#### UPDATE … OPTIONS options
|
||||
### UPDATE … OPTIONS options
|
||||
|
||||
`UpdateExpression::options(options) : Statement`
|
||||
|
||||
#### REPLACE expression1 WITH expression2 IN collection
|
||||
### REPLACE expression1 WITH expression2 IN collection
|
||||
|
||||
`PartialStatement::replace(expression1).with(expression2).in(collection) : ReplaceExpression`
|
||||
|
||||
|
@ -490,7 +539,7 @@ qb.for('doc').in('my_collection').return('doc._key').toAQL()
|
|||
* `replace(expression1).with_(expression2).in_(collection)`
|
||||
* `replace(expression1).with_(expression2).into(collection)`
|
||||
|
||||
#### REPLACE expression IN collection
|
||||
### REPLACE expression IN collection
|
||||
|
||||
`PartialStatement::replace(expression).in(collection) : ReplaceExpression`
|
||||
|
||||
|
@ -499,6 +548,10 @@ qb.for('doc').in('my_collection').return('doc._key').toAQL()
|
|||
* `replace(expression).in_(collection)`
|
||||
* `replace(expression).into(collection)`
|
||||
|
||||
#### REPLACE … OPTIONS options
|
||||
### REPLACE … OPTIONS options
|
||||
|
||||
`ReplaceExpression::options(options) : Statement`
|
||||
|
||||
# License
|
||||
|
||||
The Apache License, Version 2.0. For more information, see the accompanying LICENSE file.
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
/* jshint globalstrict: true, es3: true */
|
||||
/* globals require: false, exports: false */
|
||||
/*jshint browserify: true */
|
||||
'use strict';
|
||||
exports.keywords = [
|
||||
'asc',
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
/* jshint globalstrict: true, es3: true */
|
||||
/* globals require: false, exports: false */
|
||||
/*jshint browserify: true */
|
||||
'use strict';
|
||||
function AqlError(message) {
|
||||
this.message = message;
|
||||
|
|
|
@ -1,26 +1,14 @@
|
|||
/* jshint globalstrict: true, es3: true */
|
||||
/* globals require: false, module: false, console: false */
|
||||
/*jshint browserify: true */
|
||||
/*globals console: false */
|
||||
'use strict';
|
||||
var AqlError = require('./errors').AqlError,
|
||||
assumptions = require('./assumptions'),
|
||||
types = require('./types'),
|
||||
QB = {},
|
||||
bind,
|
||||
toArray,
|
||||
warn;
|
||||
|
||||
|
||||
bind = (typeof Function.prototype.bind === 'function' ? (
|
||||
Function.prototype.call.bind(Function.prototype.bind)
|
||||
) : (
|
||||
function (fn, self) {
|
||||
return function () {
|
||||
return fn.apply(self, arguments);
|
||||
};
|
||||
}
|
||||
));
|
||||
|
||||
toArray = bind(Function.prototype.call, Array.prototype.slice);
|
||||
toArray = Function.prototype.call.bind(Array.prototype.slice);
|
||||
|
||||
warn = (function () {
|
||||
if (typeof console !== 'undefined') {
|
||||
|
@ -34,15 +22,14 @@ warn = (function () {
|
|||
}
|
||||
}());
|
||||
|
||||
for (var key in types._PartialStatement.prototype) {
|
||||
if (types._PartialStatement.prototype.hasOwnProperty(key) && key !== 'constructor') {
|
||||
QB[key] = bind(types._PartialStatement.prototype[key], null);
|
||||
}
|
||||
}
|
||||
Object.keys(types._PartialStatement.prototype).forEach(function (key) {
|
||||
if (key === 'constructor') return;
|
||||
QB[key] = types._PartialStatement.prototype[key].bind(null);
|
||||
});
|
||||
|
||||
QB.bool = function (value) {return new types.BooleanLiteral(value);};
|
||||
QB.num = function (value) {return new types.NumberLiteral(value);};
|
||||
QB.int_ = function (value) {return new types.IntegerLiteral(value);};
|
||||
QB.int_ = QB.int = function (value) {return new types.IntegerLiteral(value);};
|
||||
QB.str = function (value) {return new types.StringLiteral(value);};
|
||||
QB.list = function (arr) {return new types.ListLiteral(arr);};
|
||||
QB.obj = function (obj) {return new types.ObjectLiteral(obj);};
|
||||
|
@ -58,9 +45,9 @@ QB.expr = function (value) {return new types.RawExpression(value);};
|
|||
|
||||
QB.and = function (/* x, y... */) {return new types.NAryOperation('&&', toArray(arguments));};
|
||||
QB.or = function (/* x, y... */) {return new types.NAryOperation('||', toArray(arguments));};
|
||||
QB.add = function (/* x, y... */) {return new types.NAryOperation('+', toArray(arguments));};
|
||||
QB.sub = function (/* x, y... */) {return new types.NAryOperation('-', toArray(arguments));};
|
||||
QB.mul = function (/* x, y... */) {return new types.NAryOperation('*', toArray(arguments));};
|
||||
QB.plus = QB.add = function (/* x, y... */) {return new types.NAryOperation('+', toArray(arguments));};
|
||||
QB.minus = QB.sub = function (/* x, y... */) {return new types.NAryOperation('-', toArray(arguments));};
|
||||
QB.times = QB.mul = function (/* x, y... */) {return new types.NAryOperation('*', toArray(arguments));};
|
||||
QB.div = function (/* x, y... */) {return new types.NAryOperation('/', toArray(arguments));};
|
||||
QB.mod = function (/* x, y... */) {return new types.NAryOperation('%', toArray(arguments));};
|
||||
QB.eq = function (x, y) {return new types.BinaryOperation('==', x, y);};
|
||||
|
@ -71,16 +58,8 @@ QB.lte = function (x, y) {return new types.BinaryOperation('<=', x, y);};
|
|||
QB.neq = function (x, y) {return new types.BinaryOperation('!=', x, y);};
|
||||
QB.not = function (x) {return new types.UnaryOperation('!', x);};
|
||||
QB.neg = function (x) {return new types.UnaryOperation('-', x);};
|
||||
QB.in_ = function (x, y) {return new types.BinaryOperation('in', x, y);};
|
||||
QB.if_ = function (x, y, z) {return new types.TernaryOperation('?', ':', x, y, z);};
|
||||
|
||||
QB.plus = QB.add;
|
||||
QB.minus = QB.sub;
|
||||
QB.times = QB.mul;
|
||||
|
||||
QB['int'] = QB.int_;
|
||||
QB['in'] = QB.in_;
|
||||
QB['if'] = QB.if_;
|
||||
QB.in_ = QB.in = function (x, y) {return new types.BinaryOperation('in', x, y);};
|
||||
QB.if_ = QB.if = function (x, y, z) {return new types.TernaryOperation('?', ':', x, y, z);};
|
||||
|
||||
QB.fn = function (functionName, arity) {
|
||||
if (typeof arity === 'number') {
|
||||
|
@ -120,13 +99,11 @@ function deprecateAqlFunction(fn, functionName) {
|
|||
};
|
||||
}
|
||||
|
||||
for (var key in assumptions.builtins) {
|
||||
if (assumptions.builtins.hasOwnProperty(key)) {
|
||||
QB[key] = QB.fn(key, assumptions.builtins[key]);
|
||||
if (assumptions.deprecatedBuiltins.indexOf(key) !== -1) {
|
||||
QB[key] = deprecateAqlFunction(QB[key], key);
|
||||
}
|
||||
Object.keys(assumptions.builtins).forEach(function (key) {
|
||||
QB[key] = QB.fn(key, assumptions.builtins[key]);
|
||||
if (assumptions.deprecatedBuiltins.indexOf(key) !== -1) {
|
||||
QB[key] = deprecateAqlFunction(QB[key], key);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = QB;
|
File diff suppressed because one or more lines are too long
|
@ -1,8 +1,7 @@
|
|||
/* jshint globalstrict: true, es3: true */
|
||||
/* globals require: false, exports: false */
|
||||
/*jshint browserify: true */
|
||||
'use strict';
|
||||
var AqlError = require('./errors').AqlError,
|
||||
keywords = require('./assumptions').keywords;
|
||||
var AqlError = require('./errors').AqlError;
|
||||
var keywords = require('./assumptions').keywords;
|
||||
|
||||
function wrapAQL(expr) {
|
||||
if (
|
||||
|
@ -15,50 +14,69 @@ function wrapAQL(expr) {
|
|||
return expr.toAQL();
|
||||
}
|
||||
|
||||
function isValidNumber(number) {
|
||||
return (
|
||||
number === number &&
|
||||
number !== Infinity &&
|
||||
number !== -Infinity
|
||||
);
|
||||
}
|
||||
|
||||
function castNumber(number) {
|
||||
if (Math.floor(number) === number) {
|
||||
return new IntegerLiteral(number);
|
||||
}
|
||||
return new NumberLiteral(number);
|
||||
}
|
||||
|
||||
function castBoolean(bool) {
|
||||
return new BooleanLiteral(bool);
|
||||
}
|
||||
|
||||
function castString(str) {
|
||||
if (str.match(NumberLiteral.re)) {
|
||||
return autoCastToken(Number(str));
|
||||
}
|
||||
if (str.charAt(0) === '"') {
|
||||
return new StringLiteral(JSON.parse(str));
|
||||
}
|
||||
var match = str.match(RangeExpression.re);
|
||||
if (match) {
|
||||
return new RangeExpression(Number(match[1]), Number(match[2]));
|
||||
}
|
||||
if (str.match(Identifier.re)) {
|
||||
return new Identifier(str);
|
||||
}
|
||||
return new SimpleReference(str);
|
||||
}
|
||||
|
||||
function castObject(obj) {
|
||||
if (obj.constructor && obj.constructor.name === 'ArangoCollection') {
|
||||
return new Identifier(obj.name());
|
||||
}
|
||||
if (Array.isArray(obj)) {
|
||||
return new ListLiteral(obj);
|
||||
}
|
||||
return new ObjectLiteral(obj);
|
||||
}
|
||||
|
||||
function autoCastToken(token) {
|
||||
var match;
|
||||
if (token === null || token === undefined) {
|
||||
return new NullLiteral();
|
||||
}
|
||||
if (token instanceof Expression || token instanceof PartialStatement) {
|
||||
return token;
|
||||
}
|
||||
if (typeof token === 'number' && token === token && token !== Infinity && token !== -Infinity) {
|
||||
if (Math.floor(token) === token) {
|
||||
return new IntegerLiteral(token);
|
||||
}
|
||||
return new NumberLiteral(token);
|
||||
var type = typeof token;
|
||||
if (Object.keys(autoCastToken).indexOf(type) === -1) {
|
||||
throw new AqlError('Invalid AQL value: (' + type + ') ' + token);
|
||||
}
|
||||
if (typeof token === 'boolean') {
|
||||
return new BooleanLiteral(token);
|
||||
}
|
||||
if (typeof token === 'string') {
|
||||
if (token.match(/^[-+]?[0-9]+(\.[0-9]+)?$/)) {
|
||||
return autoCastToken(Number(token));
|
||||
}
|
||||
if (token.charAt(0) === '"') {
|
||||
return new StringLiteral(JSON.parse(token));
|
||||
}
|
||||
match = token.match(/^([0-9]+)\.\.([0-9]+)$/);
|
||||
if (match) {
|
||||
return new RangeExpression(Number(match[1]), Number(match[2]));
|
||||
}
|
||||
if (token.match(Identifier.re)) {
|
||||
return new Identifier(token);
|
||||
}
|
||||
return new SimpleReference(token);
|
||||
}
|
||||
if (typeof token === 'object') {
|
||||
if (token.constructor && token.constructor.name === 'ArangoCollection') {
|
||||
return new Identifier(token.name());
|
||||
}
|
||||
if (Object.prototype.toString.call(token) === '[object Array]') {
|
||||
return new ListLiteral(token);
|
||||
}
|
||||
return new ObjectLiteral(token);
|
||||
}
|
||||
throw new AqlError('Invalid AQL value: (' + (typeof token) + ') ' + token);
|
||||
return autoCastToken[type](token);
|
||||
}
|
||||
autoCastToken.number = castNumber;
|
||||
autoCastToken.boolean = castBoolean;
|
||||
autoCastToken.string = castString;
|
||||
autoCastToken.object = castObject;
|
||||
|
||||
function Expression() {}
|
||||
function Operation() {}
|
||||
|
@ -98,10 +116,11 @@ function NumberLiteral(value) {
|
|||
value instanceof IntegerLiteral
|
||||
)) {value = value.value;}
|
||||
this.value = Number(value);
|
||||
if (this.value !== this.value || this.value === Infinity) {
|
||||
if (!isValidNumber(this.value)) {
|
||||
throw new AqlError('Expected value to be a finite number: ' + value);
|
||||
}
|
||||
}
|
||||
NumberLiteral.re = /^[-+]?[0-9]+(\.[0-9]+)?$/;
|
||||
NumberLiteral.prototype = new Expression();
|
||||
NumberLiteral.prototype.constructor = NumberLiteral;
|
||||
NumberLiteral.prototype.toAQL = function () {return String(this.value);};
|
||||
|
@ -112,7 +131,7 @@ function IntegerLiteral(value) {
|
|||
value instanceof IntegerLiteral
|
||||
)) {value = value.value;}
|
||||
this.value = Number(value);
|
||||
if (this.value !== this.value || this.value === Infinity || Math.floor(this.value) !== this.value) {
|
||||
if (!isValidNumber(this.value) || Math.floor(this.value) !== this.value) {
|
||||
throw new AqlError('Expected value to be a finite integer: ' + value);
|
||||
}
|
||||
}
|
||||
|
@ -131,21 +150,15 @@ StringLiteral.prototype.toAQL = function () {return JSON.stringify(this.value);}
|
|||
|
||||
function ListLiteral(value) {
|
||||
if (value && value instanceof ListLiteral) {value = value.value;}
|
||||
if (!value || Object.prototype.toString.call(value) !== '[object Array]') {
|
||||
if (!value || !Array.isArray(value)) {
|
||||
throw new AqlError('Expected value to be an array: ' + value);
|
||||
}
|
||||
this.value = [];
|
||||
for (var i = 0; i < value.length; i++) {
|
||||
this.value[i] = autoCastToken(value[i]);
|
||||
}
|
||||
this.value = value.map(autoCastToken);
|
||||
}
|
||||
ListLiteral.prototype = new Expression();
|
||||
ListLiteral.prototype.constructor = ListLiteral;
|
||||
ListLiteral.prototype.toAQL = function () {
|
||||
var value = [], i;
|
||||
for (i = 0; i < this.value.length; i++) {
|
||||
value.push(wrapAQL(this.value[i]));
|
||||
}
|
||||
var value = this.value.map(wrapAQL);
|
||||
return '[' + value.join(', ') + ']';
|
||||
};
|
||||
|
||||
|
@ -155,25 +168,22 @@ function ObjectLiteral(value) {
|
|||
throw new AqlError('Expected value to be an object: ' + value);
|
||||
}
|
||||
this.value = {};
|
||||
for (var key in value) {
|
||||
if (value.hasOwnProperty(key)) {
|
||||
this.value[key] = autoCastToken(value[key]);
|
||||
}
|
||||
}
|
||||
var self = this;
|
||||
Object.keys(value).forEach(function (key) {
|
||||
self.value[key] = autoCastToken(value[key]);
|
||||
});
|
||||
}
|
||||
ObjectLiteral.prototype = new Expression();
|
||||
ObjectLiteral.prototype.constructor = ObjectLiteral;
|
||||
ObjectLiteral.prototype.toAQL = function () {
|
||||
var items = [], key;
|
||||
for (key in this.value) {
|
||||
if (this.value.hasOwnProperty(key)) {
|
||||
if (key.match(Identifier.re) || key === String(Number(key))) {
|
||||
items.push(key + ': ' + wrapAQL(this.value[key]));
|
||||
} else {
|
||||
items.push(JSON.stringify(key) + ': ' + wrapAQL(this.value[key]));
|
||||
}
|
||||
var value = this.value;
|
||||
var items = Object.keys(value).map(function (key) {
|
||||
if (key.match(Identifier.re) || key === String(Number(key))) {
|
||||
return key + ': ' + wrapAQL(value[key]);
|
||||
} else {
|
||||
return JSON.stringify(key) + ': ' + wrapAQL(value[key]);
|
||||
}
|
||||
}
|
||||
});
|
||||
return '{' + items.join(', ') + '}';
|
||||
};
|
||||
|
||||
|
@ -181,6 +191,7 @@ function RangeExpression(start, end) {
|
|||
this.start = autoCastToken(start);
|
||||
this.end = autoCastToken(end);
|
||||
}
|
||||
RangeExpression.re = /^([0-9]+)\.\.([0-9]+)$/;
|
||||
RangeExpression.prototype = new Expression();
|
||||
RangeExpression.prototype.constructor = RangeExpression;
|
||||
RangeExpression.prototype.toAQL = function () {
|
||||
|
@ -224,7 +235,7 @@ function Identifier(value) {
|
|||
}
|
||||
this.value = value;
|
||||
}
|
||||
Identifier.re = /^[_a-z][_0-9a-z]*$/i;
|
||||
Identifier.re = /^[_a-z][-_0-9a-z]*$/i;
|
||||
Identifier.prototype = new Expression();
|
||||
Identifier.prototype.constructor = Identifier;
|
||||
Identifier.prototype.toAQL = function () {
|
||||
|
@ -249,14 +260,10 @@ SimpleReference.re = /^@{0,2}[_a-z][_0-9a-z]*(\.[_a-z][_0-9a-z]*|\[\*\])*$/i;
|
|||
SimpleReference.prototype = new Expression();
|
||||
SimpleReference.prototype.constructor = SimpleReference;
|
||||
SimpleReference.prototype.toAQL = function () {
|
||||
var value = String(this.value),
|
||||
tokens = value.split('.'),
|
||||
i;
|
||||
for (i = 0; i < tokens.length; i++) {
|
||||
if (keywords.indexOf(tokens[i]) !== -1) {
|
||||
tokens[i] = '`' + tokens[i] + '`';
|
||||
}
|
||||
}
|
||||
var value = String(this.value);
|
||||
var tokens = value.split('.').map(function (token) {
|
||||
return keywords.indexOf(token) === -1 ? token : '`' + token + '`';
|
||||
});
|
||||
return tokens.join('.');
|
||||
};
|
||||
|
||||
|
@ -317,18 +324,12 @@ function NAryOperation(operator, values) {
|
|||
throw new AqlError('Expected operator to be a string: ' + operator);
|
||||
}
|
||||
this.operator = operator;
|
||||
this.values = [];
|
||||
for (var i = 0; i < values.length; i++) {
|
||||
this.values.push(autoCastToken(values[i]));
|
||||
}
|
||||
this.values = values.map(autoCastToken);
|
||||
}
|
||||
NAryOperation.prototype = new Operation();
|
||||
NAryOperation.prototype.constructor = NAryOperation;
|
||||
NAryOperation.prototype.toAQL = function () {
|
||||
var values = [], i;
|
||||
for (i = 0; i < this.values.length; i++) {
|
||||
values.push(wrapAQL(this.values[i]));
|
||||
}
|
||||
var values = this.values.map(wrapAQL);
|
||||
return values.join(' ' + this.operator + ' ');
|
||||
};
|
||||
|
||||
|
@ -339,30 +340,22 @@ function FunctionCall(functionName, args) {
|
|||
if (!functionName.match(FunctionCall.re)) {
|
||||
throw new AqlError('Not a valid function name: ' + functionName);
|
||||
}
|
||||
if (args && Object.prototype.toString.call(args) !== '[object Array]') {
|
||||
if (args && !Array.isArray(args)) {
|
||||
throw new AqlError('Expected arguments to be an array: ' + args);
|
||||
}
|
||||
this.functionName = functionName;
|
||||
this.args = [];
|
||||
if (args) {
|
||||
for (var i = 0; i < args.length; i++) {
|
||||
this.args[i] = autoCastToken(args[i]);
|
||||
}
|
||||
}
|
||||
this.args = args ? args.map(autoCastToken) : [];
|
||||
}
|
||||
FunctionCall.re = /^[_a-z][_0-9a-z]*(::[_a-z][_0-9a-z]*)*$/i;
|
||||
FunctionCall.prototype = new Expression();
|
||||
FunctionCall.prototype.constructor = FunctionCall;
|
||||
FunctionCall.prototype.toAQL = function () {
|
||||
var args = [], i;
|
||||
for (i = 0; i < this.args.length; i++) {
|
||||
args.push(wrapAQL(this.args[i]));
|
||||
}
|
||||
var args = this.args.map(wrapAQL);
|
||||
return this.functionName + '(' + args.join(', ') + ')';
|
||||
};
|
||||
|
||||
function PartialStatement() {}
|
||||
PartialStatement.prototype.for_ = function (varname) {
|
||||
PartialStatement.prototype.for = function (varname) {
|
||||
var self = this, inFn;
|
||||
inFn = function (expr) {
|
||||
// assert expr is an expression
|
||||
|
@ -371,7 +364,7 @@ PartialStatement.prototype.for_ = function (varname) {
|
|||
return {'in': inFn, in_: inFn};
|
||||
};
|
||||
PartialStatement.prototype.filter = function (expr) {return new FilterExpression(this, expr);};
|
||||
PartialStatement.prototype.let_ = function (varname, expr) {
|
||||
PartialStatement.prototype.let = function (varname, expr) {
|
||||
if (expr === undefined) {
|
||||
return new LetExpression(this, varname);
|
||||
}
|
||||
|
@ -388,7 +381,7 @@ PartialStatement.prototype.sort = function () {
|
|||
return new SortExpression(this, args);
|
||||
};
|
||||
PartialStatement.prototype.limit = function (x, y) {return new LimitExpression(this, x, y);};
|
||||
PartialStatement.prototype.return_ = function (x) {return new ReturnExpression(this, x);};
|
||||
PartialStatement.prototype.return = function (x) {return new ReturnExpression(this, x);};
|
||||
PartialStatement.prototype.remove = function (expr) {
|
||||
var self = this, inFn;
|
||||
inFn = function (collection) {
|
||||
|
@ -430,42 +423,39 @@ PartialStatement.prototype.replace = function (expr) {
|
|||
return {'with': withFn, with_: withFn, into: inFn, 'in': inFn, in_: inFn};
|
||||
};
|
||||
|
||||
PartialStatement.prototype['for'] = PartialStatement.prototype.for_;
|
||||
PartialStatement.prototype['let'] = PartialStatement.prototype.let_;
|
||||
PartialStatement.prototype['return'] = PartialStatement.prototype.return_;
|
||||
PartialStatement.prototype.for_ = PartialStatement.prototype.for;
|
||||
PartialStatement.prototype.let_ = PartialStatement.prototype.let;
|
||||
PartialStatement.prototype.return_ = PartialStatement.prototype.return;
|
||||
|
||||
function Definitions(dfns) {
|
||||
if (dfns instanceof Definitions) {
|
||||
dfns = dfns.dfns;
|
||||
}
|
||||
this.dfns = [];
|
||||
var self = this;
|
||||
if (!dfns || typeof dfns !== 'object') {
|
||||
throw new AqlError('Expected definitions to be an object');
|
||||
}
|
||||
if (Object.prototype.toString.call(dfns) === '[object Array]') {
|
||||
for (var i = 0; i < dfns.length; i++) {
|
||||
if (Object.prototype.toString.call(dfns[i]) !== '[object Array]' || dfns[i].length !== 2) {
|
||||
if (Array.isArray(dfns)) {
|
||||
dfns.forEach(function (dfn, i) {
|
||||
if (!Array.isArray(dfn) || dfn.length !== 2) {
|
||||
throw new AqlError('Expected definitions[' + i + '] to be a tuple');
|
||||
}
|
||||
this.dfns.push([new Identifier(dfns[i][0]), autoCastToken(dfns[i][1])]);
|
||||
}
|
||||
self.dfns.push([new Identifier(dfn[0]), autoCastToken(dfn[1])]);
|
||||
});
|
||||
} else {
|
||||
for (var key in dfns) {
|
||||
if (dfns.hasOwnProperty(key)) {
|
||||
this.dfns.push([new Identifier(key), autoCastToken(dfns[key])]);
|
||||
}
|
||||
}
|
||||
Object.keys(dfns).forEach(function (key) {
|
||||
self.dfns.push([new Identifier(key), autoCastToken(dfns[key])]);
|
||||
});
|
||||
}
|
||||
if (this.dfns.length === 0) {
|
||||
throw new AqlError('Expected definitions not to be empty');
|
||||
}
|
||||
}
|
||||
Definitions.prototype.toAQL = function () {
|
||||
var dfns = [];
|
||||
for (var i = 0; i < this.dfns.length; i++) {
|
||||
dfns.push(this.dfns[i][0].toAQL() + ' = ' + wrapAQL(this.dfns[i][1]));
|
||||
}
|
||||
return dfns.join(', ');
|
||||
return this.dfns.map(function (dfn) {
|
||||
return dfn[0].toAQL() + ' = ' + wrapAQL(dfn[1]);
|
||||
}).join(', ');
|
||||
};
|
||||
|
||||
function ForExpression(prev, varname, expr) {
|
||||
|
@ -541,7 +531,7 @@ CollectIntoExpression.prototype.toAQL = function () {
|
|||
};
|
||||
|
||||
function SortExpression(prev, args) {
|
||||
if (!args || Object.prototype.toString.call(args) !== '[object Array]') {
|
||||
if (!args || !Array.isArray(args)) {
|
||||
throw new AqlError('Expected sort list to be an array: ' + args);
|
||||
}
|
||||
if (!args.length) {
|
||||
|
@ -549,37 +539,36 @@ function SortExpression(prev, args) {
|
|||
}
|
||||
this.prev = prev;
|
||||
this.args = [];
|
||||
var allowKeyword = false, i, value;
|
||||
for (i = 0; i < args.length; i++) {
|
||||
value = args[i];
|
||||
if (!allowKeyword && value) {
|
||||
if (value instanceof Keyword || (
|
||||
typeof value === 'string' && SortExpression.keywords.indexOf(value.toUpperCase()) !== -1
|
||||
var allowKeyword = false;
|
||||
this.args = args.map(function (arg, i) {
|
||||
if (!allowKeyword && arg) {
|
||||
if (arg instanceof Keyword || (
|
||||
typeof arg === 'string' && SortExpression.keywords.indexOf(arg.toUpperCase()) !== -1
|
||||
)) {
|
||||
throw new AqlError('Unexpected keyword ' + value.toString() + ' at offset ' + i);
|
||||
throw new AqlError('Unexpected keyword ' + arg.toString() + ' at offset ' + i);
|
||||
}
|
||||
}
|
||||
if (typeof value === 'string' && SortExpression.keywords.indexOf(value.toUpperCase()) !== -1) {
|
||||
this.args[i] = new Keyword(value);
|
||||
if (typeof arg === 'string' && SortExpression.keywords.indexOf(arg.toUpperCase()) !== -1) {
|
||||
allowKeyword = false;
|
||||
return new Keyword(arg);
|
||||
} else {
|
||||
this.args[i] = autoCastToken(value);
|
||||
allowKeyword = true;
|
||||
return autoCastToken(arg);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
SortExpression.keywords = ['ASC', 'DESC'];
|
||||
SortExpression.prototype = new PartialStatement();
|
||||
SortExpression.prototype.constructor = SortExpression;
|
||||
SortExpression.prototype.toAQL = function () {
|
||||
var args = [], j = 0;
|
||||
for (var i = 0; i < this.args.length; i++) {
|
||||
if (this.args[i] instanceof Keyword) {
|
||||
args[j] += ' ' + this.args[i].toAQL();
|
||||
this.args.forEach(function (arg, i) {
|
||||
if (arg instanceof Keyword) {
|
||||
args[j] += ' ' + arg.toAQL();
|
||||
} else {
|
||||
j = args.push(wrapAQL(this.args[i])) - 1;
|
||||
j = args.push(wrapAQL(arg)) - 1;
|
||||
}
|
||||
}
|
||||
});
|
||||
return (
|
||||
(this.prev ? this.prev.toAQL() + ' ' : '') +
|
||||
'SORT ' +
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"dependencies": {
|
||||
"aqb": "1.4.1",
|
||||
"aqb": "1.5.1",
|
||||
"buster-format": "0.5.6",
|
||||
"cheerio": "0.17.0",
|
||||
"coffee-script": "1.7.1",
|
||||
|
|
|
@ -181,6 +181,7 @@ ERROR_CLUSTER_ONLY_ON_COORDINATOR,1471,"this operation is only valid on a coordi
|
|||
ERROR_CLUSTER_READING_PLAN_AGENCY,1472,"error reading Plan in agency","Will be raised if a coordinator or DBserver cannot read the Plan in the agency."
|
||||
ERROR_CLUSTER_COULD_NOT_TRUNCATE_COLLECTION,1473,"could not truncate collection","Will be raised if a coordinator cannot truncate all shards of a cluster collection."
|
||||
ERROR_CLUSTER_AQL_COMMUNICATION,1474,"error in cluster internal communication for AQL","Will be raised if the internal communication of the cluster for AQL produces an error."
|
||||
ERROR_ARANGO_DOCUMENT_NOT_FOUND_OR_SHARDING_ATTRIBUTES_CHANGED,1475,"document not found or sharding attributes changed","Will be raised when a document with a given identifier or handle is unknown, or if the sharding attributes have been changed in a REPLACE operation in the cluster."
|
||||
|
||||
################################################################################
|
||||
## ArangoDB query errors
|
||||
|
|
|
@ -141,6 +141,7 @@ void TRI_InitialiseErrorMessages () {
|
|||
REG_ERROR(ERROR_CLUSTER_READING_PLAN_AGENCY, "error reading Plan in agency");
|
||||
REG_ERROR(ERROR_CLUSTER_COULD_NOT_TRUNCATE_COLLECTION, "could not truncate collection");
|
||||
REG_ERROR(ERROR_CLUSTER_AQL_COMMUNICATION, "error in cluster internal communication for AQL");
|
||||
REG_ERROR(ERROR_ARANGO_DOCUMENT_NOT_FOUND_OR_SHARDING_ATTRIBUTES_CHANGED, "document not found or sharding attributes changed");
|
||||
REG_ERROR(ERROR_QUERY_KILLED, "query killed");
|
||||
REG_ERROR(ERROR_QUERY_PARSE, "%s");
|
||||
REG_ERROR(ERROR_QUERY_EMPTY, "query is empty");
|
||||
|
|
|
@ -344,6 +344,10 @@
|
|||
/// - 1474: @LIT{error in cluster internal communication for AQL}
|
||||
/// Will be raised if the internal communication of the cluster for AQL
|
||||
/// produces an error.
|
||||
/// - 1475: @LIT{document not found or sharding attributes changed}
|
||||
/// Will be raised when a document with a given identifier or handle is
|
||||
/// unknown, or if the sharding attributes have been changed in a REPLACE
|
||||
/// operation in the cluster.
|
||||
/// - 1500: @LIT{query killed}
|
||||
/// Will be raised when a running query is killed by an explicit admin
|
||||
/// command.
|
||||
|
@ -2018,6 +2022,18 @@ void TRI_InitialiseErrorMessages ();
|
|||
|
||||
#define TRI_ERROR_CLUSTER_AQL_COMMUNICATION (1474)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief 1475: ERROR_ARANGO_DOCUMENT_NOT_FOUND_OR_SHARDING_ATTRIBUTES_CHANGED
|
||||
///
|
||||
/// document not found or sharding attributes changed
|
||||
///
|
||||
/// Will be raised when a document with a given identifier or handle is
|
||||
/// unknown, or if the sharding attributes have been changed in a REPLACE
|
||||
/// operation in the cluster.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define TRI_ERROR_ARANGO_DOCUMENT_NOT_FOUND_OR_SHARDING_ATTRIBUTES_CHANGED (1475)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief 1500: ERROR_QUERY_KILLED
|
||||
///
|
||||
|
|
Loading…
Reference in New Issue