diff --git a/Makefile.am b/Makefile.am index e6cbbbd596..6ff0187405 100644 --- a/Makefile.am +++ b/Makefile.am @@ -297,16 +297,16 @@ install-exec-hook: ################################################################################ install-data-hook: - rm -f $(pkgdataNODEdir)/js/node/node_modules/docco/node_modules/fs-extra/node_modules/ncp/test/fixtures/src/c - rm -f $(pkgdataNODEdir)/js/node/node_modules/docco/node_modules/fs-extra/node_modules/ncp/test/fixtures/src/d - rm -f $(pkgdataNODEdir)/js/node/node_modules/docco/node_modules/fs-extra/node_modules/ncp/test/fixtures/src/e - rm -f $(pkgdataNODEdir)/js/node/node_modules/docco/node_modules/fs-extra/node_modules/ncp/test/fixtures/src/f - rm -f $(pkgdataNODEdir)/js/node/node_modules/docco/node_modules/fs-extra/node_modules/ncp/test/fixtures/src/sub/b - rm -f $(pkgdataNODEdir)/js/node/node_modules/joi/node_modules/hoek/test/modules/ignore.txt - rm -f $(pkgdataNODEdir)/js/node/node_modules/docco/node_modules/fs-extra/node_modules/rimraf/test/setup.sh - rm -f $(pkgdataNODEdir)/js/node/node_modules/joi/node_modules/isemail/dns-no-mx.js - rm -f $(pkgdataNODEdir)/js/node/node_modules/docco/node_modules/fs-extra/node_modules/rimraf/test/run.sh - rm -f $(pkgdataNODEdir)/js/node/node_modules/docco/node_modules/fs-extra/node_modules/rimraf/test/run.sh + rm -f $(DESTDIR)$(pkgdataNODEdir)/js/node/node_modules/docco/node_modules/fs-extra/node_modules/ncp/test/fixtures/src/c + rm -f $(DESTDIR)$(pkgdataNODEdir)/js/node/node_modules/docco/node_modules/fs-extra/node_modules/ncp/test/fixtures/src/d + rm -f $(DESTDIR)$(pkgdataNODEdir)/js/node/node_modules/docco/node_modules/fs-extra/node_modules/ncp/test/fixtures/src/e + rm -f $(DESTDIR)$(pkgdataNODEdir)/js/node/node_modules/docco/node_modules/fs-extra/node_modules/ncp/test/fixtures/src/f + rm -f $(DESTDIR)$(pkgdataNODEdir)/js/node/node_modules/docco/node_modules/fs-extra/node_modules/ncp/test/fixtures/src/sub/b + rm -f $(DESTDIR)$(pkgdataNODEdir)/js/node/node_modules/joi/node_modules/hoek/test/modules/ignore.txt + rm -f $(DESTDIR)$(pkgdataNODEdir)/js/node/node_modules/docco/node_modules/fs-extra/node_modules/rimraf/test/setup.sh + rm -f $(DESTDIR)$(pkgdataNODEdir)/js/node/node_modules/joi/node_modules/isemail/dns-no-mx.js + rm -f $(DESTDIR)$(pkgdataNODEdir)/js/node/node_modules/docco/node_modules/fs-extra/node_modules/rimraf/test/run.sh + rm -f $(DESTDIR)$(pkgdataNODEdir)/js/node/node_modules/docco/node_modules/fs-extra/node_modules/rimraf/test/run.sh ## ----------------------------------------------------------------------------- diff --git a/arangod/Aql/AqlValue.h b/arangod/Aql/AqlValue.h index ce496bd6a7..a8fbdf84be 100644 --- a/arangod/Aql/AqlValue.h +++ b/arangod/Aql/AqlValue.h @@ -205,7 +205,6 @@ namespace triagens { std::string toString () const; - //////////////////////////////////////////////////////////////////////////////// /// @brief get a string representation of the AqlValue /// this will fail if the value is not a string diff --git a/arangod/Aql/Expression.cpp b/arangod/Aql/Expression.cpp index 919fa28368..7a994c8a91 100644 --- a/arangod/Aql/Expression.cpp +++ b/arangod/Aql/Expression.cpp @@ -376,18 +376,22 @@ AqlValue Expression::executeSimpleExpression (AstNode const* node, AqlValue result = executeSimpleExpression(member, &myCollection, trx, docColls, argv, startPos, vars, regs); if (result.isList()) { - if (index->isNumericValue()) { - auto j = result.extractListMember(trx, myCollection, index->getIntValue(), true); + TRI_document_collection_t const* myCollection2 = nullptr; + AqlValue indexResult = executeSimpleExpression(index, &myCollection2, trx, docColls, argv, startPos, vars, regs); + + if (indexResult.isNumber()) { + auto j = result.extractListMember(trx, myCollection, indexResult.toInt64(), true); + indexResult.destroy(); result.destroy(); return AqlValue(new Json(TRI_UNKNOWN_MEM_ZONE, j.steal())); } - else if (index->isStringValue()) { - char const* p = index->getStringValue(); - TRI_ASSERT(p != nullptr); + else if (indexResult.isString()) { + auto&& value = indexResult.toString(); + indexResult.destroy(); try { // stoll() might throw an exception if the string is not a number - int64_t position = static_cast(std::stoll(p)); + int64_t position = static_cast(std::stoll(value.c_str())); auto j = result.extractListMember(trx, myCollection, position, true); result.destroy(); return AqlValue(new Json(TRI_UNKNOWN_MEM_ZONE, j.steal())); @@ -399,17 +403,21 @@ AqlValue Expression::executeSimpleExpression (AstNode const* node, // fall-through to returning null } else if (result.isArray()) { - if (index->isNumericValue()) { - std::string const indexString = std::to_string(index->getIntValue()); + TRI_document_collection_t const* myCollection2 = nullptr; + AqlValue indexResult = executeSimpleExpression(index, &myCollection2, trx, docColls, argv, startPos, vars, regs); + + if (indexResult.isNumber()) { + auto&& indexString = std::to_string(indexResult.toInt64()); auto j = result.extractArrayMember(trx, myCollection, indexString.c_str()); + indexResult.destroy(); result.destroy(); return AqlValue(new Json(TRI_UNKNOWN_MEM_ZONE, j.steal())); } - else if (index->isStringValue()) { - char const* p = index->getStringValue(); - TRI_ASSERT(p != nullptr); + else if (indexResult.isString()) { + auto&& value = indexResult.toString(); + indexResult.destroy(); - auto j = result.extractArrayMember(trx, myCollection, p); + auto j = result.extractArrayMember(trx, myCollection, value.c_str()); result.destroy(); return AqlValue(new Json(TRI_UNKNOWN_MEM_ZONE, j.steal())); } diff --git a/arangod/Utils/ExplicitTransaction.h b/arangod/Utils/ExplicitTransaction.h index 998c16cb78..140f2975f6 100644 --- a/arangod/Utils/ExplicitTransaction.h +++ b/arangod/Utils/ExplicitTransaction.h @@ -62,8 +62,9 @@ namespace triagens { std::vector const& readCollections, std::vector const& writeCollections, double lockTimeout, - bool waitForSync) - : Transaction(new V8TransactionContext(false), vocbase, 0) { + bool waitForSync, + bool embed) + : Transaction(new V8TransactionContext(embed), vocbase, 0) { // std::cout << TRI_CurrentThreadId() << ", EXPLICITTRANSACTION " << this << " CTOR\r\n"; this->addHint(TRI_TRANSACTION_HINT_LOCK_ENTIRELY, false); diff --git a/arangod/V8Server/v8-vocbase.cpp b/arangod/V8Server/v8-vocbase.cpp index 2728c2bc01..5e2196daa0 100644 --- a/arangod/V8Server/v8-vocbase.cpp +++ b/arangod/V8Server/v8-vocbase.cpp @@ -277,6 +277,11 @@ static v8::Handle JS_Transaction (v8::Arguments const& argv) { TRI_V8_EXCEPTION(scope, TRI_ERROR_INTERNAL); } + bool embed = false; + if (object->Has(TRI_V8_SYMBOL("embed"))) { + v8::Handle v = v8::Handle::Cast(object->Get(TRI_V8_SYMBOL("embed"))); + embed = TRI_ObjectToBoolean(v); + } v8::Handle current = v8::Context::GetCurrent()->Global(); @@ -306,13 +311,13 @@ static v8::Handle JS_Transaction (v8::Arguments const& argv) { TRI_V8_EXCEPTION_PARAMETER(scope, actionError); } - // start actual transaction ExplicitTransaction trx(vocbase, readCollections, writeCollections, lockTimeout, - waitForSync); + waitForSync, + embed); int res = trx.begin(); diff --git a/js/actions/api-simple.js b/js/actions/api-simple.js index 5277f8db25..343d1a7a03 100644 --- a/js/actions/api-simple.js +++ b/js/actions/api-simple.js @@ -325,7 +325,7 @@ setupIndexQueries(); /// @EXAMPLE_ARANGOSH_RUN{RestSimpleAllSkipLimit} /// var cn = "products"; /// db._drop(cn); -/// var collection = db._create(cn, { waitForSync: true }); +/// var collection = db._create(cn); /// collection.save({"Hello1" : "World1" }); /// collection.save({"Hello2" : "World2" }); /// collection.save({"Hello3" : "World3" }); @@ -523,7 +523,7 @@ actions.defineHttp({ /// /// In order to use the *near* operator, a geo index must be defined for the /// collection. This index also defines which attribute holds the coordinates -/// for the document. If you have more then one geo-spatial index, you can use +/// for the document. If you have more than one geo-spatial index, you can use /// the *geo* field to select a particular index. /// /// The call expects a JSON object as body with the following attributes: @@ -566,7 +566,7 @@ actions.defineHttp({ /// @EXAMPLE_ARANGOSH_RUN{RestSimpleNear} /// var cn = "products"; /// db._drop(cn); -/// var products = db._create(cn, { waitForSync: true }); +/// var products = db._create(cn); /// var loc = products.ensureGeoIndex("loc"); /// var i; /// for (i = -0.01; i <= 0.01; i += 0.002) { @@ -594,7 +594,7 @@ actions.defineHttp({ /// @EXAMPLE_ARANGOSH_RUN{RestSimpleNearDistance} /// var cn = "products"; /// db._drop(cn); -/// var products = db._create(cn, { waitForSync: true }); +/// var products = db._create(cn); /// var loc = products.ensureGeoIndex("loc"); /// var i; /// for (i = -0.01; i <= 0.01; i += 0.002) { @@ -702,7 +702,7 @@ actions.defineHttp({ /// /// In order to use the *within* operator, a geo index must be defined for /// the collection. This index also defines which attribute holds the -/// coordinates for the document. If you have more then one geo-spatial index, +/// coordinates for the document. If you have more than one geo-spatial index, /// you can use the *geo* field to select a particular index. /// /// The call expects a JSON object as body with the following attributes: @@ -747,7 +747,7 @@ actions.defineHttp({ /// @EXAMPLE_ARANGOSH_RUN{RestSimpleWithin} /// var cn = "products"; /// db._drop(cn); -/// var products = db._create(cn, { waitForSync: true }); +/// var products = db._create(cn); /// var loc = products.ensureGeoIndex("loc"); /// var i; /// for (i = -0.01; i <= 0.01; i += 0.002) { @@ -776,7 +776,7 @@ actions.defineHttp({ /// @EXAMPLE_ARANGOSH_RUN{RestSimpleWithinDistance} /// var cn = "products"; /// db._drop(cn); -/// var products = db._create(cn, { waitForSync: true }); +/// var products = db._create(cn); /// var loc = products.ensureGeoIndex("loc"); /// var i; /// for (i = -0.01; i <= 0.01; i += 0.002) { @@ -862,6 +862,159 @@ actions.defineHttp({ } }); +//////////////////////////////////////////////////////////////////////////////// +/// @startDocuBlock JSA_put_api_simple_within_rectangle +/// @brief returns all documents of a collection within a rectangle +/// +/// @RESTHEADER{PUT /_api/simple/within-rectangle, Within rectangle query} +/// +/// @RESTBODYPARAM{query,string,required} +/// Contains the query. +/// +/// @RESTDESCRIPTION +/// +/// This will find all documents within the specified rectangle (determined by +/// the given coordinates (*latitude1*, *longitude1*, *latitude2*, *longitude2*). +/// +/// In order to use the *within-rectangle* query, a geo index must be defined for +/// the collection. This index also defines which attribute holds the +/// coordinates for the document. If you have more than one geo-spatial index, +/// you can use the *geo* field to select a particular index. +/// +/// The call expects a JSON object as body with the following attributes: +/// +/// - *collection*: The name of the collection to query. +/// +/// - *latitude1*: The latitude of the first rectangle coordinate. +/// +/// - *longitude1*: The longitude of the first rectangle coordinate. +/// +/// - *latitude2*: The latitude of the second rectangle coordinate. +/// +/// - *longitude2*: The longitude of the second rectangle coordinate. +/// +/// - *skip*: The number of documents to skip in the query. (optional) +/// +/// - *limit*: The maximal amount of documents to return. The *skip* is +/// applied before the *limit* restriction. The default is 100. (optional) +/// +/// - *geo*: If given, the identifier of the geo-index to use. (optional) +/// +/// Returns a cursor containing the result, see [Http Cursor](../HttpAqlQueryCursor/README.md) for details. +/// +/// @RESTRETURNCODES +/// +/// @RESTRETURNCODE{201} +/// is returned if the query was executed successfully. +/// +/// @RESTRETURNCODE{400} +/// is returned if the body does not contain a valid JSON representation of a +/// query. The response body contains an error document in this case. +/// +/// @RESTRETURNCODE{404} +/// is returned if the collection specified by *collection* is unknown. The +/// response body contains an error document in this case. +/// +/// @EXAMPLES +/// +/// @EXAMPLE_ARANGOSH_RUN{RestSimpleWithinRectangle} +/// var cn = "products"; +/// db._drop(cn); +/// var products = db._create(cn); +/// var loc = products.ensureGeoIndex("loc"); +/// var i; +/// for (i = -0.01; i <= 0.01; i += 0.002) { +/// products.save({ name : "Name/" + i + "/",loc: [ i, 0 ] }); +/// } +/// var url = "/_api/simple/within-rectangle"; +/// var body = { +/// collection: "products", +/// latitude1 : 0, +/// longitude1 : 0, +/// latitude2 : 0.2, +/// longitude2 : 0.2, +/// skip : 1, +/// limit : 2 +/// }; +/// +/// var response = logCurlRequest('PUT', url, JSON.stringify(body)); +/// +/// assert(response.code === 201); +/// +/// logJsonResponse(response); +/// db._drop(cn); +/// @END_EXAMPLE_ARANGOSH_RUN +/// @endDocuBlock +//////////////////////////////////////////////////////////////////////////////// + +actions.defineHttp({ + url: API + "within-rectangle", + + callback : function (req, res) { + try { + var body = actions.getJsonBody(req, res); + + if (body === undefined) { + return; + } + + if (req.requestType !== actions.PUT) { + actions.resultUnsupported(req, res); + } + else { + var limit = body.limit; + var skip = body.skip; + var latitude1 = body.latitude1; + var longitude1 = body.longitude1; + var latitude2 = body.latitude2; + var longitude2 = body.longitude2; + var geo = body.geo; + var name = body.collection; + var collection = db._collection(name); + + if (collection === null) { + actions.collectionNotFound(req, res, name); + } + else if (latitude1 === null || latitude1 === undefined) { + actions.badParameter(req, res, "latitude1"); + } + else if (longitude1 === null || longitude1 === undefined) { + actions.badParameter(req, res, "longitude1"); + } + else if (latitude2 === null || latitude2 === undefined) { + actions.badParameter(req, res, "latitude2"); + } + else if (longitude2 === null || longitude2 === undefined) { + actions.badParameter(req, res, "longitude2"); + } + else { + var result; + + if (geo === null || geo === undefined) { + result = collection.withinRectangle(latitude1, longitude1, latitude2, longitude2); + } + else { + result = collection.geo({ id : geo }).withinRectangle(latitude1, longitude1, latitude2, longitude2); + } + + if (skip !== null && skip !== undefined) { + result = result.skip(skip); + } + + if (limit !== null && limit !== undefined) { + result = result.limit(limit); + } + + createCursorResponse(req, res, CREATE_CURSOR(result.toArray(), true, body.batchSize, body.ttl)); + } + } + } + catch (err) { + actions.resultException(req, res, err, undefined, false); + } + } +}); + //////////////////////////////////////////////////////////////////////////////// /// @startDocuBlock JSA_put_api_simple_fulltext /// @brief returns documents of a collection as a result of a fulltext query @@ -915,7 +1068,7 @@ actions.defineHttp({ /// @EXAMPLE_ARANGOSH_RUN{RestSimpleFulltext} /// var cn = "products"; /// db._drop(cn); -/// var products = db._create(cn, { waitForSync: true }); +/// var products = db._create(cn); /// products.save({"text" : "this text contains word" }); /// products.save({"text" : "this text also has a word" }); /// products.save({"text" : "this is nothing" }); @@ -1032,7 +1185,7 @@ actions.defineHttp({ /// @EXAMPLE_ARANGOSH_RUN{RestSimpleByExample} /// var cn = "products"; /// db._drop(cn); -/// var products = db._create(cn, { waitForSync: true }); +/// var products = db._create(cn); /// products.save({ "a": { "k": 1, "j": 1 }, "i": 1}); /// products.save({ "a": { "j": 1 }, "i": 1}); /// products.save({ "i": 1}); @@ -1053,7 +1206,7 @@ actions.defineHttp({ /// @EXAMPLE_ARANGOSH_RUN{RestSimpleByExample2} /// var cn = "products"; /// db._drop(cn); -/// var products = db._create(cn, { waitForSync: true }); +/// var products = db._create(cn); /// products.save({ "a": { "k": 1, "j": 1 }, "i": 1}); /// products.save({ "a": { "j": 1 }, "i": 1}); /// products.save({ "i": 1}); @@ -1074,7 +1227,7 @@ actions.defineHttp({ /// @EXAMPLE_ARANGOSH_RUN{RestSimpleByExample3} /// var cn = "products"; /// db._drop(cn); -/// var products = db._create(cn, { waitForSync: true }); +/// var products = db._create(cn); /// products.save({ "a": { "k": 1, "j": 1 }, "i": 1}); /// products.save({ "a": { "j": 1 }, "i": 1}); /// products.save({ "i": 1}); @@ -1186,7 +1339,7 @@ actions.defineHttp({ /// @EXAMPLE_ARANGOSH_RUN{RestSimpleFirstExample} /// var cn = "products"; /// db._drop(cn); -/// var products = db._create(cn, { waitForSync: true }); +/// var products = db._create(cn); /// products.save({ "a": { "k": 1, "j": 1 }, "i": 1}); /// products.save({ "a": { "j": 1 }, "i": 1}); /// products.save({ "i": 1}); @@ -1207,7 +1360,7 @@ actions.defineHttp({ /// @EXAMPLE_ARANGOSH_RUN{RestSimpleFirstExampleNotFound} /// var cn = "products"; /// db._drop(cn); -/// var products = db._create(cn, { waitForSync: true }); +/// var products = db._create(cn); /// products.save({ "a": { "k": 1, "j": 1 }, "i": 1}); /// products.save({ "a": { "j": 1 }, "i": 1}); /// products.save({ "i": 1}); @@ -1315,7 +1468,7 @@ actions.defineHttp({ /// @EXAMPLE_ARANGOSH_RUN{RestSimpleFirst} /// var cn = "products"; /// db._drop(cn); -/// var products = db._create(cn, { waitForSync: false }); +/// var products = db._create(cn); /// products.save({ "a": { "k": 1, "j": 1 }, "i": 1}); /// products.save({ "a": { "j": 1 }, "i": 1}); /// products.save({ "i": 1}); @@ -1336,7 +1489,7 @@ actions.defineHttp({ /// @EXAMPLE_ARANGOSH_RUN{RestSimpleFirstSingle} /// var cn = "products"; /// db._drop(cn); -/// var products = db._create(cn, { waitForSync: false }); +/// var products = db._create(cn); /// products.save({ "a": { "k": 1, "j": 1 }, "i": 1}); /// products.save({ "a": { "j": 1 }, "i": 1}); /// products.save({ "i": 1}); @@ -1434,7 +1587,7 @@ actions.defineHttp({ /// @EXAMPLE_ARANGOSH_RUN{RestSimpleLast} /// var cn = "products"; /// db._drop(cn); -/// var products = db._create(cn, { waitForSync: false }); +/// var products = db._create(cn); /// products.save({ "a": { "k": 1, "j": 1 }, "i": 1}); /// products.save({ "a": { "j": 1 }, "i": 1}); /// products.save({ "i": 1}); @@ -1455,7 +1608,7 @@ actions.defineHttp({ /// @EXAMPLE_ARANGOSH_RUN{RestSimpleLastSingle} /// var cn = "products"; /// db._drop(cn); -/// var products = db._create(cn, { waitForSync: false }); +/// var products = db._create(cn); /// products.save({ "a": { "k": 1, "j": 1 }, "i": 1}); /// products.save({ "a": { "j": 1 }, "i": 1}); /// products.save({ "i": 1}); @@ -1557,7 +1710,7 @@ actions.defineHttp({ /// @EXAMPLE_ARANGOSH_RUN{RestSimpleRange} /// var cn = "products"; /// db._drop(cn); -/// var products = db._create(cn, { waitForSync: true }); +/// var products = db._create(cn); /// products.ensureUniqueSkiplist("i"); /// products.save({ "i": 1}); /// products.save({ "i": 2}); @@ -1689,7 +1842,7 @@ actions.defineHttp({ /// @EXAMPLE_ARANGOSH_RUN{RestSimpleRemoveByExample} /// var cn = "products"; /// db._drop(cn); -/// var products = db._create(cn, { waitForSync: true }); +/// var products = db._create(cn); /// products.save({ "a": { "k": 1, "j": 1 }, "i": 1}); /// products.save({ "a": { "j": 1 }, "i": 1}); /// products.save({ "i": 1}); @@ -1708,7 +1861,7 @@ actions.defineHttp({ /// @EXAMPLE_ARANGOSH_RUN{RestSimpleRemoveByExample_1} /// var cn = "products"; /// db._drop(cn); -/// var products = db._create(cn, { waitForSync: true }); +/// var products = db._create(cn); /// products.save({ "a": { "k": 1, "j": 1 }, "i": 1}); /// products.save({ "a": { "j": 1 }, "i": 1}); /// products.save({ "i": 1}); @@ -1728,7 +1881,7 @@ actions.defineHttp({ /// @EXAMPLE_ARANGOSH_RUN{RestSimpleRemoveByExample_2} /// var cn = "products"; /// db._drop(cn); -/// var products = db._create(cn, { waitForSync: true }); +/// var products = db._create(cn); /// products.save({ "a": { "k": 1, "j": 1 }, "i": 1}); /// products.save({ "a": { "j": 1 }, "i": 1}); /// products.save({ "i": 1}); @@ -1857,7 +2010,7 @@ actions.defineHttp({ /// @EXAMPLE_ARANGOSH_RUN{RestSimpleReplaceByExample} /// var cn = "products"; /// db._drop(cn); -/// var products = db._create(cn, { waitForSync: true }); +/// var products = db._create(cn); /// products.save({ "a": { "k": 1, "j": 1 }, "i": 1}); /// products.save({ "a": { "j": 1 }, "i": 1}); /// products.save({ "i": 1}); @@ -1881,7 +2034,7 @@ actions.defineHttp({ /// @EXAMPLE_ARANGOSH_RUN{RestSimpleReplaceByExampleWaitForSync} /// var cn = "products"; /// db._drop(cn); -/// var products = db._create(cn, { waitForSync: true }); +/// var products = db._create(cn); /// products.save({ "a": { "k": 1, "j": 1 }, "i": 1}); /// products.save({ "a": { "j": 1 }, "i": 1}); /// products.save({ "i": 1}); @@ -2020,7 +2173,7 @@ actions.defineHttp({ /// @EXAMPLE_ARANGOSH_RUN{RestSimpleUpdateByExample} /// var cn = "products"; /// db._drop(cn); -/// var products = db._create(cn, { waitForSync: true }); +/// var products = db._create(cn); /// products.save({ "a": { "k": 1, "j": 1 }, "i": 1}); /// products.save({ "a": { "j": 1 }, "i": 1}); /// products.save({ "i": 1}); @@ -2044,7 +2197,7 @@ actions.defineHttp({ /// @EXAMPLE_ARANGOSH_RUN{RestSimpleUpdateByExample_1} /// var cn = "products"; /// db._drop(cn); -/// var products = db._create(cn, { waitForSync: true }); +/// var products = db._create(cn); /// products.save({ "a": { "k": 1, "j": 1 }, "i": 1}); /// products.save({ "a": { "j": 1 }, "i": 1}); /// products.save({ "i": 1}); diff --git a/js/apps/system/aardvark/aardvark.js b/js/apps/system/aardvark/aardvark.js index 131176f317..f70a835068 100644 --- a/js/apps/system/aardvark/aardvark.js +++ b/js/apps/system/aardvark/aardvark.js @@ -367,25 +367,24 @@ controller.post("/query/upload/:user", function(req, res) { queries = req.body(); userColl = db._users.byExample({"user": user}).toArray()[0]; - storedQueries = userColl.extra.queries; - queriesToSave = []; + queriesToSave = userColl.userData.queries || [ ]; underscore.each(queries, function(newq) { - var toBeStored = true; - underscore.each(storedQueries, function(stored) { - if (stored.name === newq.name) { - toBeStored = false; + var found = false, i; + for (i = 0; i < queriesToSave.length; ++i) { + if (queriesToSave[i].name === newq.name) { + queriesToSave[i] = newq; + found = true; + break; } - }); - if (toBeStored === true) { + } + if (! found) { queriesToSave.push(newq); } }); - queriesToSave = queriesToSave.concat(storedQueries); - var toUpdate = { - extra: { + userData: { queries: queriesToSave } } diff --git a/js/apps/system/aardvark/frontend/js/modules/org/arangodb/arango-collection-common.js b/js/apps/system/aardvark/frontend/js/modules/org/arangodb/arango-collection-common.js index bb21325a8a..0072ab137f 100644 --- a/js/apps/system/aardvark/frontend/js/modules/org/arangodb/arango-collection-common.js +++ b/js/apps/system/aardvark/frontend/js/modules/org/arangodb/arango-collection-common.js @@ -46,6 +46,7 @@ var SimpleQueryRange = simple.SimpleQueryRange; var SimpleQueryGeo = simple.SimpleQueryGeo; var SimpleQueryNear = simple.SimpleQueryNear; var SimpleQueryWithin = simple.SimpleQueryWithin; +var SimpleQueryWithinRectangle = simple.SimpleQueryWithinRectangle; var SimpleQueryFulltext = simple.SimpleQueryFulltext; // ----------------------------------------------------------------------------- @@ -740,6 +741,10 @@ ArangoCollection.prototype.within = function (lat, lon, radius) { return new SimpleQueryWithin(this, lat, lon, radius); }; +ArangoCollection.prototype.withinRectangle = function (lat1, lon1, lat2, lon2) { + return new SimpleQueryWithinRectangle(this, lat1, lon1, lat2, lon2); +}; + //////////////////////////////////////////////////////////////////////////////// /// @brief constructs a fulltext query for a collection /// @startDocuBlock collectionFulltext diff --git a/js/apps/system/aardvark/frontend/js/modules/org/arangodb/general-graph.js b/js/apps/system/aardvark/frontend/js/modules/org/arangodb/general-graph.js index ca153424a0..1e930271ea 100644 --- a/js/apps/system/aardvark/frontend/js/modules/org/arangodb/general-graph.js +++ b/js/apps/system/aardvark/frontend/js/modules/org/arangodb/general-graph.js @@ -1911,7 +1911,6 @@ var bindEdgeCollections = function(self, edgeCollections) { // remove wrap.remove = function(edgeId, options) { - var result; //if _key make _id (only on 1st call) if (edgeId.indexOf("/") === -1) { edgeId = key + "/" + edgeId; @@ -1923,6 +1922,7 @@ var bindEdgeCollections = function(self, edgeCollections) { collections: { write: self.__collectionsToLock }, + embed: true, action: function (params) { var db = require("internal").db; params.ids.forEach( @@ -1940,13 +1940,15 @@ var bindEdgeCollections = function(self, edgeCollections) { options: options } }); - result = true; } catch (e) { - result = false; + self.__idsToRemove = []; + self.__collectionsToLock = []; + throw e; } self.__idsToRemove = []; self.__collectionsToLock = []; - return result; + + return true; }; self[key] = wrap; @@ -1956,7 +1958,6 @@ var bindEdgeCollections = function(self, edgeCollections) { var bindVertexCollections = function(self, vertexCollections) { _.each(vertexCollections, function(key) { var obj = db._collection(key); - var result; var wrap = wrapCollection(obj); wrap.remove = function(vertexId, options) { //delete all edges using the vertex in all graphs @@ -1998,6 +1999,7 @@ var bindVertexCollections = function(self, vertexCollections) { collections: { write: self.__collectionsToLock }, + embed: true, action: function (params) { var db = require("internal").db; params.ids.forEach( @@ -2021,14 +2023,15 @@ var bindVertexCollections = function(self, vertexCollections) { vertexId: vertexId } }); - result = true; } catch (e) { - result = false; + self.__idsToRemove = []; + self.__collectionsToLock = []; + throw e; } self.__idsToRemove = []; self.__collectionsToLock = []; - return result; + return true; }; self[key] = wrap; }); diff --git a/js/apps/system/aardvark/frontend/js/modules/org/arangodb/simple-query-common.js b/js/apps/system/aardvark/frontend/js/modules/org/arangodb/simple-query-common.js index 56939193e1..9adcda7b6f 100644 --- a/js/apps/system/aardvark/frontend/js/modules/org/arangodb/simple-query-common.js +++ b/js/apps/system/aardvark/frontend/js/modules/org/arangodb/simple-query-common.js @@ -37,6 +37,7 @@ var ArangoError = arangodb.ArangoError; var SimpleQueryArray; var SimpleQueryNear; var SimpleQueryWithin; +var SimpleQueryWithinRectangle; // ----------------------------------------------------------------------------- // --SECTION-- GENERAL ARRAY CURSOR @@ -990,6 +991,14 @@ SimpleQueryGeo.prototype.within = function (lat, lon, radius) { return new SimpleQueryWithin(this._collection, lat, lon, radius, this._index); }; +//////////////////////////////////////////////////////////////////////////////// +/// @brief constructs a within-rectangle query for an index +//////////////////////////////////////////////////////////////////////////////// + +SimpleQueryGeo.prototype.withinRectangle = function (lat1, lon1, lat2, lon2) { + return new SimpleQueryWithinRectangle(this._collection, lat1, lon1, lat2, lon2, this._index); +}; + // ----------------------------------------------------------------------------- // --SECTION-- SIMPLE QUERY NEAR // ----------------------------------------------------------------------------- @@ -1214,10 +1223,6 @@ SimpleQueryWithin.prototype._PRINT = function (context) { context.output += text; }; -// ----------------------------------------------------------------------------- -// --SECTION-- public methods -// ----------------------------------------------------------------------------- - //////////////////////////////////////////////////////////////////////////////// /// @brief adds the distance attribute //////////////////////////////////////////////////////////////////////////////// @@ -1237,6 +1242,112 @@ SimpleQueryWithin.prototype.distance = function (attribute) { return clone; }; +// ----------------------------------------------------------------------------- +// --SECTION-- SIMPLE QUERY WITHINRECTANGLE +// ----------------------------------------------------------------------------- + +// ----------------------------------------------------------------------------- +// --SECTION-- constructors and destructors +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief within-rectangle query +//////////////////////////////////////////////////////////////////////////////// + +SimpleQueryWithinRectangle = function (collection, latitude1, longitude1, latitude2, longitude2, iid) { + var idx; + var i; + + this._collection = collection; + this._latitude1 = latitude1; + this._longitude1 = longitude1; + this._latitude2 = latitude2; + this._longitude2 = longitude2; + this._index = (iid === undefined ? null : iid); + + if (iid === undefined) { + idx = collection.getIndexes(); + + for (i = 0; i < idx.length; ++i) { + var index = idx[i]; + + if (index.type === "geo1" || index.type === "geo2") { + if (this._index === null) { + this._index = index.id; + } + else if (index.id < this._index) { + this._index = index.id; + } + } + } + } + + if (this._index === null) { + var err = new ArangoError(); + err.errorNum = arangodb.ERROR_QUERY_GEO_INDEX_MISSING; + err.errorMessage = arangodb.errors.ERROR_QUERY_GEO_INDEX_MISSING.message; + throw err; + } +}; + +SimpleQueryWithinRectangle.prototype = new SimpleQuery(); +SimpleQueryWithinRectangle.prototype.constructor = SimpleQueryWithinRectangle; + +// ----------------------------------------------------------------------------- +// --SECTION-- private methods +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief clones a within-rectangle query +//////////////////////////////////////////////////////////////////////////////// + +SimpleQueryWithinRectangle.prototype.clone = function () { + var query; + + query = new SimpleQueryWithinRectangle(this._collection, + this._latitude1, + this._longitude1, + this._latitude2, + this._longitude2, + this._index); + query._skip = this._skip; + query._limit = this._limit; + + return query; +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief prints a within-rectangle query +//////////////////////////////////////////////////////////////////////////////// + +SimpleQueryWithinRectangle.prototype._PRINT = function (context) { + var text; + + text = "SimpleQueryWithinRectangle(" + + this._collection.name() + + ", " + + this._latitude1 + + ", " + + this._longitude1 + + ", " + + this._latitude1 + + ", " + + this._longitude2 + + ", " + + this._index + + ")"; + + if (this._skip !== null && this._skip !== 0) { + text += ".skip(" + this._skip + ")"; + } + + if (this._limit !== null) { + text += ".limit(" + this._limit + ")"; + } + + context.output += text; +}; + // ----------------------------------------------------------------------------- // --SECTION-- SIMPLE QUERY FULLTEXT // ----------------------------------------------------------------------------- @@ -1342,6 +1453,7 @@ exports.SimpleQueryRange = SimpleQueryRange; exports.SimpleQueryGeo = SimpleQueryGeo; exports.SimpleQueryNear = SimpleQueryNear; exports.SimpleQueryWithin = SimpleQueryWithin; +exports.SimpleQueryWithinRectangle = SimpleQueryWithinRectangle; exports.SimpleQueryFulltext = SimpleQueryFulltext; // ----------------------------------------------------------------------------- diff --git a/js/apps/system/aardvark/frontend/js/modules/org/arangodb/simple-query.js b/js/apps/system/aardvark/frontend/js/modules/org/arangodb/simple-query.js index 2b947793fa..90126d9758 100644 --- a/js/apps/system/aardvark/frontend/js/modules/org/arangodb/simple-query.js +++ b/js/apps/system/aardvark/frontend/js/modules/org/arangodb/simple-query.js @@ -45,6 +45,7 @@ var SimpleQueryGeo = sq.SimpleQueryGeo; var SimpleQueryNear = sq.SimpleQueryNear; var SimpleQueryRange = sq.SimpleQueryRange; var SimpleQueryWithin = sq.SimpleQueryWithin; +var SimpleQueryWithinRectangle = sq.SimpleQueryWithinRectangle; // ----------------------------------------------------------------------------- // --SECTION-- SIMPLE QUERY ALL @@ -382,6 +383,65 @@ SimpleQueryWithin.prototype.execute = function (batchSize) { } }; +// ----------------------------------------------------------------------------- +// --SECTION-- SIMPLE QUERY WITHINRECTANGLE +// ----------------------------------------------------------------------------- + +// ----------------------------------------------------------------------------- +// --SECTION-- private functions +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief executes a withinRectangle query +//////////////////////////////////////////////////////////////////////////////// + +SimpleQueryWithinRectangle.prototype.execute = function (batchSize) { + if (this._execution === null) { + if (batchSize !== undefined && batchSize > 0) { + this._batchSize = batchSize; + } + + var data = { + collection: this._collection.name(), + latitude1: this._latitude1, + longitude1: this._longitude1, + latitude2: this._latitude2, + longitude2: this._longitude2 + }; + + if (this._limit !== null) { + data.limit = this._limit; + } + + if (this._skip !== null) { + data.skip = this._skip; + } + + if (this._index !== null) { + data.geo = this._index; + } + + if (this._distance !== null) { + data.distance = this._distance; + } + + if (this._batchSize !== null) { + data.batchSize = this._batchSize; + } + + var requestResult = this._collection._database._connection.PUT( + "/_api/simple/within-rectangle", JSON.stringify(data)); + + arangosh.checkRequestResult(requestResult); + + this._execution = new ArangoQueryCursor(this._collection._database, requestResult); + + if (requestResult.hasOwnProperty("count")) { + this._countQuery = requestResult.count; + } + } +}; + // ----------------------------------------------------------------------------- // --SECTION-- SIMPLE QUERY FULLTEXT // ----------------------------------------------------------------------------- @@ -449,6 +509,7 @@ exports.SimpleQueryGeo = SimpleQueryGeo; exports.SimpleQueryNear = SimpleQueryNear; exports.SimpleQueryRange = SimpleQueryRange; exports.SimpleQueryWithin = SimpleQueryWithin; +exports.SimpleQueryWithinRectangle = SimpleQueryWithinRectangle; // ----------------------------------------------------------------------------- // --SECTION-- END-OF-FILE diff --git a/js/client/modules/org/arangodb/simple-query.js b/js/client/modules/org/arangodb/simple-query.js index 1de1f04d91..501b8fbd76 100644 --- a/js/client/modules/org/arangodb/simple-query.js +++ b/js/client/modules/org/arangodb/simple-query.js @@ -44,6 +44,7 @@ var SimpleQueryGeo = sq.SimpleQueryGeo; var SimpleQueryNear = sq.SimpleQueryNear; var SimpleQueryRange = sq.SimpleQueryRange; var SimpleQueryWithin = sq.SimpleQueryWithin; +var SimpleQueryWithinRectangle = sq.SimpleQueryWithinRectangle; // ----------------------------------------------------------------------------- // --SECTION-- SIMPLE QUERY ALL @@ -381,6 +382,65 @@ SimpleQueryWithin.prototype.execute = function (batchSize) { } }; +// ----------------------------------------------------------------------------- +// --SECTION-- SIMPLE QUERY WITHINRECTANGLE +// ----------------------------------------------------------------------------- + +// ----------------------------------------------------------------------------- +// --SECTION-- private functions +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief executes a withinRectangle query +//////////////////////////////////////////////////////////////////////////////// + +SimpleQueryWithinRectangle.prototype.execute = function (batchSize) { + if (this._execution === null) { + if (batchSize !== undefined && batchSize > 0) { + this._batchSize = batchSize; + } + + var data = { + collection: this._collection.name(), + latitude1: this._latitude1, + longitude1: this._longitude1, + latitude2: this._latitude2, + longitude2: this._longitude2 + }; + + if (this._limit !== null) { + data.limit = this._limit; + } + + if (this._skip !== null) { + data.skip = this._skip; + } + + if (this._index !== null) { + data.geo = this._index; + } + + if (this._distance !== null) { + data.distance = this._distance; + } + + if (this._batchSize !== null) { + data.batchSize = this._batchSize; + } + + var requestResult = this._collection._database._connection.PUT( + "/_api/simple/within-rectangle", JSON.stringify(data)); + + arangosh.checkRequestResult(requestResult); + + this._execution = new ArangoQueryCursor(this._collection._database, requestResult); + + if (requestResult.hasOwnProperty("count")) { + this._countQuery = requestResult.count; + } + } +}; + // ----------------------------------------------------------------------------- // --SECTION-- SIMPLE QUERY FULLTEXT // ----------------------------------------------------------------------------- @@ -448,6 +508,7 @@ exports.SimpleQueryGeo = SimpleQueryGeo; exports.SimpleQueryNear = SimpleQueryNear; exports.SimpleQueryRange = SimpleQueryRange; exports.SimpleQueryWithin = SimpleQueryWithin; +exports.SimpleQueryWithinRectangle = SimpleQueryWithinRectangle; // ----------------------------------------------------------------------------- // --SECTION-- END-OF-FILE diff --git a/js/common/modules/org/arangodb/arango-collection-common.js b/js/common/modules/org/arangodb/arango-collection-common.js index b7493c1d90..8680cc5305 100644 --- a/js/common/modules/org/arangodb/arango-collection-common.js +++ b/js/common/modules/org/arangodb/arango-collection-common.js @@ -45,6 +45,7 @@ var SimpleQueryRange = simple.SimpleQueryRange; var SimpleQueryGeo = simple.SimpleQueryGeo; var SimpleQueryNear = simple.SimpleQueryNear; var SimpleQueryWithin = simple.SimpleQueryWithin; +var SimpleQueryWithinRectangle = simple.SimpleQueryWithinRectangle; var SimpleQueryFulltext = simple.SimpleQueryFulltext; // ----------------------------------------------------------------------------- @@ -739,6 +740,10 @@ ArangoCollection.prototype.within = function (lat, lon, radius) { return new SimpleQueryWithin(this, lat, lon, radius); }; +ArangoCollection.prototype.withinRectangle = function (lat1, lon1, lat2, lon2) { + return new SimpleQueryWithinRectangle(this, lat1, lon1, lat2, lon2); +}; + //////////////////////////////////////////////////////////////////////////////// /// @brief constructs a fulltext query for a collection /// @startDocuBlock collectionFulltext diff --git a/js/common/modules/org/arangodb/general-graph.js b/js/common/modules/org/arangodb/general-graph.js index edaf3d7b8b..187c7fdaa1 100644 --- a/js/common/modules/org/arangodb/general-graph.js +++ b/js/common/modules/org/arangodb/general-graph.js @@ -1910,7 +1910,6 @@ var bindEdgeCollections = function(self, edgeCollections) { // remove wrap.remove = function(edgeId, options) { - var result; //if _key make _id (only on 1st call) if (edgeId.indexOf("/") === -1) { edgeId = key + "/" + edgeId; @@ -1922,6 +1921,7 @@ var bindEdgeCollections = function(self, edgeCollections) { collections: { write: self.__collectionsToLock }, + embed: true, action: function (params) { var db = require("internal").db; params.ids.forEach( @@ -1939,13 +1939,15 @@ var bindEdgeCollections = function(self, edgeCollections) { options: options } }); - result = true; } catch (e) { - result = false; + self.__idsToRemove = []; + self.__collectionsToLock = []; + throw e; } self.__idsToRemove = []; self.__collectionsToLock = []; - return result; + + return true; }; self[key] = wrap; @@ -1955,7 +1957,6 @@ var bindEdgeCollections = function(self, edgeCollections) { var bindVertexCollections = function(self, vertexCollections) { _.each(vertexCollections, function(key) { var obj = db._collection(key); - var result; var wrap = wrapCollection(obj); wrap.remove = function(vertexId, options) { //delete all edges using the vertex in all graphs @@ -1997,6 +1998,7 @@ var bindVertexCollections = function(self, vertexCollections) { collections: { write: self.__collectionsToLock }, + embed: true, action: function (params) { var db = require("internal").db; params.ids.forEach( @@ -2020,14 +2022,15 @@ var bindVertexCollections = function(self, vertexCollections) { vertexId: vertexId } }); - result = true; } catch (e) { - result = false; + self.__idsToRemove = []; + self.__collectionsToLock = []; + throw e; } self.__idsToRemove = []; self.__collectionsToLock = []; - return result; + return true; }; self[key] = wrap; }); diff --git a/js/common/modules/org/arangodb/simple-query-common.js b/js/common/modules/org/arangodb/simple-query-common.js index 2ce4788f02..a5cad4d797 100644 --- a/js/common/modules/org/arangodb/simple-query-common.js +++ b/js/common/modules/org/arangodb/simple-query-common.js @@ -36,6 +36,7 @@ var ArangoError = arangodb.ArangoError; var SimpleQueryArray; var SimpleQueryNear; var SimpleQueryWithin; +var SimpleQueryWithinRectangle; // ----------------------------------------------------------------------------- // --SECTION-- GENERAL ARRAY CURSOR @@ -989,6 +990,14 @@ SimpleQueryGeo.prototype.within = function (lat, lon, radius) { return new SimpleQueryWithin(this._collection, lat, lon, radius, this._index); }; +//////////////////////////////////////////////////////////////////////////////// +/// @brief constructs a within-rectangle query for an index +//////////////////////////////////////////////////////////////////////////////// + +SimpleQueryGeo.prototype.withinRectangle = function (lat1, lon1, lat2, lon2) { + return new SimpleQueryWithinRectangle(this._collection, lat1, lon1, lat2, lon2, this._index); +}; + // ----------------------------------------------------------------------------- // --SECTION-- SIMPLE QUERY NEAR // ----------------------------------------------------------------------------- @@ -1213,10 +1222,6 @@ SimpleQueryWithin.prototype._PRINT = function (context) { context.output += text; }; -// ----------------------------------------------------------------------------- -// --SECTION-- public methods -// ----------------------------------------------------------------------------- - //////////////////////////////////////////////////////////////////////////////// /// @brief adds the distance attribute //////////////////////////////////////////////////////////////////////////////// @@ -1236,6 +1241,112 @@ SimpleQueryWithin.prototype.distance = function (attribute) { return clone; }; +// ----------------------------------------------------------------------------- +// --SECTION-- SIMPLE QUERY WITHINRECTANGLE +// ----------------------------------------------------------------------------- + +// ----------------------------------------------------------------------------- +// --SECTION-- constructors and destructors +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief within-rectangle query +//////////////////////////////////////////////////////////////////////////////// + +SimpleQueryWithinRectangle = function (collection, latitude1, longitude1, latitude2, longitude2, iid) { + var idx; + var i; + + this._collection = collection; + this._latitude1 = latitude1; + this._longitude1 = longitude1; + this._latitude2 = latitude2; + this._longitude2 = longitude2; + this._index = (iid === undefined ? null : iid); + + if (iid === undefined) { + idx = collection.getIndexes(); + + for (i = 0; i < idx.length; ++i) { + var index = idx[i]; + + if (index.type === "geo1" || index.type === "geo2") { + if (this._index === null) { + this._index = index.id; + } + else if (index.id < this._index) { + this._index = index.id; + } + } + } + } + + if (this._index === null) { + var err = new ArangoError(); + err.errorNum = arangodb.ERROR_QUERY_GEO_INDEX_MISSING; + err.errorMessage = arangodb.errors.ERROR_QUERY_GEO_INDEX_MISSING.message; + throw err; + } +}; + +SimpleQueryWithinRectangle.prototype = new SimpleQuery(); +SimpleQueryWithinRectangle.prototype.constructor = SimpleQueryWithinRectangle; + +// ----------------------------------------------------------------------------- +// --SECTION-- private methods +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief clones a within-rectangle query +//////////////////////////////////////////////////////////////////////////////// + +SimpleQueryWithinRectangle.prototype.clone = function () { + var query; + + query = new SimpleQueryWithinRectangle(this._collection, + this._latitude1, + this._longitude1, + this._latitude2, + this._longitude2, + this._index); + query._skip = this._skip; + query._limit = this._limit; + + return query; +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief prints a within-rectangle query +//////////////////////////////////////////////////////////////////////////////// + +SimpleQueryWithinRectangle.prototype._PRINT = function (context) { + var text; + + text = "SimpleQueryWithinRectangle(" + + this._collection.name() + + ", " + + this._latitude1 + + ", " + + this._longitude1 + + ", " + + this._latitude1 + + ", " + + this._longitude2 + + ", " + + this._index + + ")"; + + if (this._skip !== null && this._skip !== 0) { + text += ".skip(" + this._skip + ")"; + } + + if (this._limit !== null) { + text += ".limit(" + this._limit + ")"; + } + + context.output += text; +}; + // ----------------------------------------------------------------------------- // --SECTION-- SIMPLE QUERY FULLTEXT // ----------------------------------------------------------------------------- @@ -1341,6 +1452,7 @@ exports.SimpleQueryRange = SimpleQueryRange; exports.SimpleQueryGeo = SimpleQueryGeo; exports.SimpleQueryNear = SimpleQueryNear; exports.SimpleQueryWithin = SimpleQueryWithin; +exports.SimpleQueryWithinRectangle = SimpleQueryWithinRectangle; exports.SimpleQueryFulltext = SimpleQueryFulltext; // ----------------------------------------------------------------------------- diff --git a/js/server/modules/org/arangodb/aql.js b/js/server/modules/org/arangodb/aql.js index 22366fed6f..2a426d3411 100644 --- a/js/server/modules/org/arangodb/aql.js +++ b/js/server/modules/org/arangodb/aql.js @@ -3112,17 +3112,17 @@ function AQL_NEAR (collection, latitude, longitude, limit, distanceAttribute) { function AQL_WITHIN (collection, latitude, longitude, radius, distanceAttribute) { "use strict"; + var weight = TYPEWEIGHT(distanceAttribute); + if (weight !== TYPEWEIGHT_NULL && weight !== TYPEWEIGHT_STRING) { + WARN("WITHIN", INTERNAL.errors.ERROR_QUERY_FUNCTION_ARGUMENT_TYPE_MISMATCH); + } + if (isCoordinator) { var query = COLLECTION(collection).within(latitude, longitude, radius); query._distance = distanceAttribute; return query.toArray(); } - var weight = TYPEWEIGHT(distanceAttribute); - if (weight !== TYPEWEIGHT_NULL && weight !== TYPEWEIGHT_STRING) { - WARN("WITHIN", INTERNAL.errors.ERROR_QUERY_FUNCTION_ARGUMENT_TYPE_MISMATCH); - } - var idx = INDEX(COLLECTION(collection), [ "geo1", "geo2" ]); if (idx === null) { @@ -3160,92 +3160,8 @@ function AQL_WITHIN_RECTANGLE (collection, latitude1, longitude1, latitude2, lon WARN("WITHIN_RECTANGLE", INTERNAL.errors.ERROR_QUERY_FUNCTION_ARGUMENT_TYPE_MISMATCH); return null; } - - var distanceMeters = function (lat1, lon1, lat2, lon2) { - var deltaLat = (lat2 - lat1) * Math.PI / 180; - var deltaLon = (lon2 - lon1) * Math.PI / 180; - var a = Math.sin(deltaLat / 2) * Math.sin(deltaLat / 2) + - Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) * - Math.sin(deltaLon / 2) * Math.sin(deltaLon / 2); - var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); - - return 6378.137 /* radius of earth in kilometers */ - * c - * 1000; // kilometers to meters; - }; - - var midpoint = [ - latitude1 + (latitude2 - latitude1) * 0.5, - longitude1 + (longitude2 - longitude1) * 0.5 - ]; - - var idx = INDEX(COLLECTION(collection), [ "geo1", "geo2" ]); - - if (idx === null) { - THROW("WITHIN_RECTANGLE", INTERNAL.errors.ERROR_QUERY_GEO_INDEX_MISSING, collection); - } - - var diameter = distanceMeters(latitude1, longitude1, latitude2, longitude2); - var latLower, latUpper, lonLower, lonUpper; - - if (latitude1 < latitude2) { - latLower = latitude1; - latUpper = latitude2; - } - else { - latLower = latitude2; - latUpper = latitude1; - } - - if (longitude1 < longitude2) { - lonLower = longitude1; - lonUpper = longitude2; - } - else { - lonLower = longitude2; - lonUpper = longitude1; - } - - var result = COLLECTION(collection).WITHIN(idx.id, midpoint[0], midpoint[1], diameter); - - var documents = [ ]; - if (idx.type === 'geo1') { - // geo1, we have both coordinates in a list - var attribute = idx.fields[0]; - if (idx.geoJson) { - result.documents.forEach(function(doc) { - // check if within bounding rectangle - // first list value is longitude, then latitude - if (doc[attribute][1] >= latLower && doc[attribute][1] <= latUpper && - doc[attribute][0] >= lonLower && doc[attribute][0] <= lonUpper) { - documents.push(doc); - } - }); - } - else { - result.documents.forEach(function(doc) { - // check if within bounding rectangle - // first list value is latitude, then longitude - if (doc[attribute][0] >= latLower && doc[attribute][0] <= latUpper && - doc[attribute][1] >= lonLower && doc[attribute][1] <= lonUpper) { - documents.push(doc); - } - }); - } - } - else { - // geo2, we have dedicated latitude and longitude attributes - var latAtt = idx.fields[0], lonAtt = idx.fields[1]; - result.documents.forEach(function(doc) { - // check if within bounding rectangle - if (doc[latAtt] >= latLower && doc[latAtt] <= latUpper && - doc[lonAtt] >= lonLower && doc[lonAtt] <= lonUpper) { - documents.push(doc); - } - }); - } - - return documents; + + return COLLECTION(collection).withinRectangle(latitude1, longitude1, latitude2, longitude2).toArray(); } //////////////////////////////////////////////////////////////////////////////// @@ -3513,8 +3429,7 @@ function AQL_HAS (element, name) { } if (weight !== TYPEWEIGHT_DOCUMENT) { - WARN("HAS", INTERNAL.errors.ERROR_QUERY_FUNCTION_ARGUMENT_TYPE_MISMATCH); - return; + return false; } return element.hasOwnProperty(AQL_TO_STRING(name)); diff --git a/js/server/modules/org/arangodb/simple-query.js b/js/server/modules/org/arangodb/simple-query.js index 556dad01dd..73514c86a1 100644 --- a/js/server/modules/org/arangodb/simple-query.js +++ b/js/server/modules/org/arangodb/simple-query.js @@ -45,6 +45,7 @@ var SimpleQueryGeo = sq.SimpleQueryGeo; var SimpleQueryNear = sq.SimpleQueryNear; var SimpleQueryRange = sq.SimpleQueryRange; var SimpleQueryWithin = sq.SimpleQueryWithin; +var SimpleQueryWithinRectangle = sq.SimpleQueryWithinRectangle; //////////////////////////////////////////////////////////////////////////////// /// @brief rewrites an index id by stripping the collection name from it @@ -1188,6 +1189,190 @@ SimpleQueryWithin.prototype.execute = function () { this._countTotal = documents.total; }; +// ----------------------------------------------------------------------------- +// --SECTION-- SIMPLE QUERY WITHINRECTANGLE +// ----------------------------------------------------------------------------- + +// ----------------------------------------------------------------------------- +// --SECTION-- private functions +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief executes a within-rectangle query +//////////////////////////////////////////////////////////////////////////////// + +SimpleQueryWithinRectangle.prototype.execute = function () { + var result; + var documents; + + if (this._execution !== null) { + return; + } + + if (this._skip === null) { + this._skip = 0; + } + + if (this._skip < 0) { + var err = new ArangoError(); + err.errorNum = internal.errors.ERROR_BAD_PARAMETER; + err.errorMessage = "skip must be non-negative"; + throw err; + } + + var cluster = require("org/arangodb/cluster"); + + if (cluster.isCoordinator()) { + var dbName = require("internal").db._name(); + var shards = cluster.shardList(dbName, this._collection.name()); + var coord = { coordTransactionID: ArangoClusterInfo.uniqid() }; + var options = { coordTransactionID: coord.coordTransactionID, timeout: 360 }; + var _limit = 0; + if (this._limit > 0) { + if (this._skip >= 0) { + _limit = this._skip + this._limit; + } + } + + var self = this; + shards.forEach(function (shard) { + ArangoClusterComm.asyncRequest("put", + "shard:" + shard, + dbName, + "/_api/simple/within-rectangle", + JSON.stringify({ + collection: shard, + latitude1: self._latitude1, + longitude1: self._longitude1, + latitude2: self._latitude2, + longitude2: self._longitude2, + geo: rewriteIndex(self._index), + skip: 0, + limit: _limit || undefined, + batchSize: 100000000 + }), + { }, + options); + }); + + var _documents = [ ], total = 0; + result = cluster.wait(coord, shards); + + result.forEach(function(part) { + var body = JSON.parse(part.body); + total += body.total; + + _documents = _documents.concat(body.result); + }); + + if (this._limit > 0) { + _documents = _documents.slice(0, this._skip + this._limit); + } + + documents = { + documents: _documents, + count: _documents.length, + total: total + }; + } + else { + var distanceMeters = function (lat1, lon1, lat2, lon2) { + var deltaLat = (lat2 - lat1) * Math.PI / 180; + var deltaLon = (lon2 - lon1) * Math.PI / 180; + var a = Math.sin(deltaLat / 2) * Math.sin(deltaLat / 2) + + Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) * + Math.sin(deltaLon / 2) * Math.sin(deltaLon / 2); + var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); + + return 6378.137 /* radius of earth in kilometers */ + * c + * 1000; // kilometers to meters; + }; + + var diameter = distanceMeters(this._latitude1, this._longitude1, this._latitude2, this._longitude2); + var midpoint = [ + this._latitude1 + (this._latitude2 - this._latitude1) * 0.5, + this._longitude1 + (this._longitude2 - this._longitude1) * 0.5 + ]; + + result = this._collection.WITHIN(this._index, midpoint[0], midpoint[1], diameter); + + var idx = this._collection.index(this._index); + var latLower, latUpper, lonLower, lonUpper; + + if (this._latitude1 < this._latitude2) { + latLower = this._latitude1; + latUpper = this._latitude2; + } + else { + latLower = this._latitude2; + latUpper = this._latitude1; + } + + if (this._longitude1 < this._longitude2) { + lonLower = this._longitude1; + lonUpper = this._longitude2; + } + else { + lonLower = this._longitude2; + lonUpper = this._longitude1; + } + + documents = [ ]; + if (idx.type === 'geo1') { + // geo1, we have both coordinates in a list + var attribute = idx.fields[0]; + if (idx.geoJson) { + result.documents.forEach(function(doc) { + // check if within bounding rectangle + // first list value is longitude, then latitude + if (doc[attribute][1] >= latLower && doc[attribute][1] <= latUpper && + doc[attribute][0] >= lonLower && doc[attribute][0] <= lonUpper) { + documents.push(doc); + } + }); + } + else { + result.documents.forEach(function(doc) { + // check if within bounding rectangle + // first list value is latitude, then longitude + if (doc[attribute][0] >= latLower && doc[attribute][0] <= latUpper && + doc[attribute][1] >= lonLower && doc[attribute][1] <= lonUpper) { + documents.push(doc); + } + }); + } + } + else { + // geo2, we have dedicated latitude and longitude attributes + var latAtt = idx.fields[0], lonAtt = idx.fields[1]; + result.documents.forEach(function(doc) { + // check if within bounding rectangle + if (doc[latAtt] >= latLower && doc[latAtt] <= latUpper && + doc[lonAtt] >= lonLower && doc[lonAtt] <= lonUpper) { + documents.push(doc); + } + }); + } + + documents = { + documents: documents, + count: result.documents.length, + total: result.documents.length + }; + + if (this._limit > 0) { + documents.documents = documents.documents.slice(0, this._skip + this._limit); + documents.count = documents.documents.length; + } + + } + + this._execution = new GeneralArrayCursor(documents.documents, this._skip, null); + this._countQuery = documents.total - this._skip; + this._countTotal = documents.total; +}; + // ----------------------------------------------------------------------------- // --SECTION-- SIMPLE QUERY FULLTEXT // ----------------------------------------------------------------------------- @@ -1294,6 +1479,7 @@ exports.SimpleQueryGeo = SimpleQueryGeo; exports.SimpleQueryNear = SimpleQueryNear; exports.SimpleQueryRange = SimpleQueryRange; exports.SimpleQueryWithin = SimpleQueryWithin; +exports.SimpleQueryWithinRectangle = SimpleQueryWithinRectangle; exports.byExample = byExample; // ----------------------------------------------------------------------------- diff --git a/js/server/tests/aql-functions.js b/js/server/tests/aql-functions.js index 49a3105efb..b7b733ef84 100644 --- a/js/server/tests/aql-functions.js +++ b/js/server/tests/aql-functions.js @@ -1876,6 +1876,16 @@ function ahuacatlFunctionsTestSuite () { assertEqual(expected, actual); }, +//////////////////////////////////////////////////////////////////////////////// +/// @brief test has function +//////////////////////////////////////////////////////////////////////////////// + + testHas3 : function () { + var expected = [ [ "test2", [ "other" ] ] ]; + var actual = getQueryResults("LET doc = { \"_id\": \"test/76689250173\", \"_rev\": \"76689250173\", \"_key\": \"76689250173\", \"test1\": \"something\", \"test2\": { \"DATA\": [ \"other\" ] } } FOR attr IN ATTRIBUTES(doc) LET prop = doc[attr] FILTER HAS(prop, 'DATA') RETURN [ attr, prop.DATA ]"); + assertEqual(expected, actual); + }, + //////////////////////////////////////////////////////////////////////////////// /// @brief test has function //////////////////////////////////////////////////////////////////////////////// @@ -1884,10 +1894,10 @@ function ahuacatlFunctionsTestSuite () { assertQueryError(errors.ERROR_QUERY_FUNCTION_ARGUMENT_NUMBER_MISMATCH.code, "RETURN HAS()"); assertQueryError(errors.ERROR_QUERY_FUNCTION_ARGUMENT_NUMBER_MISMATCH.code, "RETURN HAS({ })"); assertQueryError(errors.ERROR_QUERY_FUNCTION_ARGUMENT_NUMBER_MISMATCH.code, "RETURN HAS({ }, \"fox\", true)"); - assertQueryWarningAndNull(errors.ERROR_QUERY_FUNCTION_ARGUMENT_TYPE_MISMATCH.code, "RETURN HAS(false, \"fox\")"); - assertQueryWarningAndNull(errors.ERROR_QUERY_FUNCTION_ARGUMENT_TYPE_MISMATCH.code, "RETURN HAS(3, \"fox\")"); - assertQueryWarningAndNull(errors.ERROR_QUERY_FUNCTION_ARGUMENT_TYPE_MISMATCH.code, "RETURN HAS(\"yes\", \"fox\")"); - assertQueryWarningAndNull(errors.ERROR_QUERY_FUNCTION_ARGUMENT_TYPE_MISMATCH.code, "RETURN HAS([ ], \"fox\")"); + assertEqual([ false ], getQueryResults("RETURN HAS(false, \"fox\")")); + assertEqual([ false ], getQueryResults("RETURN HAS(3, \"fox\")")); + assertEqual([ false ], getQueryResults("RETURN HAS(\"yes\", \"fox\")")); + assertEqual([ false ], getQueryResults("RETURN HAS([ ], \"fox\")")); assertEqual([ false ], getQueryResults("RETURN HAS({ }, null)")); assertEqual([ false ], getQueryResults("RETURN HAS({ }, false)")); assertEqual([ false ], getQueryResults("RETURN HAS({ }, true)")); diff --git a/js/server/tests/aql-queries-collection.js b/js/server/tests/aql-queries-collection.js index c0a1848954..e79b24cb4b 100644 --- a/js/server/tests/aql-queries-collection.js +++ b/js/server/tests/aql-queries-collection.js @@ -648,6 +648,201 @@ function ahuacatlQueryCollectionTestSuite () { var actual = getQueryResults("FOR u in " + users.name() + " SORT u.name RETURN { name : u.name, from: u._from, to: u._to }"); assertEqual(expected, actual); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test querying attributes +//////////////////////////////////////////////////////////////////////////////// + + testAttributesQuery1 : function () { + var expected = [ + [ + "Abigail", + "Abigail" + ], + [ + "Alexander", + "Alexander" + ], + [ + "Anthony", + "Anthony" + ], + [ + "Chloe", + "Chloe" + ], + [ + "Daniel", + "Daniel" + ], + [ + "Diego", + "Diego" + ], + [ + "Emma", + "Emma" + ], + [ + "Ethan", + "Ethan" + ], + [ + "Eva", + "Eva" + ], + [ + "Fred", + "Fred" + ], + [ + "Isabella", + "Isabella" + ], + [ + "Jacob", + "Jacob" + ], + [ + "Jim", + "Jim" + ], + [ + "John", + "John" + ], + [ + "Madison", + "Madison" + ], + [ + "Mariah", + "Mariah" + ], + [ + "Mary", + "Mary" + ], + [ + "Michael", + "Michael" + ], + [ + "Olivia", + "Olivia" + ], + [ + "Sophia", + "Sophia" + ] + ]; + + var actual = getQueryResults("FOR u in " + users.name() + " SORT u.name RETURN [ u.name, u['name'] ]"); + assertEqual(expected, actual); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test querying attributes +//////////////////////////////////////////////////////////////////////////////// + + testAttributesQuery2 : function () { + var expected = [ + [ + "Abigail", + "Abigail" + ], + [ + "Alexander", + "Alexander" + ], + [ + "Anthony", + "Anthony" + ], + [ + "Chloe", + "Chloe" + ], + [ + "Daniel", + "Daniel" + ], + [ + "Diego", + "Diego" + ], + [ + "Emma", + "Emma" + ], + [ + "Ethan", + "Ethan" + ], + [ + "Eva", + "Eva" + ], + [ + "Fred", + "Fred" + ], + [ + "Isabella", + "Isabella" + ], + [ + "Jacob", + "Jacob" + ], + [ + "Jim", + "Jim" + ], + [ + "John", + "John" + ], + [ + "Madison", + "Madison" + ], + [ + "Mariah", + "Mariah" + ], + [ + "Mary", + "Mary" + ], + [ + "Michael", + "Michael" + ], + [ + "Olivia", + "Olivia" + ], + [ + "Sophia", + "Sophia" + ] + ]; + var actual = getQueryResults("FOR u in " + users.name() + " LET d = 'name' SORT u.name RETURN [ u.name, u[d] ]"); + assertEqual(expected, actual); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test querying attributes +//////////////////////////////////////////////////////////////////////////////// + + testAttributesQuery3 : function () { + users.save({ "hobbies" : [ "riding", "skating", "swimming" ] }); + + var expected = [ [ "riding", "skating", "swimming", null, "swimming" ] ]; + var actual = getQueryResults("FOR u in " + users.name() + " FILTER HAS(u, 'hobbies') RETURN [ u.hobbies[0], u.hobbies[1], u.hobbies[2], u.hobbies[3], u.hobbies[-1] ]"); + assertEqual(expected, actual); } }; diff --git a/js/server/tests/aql-within-rectangle.js b/js/server/tests/aql-within-rectangle.js index 75560348bf..6c15aa15de 100644 --- a/js/server/tests/aql-within-rectangle.js +++ b/js/server/tests/aql-within-rectangle.js @@ -84,17 +84,17 @@ function withinRectangleSuite () { //////////////////////////////////////////////////////////////////////////////// testWithinRectangleAsResult : function () { - var actual =AQL_EXECUTE("RETURN WITHIN_RECTANGLE(geo, -1, -1, 1, 1)").json[0]; + var actual = AQL_EXECUTE("RETURN WITHIN_RECTANGLE(geo, -1, -1, 1, 1)").json[0]; assertEqual(actual.length , 9); }, testWithinRectangleAsResultForSingleDocument : function () { - var actual =AQL_EXECUTE("RETURN WITHIN_RECTANGLE(geo, -0.8, -1.2, -1.2, -0.8)").json[0]; + var actual = AQL_EXECUTE("RETURN WITHIN_RECTANGLE(geo, -0.8, -1.2, -1.2, -0.8)").json[0]; assertEqual(actual.length , 1); }, testWithinRectangleAsResultForMissingDocument : function () { - var actual =AQL_EXECUTE("RETURN WITHIN_RECTANGLE(geo, -41, -41, -41, -41)").json[0]; + var actual = AQL_EXECUTE("RETURN WITHIN_RECTANGLE(geo, -41, -41, -41, -41)").json[0]; assertEqual(actual.length , 0); },