1
0
Fork 0

added SKIPLIST AQL function

This commit is contained in:
Jan Steemann 2014-02-03 16:37:07 +01:00
parent dc1d28fe5d
commit fe9464afec
5 changed files with 193 additions and 0 deletions

View File

@ -146,6 +146,35 @@ v1.5.0 (XXXX-XX-XX)
v1.4.9 (XXXX-XX-XX)
-------------------
* added AQL function `SKIPLIST` to directly access skiplist indexes from AQL
This is a shortcut method to use a skiplist index for retrieving specific documents in
indexed order. The function capability is rather limited, but it may be used
for several cases to speed up queries. The documents are returned in index order if
only one condition is used.
/* return all documents with mycollection.created > 12345678 */
FOR doc IN SKIPLIST(mycollection, { created: [[ '>', 12345678 ]] })
RETURN doc
/* return first document with mycollection.created > 12345678 */
FOR doc IN SKIPLIST(mycollection, { created: [[ '>', 12345678 ]] }, 0, 1)
RETURN doc
/* return all documents with mycollection.created between 12345678 and 123456790 */
FOR doc IN SKIPLIST(mycollection, { created: [[ '>', 12345678 ], [ '<=', 123456790 ]] })
RETURN doc
/* return all documents with mycollection.a equal 1 and .b equal 2 */
FOR doc IN SKIPLIST(mycollection, { a: [[ '==', 1 ]], b: [[ '==', 2 ]] })
RETURN doc
The function requires a skiplist index with the exact same attributes to
be present on the specified colelction. All attributes present in the skiplist
index must be specified in the conditions specified for the `SKIPLIST` function.
Attribute declaration order is important, too: attributes must be specified in the
same order in the condition as they have been declared in the skiplist index.
* added command-line option `--server.disable-authentication-unix-sockets`
with this option, authentication can be disabled for all requests coming

View File

@ -1617,6 +1617,23 @@ function categories:
DOCUMENT("users/john")
DOCUMENT([ "users/john", "users/amy" ])
- @FN{SKIPLIST(@FA{collection}, @FA{condition}, @FA{skip}, @FA{limit})}: return all documents
from a skiplist index on collection @FA{collection} that match the specified @FA{condition}.
This is a shortcut method to use a skiplist index for retrieving specific documents in
indexed order. The skiplist index supports equality and less than/greater than queries. The
@FA{skip} and @FA{limit} parameters are optional but can be specified to further limit the
results:
SKIPLIST(test, { created: [[ '>', 0 ]] }, 0, 100)
SKIPLIST(test, { age: [[ '>', 25 ], [ '<=', 65 ]] })
SKIPLIST(test, { a: [[ '==', 10 ]], b: [[ '==', 25 ]] }
The @FA{condition} document must contain an entry for each attribute that is contained in the
index. It is not allowed to specify just a subset of attributes that are present in an index.
Additionally the attributes in the @FA{condition} document must be specified in the same order
as in the index.
If no suitable skiplist index is found, an error will be raised and the query will be aborted.
High-level operations {#AqlOperations}
======================================

View File

@ -713,6 +713,7 @@ TRI_associative_pointer_t* TRI_CreateFunctionsAql (void) {
REGISTER_FUNCTION("FIRST_LIST", "FIRST_LIST", true, false, ".|+", NULL);
REGISTER_FUNCTION("FIRST_DOCUMENT", "FIRST_DOCUMENT", true, false, ".|+", NULL);
REGISTER_FUNCTION("PARSE_IDENTIFIER", "PARSE_IDENTIFIER", true, false, ".", NULL);
REGISTER_FUNCTION("SKIPLIST", "SKIPLIST_QUERY", false, false, "h,a|n,n", NULL);
if (! result) {
TRI_FreeFunctionsAql(functions);

View File

@ -3246,6 +3246,51 @@ function PARSE_IDENTIFIER (value) {
THROW(INTERNAL.errors.ERROR_QUERY_FUNCTION_ARGUMENT_TYPE_MISMATCH, "PARSE_IDENTIFIER");
}
////////////////////////////////////////////////////////////////////////////////
/// @brief query a skiplist index
///
/// returns a documents from a skiplist index on the specified collection. Only
/// documents that match the specified condition will be returned.
////////////////////////////////////////////////////////////////////////////////
function SKIPLIST_QUERY (collection, condition, skip, limit) {
"use strict";
var keys = [ ], key, idx;
for (key in condition) {
if (condition.hasOwnProperty(key)) {
keys.push(key);
}
}
var c = COLLECTION(collection);
if (c === null) {
THROW(INTERNAL.errors.ERROR_ARANGO_COLLECTION_NOT_FOUND, collection);
}
idx = c.lookupSkiplist.apply(c, keys);
if (idx === null) {
THROW(INTERNAL.errors.ERROR_ARANGO_NO_INDEX);
}
if (skip === undefined || skip === null) {
skip = 0;
}
if (limit === undefined || limit === null) {
limit = null;
}
try {
return c.BY_CONDITION_SKIPLIST(idx.id, condition, skip, limit).documents;
}
catch (err) {
THROW(INTERNAL.errors.ERROR_ARANGO_NO_INDEX);
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief check whether a document has a specific attribute
////////////////////////////////////////////////////////////////////////////////
@ -4109,6 +4154,7 @@ exports.NOT_NULL = NOT_NULL;
exports.FIRST_LIST = FIRST_LIST;
exports.FIRST_DOCUMENT = FIRST_DOCUMENT;
exports.PARSE_IDENTIFIER = PARSE_IDENTIFIER;
exports.SKIPLIST_QUERY = SKIPLIST_QUERY;
exports.HAS = HAS;
exports.ATTRIBUTES = ATTRIBUTES;
exports.UNSET = UNSET;

View File

@ -2004,6 +2004,106 @@ function ahuacatlFunctionsTestSuite () {
internal.db._drop(cn);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test skiplist function
////////////////////////////////////////////////////////////////////////////////
testSkiplist1 : function () {
var cn = "UnitTestsAhuacatlFunctions";
internal.db._drop(cn);
var cx = internal.db._create(cn);
cx.ensureSkiplist("created");
var i;
for (i = 0; i < 1000; ++i) {
cx.save({ created: i });
}
expected = [ { created: 0 } ];
actual = getQueryResults("FOR x IN SKIPLIST(" + cn + ", { created: [[ '>=', 0 ]] }, 0, 1) RETURN x");
assertEqual(expected, actual);
actual = getQueryResults("FOR x IN SKIPLIST(" + cn + ", { created: [[ '>=', 0 ], [ '<', 1 ]] }) RETURN x");
assertEqual(expected, actual);
expected = [ { created: 1 } ];
actual = getQueryResults("FOR x IN SKIPLIST(" + cn + ", { created: [[ '>', 0 ]] }, 0, 1) RETURN x");
assertEqual(expected, actual);
actual = getQueryResults("FOR x IN SKIPLIST(" + cn + ", { created: [[ '>', 0 ], [ '<=', 1 ]] }) RETURN x");
assertEqual(expected, actual);
expected = [ { created: 0 }, { created: 1 }, { created: 2 } ];
actual = getQueryResults("FOR x IN SKIPLIST(" + cn + ", { created: [[ '>=', 0 ]] }, 0, 3) RETURN x");
assertEqual(expected, actual);
expected = [ { created: 5 }, { created: 6 } ];
actual = getQueryResults("FOR x IN SKIPLIST(" + cn + ", { created: [[ '>=', 0 ]] }, 5, 2) RETURN x");
assertEqual(expected, actual);
expected = [ { created: 5 }, { created: 6 } ];
actual = getQueryResults("FOR x IN SKIPLIST(" + cn + ", { created: [[ '>=', 5 ], [ '<=', 6 ]] }, 0, 5) RETURN x");
assertEqual(expected, actual);
expected = [ { created: 5 } ];
actual = getQueryResults("FOR x IN SKIPLIST(" + cn + ", { created: [[ '>=', 5 ], [ '<=', 6 ]] }, 0, 1) RETURN x");
assertEqual(expected, actual);
expected = [ { created: 2 }, { created: 3 } ];
actual = getQueryResults("FOR x IN SKIPLIST(" + cn + ", { created: [[ '<', 4 ]] }, 2, 10) RETURN x");
assertEqual(expected, actual);
expected = [ ];
actual = getQueryResults("FOR x IN SKIPLIST(" + cn + ", { created: [[ '>', 0 ]] }, 10000, 10) RETURN x");
assertEqual(expected, actual);
assertQueryError(errors.ERROR_ARANGO_NO_INDEX.code, "RETURN SKIPLIST(" + cn + ", { a: [[ '==', 1 ]] })");
internal.db._drop(cn);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test skiplist function
////////////////////////////////////////////////////////////////////////////////
testSkiplist2 : function () {
var cn = "UnitTestsAhuacatlFunctions";
internal.db._drop(cn);
var cx = internal.db._create(cn);
assertQueryError(errors.ERROR_ARANGO_NO_INDEX.code, "RETURN SKIPLIST(" + cn + ", { a: [[ '==', 1 ]], b: [[ '==', 0 ]] })");
cx.ensureSkiplist("a", "b");
var i;
for (i = 0; i < 1000; ++i) {
cx.save({ a: i, b: i + 1 });
}
expected = [ { a: 1, b: 2 } ];
actual = getQueryResults("FOR x IN SKIPLIST(" + cn + ", { a: [[ '==', 1 ]], b: [[ '>=', 2 ], [ '<=', 3 ]] }) RETURN x");
assertEqual(expected, actual);
expected = [ { a: 1, b: 2 } ];
actual = getQueryResults("FOR x IN SKIPLIST(" + cn + ", { a: [[ '==', 1 ]], b: [[ '==', 2 ]] }) RETURN x");
assertEqual(expected, actual);
assertQueryError(errors.ERROR_ARANGO_NO_INDEX.code, "RETURN SKIPLIST(" + cn + ", { b: [[ '==', 2 ]], a: [[ '==', 1 ]] })");
expected = [ ];
actual = getQueryResults("FOR x IN SKIPLIST(" + cn + ", { a: [[ '==', 2 ]], b: [[ '==', 1 ]] }, 1, 1) RETURN x");
assertEqual(expected, actual);
actual = getQueryResults("FOR x IN SKIPLIST(" + cn + ", { a: [[ '==', 99 ]], b: [[ '==', 17 ]] }, 1, 1) RETURN x");
assertEqual(expected, actual);
assertQueryError(errors.ERROR_ARANGO_NO_INDEX.code, "RETURN SKIPLIST(" + cn + ", { a: [[ '==', 1 ]] })");
assertQueryError(errors.ERROR_ARANGO_NO_INDEX.code, "RETURN SKIPLIST(" + cn + ", { b: [[ '==', 1 ]] })");
assertQueryError(errors.ERROR_ARANGO_NO_INDEX.code, "RETURN SKIPLIST(" + cn + ", { c: [[ '==', 1 ]] })");
internal.db._drop(cn);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test min function
////////////////////////////////////////////////////////////////////////////////