1
0
Fork 0

first test cases for fulltext index

This commit is contained in:
Jan Steemann 2012-12-03 23:55:46 +01:00
parent bd4a56c269
commit efde7b5efa
6 changed files with 367 additions and 58 deletions

View File

@ -0,0 +1,17 @@
> curl --data @- -X POST --dump - http://localhost:8529/_api/index?collection=109061392
{ "type" : "fulltext", "indexSubstrings" : false, "fields" : [ "text" ] }
HTTP/1.1 201 Created
content-type: application/json
{
"code": 201,
"fields": [
"text"
],
"id": "109061392/1748352392",
"type": "fulltext",
"isNewlyCreated": true,
"indexSubstrings": false,
"error": false
}

View File

@ -212,7 +212,8 @@ SHELL_COMMON = @top_srcdir@/js/common/tests/shell-document.js \
@top_srcdir@/js/common/tests/shell-index-geo.js \
@top_srcdir@/js/common/tests/shell-cap-constraint.js \
@top_srcdir@/js/common/tests/shell-unique-constraint.js \
@top_srcdir@/js/common/tests/shell-hash-index.js
@top_srcdir@/js/common/tests/shell-hash-index.js \
@top_srcdir@/js/common/tests/shell-fulltext.js
SHELL_SERVER = $(SHELL_COMMON) \
@top_srcdir@/js/common/tests/shell-graph.js \

View File

@ -4079,12 +4079,16 @@ static v8::Handle<v8::Value> JS_LookupSkiplistVocbaseCol (v8::Arguments const& a
////////////////////////////////////////////////////////////////////////////////
/// @brief ensures that a fulltext index exists
///
/// @FUN{ensureFulltextIndex(@FA{field}}
/// @FUN{ensureFulltextIndex(@FA{field}, @FA{indexSubstrings}}
///
/// Creates a fulltext index on all documents on attribute @FA{field}.
/// All documents, which do not have the attribute or with a non-textual value
/// inside the attribute are ignored.
///
/// IF @FA{indexSubstrings} is set to @LIT{true}, then substrings are also
/// indexed. This allows search substring search queries, but will make the
/// index consume more memory.
///
/// In case that the index was successfully created, the index identifier
/// is returned.
///

View File

@ -55,7 +55,7 @@ var API = "_api/index";
///
/// @RESTHEADER{GET /_api/index,reads all indexes of a collection}
///
/// @REST{GET /_api/index?collection=@FA{collection-identifier}}
/// @REST{GET /_api/index?collection=@FA{collection-name}}
///
/// Returns an object with an attribute @LIT{indexes} containing a list of all
/// index descriptions for the given collection. The same information is also
@ -104,7 +104,7 @@ function GET_api_indexes (req, res) {
/// The result is an objects describing the index. It has at least the following
/// attributes:
///
/// - @LIT{id}: The identifier of the collection.
/// - @LIT{id}: The identifier of the index.
///
/// - @LIT{type}: The type of the collection.
///
@ -118,7 +118,7 @@ function GET_api_indexes (req, res) {
function GET_api_index (req, res) {
// .............................................................................
// /_api/index?collection=<collection-identifier>
// /_api/index?collection=<collection-name>
// .............................................................................
if (req.suffix.length === 0) {
@ -126,7 +126,7 @@ function GET_api_index (req, res) {
}
// .............................................................................
// /_api/index/<collection-identifier>/<index-identifier>
// /_api/index/<collection-name>/<index-identifier>
// .............................................................................
else if (req.suffix.length === 2) {
@ -159,21 +159,20 @@ function GET_api_index (req, res) {
///
/// @RESTHEADER{POST /_api/index,creates a cap constraint}
///
/// @REST{POST /_api/index?collection=@FA{collection-identifier}}
/// @REST{POST /_api/index?collection=@FA{collection-name}}
///
/// Creates a cap constraint for the collection @FA{collection-identifier}, if
/// Creates a cap constraint for the collection @FA{collection-name}, if
/// it does not already exist. Expects an object containing the index details.
///
/// - @LIT{type}: must be equal to @LIT{"cap"}.
///
/// - @LIT{size}: The maximal size of documents.
///
/// If the index does not already exists and could be created, then a @LIT{HTTP
/// If the index does not already exist 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 @FA{collection-name} is unknown, then a @LIT{HTTP 404} is returned.
///
/// @EXAMPLES
///
@ -204,9 +203,9 @@ function POST_api_index_cap (req, res, collection, body) {
///
/// @RESTHEADER{GET /_api/index,creates a geo-spatial index}
///
/// @REST{POST /_api/index?collection=@FA{collection-identifier}}
/// @REST{POST /_api/index?collection=@FA{collection-name}}
///
/// Creates a geo-spatial index in the collection @FA{collection-identifier}, if
/// Creates a geo-spatial index in the collection @FA{collection-name}, if
/// it does not already exist. Expects an object containing the index details.
///
/// - @LIT{type}: must be equal to @LIT{"geo"}.
@ -240,12 +239,11 @@ function POST_api_index_cap (req, res, collection, body) {
/// @FA{ignoreNull} is true, then documents with a null in @FA{location} or at
/// least one null in @FA{latitude} or @FA{longitude} are ignored.
///
/// If the index does not already exists and could be created, then a @LIT{HTTP
/// If the index does not already exist 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 @FA{collection-name} is unknown, then a @LIT{HTTP 404} is returned.
///
/// @EXAMPLES
///
@ -336,9 +334,9 @@ function POST_api_index_geo (req, res, collection, body) {
///
/// @RESTHEADER{POST /_api/index,creates a hash index}
///
/// @REST{POST /_api/index?collection=@FA{collection-identifier}}
/// @REST{POST /_api/index?collection=@FA{collection-name}}
///
/// Creates a hash index for the collection @FA{collection-identifier}, if it
/// Creates a hash index for the collection @FA{collection-name}, if it
/// does not already exist. The call expects an object containing the index
/// details.
///
@ -348,12 +346,11 @@ function POST_api_index_geo (req, res, collection, body) {
///
/// - @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
/// If the index does not already exist 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 @FA{collection-name} is unknown, then a @LIT{HTTP 404} is returned.
///
/// If the collection already contains documents and you try to create a unique
/// hash index in such a way that there are documents violating the uniqueness,
@ -400,9 +397,9 @@ function POST_api_index_hash (req, res, collection, body) {
///
/// @RESTHEADER{POST /_api/index,creates a hash index}
///
/// @REST{POST /_api/index?collection=@FA{collection-identifier}}
/// @REST{POST /_api/index?collection=@FA{collection-name}}
///
/// Creates a skip-list index for the collection @FA{collection-identifier}, if
/// Creates a skip-list index for the collection @FA{collection-name}, if
/// it does not already exist. The call expects an object containing the index
/// details.
///
@ -412,12 +409,11 @@ function POST_api_index_hash (req, res, collection, body) {
///
/// - @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
/// If the index does not already exist 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 @FA{collection-name} is unknown, then a @LIT{HTTP 404} is returned.
///
/// 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
@ -455,14 +451,70 @@ function POST_api_index_skiplist (req, res, collection, body) {
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief creates a fulltext index
///
/// @RESTHEADER{POST /_api/index,creates a fulltext index}
///
/// @REST{POST /_api/index?collection=@FA{collection-name}}
///
/// Creates a fulltext index for the collection @FA{collection-name}, if
/// it does not already exist. The call expects an object containing the index
/// details.
///
/// - @LIT{type}: must be equal to @LIT{"fulltext"}.
///
/// - @LIT{fields}: A list of attribute names. Currently, the list is limited
/// to exactly one attribute.
///
/// - @LIT{indexSubstrings}: If @LIT{true}, then substrings are also indexes.
/// This will enable substring searching via the index but will make the index
/// consume more memory.
///
/// If the index does not already exist 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-name} is unknown, then a @LIT{HTTP 404} is returned.
///
/// @EXAMPLES
///
/// Creating a fulltext index:
///
/// @verbinclude api-index-create-new-fulltext
////////////////////////////////////////////////////////////////////////////////
function POST_api_index_fulltext (req, res, collection, body) {
var fields = body.fields;
if (! (fields instanceof Array)) {
actions.resultBad(req, res, actions.ERROR_HTTP_BAD_PARAMETER,
"fields must be a list of attribute names: " + fields);
}
if (fields.length != 1 || typeof fields[0] !== 'string') {
actions.resultBad(req, res, actions.ERROR_HTTP_BAD_PARAMETER,
"fields must contain exactly one attribute name");
}
var index = collection.ensureFulltextIndex.call(collection, fields, body.indexSubstrings || false);
if (index.isNewlyCreated) {
actions.resultOk(req, res, actions.HTTP_CREATED, index);
}
else {
actions.resultOk(req, res, actions.HTTP_OK, index);
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief creates a bitarray
///
/// @RESTHEADER{POST /_api/index,creates a bitarray index}
///
/// @REST{POST /_api/index?collection=@FA{collection-identifier}}
/// @REST{POST /_api/index?collection=@FA{collection-name}}
///
/// Creates a bitarray index for the collection @FA{collection-identifier}, if
/// Creates a bitarray index for the collection @FA{collection-name}, if
/// it does not already exist. The call expects an object containing the index
/// details.
///
@ -472,12 +524,11 @@ function POST_api_index_skiplist (req, res, collection, body) {
///
/// - @LIT{unique}: Must always be set to @LIT{false}.
///
/// If the index does not already exists and could be created, then a @LIT{HTTP
/// If the index does not already exist 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 @FA{collection-name} is unknown, then a @LIT{HTTP 404} is returned.
///
/// @EXAMPLES
///
@ -516,9 +567,9 @@ function POST_api_index_bitarray (req, res, collection, body) {
///
/// @RESTHEADER{POST /_api/index,creates an index}
///
/// @REST{POST /_api/index?collection=@FA{collection-identifier}}
/// @REST{POST /_api/index?collection=@FA{collection-name}}
///
/// Creates a new index in the collection @FA{collection-identifier}. Expects
/// Creates a new index in the collection @FA{collection-name}. Expects
/// an object containing the index details.
///
/// See @ref IndexCapHttp, @ref IndexGeoHttp, @ref IndexHashHttp, and
@ -526,16 +577,15 @@ function POST_api_index_bitarray (req, res, collection, body) {
/// be created. To change this, use the @LIT{unique} attribute in the index details
/// and set its value to @LIT{true}.
///
/// If the index does not already exists and could be created, then a @LIT{HTTP
/// If the index does not already exist 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 @FA{collection-name} is unknown, then a @LIT{HTTP 404} is returned.
///
/// @EXAMPLES
///
/// Creating an unique constraint:
/// Creating a unique constraint:
///
/// @verbinclude api-index-create-new-unique-constraint
///
@ -550,13 +600,17 @@ function POST_api_index_bitarray (req, res, collection, body) {
/// Creating a unique skip-list:
///
/// @verbinclude api-index-create-new-unique-skiplist
///
/// Creating a fulltext index:
///
/// @verbinclude api-index-create-new-fulltext
////////////////////////////////////////////////////////////////////////////////
function POST_api_index (req, res) {
if (req.suffix.length !== 0) {
actions.resultBad(req, res, actions.ERROR_HTTP_BAD_PARAMETER,
"expect POST /" + API + "?collection=<collection-identifer>");
"expect POST /" + API + "?collection=<collection-name>");
return;
}
@ -586,6 +640,9 @@ function POST_api_index (req, res) {
else if (body.type === "skiplist") {
POST_api_index_skiplist(req, res, collection, body);
}
else if (body.type === "fulltext") {
POST_api_index_fulltext(req, res, collection, body);
}
else if (body.type === "bitarray") {
POST_api_index_bitarray(req, res, collection, body);
}

View File

@ -1265,8 +1265,7 @@ function ArangoCollection (database, data) {
body = { type : "bitarray", unique : false, fields : fields };
var requestResult = this._database._connection.POST(this._indexurl(),
JSON.stringify(body));
var requestResult = this._database._connection.POST(this._indexurl(), JSON.stringify(body));
client.checkRequestResult(requestResult);
@ -1283,8 +1282,7 @@ function ArangoCollection (database, data) {
body = { type : "cap", size : size };
var requestResult = this._database._connection.POST(this._indexurl(),
JSON.stringify(body));
var requestResult = this._database._connection.POST(this._indexurl(), JSON.stringify(body));
client.checkRequestResult(requestResult);
@ -1306,8 +1304,7 @@ function ArangoCollection (database, data) {
body = { type : "skiplist", unique : true, fields : fields };
var requestResult = this._database._connection.POST(this._indexurl(),
JSON.stringify(body));
var requestResult = this._database._connection.POST(this._indexurl(), JSON.stringify(body));
client.checkRequestResult(requestResult);
@ -1329,8 +1326,24 @@ function ArangoCollection (database, data) {
body = { type : "skiplist", unique : false, fields : fields };
var requestResult = this._database._connection.POST(this._indexurl(),
JSON.stringify(body));
var requestResult = this._database._connection.POST(this._indexurl(), JSON.stringify(body));
client.checkRequestResult(requestResult);
return requestResult;
};
////////////////////////////////////////////////////////////////////////////////
/// @brief adds a fulltext index
////////////////////////////////////////////////////////////////////////////////
ArangoCollection.prototype.ensureFulltextIndex = function (attribute, indexSubstrings) {
var doIndexSubstrings = indexSubstrings || false;
var body;
body = { type : "fulltext", indexSubstrings : doIndexSubstrings, fields : [ attribute ] };
var requestResult = this._database._connection.POST(this._indexurl(), JSON.stringify(body));
client.checkRequestResult(requestResult);
@ -1352,8 +1365,7 @@ function ArangoCollection (database, data) {
body = { type : "hash", unique : true, fields : fields };
var requestResult = this._database._connection.POST(this._indexurl(),
JSON.stringify(body));
var requestResult = this._database._connection.POST(this._indexurl(), JSON.stringify(body));
client.checkRequestResult(requestResult);
@ -1375,8 +1387,7 @@ function ArangoCollection (database, data) {
body = { type : "hash", unique : false, fields : fields };
var requestResult = this._database._connection.POST(this._indexurl(),
JSON.stringify(body));
var requestResult = this._database._connection.POST(this._indexurl(), JSON.stringify(body));
client.checkRequestResult(requestResult);
@ -1418,8 +1429,7 @@ function ArangoCollection (database, data) {
};
}
var requestResult = this._database._connection.POST(this._indexurl(),
JSON.stringify(body));
var requestResult = this._database._connection.POST(this._indexurl(), JSON.stringify(body));
client.checkRequestResult(requestResult);
@ -1472,8 +1482,7 @@ function ArangoCollection (database, data) {
}
}
var requestResult = this._database._connection.POST(this._indexurl(),
JSON.stringify(body));
var requestResult = this._database._connection.POST(this._indexurl(), JSON.stringify(body));
client.checkRequestResult(requestResult);
@ -1560,8 +1569,7 @@ function ArangoCollection (database, data) {
ArangoCollection.prototype.rename = function (name) {
var body = { name : name };
var requestResult = this._database._connection.PUT(this._baseurl("rename"),
JSON.stringify(body));
var requestResult = this._database._connection.PUT(this._baseurl("rename"), JSON.stringify(body));
client.checkRequestResult(requestResult);
@ -1577,8 +1585,7 @@ function ArangoCollection (database, data) {
////////////////////////////////////////////////////////////////////////////////
ArangoCollection.prototype.refresh = function () {
var requestResult = this._database._connection.GET(
this._database._collectionurl(this._id) + "?useId=true");
var requestResult = this._database._connection.GET( this._database._collectionurl(this._id) + "?useId=true");
client.checkRequestResult(requestResult);

View File

@ -0,0 +1,223 @@
////////////////////////////////////////////////////////////////////////////////
/// @brief tests for fulltext indexes
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2010-2012 triagens GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is triAGENS GmbH, Cologne, Germany
///
/// @author Jan Steemann
/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////
var jsunity = require("jsunity");
var internal = require("internal");
////////////////////////////////////////////////////////////////////////////////
/// @brief test suite
////////////////////////////////////////////////////////////////////////////////
function fulltextTestSuite () {
var cn = "UnitTestsFulltext";
var c = null;
return {
////////////////////////////////////////////////////////////////////////////////
/// @brief set up
////////////////////////////////////////////////////////////////////////////////
setUp : function () {
internal.db._drop(cn);
c = internal.db._create(cn);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief tear down
////////////////////////////////////////////////////////////////////////////////
tearDown : function () {
internal.db._drop(cn);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief checks the index creation
////////////////////////////////////////////////////////////////////////////////
testCreateIndexSimple : function () {
var idx = c.ensureFulltextIndex("text");
var indexes = c.getIndexes();
for (var i = 0; i < indexes.length; ++i) {
var index = indexes[i];
if (index.type != "fulltext") {
continue;
}
assertEqual(idx.id, index.id);
assertEqual("fulltext", index.type);
assertEqual([ "text" ], index.fields);
assertEqual(false, index.indexSubstrings);
return;
}
fail();
},
////////////////////////////////////////////////////////////////////////////////
/// @brief checks the index creation
////////////////////////////////////////////////////////////////////////////////
testCreateIndexExisting : function () {
var idx = c.ensureFulltextIndex("textattr", false);
var indexes = c.getIndexes();
for (var i = 0; i < indexes.length; ++i) {
var index = indexes[i];
if (index.type != "fulltext") {
continue;
}
assertEqual(idx.id, index.id);
assertEqual("fulltext", index.type);
assertEqual([ "textattr" ], index.fields);
assertEqual(false, index.indexSubstrings);
assertEqual(idx.id, c.ensureFulltextIndex("textattr", false).id);
return;
}
fail();
},
////////////////////////////////////////////////////////////////////////////////
/// @brief checks the index creation
////////////////////////////////////////////////////////////////////////////////
testCreateIndexSubstrings : function () {
var idx = c.ensureFulltextIndex("iam-an-indexed-ATTRIBUTE", true);
var indexes = c.getIndexes();
for (var i = 0; i < indexes.length; ++i) {
var index = indexes[i];
if (index.type != "fulltext") {
continue;
}
assertEqual(idx.id, index.id);
assertEqual("fulltext", index.type);
assertEqual([ "iam-an-indexed-ATTRIBUTE" ], index.fields);
assertEqual(true, index.indexSubstrings);
return;
}
fail();
},
////////////////////////////////////////////////////////////////////////////////
/// @brief checks the index creation
////////////////////////////////////////////////////////////////////////////////
testCreateIndexSubstringsExsiting : function () {
var idx = c.ensureFulltextIndex("iam-an-indexed-ATTRIBUTE", true);
var indexes = c.getIndexes();
for (var i = 0; i < indexes.length; ++i) {
var index = indexes[i];
if (index.type != "fulltext") {
continue;
}
assertEqual(idx.id, index.id);
assertEqual("fulltext", index.type);
assertEqual([ "iam-an-indexed-ATTRIBUTE" ], index.fields);
assertEqual(true, index.indexSubstrings);
assertEqual(idx.id, c.ensureFulltextIndex("iam-an-indexed-ATTRIBUTE", true).id);
return;
}
fail();
},
////////////////////////////////////////////////////////////////////////////////
/// @brief checks the index creation
////////////////////////////////////////////////////////////////////////////////
testCreateMultipleIndexes : function () {
var idx1 = c.ensureFulltextIndex("attr1", false);
var idx2 = c.ensureFulltextIndex("attr1", true);
var idx3 = c.ensureFulltextIndex("attr2", true);
var indexes = c.getIndexes();
for (var i = 0; i < indexes.length; ++i) {
var index = indexes[i];
if (index.type != "fulltext") {
continue;
}
if (index.id == idx1.id) {
assertEqual("fulltext", index.type);
assertEqual([ "attr1" ], index.fields);
assertEqual(false, index.indexSubstrings);
}
else if (index.id == idx2.id) {
assertEqual("fulltext", index.type);
assertEqual([ "attr1" ], index.fields);
assertEqual(true, index.indexSubstrings);
}
else if (index.id == idx3.id) {
assertEqual("fulltext", index.type);
assertEqual([ "attr2" ], index.fields);
assertEqual(true, index.indexSubstrings);
}
else {
fail();
}
}
},
////////////////////////////////////////////////////////////////////////////////
/// @brief checks dropping indexes
////////////////////////////////////////////////////////////////////////////////
testDropIndexes : function () {
var idx1 = c.ensureFulltextIndex("attr1", false);
var idx2 = c.ensureFulltextIndex("attr1", true);
var idx3 = c.ensureFulltextIndex("attr2", true);
assertTrue(c.dropIndex(idx1));
assertTrue(c.dropIndex(idx2));
assertTrue(c.dropIndex(idx3));
},
};
}
////////////////////////////////////////////////////////////////////////////////
/// @brief executes the test suite
////////////////////////////////////////////////////////////////////////////////
jsunity.run(fulltextTestSuite);
return jsunity.done();
// Local Variables:
// mode: outline-minor
// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)"
// End: