diff --git a/Doxygen/Examples.Durham/simple-query-closed-range-to-array b/Doxygen/Examples.Durham/simple-query-closed-range-to-array new file mode 100644 index 0000000000..d6c9bc775b --- /dev/null +++ b/Doxygen/Examples.Durham/simple-query-closed-range-to-array @@ -0,0 +1,6 @@ +arangod> l = db.skip.closedRange("age", 10, 12).toArray(); +[ + { "_id" : "2097590/4260278", "_rev" : 4260278, "age" : 10 }, + { "_id" : "2097590/4325814", "_rev" : 4325814, "age" : 11 }, + { "_id" : "2097590/4391350", "_rev" : 4391350, "age" : 12 } +] diff --git a/Doxygen/Examples.Durham/simple-query-range-to-array b/Doxygen/Examples.Durham/simple-query-range-to-array new file mode 100644 index 0000000000..dc7029e589 --- /dev/null +++ b/Doxygen/Examples.Durham/simple-query-range-to-array @@ -0,0 +1,6 @@ +arangod> l = db.skip.range("age", 10, 13).toArray(); +[ + { "_id" : "2097590/4260278", "_rev" : 4260278, "age" : 10 }, + { "_id" : "2097590/4325814", "_rev" : 4325814, "age" : 11 }, + { "_id" : "2097590/4391350", "_rev" : 4391350, "age" : 12 } +] diff --git a/RestServer/ArangoServer.cpp b/RestServer/ArangoServer.cpp index 0a2130581b..ba8e935ae0 100644 --- a/RestServer/ArangoServer.cpp +++ b/RestServer/ArangoServer.cpp @@ -868,7 +868,7 @@ int ArangoServer::executeShell (bool tests) { char* input = console->prompt("arangod> "); if (input == 0) { - printf("\nBye Bye! Auf Wiedersehen! さようなら\n"); + printf("\nBye Bye! Auf Wiedersehen! До свидания! さようなら\n"); break; } diff --git a/js/actions/system/api-index.js b/js/actions/system/api-index.js index cc21072b42..2b486b6bd5 100644 --- a/js/actions/system/api-index.js +++ b/js/actions/system/api-index.js @@ -414,6 +414,41 @@ function POST_api_index_hash (req, res, collection, body) { //////////////////////////////////////////////////////////////////////////////// /// @brief creates a skip-list +/// +/// @RESTHEADER{POST /_api/index,creates a hash index} +/// +/// @REST{POST /_api/index?collection=@FA{collection-identifier}} +/// +/// Creates a skip-list index for the collection @FA{collection-identifier}, if +/// it does not already exist. The call expects an object containing the index +/// details. +/// +/// - @LIT{type}: must be equal to @LIT{"skiplist"}. +/// +/// - @LIT{fields}: A list of attribute paths. +/// +/// - @LIT{unique}: If @LIT{true}, then create a unique index. +/// +/// If the index does not already exists and could be created, then a @LIT{HTTP +/// 201} is returned. If the index already exists, then a @LIT{HTTP 200} is +/// returned. +/// +/// If the @FA{collection-identifier} is unknown, then a @LIT{HTTP 404} is +/// returned. It is possible to specify a name instead of an identifier. +/// +/// If the collection already contains documents and you try to create a unique +/// skip-list index in such a way that there are documents violating the +/// uniqueness, then a @LIT{HTTP 400} is returned. +/// +/// @EXAMPLES +/// +/// Creating an unique constraint: +/// +/// @verbinclude api-index-create-new-unique-skiplist +/// +/// Creating a hash index: +/// +/// @verbinclude api-index-create-new-skiplist //////////////////////////////////////////////////////////////////////////////// function POST_api_index_skiplist (req, res, collection, body) { diff --git a/js/actions/system/api-simple.js b/js/actions/system/api-simple.js index fcbf187be1..3f62b9e2b4 100644 --- a/js/actions/system/api-simple.js +++ b/js/actions/system/api-simple.js @@ -452,10 +452,6 @@ actions.defineHttp({ /// /// - @LIT{example}: The example. /// -/// - @LIT{skip}: The documents to skip in the query. (optional) -/// -/// - @LIT{limit}: The maximal amount of documents to return. (optional) -/// /// Returns a result containing the document or @LIT{HTTP 404} if no /// document matched the example. /// @@ -560,6 +556,99 @@ actions.defineHttp({ } }); +//////////////////////////////////////////////////////////////////////////////// +/// @fn JSA_PUT_api_simple_range +/// @brief returns all documents of a collection within a range +/// +/// @RESTHEADER{PUT /_api/simple/range,executes simple range query} +/// +/// @REST{PUT /_api/simple/range} +/// +/// This will find all documents within a given range. +/// +/// The call expects a JSON hash array as body with the following attributes: +/// +/// - @LIT{collection}: The identifier or name of the collection to query. +/// +/// - @LIT{attribute}: The attribute path to check. +/// +/// - @LIT{left}: The lower bound. +/// +/// - @LIT{right}: The upper bound. +/// +/// - @LIT{closed}: If true, use intervall including @LIT{left} and @LIT{right}, +/// otherwise exclude @LIT{right}, but include @LIT{left}. +/// +/// - @LIT{skip}: The documents to skip in the query. (optional) +/// +/// - @LIT{limit}: The maximal amount of documents to return. (optional) +/// +/// Returns a cursor containing the result, see @ref HttpCursor for details. +/// +/// @EXAMPLES +/// +/// @verbinclude api-simple-range +//////////////////////////////////////////////////////////////////////////////// + +actions.defineHttp({ + url : API + "range", + context : "api", + + callback : function (req, res) { + 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 name = body.collection; + var attribute = body.attribute; + var left = body.left; + var right = body.right; + var closed = body.closed; + + var name = body.collection; + var id = parseInt(name) || name; + var collection = internal.db._collection(id); + + if (collection == null) { + actions.collectionNotFound(req, res, name); + } + else { + try { + var result; + + if (closed) { + result = collection.closedRange(attribute, left, right); + } + else { + result = collection.range(attribute, left, right); + } + + if (skip != null) { + result = result.skip(skip); + } + + if (limit != null) { + result = result.limit(limit); + } + + actions.resultCursor(req, res, CREATE_CURSOR(result.toArray(), true)); + } + catch (err) { + actions.resultException(req, res, err); + } + } + } + } +}); + //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// diff --git a/js/client/client.js b/js/client/client.js index cf9862cae4..26b988812b 100644 --- a/js/client/client.js +++ b/js/client/client.js @@ -1237,6 +1237,48 @@ ArangoCollection.prototype.ensureCapConstraint = function (size) { return requestResult; } +//////////////////////////////////////////////////////////////////////////////// +/// @brief adds a unique skip-list index +//////////////////////////////////////////////////////////////////////////////// + +ArangoCollection.prototype.ensureUniqueSkiplist = function () { + var body; + var fields = []; + + for (var i = 0; i < arguments.length; ++i) { + fields.push(arguments[i]); + } + + body = { type : "skiplist", unique : true, fields : fields }; + + var requestResult = this._database._connection.POST("/_api/index?collection=" + encodeURIComponent(this._id), JSON.stringify(body)); + + TRI_CheckRequestResult(requestResult); + + return requestResult; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief adds a skip-list index +//////////////////////////////////////////////////////////////////////////////// + +ArangoCollection.prototype.ensureSkiplist = function () { + var body; + var fields = []; + + for (var i = 0; i < arguments.length; ++i) { + fields.push(arguments[i]); + } + + body = { type : "skiplist", unique : false, fields : fields }; + + var requestResult = this._database._connection.POST("/_api/index?collection=" + encodeURIComponent(this._id), JSON.stringify(body)); + + TRI_CheckRequestResult(requestResult); + + return requestResult; +} + //////////////////////////////////////////////////////////////////////////////// /// @brief adds a unique constraint //////////////////////////////////////////////////////////////////////////////// @@ -1279,35 +1321,6 @@ ArangoCollection.prototype.ensureHashIndex = function () { return requestResult; } -//////////////////////////////////////////////////////////////////////////////// -/// @brief queries by example -//////////////////////////////////////////////////////////////////////////////// - -ArangoCollection.prototype.BY_EXAMPLE_HASH = function (index, example, skip, limit) { - var body; - - limit = limit || null; - skip = skip || null; - - if (index.hasOwnProperty("id")) { - index = index.id; - } - - body = { collection : this._id, index : index, skip : skip, limit : limit, example : {} }; - - for (var key in example) { - if (example.hasOwnProperty(key)) { - body.example[key] = example[key]; - } - } - - var requestResult = this._database._connection.PUT("/_api/simple/BY-EXAMPLE-HASH", JSON.stringify(body)); - - TRI_CheckRequestResult(requestResult); - - return requestResult; -} - //////////////////////////////////////////////////////////////////////////////// /// @brief adds an geo index //////////////////////////////////////////////////////////////////////////////// @@ -1367,6 +1380,35 @@ ArangoCollection.prototype.ensureGeoConstraint = function (lat, lon, ignoreNull) return requestResult; } +//////////////////////////////////////////////////////////////////////////////// +/// @brief queries by example +//////////////////////////////////////////////////////////////////////////////// + +ArangoCollection.prototype.BY_EXAMPLE_HASH = function (index, example, skip, limit) { + var body; + + limit = limit || null; + skip = skip || null; + + if (index.hasOwnProperty("id")) { + index = index.id; + } + + body = { collection : this._id, index : index, skip : skip, limit : limit, example : {} }; + + for (var key in example) { + if (example.hasOwnProperty(key)) { + body.example[key] = example[key]; + } + } + + var requestResult = this._database._connection.PUT("/_api/simple/BY-EXAMPLE-HASH", JSON.stringify(body)); + + TRI_CheckRequestResult(requestResult); + + return requestResult; +} + //////////////////////////////////////////////////////////////////////////////// /// @brief truncates a collection //////////////////////////////////////////////////////////////////////////////// diff --git a/js/client/js-client.h b/js/client/js-client.h index 39b11db689..715429eb3f 100644 --- a/js/client/js-client.h +++ b/js/client/js-client.h @@ -1239,6 +1239,48 @@ static string JS_client_client = "}\n" "\n" "////////////////////////////////////////////////////////////////////////////////\n" + "/// @brief adds a unique skip-list index\n" + "////////////////////////////////////////////////////////////////////////////////\n" + "\n" + "ArangoCollection.prototype.ensureUniqueSkiplist = function () {\n" + " var body;\n" + " var fields = [];\n" + " \n" + " for (var i = 0; i < arguments.length; ++i) {\n" + " fields.push(arguments[i]);\n" + " }\n" + "\n" + " body = { type : \"skiplist\", unique : true, fields : fields };\n" + "\n" + " var requestResult = this._database._connection.POST(\"/_api/index?collection=\" + encodeURIComponent(this._id), JSON.stringify(body));\n" + "\n" + " TRI_CheckRequestResult(requestResult);\n" + "\n" + " return requestResult;\n" + "}\n" + "\n" + "////////////////////////////////////////////////////////////////////////////////\n" + "/// @brief adds a skip-list index\n" + "////////////////////////////////////////////////////////////////////////////////\n" + "\n" + "ArangoCollection.prototype.ensureSkiplist = function () {\n" + " var body;\n" + " var fields = [];\n" + " \n" + " for (var i = 0; i < arguments.length; ++i) {\n" + " fields.push(arguments[i]);\n" + " }\n" + "\n" + " body = { type : \"skiplist\", unique : false, fields : fields };\n" + "\n" + " var requestResult = this._database._connection.POST(\"/_api/index?collection=\" + encodeURIComponent(this._id), JSON.stringify(body));\n" + "\n" + " TRI_CheckRequestResult(requestResult);\n" + "\n" + " return requestResult;\n" + "}\n" + "\n" + "////////////////////////////////////////////////////////////////////////////////\n" "/// @brief adds a unique constraint\n" "////////////////////////////////////////////////////////////////////////////////\n" "\n" @@ -1281,35 +1323,6 @@ static string JS_client_client = "}\n" "\n" "////////////////////////////////////////////////////////////////////////////////\n" - "/// @brief queries by example\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "\n" - "ArangoCollection.prototype.BY_EXAMPLE_HASH = function (index, example, skip, limit) {\n" - " var body;\n" - "\n" - " limit = limit || null;\n" - " skip = skip || null;\n" - "\n" - " if (index.hasOwnProperty(\"id\")) {\n" - " index = index.id;\n" - " }\n" - "\n" - " body = { collection : this._id, index : index, skip : skip, limit : limit, example : {} };\n" - "\n" - " for (var key in example) {\n" - " if (example.hasOwnProperty(key)) {\n" - " body.example[key] = example[key];\n" - " }\n" - " }\n" - "\n" - " var requestResult = this._database._connection.PUT(\"/_api/simple/BY-EXAMPLE-HASH\", JSON.stringify(body));\n" - "\n" - " TRI_CheckRequestResult(requestResult);\n" - "\n" - " return requestResult;\n" - "}\n" - "\n" - "////////////////////////////////////////////////////////////////////////////////\n" "/// @brief adds an geo index\n" "////////////////////////////////////////////////////////////////////////////////\n" "\n" @@ -1369,6 +1382,35 @@ static string JS_client_client = "}\n" "\n" "////////////////////////////////////////////////////////////////////////////////\n" + "/// @brief queries by example\n" + "////////////////////////////////////////////////////////////////////////////////\n" + "\n" + "ArangoCollection.prototype.BY_EXAMPLE_HASH = function (index, example, skip, limit) {\n" + " var body;\n" + "\n" + " limit = limit || null;\n" + " skip = skip || null;\n" + "\n" + " if (index.hasOwnProperty(\"id\")) {\n" + " index = index.id;\n" + " }\n" + "\n" + " body = { collection : this._id, index : index, skip : skip, limit : limit, example : {} };\n" + "\n" + " for (var key in example) {\n" + " if (example.hasOwnProperty(key)) {\n" + " body.example[key] = example[key];\n" + " }\n" + " }\n" + "\n" + " var requestResult = this._database._connection.PUT(\"/_api/simple/BY-EXAMPLE-HASH\", JSON.stringify(body));\n" + "\n" + " TRI_CheckRequestResult(requestResult);\n" + "\n" + " return requestResult;\n" + "}\n" + "\n" + "////////////////////////////////////////////////////////////////////////////////\n" "/// @brief truncates a collection\n" "////////////////////////////////////////////////////////////////////////////////\n" "\n" diff --git a/js/client/modules/simple-query.js b/js/client/modules/simple-query.js index 576c126915..1214668492 100644 --- a/js/client/modules/simple-query.js +++ b/js/client/modules/simple-query.js @@ -181,6 +181,59 @@ ArangoEdgesCollection.prototype.firstExample = ArangoCollection.prototype.firstE /// @} //////////////////////////////////////////////////////////////////////////////// +// ----------------------------------------------------------------------------- +// --SECTION-- RANGE QUERY +// ----------------------------------------------------------------------------- + +// ----------------------------------------------------------------------------- +// --SECTION-- private functions +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup SimpleQuery +/// @{ +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @brief executes a range query +//////////////////////////////////////////////////////////////////////////////// + +SQ.SimpleQueryRange.prototype.execute = function () { + var documents; + + if (this._execution == null) { + var data = { + collection : this._collection._id, + attribute : this._attribute, + right : this._right, + left : this._left, + closed : this._type == 1 + } + + if (this._limit != null) { + data.limit = this._limit; + } + + if (this._skip != null) { + data.skip = this._skip; + } + + var requestResult = this._collection._database._connection.PUT("/_api/simple/range", JSON.stringify(data)); + + TRI_CheckRequestResult(requestResult); + + this._execution = new ArangoQueryCursor(this._collection._database, requestResult); + + if (requestResult.hasOwnProperty("count")) { + this._countQuery = requestResult.count; + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + // ----------------------------------------------------------------------------- // --SECTION-- SIMPLE QUERY NEAR // ----------------------------------------------------------------------------- diff --git a/js/common/modules/simple-query-basics.js b/js/common/modules/simple-query-basics.js index 62e7092879..e7bab92ba1 100644 --- a/js/common/modules/simple-query-basics.js +++ b/js/common/modules/simple-query-basics.js @@ -29,318 +29,6 @@ var internal = require("internal"); var ArangoCollection = internal.ArangoCollection; var ArangoEdgesCollection = internal.ArangoEdgesCollection; -// ----------------------------------------------------------------------------- -// --SECTION-- ARANGO COLLECTION -// ----------------------------------------------------------------------------- - -// ----------------------------------------------------------------------------- -// --SECTION-- public functions -// ----------------------------------------------------------------------------- - -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup SimpleQuery -/// @{ -//////////////////////////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////////////////////////// -/// @brief constructs an all query for a collection -/// -/// @FUN{all()} -/// -/// Selects all documents of a collection and returns a cursor. You can use -/// @FN{toArray}, @FN{next}, or @FN{hasNext} to access the result. The result -/// can be limited using the @FN{skip} and @FN{limit} operator. -/// -/// @EXAMPLES -/// -/// Use @FN{toArray} to get all documents at once: -/// -/// @verbinclude simple3 -/// -/// Use @FN{next} to loop over all documents: -/// -/// @verbinclude simple4 -//////////////////////////////////////////////////////////////////////////////// - -ArangoCollection.prototype.all = function () { - return new SimpleQueryAll(this); -} - -ArangoEdgesCollection.prototype.all = ArangoCollection.prototype.all; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief constructs a near query for a collection -/// -/// @FUN{@FA{collection}.near(@FA{latitude}, @FA{longitude})} -///////////////////////////////////////////////////////////// -/// -/// The default will find at most 100 documents near the coordinate -/// (@FA{latitude}, @FA{longitude}). The returned list is sorted according to -/// the distance, with the nearest document coming first. If there are near -/// documents of equal distance, documents are chosen randomly from this set -/// until the limit is reached. It is possible to change the limit using the -/// @FA{limit} operator. -/// -/// In order to use the @FN{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 -/// the @FN{geo} operator to select a particular index. -/// -/// @note @FN{near} does not support negative skips. However, you can still use -/// @FN{limit} followed to @FN{skip}. -/// -/// @FUN{@FA{collection}.near(@FA{latitude}, @FA{longitude}).limit(@FA{limit})} -/////////////////////////////////////////////////////////////////////////////// -/// -/// Limits the result to @FA{limit} documents instead of the default 100. -/// -/// @note Unlike with multiple explicit limits, @FA{limit} will raise -/// the implicit default limit imposed by @FN{within}. -/// -/// @FUN{@FA{collection}.near(@FA{latitude}, @FA{longitude}).distance()} -//////////////////////////////////////////////////////////////////////// -/// -/// This will add an attribute @LIT{distance} to all documents returned, which -/// contains the distance between the given point and the document in meter. -/// -/// @FUN{@FA{collection}.near(@FA{latitude}, @FA{longitude}).distance(@FA{name})} -///////////////////////////////////////////////////////////////////////////////// -/// -/// This will add an attribute @FA{name} to all documents returned, which -/// contains the distance between the given point and the document in meter. -/// -/// @EXAMPLES -/// -/// To get the nearst two locations: -/// -/// @TINYEXAMPLE{simple-query-near,nearest two location} -/// -/// If you need the distance as well, then you can use the @FN{distance} -/// operator: -/// -/// @TINYEXAMPLE{simple-query-near2,nearest two location with distance in meter} -//////////////////////////////////////////////////////////////////////////////// - -ArangoCollection.prototype.near = function (lat, lon) { - return new SimpleQueryNear(this, lat, lon); -} - -ArangoEdgesCollection.prototype.near = ArangoCollection.prototype.near; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief constructs a within query for a collection -/// -/// @FUN{@FA{collection}.within(@FA{latitude}, @FA{longitude}, @FA{radius})} -//////////////////////////////////////////////////////////////////////////// -/// -/// This will find all documents with in a given radius around the coordinate -/// (@FA{latitude}, @FA{longitude}). The returned list is sorted by distance. -/// -/// In order to use the @FN{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, you can use -/// the @FN{geo} operator to select a particular index. -/// -/// @FUN{@FA{collection}.within(@FA{latitude}, @FA{longitude}, @FA{radius})@LATEXBREAK.distance()} -////////////////////////////////////////////////////////////////////////////////////////////////// -/// -/// This will add an attribute @LIT{_distance} to all documents returned, which -/// contains the distance between the given point and the document in meter. -/// -/// @FUN{@FA{collection}.within(@FA{latitude}, @FA{longitude}, @FA{radius})@LATEXBREAK.distance(@FA{name})} -/////////////////////////////////////////////////////////////////////////////////////////////////////////// -/// -/// This will add an attribute @FA{name} to all documents returned, which -/// contains the distance between the given point and the document in meter. -/// -/// @EXAMPLES -/// -/// To find all documents within a radius of 2000 km use: -/// -/// @TINYEXAMPLE{simple-query-within,within a radius} -//////////////////////////////////////////////////////////////////////////////// - -ArangoCollection.prototype.within = function (lat, lon, radius) { - return new SimpleQueryWithin(this, lat, lon, radius); -} - -ArangoEdgesCollection.prototype.within = ArangoCollection.prototype.within; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief constructs a geo index selection -/// -/// @FUN{@FA{collection}.geo(@FA{location})} -//////////////////////////////////////////// -/// -/// The next @FN{near} or @FN{within} operator will use the specific geo-spatial -/// index. -/// -/// @FUN{@FA{collection}.geo(@FA{location}, @LIT{true})} -//////////////////////////////////////////////////////// -/// -/// The next @FN{near} or @FN{within} operator will use the specific geo-spatial -/// index. -/// -/// @FUN{@FA{collection}.geo(@FA{latitude}, @FA{longitude})} -//////////////////////////////////////////////////////////// -/// -/// The next @FN{near} or @FN{within} operator will use the specific geo-spatial -/// index. -/// -/// @EXAMPLES -/// -/// Assume you have a location stored as list in the attribute @LIT{home} -/// and a destination stored in the attribute @LIT{work}. Than you can use the -/// @FN{geo} operator to select, which coordinates to use in a near query. -/// -/// @TINYEXAMPLE{simple-query-geo,use a specific index} -//////////////////////////////////////////////////////////////////////////////// - -ArangoCollection.prototype.geo = function(loc, order) { - var idx; - - var locateGeoIndex1 = function(collection, loc, order) { - var inds = collection.getIndexes(); - - for (var i = 0; i < inds.length; ++i) { - var index = inds[i]; - - if (index.type == "geo1") { - if (index.fields[0] == loc && index.geoJson == order) { - return index; - } - } - } - - return null; - }; - - var locateGeoIndex2 = function(collection, lat, lon) { - var inds = collection.getIndexes(); - - for (var i = 0; i < inds.length; ++i) { - var index = inds[i]; - - if (index.type == "geo2") { - if (index.fields[0] == lat && index.fields[1] == lon) { - return index; - } - } - } - - return null; - }; - - if (order === undefined) { - if (typeof loc === "object") { - idx = this.index(loc); - } - else { - idx = locateGeoIndex1(this, loc, false); - } - } - else if (typeof order === "boolean") { - idx = locateGeoIndex1(this, loc, order); - } - else { - idx = locateGeoIndex2(this, loc, order); - } - - if (idx == null) { - var err = new ArangoError(); - err.errorNum = internal.errors.ERROR_QUERY_GEO_INDEX_MISSING.code; - err.errorMessage = internal.errors.ERROR_QUERY_GEO_INDEX_MISSING.message; - throw err; - } - - return new SimpleQueryGeo(this, idx.id); -} - -ArangoEdgesCollection.prototype.geo = ArangoCollection.geo; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief constructs a query-by-example for a collection -/// -/// @FUN{@FA{collection}.byExample(@FA{example})} -/// -/// Selects all documents of a collection that match the specified -/// example and returns a cursor. -/// -/// You can use @FN{toArray}, @FN{next}, or @FN{hasNext} to access the -/// result. The result can be limited using the @FN{skip} and @FN{limit} -/// operator. -/// -/// An attribute name of the form @LIT{a.b} is interpreted as attribute path, -/// not as attribute. If you use -/// -/// @LIT{{ a : { c : 1 } }} -/// -/// as example, then you will find all documents, such that the attribute -/// @LIT{a} contains a document of the form @LIT{{c : 1 }}. E.g., the document -/// -/// @LIT{{ a : { c : 1 }\, b : 1 }} -/// -/// will match, but the document -/// -/// @LIT{{ a : { c : 1\, b : 1 } }} -/// -/// will not. -/// -/// However, if you use -/// -/// @LIT{{ a.c : 1 }}, -/// -/// then you will find all documents, which contain a sub-document in @LIT{a} -/// that has an attribute @LIT{c} of value @LIT{1}. E.g., both documents -/// -/// @LIT{{ a : { c : 1 }\, b : 1 }} and -/// -/// @LIT{{ a : { c : 1\, b : 1 } }} -/// -/// will match. -/// -/// @FUN{@FA{collection}.byExample(@FA{path1}, @FA{value1}, ...)} -/// -/// As alternative you can supply a list of paths and values. -/// -/// @EXAMPLES -/// -/// Use @FN{toArray} to get all documents at once: -/// -/// @TINYEXAMPLE{simple18,convert into a list} -/// -/// Use @FN{next} to loop over all documents: -/// -/// @TINYEXAMPLE{simple19,iterate over the result-set} -//////////////////////////////////////////////////////////////////////////////// - -ArangoCollection.prototype.byExample = function () { - var example; - - // example is given as only argument - if (arguments.length == 1) { - example = arguments[0]; - } - - // example is given as list - else { - example = {}; - - for (var i = 0; i < arguments.length; i += 2) { - example[arguments[i]] = arguments[i + 1]; - } - } - - // create a REAL array, otherwise JSON.stringify will fail - return new SimpleQueryByExample(this, example); -} - -ArangoEdgesCollection.prototype.byExample = ArangoCollection.prototype.byExample; - -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// - // ----------------------------------------------------------------------------- // --SECTION-- GENERAL ARRAY CURSOR // ----------------------------------------------------------------------------- @@ -837,6 +525,32 @@ function SimpleQueryAll (collection) { SimpleQueryAll.prototype = new SimpleQuery(); SimpleQueryAll.prototype.constructor = SimpleQueryAll; +//////////////////////////////////////////////////////////////////////////////// +/// @brief constructs an all query for a collection +/// +/// @FUN{all()} +/// +/// Selects all documents of a collection and returns a cursor. You can use +/// @FN{toArray}, @FN{next}, or @FN{hasNext} to access the result. The result +/// can be limited using the @FN{skip} and @FN{limit} operator. +/// +/// @EXAMPLES +/// +/// Use @FN{toArray} to get all documents at once: +/// +/// @verbinclude simple3 +/// +/// Use @FN{next} to loop over all documents: +/// +/// @verbinclude simple4 +//////////////////////////////////////////////////////////////////////////////// + +ArangoCollection.prototype.all = function () { + return new SimpleQueryAll(this); +} + +ArangoEdgesCollection.prototype.all = ArangoCollection.prototype.all; + //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// @@ -991,7 +705,7 @@ SimpleQueryArray.prototype._PRINT = function () { //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// -/// @brief select query +/// @brief query-by-example //////////////////////////////////////////////////////////////////////////////// function SimpleQueryByExample (collection, example) { @@ -1002,6 +716,85 @@ function SimpleQueryByExample (collection, example) { SimpleQueryByExample.prototype = new SimpleQuery(); SimpleQueryByExample.prototype.constructor = SimpleQueryByExample; +//////////////////////////////////////////////////////////////////////////////// +/// @brief constructs a query-by-example for a collection +/// +/// @FUN{@FA{collection}.byExample(@FA{example})} +/// +/// Selects all documents of a collection that match the specified +/// example and returns a cursor. +/// +/// You can use @FN{toArray}, @FN{next}, or @FN{hasNext} to access the +/// result. The result can be limited using the @FN{skip} and @FN{limit} +/// operator. +/// +/// An attribute name of the form @LIT{a.b} is interpreted as attribute path, +/// not as attribute. If you use +/// +/// @LIT{{ a : { c : 1 } }} +/// +/// as example, then you will find all documents, such that the attribute +/// @LIT{a} contains a document of the form @LIT{{c : 1 }}. E.g., the document +/// +/// @LIT{{ a : { c : 1 }\, b : 1 }} +/// +/// will match, but the document +/// +/// @LIT{{ a : { c : 1\, b : 1 } }} +/// +/// will not. +/// +/// However, if you use +/// +/// @LIT{{ a.c : 1 }}, +/// +/// then you will find all documents, which contain a sub-document in @LIT{a} +/// that has an attribute @LIT{c} of value @LIT{1}. E.g., both documents +/// +/// @LIT{{ a : { c : 1 }\, b : 1 }} and +/// +/// @LIT{{ a : { c : 1\, b : 1 } }} +/// +/// will match. +/// +/// @FUN{@FA{collection}.byExample(@FA{path1}, @FA{value1}, ...)} +/// +/// As alternative you can supply a list of paths and values. +/// +/// @EXAMPLES +/// +/// Use @FN{toArray} to get all documents at once: +/// +/// @TINYEXAMPLE{simple18,convert into a list} +/// +/// Use @FN{next} to loop over all documents: +/// +/// @TINYEXAMPLE{simple19,iterate over the result-set} +//////////////////////////////////////////////////////////////////////////////// + +ArangoCollection.prototype.byExample = function () { + var example; + + // example is given as only argument + if (arguments.length == 1) { + example = arguments[0]; + } + + // example is given as list + else { + example = {}; + + for (var i = 0; i < arguments.length; i += 2) { + example[arguments[i]] = arguments[i + 1]; + } + } + + // create a REAL array, otherwise JSON.stringify will fail + return new SimpleQueryByExample(this, example); +} + +ArangoEdgesCollection.prototype.byExample = ArangoCollection.prototype.byExample; + //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// @@ -1053,6 +846,141 @@ SimpleQueryByExample.prototype._PRINT = function () { /// @} //////////////////////////////////////////////////////////////////////////////// +// ----------------------------------------------------------------------------- +// --SECTION-- RANGE QUERY +// ----------------------------------------------------------------------------- + +// ----------------------------------------------------------------------------- +// --SECTION-- constructors and destructors +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup SimpleQuery +/// @{ +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @brief range query +//////////////////////////////////////////////////////////////////////////////// + +function SimpleQueryRange (collection, attribute, left, right, type) { + this._collection = collection; + this._attribute = attribute; + this._left = left; + this._right = right; + this._type = type; +} + +SimpleQueryRange.prototype = new SimpleQuery(); +SimpleQueryRange.prototype.constructor = SimpleQueryRange; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief constructs a range query for a collection +/// +/// @FUN{@FA{collection}.range(@FA{attribute}, @FA{left}, @FA{right})} +/// +/// Selects all documents of a collection such that the @FA{attribute} is +/// greater or equal than @FA{left} and strictly less than @FA{right}. +/// +/// You can use @FN{toArray}, @FN{next}, or @FN{hasNext} to access the +/// result. The result can be limited using the @FN{skip} and @FN{limit} +/// operator. +/// +/// An attribute name of the form @LIT{a.b} is interpreted as attribute path, +/// not as attribute. +/// +/// @EXAMPLES +/// +/// Use @FN{toArray} to get all documents at once: +/// +/// @TINYEXAMPLE{simple-query-range-to-array,convert into a list} +//////////////////////////////////////////////////////////////////////////////// + +ArangoCollection.prototype.range = function (name, left, right) { + return new SimpleQueryRange(this, name, left, right, 0); +} + +ArangoEdgesCollection.prototype.range = ArangoCollection.prototype.range; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief constructs a closed range query for a collection +/// +/// @FUN{@FA{collection}.closedRange(@FA{attribute}, @FA{left}, @FA{right})} +/// +/// Selects all documents of a collection such that the @FA{attribute} is +/// greater or equal than @FA{left} and less or equal than @FA{right}. +/// +/// You can use @FN{toArray}, @FN{next}, or @FN{hasNext} to access the +/// result. The result can be limited using the @FN{skip} and @FN{limit} +/// operator. +/// +/// An attribute name of the form @LIT{a.b} is interpreted as attribute path, +/// not as attribute. +/// +/// @EXAMPLES +/// +/// Use @FN{toArray} to get all documents at once: +/// +/// @TINYEXAMPLE{simple-query-closed-range-to-array,convert into a list} +//////////////////////////////////////////////////////////////////////////////// + +ArangoCollection.prototype.closedRange = function (name, left, right) { + return new SimpleQueryRange(this, name, left, right, 1); +} + +ArangoEdgesCollection.prototype.closedRange = ArangoCollection.prototype.closedRange; + +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + +// ----------------------------------------------------------------------------- +// --SECTION-- private functions +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup SimpleQuery +/// @{ +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @brief clones a range query +//////////////////////////////////////////////////////////////////////////////// + +SimpleQueryRange.prototype.clone = function () { + var query; + + query = new SimpleQueryRange(this._collection, this._attribute, this._left, this._right, this._type); + query._skip = this._skip; + query._limit = this._limit; + + return query; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief prints a range query +//////////////////////////////////////////////////////////////////////////////// + +SimpleQueryRange.prototype._PRINT = function () { + var text; + + text = "SimpleQueryRange(" + this._collection.name() + ")"; + + if (this._skip != null && this._skip != 0) { + text += ".skip(" + this._skip + ")"; + } + + if (this._limit != null) { + text += ".limit(" + this._limit + ")"; + } + + internal.output(text); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + // ----------------------------------------------------------------------------- // --SECTION-- SIMPLE QUERY GEO // ----------------------------------------------------------------------------- @@ -1075,6 +1003,98 @@ function SimpleQueryGeo (collection, index) { this._index = index; } +//////////////////////////////////////////////////////////////////////////////// +/// @brief constructs a geo index selection +/// +/// @FUN{@FA{collection}.geo(@FA{location})} +//////////////////////////////////////////// +/// +/// The next @FN{near} or @FN{within} operator will use the specific geo-spatial +/// index. +/// +/// @FUN{@FA{collection}.geo(@FA{location}, @LIT{true})} +//////////////////////////////////////////////////////// +/// +/// The next @FN{near} or @FN{within} operator will use the specific geo-spatial +/// index. +/// +/// @FUN{@FA{collection}.geo(@FA{latitude}, @FA{longitude})} +//////////////////////////////////////////////////////////// +/// +/// The next @FN{near} or @FN{within} operator will use the specific geo-spatial +/// index. +/// +/// @EXAMPLES +/// +/// Assume you have a location stored as list in the attribute @LIT{home} +/// and a destination stored in the attribute @LIT{work}. Than you can use the +/// @FN{geo} operator to select, which coordinates to use in a near query. +/// +/// @TINYEXAMPLE{simple-query-geo,use a specific index} +//////////////////////////////////////////////////////////////////////////////// + +ArangoCollection.prototype.geo = function(loc, order) { + var idx; + + var locateGeoIndex1 = function(collection, loc, order) { + var inds = collection.getIndexes(); + + for (var i = 0; i < inds.length; ++i) { + var index = inds[i]; + + if (index.type == "geo1") { + if (index.fields[0] == loc && index.geoJson == order) { + return index; + } + } + } + + return null; + }; + + var locateGeoIndex2 = function(collection, lat, lon) { + var inds = collection.getIndexes(); + + for (var i = 0; i < inds.length; ++i) { + var index = inds[i]; + + if (index.type == "geo2") { + if (index.fields[0] == lat && index.fields[1] == lon) { + return index; + } + } + } + + return null; + }; + + if (order === undefined) { + if (typeof loc === "object") { + idx = this.index(loc); + } + else { + idx = locateGeoIndex1(this, loc, false); + } + } + else if (typeof order === "boolean") { + idx = locateGeoIndex1(this, loc, order); + } + else { + idx = locateGeoIndex2(this, loc, order); + } + + if (idx == null) { + var err = new ArangoError(); + err.errorNum = internal.errors.ERROR_QUERY_GEO_INDEX_MISSING.code; + err.errorMessage = internal.errors.ERROR_QUERY_GEO_INDEX_MISSING.message; + throw err; + } + + return new SimpleQueryGeo(this, idx.id); +} + +ArangoEdgesCollection.prototype.geo = ArangoCollection.geo; + //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// @@ -1089,7 +1109,7 @@ function SimpleQueryGeo (collection, index) { //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// -/// @brief print a geo index +/// @brief prints a geo index //////////////////////////////////////////////////////////////////////////////// SimpleQueryGeo.prototype._PRINT = function () { @@ -1191,6 +1211,65 @@ function SimpleQueryNear (collection, latitude, longitude, iid) { SimpleQueryNear.prototype = new SimpleQuery(); SimpleQueryNear.prototype.constructor = SimpleQueryNear; +//////////////////////////////////////////////////////////////////////////////// +/// @brief constructs a near query for a collection +/// +/// @FUN{@FA{collection}.near(@FA{latitude}, @FA{longitude})} +///////////////////////////////////////////////////////////// +/// +/// The default will find at most 100 documents near the coordinate +/// (@FA{latitude}, @FA{longitude}). The returned list is sorted according to +/// the distance, with the nearest document coming first. If there are near +/// documents of equal distance, documents are chosen randomly from this set +/// until the limit is reached. It is possible to change the limit using the +/// @FA{limit} operator. +/// +/// In order to use the @FN{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 +/// the @FN{geo} operator to select a particular index. +/// +/// @note @FN{near} does not support negative skips. However, you can still use +/// @FN{limit} followed to @FN{skip}. +/// +/// @FUN{@FA{collection}.near(@FA{latitude}, @FA{longitude}).limit(@FA{limit})} +/////////////////////////////////////////////////////////////////////////////// +/// +/// Limits the result to @FA{limit} documents instead of the default 100. +/// +/// @note Unlike with multiple explicit limits, @FA{limit} will raise +/// the implicit default limit imposed by @FN{within}. +/// +/// @FUN{@FA{collection}.near(@FA{latitude}, @FA{longitude}).distance()} +//////////////////////////////////////////////////////////////////////// +/// +/// This will add an attribute @LIT{distance} to all documents returned, which +/// contains the distance between the given point and the document in meter. +/// +/// @FUN{@FA{collection}.near(@FA{latitude}, @FA{longitude}).distance(@FA{name})} +///////////////////////////////////////////////////////////////////////////////// +/// +/// This will add an attribute @FA{name} to all documents returned, which +/// contains the distance between the given point and the document in meter. +/// +/// @EXAMPLES +/// +/// To get the nearst two locations: +/// +/// @TINYEXAMPLE{simple-query-near,nearest two location} +/// +/// If you need the distance as well, then you can use the @FN{distance} +/// operator: +/// +/// @TINYEXAMPLE{simple-query-near2,nearest two location with distance in meter} +//////////////////////////////////////////////////////////////////////////////// + +ArangoCollection.prototype.near = function (lat, lon) { + return new SimpleQueryNear(this, lat, lon); +} + +ArangoEdgesCollection.prototype.near = ArangoCollection.prototype.near; + //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// @@ -1220,7 +1299,7 @@ SimpleQueryNear.prototype.clone = function () { } //////////////////////////////////////////////////////////////////////////////// -/// @brief print a near query +/// @brief prints a near query //////////////////////////////////////////////////////////////////////////////// SimpleQueryNear.prototype._PRINT = function () { @@ -1338,6 +1417,45 @@ function SimpleQueryWithin (collection, latitude, longitude, radius, iid) { SimpleQueryWithin.prototype = new SimpleQuery(); SimpleQueryWithin.prototype.constructor = SimpleQueryWithin; +//////////////////////////////////////////////////////////////////////////////// +/// @brief constructs a within query for a collection +/// +/// @FUN{@FA{collection}.within(@FA{latitude}, @FA{longitude}, @FA{radius})} +//////////////////////////////////////////////////////////////////////////// +/// +/// This will find all documents with in a given radius around the coordinate +/// (@FA{latitude}, @FA{longitude}). The returned list is sorted by distance. +/// +/// In order to use the @FN{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, you can use +/// the @FN{geo} operator to select a particular index. +/// +/// @FUN{@FA{collection}.within(@FA{latitude}, @FA{longitude}, @FA{radius})@LATEXBREAK.distance()} +////////////////////////////////////////////////////////////////////////////////////////////////// +/// +/// This will add an attribute @LIT{_distance} to all documents returned, which +/// contains the distance between the given point and the document in meter. +/// +/// @FUN{@FA{collection}.within(@FA{latitude}, @FA{longitude}, @FA{radius})@LATEXBREAK.distance(@FA{name})} +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +/// +/// This will add an attribute @FA{name} to all documents returned, which +/// contains the distance between the given point and the document in meter. +/// +/// @EXAMPLES +/// +/// To find all documents within a radius of 2000 km use: +/// +/// @TINYEXAMPLE{simple-query-within,within a radius} +//////////////////////////////////////////////////////////////////////////////// + +ArangoCollection.prototype.within = function (lat, lon, radius) { + return new SimpleQueryWithin(this, lat, lon, radius); +} + +ArangoEdgesCollection.prototype.within = ArangoCollection.prototype.within; + //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// @@ -1367,7 +1485,7 @@ SimpleQueryWithin.prototype.clone = function () { } //////////////////////////////////////////////////////////////////////////////// -/// @brief print a within query +/// @brief prints a within query //////////////////////////////////////////////////////////////////////////////// SimpleQueryWithin.prototype._PRINT = function () { @@ -1445,6 +1563,7 @@ exports.GeneralArrayCursor = GeneralArrayCursor; exports.SimpleQueryAll = SimpleQueryAll; exports.SimpleQueryArray = SimpleQueryArray; exports.SimpleQueryByExample = SimpleQueryByExample; +exports.SimpleQueryRange = SimpleQueryRange; exports.SimpleQueryGeo = SimpleQueryGeo; exports.SimpleQueryNear = SimpleQueryNear; exports.SimpleQueryWithin = SimpleQueryWithin; diff --git a/js/common/tests/shell-simple-query.js b/js/common/tests/shell-simple-query.js index ee17dc5128..b74392a035 100644 --- a/js/common/tests/shell-simple-query.js +++ b/js/common/tests/shell-simple-query.js @@ -291,7 +291,7 @@ function SimpleQueryAllSkipLimitSuite () { // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// -/// @brief test suite: skip and limit with a collection +/// @brief test suite: query-by-example //////////////////////////////////////////////////////////////////////////////// function SimpleQueryByExampleSuite () { @@ -392,6 +392,110 @@ function SimpleQueryByExampleSuite () { }; } +// ----------------------------------------------------------------------------- +// --SECTION-- basic tests for range +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test suite: range +//////////////////////////////////////////////////////////////////////////////// + +function SimpleQueryRangeSuite () { + var cn = "UnitTestsCollectionRange"; + var collection = null; + var age = function(d) { return d.age; }; + + return { + +//////////////////////////////////////////////////////////////////////////////// +/// @brief set up +//////////////////////////////////////////////////////////////////////////////// + + setUp : function () { + internal.db._drop(cn); + collection = internal.db._create(cn, { waitForSync : false }); + + for (var i = 0; i < 100; ++i) { + collection.save({ age : i }); + } + + collection.ensureSkiplist("age"); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief tear down +//////////////////////////////////////////////////////////////////////////////// + + tearDown : function () { + collection.drop(); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test: range +//////////////////////////////////////////////////////////////////////////////// + + testRange : function () { + var l = collection.range("age", 10, 13).toArray().map(age).sort(); + assertEqual([10,11,12], l); + + l = collection.closedRange("age", 10, 13).toArray().map(age).sort(); + assertEqual([10,11,12,13], l); + } + }; +} + +// ----------------------------------------------------------------------------- +// --SECTION-- basic tests for unique range +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test suite: range +//////////////////////////////////////////////////////////////////////////////// + +function SimpleQueryUniqueRangeSuite () { + var cn = "UnitTestsCollectionRange"; + var collection = null; + var age = function(d) { return d.age; }; + + return { + +//////////////////////////////////////////////////////////////////////////////// +/// @brief set up +//////////////////////////////////////////////////////////////////////////////// + + setUp : function () { + internal.db._drop(cn); + collection = internal.db._create(cn, { waitForSync : false }); + + for (var i = 0; i < 100; ++i) { + collection.save({ age : i }); + } + + collection.ensureUniqueSkiplist("age"); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief tear down +//////////////////////////////////////////////////////////////////////////////// + + tearDown : function () { + collection.drop(); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test: range +//////////////////////////////////////////////////////////////////////////////// + + testRange : function () { + var l = collection.range("age", 10, 13).toArray().map(age).sort(); + assertEqual([10,11,12], l); + + l = collection.closedRange("age", 10, 13).toArray().map(age).sort(); + assertEqual([10,11,12,13], l); + } + }; +} + // ----------------------------------------------------------------------------- // --SECTION-- main // ----------------------------------------------------------------------------- @@ -403,6 +507,8 @@ function SimpleQueryByExampleSuite () { jsunity.run(SimpleQueryArraySkipLimitSuite); jsunity.run(SimpleQueryAllSkipLimitSuite); jsunity.run(SimpleQueryByExampleSuite); +jsunity.run(SimpleQueryRangeSuite); +jsunity.run(SimpleQueryUniqueRangeSuite); return jsunity.done(); diff --git a/js/server/js-server.h b/js/server/js-server.h index 5bcc7525d4..d7ebf374d4 100644 --- a/js/server/js-server.h +++ b/js/server/js-server.h @@ -564,83 +564,6 @@ static string JS_server_server = "/// @}\n" "////////////////////////////////////////////////////////////////////////////////\n" "\n" - "// -----------------------------------------------------------------------------\n" - "// --SECTION-- skip-list helper\n" - "// -----------------------------------------------------------------------------\n" - "\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "/// @addtogroup V8Shell\n" - "/// @{\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "/// @brief equal\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "\n" - "EQ = ModuleCache[\"/internal\"].exports.EQ = function (value) {\n" - " return [ [ \"==\", value ] ];\n" - "}\n" - "\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "/// @brief less than\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "\n" - "LT = ModuleCache[\"/internal\"].exports.LT = function (value) {\n" - " return [ [ \"<\", value ] ];\n" - "}\n" - "\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "/// @brief less than or equal\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "\n" - "LE = ModuleCache[\"/internal\"].exports.LE = function (value) {\n" - " return [ [ \"<=\", value ] ];\n" - "}\n" - "\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "/// @brief greater than\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "\n" - "GT = ModuleCache[\"/internal\"].exports.GT = function (value) {\n" - " return [ [ \">\", value ] ];\n" - "}\n" - "\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "/// @brief greater than or equal\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "\n" - "GE = ModuleCache[\"/internal\"].exports.GE = function (value) {\n" - " return [ [ \">=\", value ] ];\n" - "}\n" - "\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "/// @brief range (left closed, right open)\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "\n" - "RANGE = ModuleCache[\"/internal\"].exports.RANGE = function (left, right) {\n" - " return [ [ \">=\", left ], [ \"<\", right ] ];\n" - "}\n" - "\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "/// @brief open range\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "\n" - "ORANGE = ModuleCache[\"/internal\"].exports.ORANGE = function (left, right) {\n" - " return [ [ \">\", left ], [ \"<\", right ] ];\n" - "}\n" - "\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "/// @brief closed range\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "\n" - "CRANGE = ModuleCache[\"/internal\"].exports.CRANGE = function (left, right) {\n" - " return [ [ \">=\", left ], [ \"<=\", right ] ];\n" - "}\n" - "\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "/// @}\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "\n" "// Local Variables:\n" "// mode: outline-minor\n" "// outline-regexp: \"^\\\\(/// @brief\\\\|/// @addtogroup\\\\|// --SECTION--\\\\|/// @page\\\\|/// @}\\\\)\"\n" diff --git a/js/server/modules/simple-query.js b/js/server/modules/simple-query.js index 9d26d38e7c..09aa699a2d 100644 --- a/js/server/modules/simple-query.js +++ b/js/server/modules/simple-query.js @@ -120,11 +120,11 @@ function ByExample (collection, example, skip, limit) { idx = collection.lookupUniqueConstraint.apply(collection, attributes); if (idx != null) { - console.info("found unique constraint %s", idx.id); + console.debug("found unique constraint %s", idx.id); } } else { - console.info("found hash index %s", idx.id); + console.debug("found hash index %s", idx.id); } if (idx != null) { @@ -177,7 +177,7 @@ SQ.SimpleQueryByExample.prototype.execute = function () { /// /// Returns the a document of a collection that match the specified example or /// @LIT{null}. The example must be specified as paths and values. See @ref -/// JSF_ArangoCollection_prototype_byExample for details. +/// @FN{byExample} for details. /// /// @FUN{@FA{collection}.firstExample(@FA{path1}, @FA{value1}, ...)} /// @@ -221,6 +221,87 @@ ArangoEdgesCollection.prototype.firstExample = ArangoCollection.prototype.firstE /// @} //////////////////////////////////////////////////////////////////////////////// +// ----------------------------------------------------------------------------- +// --SECTION-- RANGED QUERY +// ----------------------------------------------------------------------------- + +// ----------------------------------------------------------------------------- +// --SECTION-- private functions +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup SimpleQuery +/// @{ +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @brief ranged query +//////////////////////////////////////////////////////////////////////////////// + +function RangedQuery (collection, attribute, left, right, type, skip, limit) { + var idx = collection.lookupSkiplist(attribute); + + if (idx == null) { + idx = collection.lookupUniqueSkiplist(attribute); + + if (idx != null) { + console.debug("found unique skip-list index %s", idx.id); + } + } + else { + console.debug("found skip-list index %s", idx.id); + } + + if (idx != null) { + var cond = {}; + + if (type == 0) { + cond[attribute] = [ [ ">=", left ], [ "<", right ] ]; + } + else if (type == 1) { + cond[attribute] = [ [ ">=", left ], [ "<=", right ] ]; + } + else { + throw "unknown type"; + } + + return collection.BY_CONDITION_SKIPLIST(idx.id, cond, skip, limit); + } + else { + throw "not implemented"; + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief executes a query-by-example +//////////////////////////////////////////////////////////////////////////////// + +SQ.SimpleQueryRange.prototype.execute = function () { + var documents; + + if (this._execution == null) { + if (this._skip == null || this._skip <= 0) { + this._skip = 0; + } + + var documents = RangedQuery(this._collection, + this._attribute, + this._left, + this._right, + this._type, + this._skip, + this._limit); + + this._execution = new SQ.GeneralArrayCursor(documents.documents); + this._countQuery = documents.count; + this._countTotal = documents.total; + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + // ----------------------------------------------------------------------------- // --SECTION-- SIMPLE QUERY NEAR // ----------------------------------------------------------------------------- diff --git a/js/server/server.js b/js/server/server.js index 03a580e2de..23887e0583 100644 --- a/js/server/server.js +++ b/js/server/server.js @@ -563,83 +563,6 @@ ArangoCollection.prototype.toString = function(seen, path, names, level) { /// @} //////////////////////////////////////////////////////////////////////////////// -// ----------------------------------------------------------------------------- -// --SECTION-- skip-list helper -// ----------------------------------------------------------------------------- - -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup V8Shell -/// @{ -//////////////////////////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////////////////////////// -/// @brief equal -//////////////////////////////////////////////////////////////////////////////// - -EQ = ModuleCache["/internal"].exports.EQ = function (value) { - return [ [ "==", value ] ]; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief less than -//////////////////////////////////////////////////////////////////////////////// - -LT = ModuleCache["/internal"].exports.LT = function (value) { - return [ [ "<", value ] ]; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief less than or equal -//////////////////////////////////////////////////////////////////////////////// - -LE = ModuleCache["/internal"].exports.LE = function (value) { - return [ [ "<=", value ] ]; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief greater than -//////////////////////////////////////////////////////////////////////////////// - -GT = ModuleCache["/internal"].exports.GT = function (value) { - return [ [ ">", value ] ]; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief greater than or equal -//////////////////////////////////////////////////////////////////////////////// - -GE = ModuleCache["/internal"].exports.GE = function (value) { - return [ [ ">=", value ] ]; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief range (left closed, right open) -//////////////////////////////////////////////////////////////////////////////// - -RANGE = ModuleCache["/internal"].exports.RANGE = function (left, right) { - return [ [ ">=", left ], [ "<", right ] ]; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief open range -//////////////////////////////////////////////////////////////////////////////// - -ORANGE = ModuleCache["/internal"].exports.ORANGE = function (left, right) { - return [ [ ">", left ], [ "<", right ] ]; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief closed range -//////////////////////////////////////////////////////////////////////////////// - -CRANGE = ModuleCache["/internal"].exports.CRANGE = function (left, right) { - return [ [ ">=", left ], [ "<=", right ] ]; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// - // Local Variables: // mode: outline-minor // outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)"