1
0
Fork 0

added select-by-example

This commit is contained in:
Frank Celler 2012-04-15 21:48:21 +02:00
parent 3c5ce283b2
commit 2d80ca4adc
4 changed files with 291 additions and 78 deletions

View File

@ -1469,7 +1469,7 @@ static v8::Handle<v8::Object> WrapWhere (TRI_qry_where_t* where) {
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief returns all elements
/// @brief selects all elements
////////////////////////////////////////////////////////////////////////////////
static v8::Handle<v8::Value> JS_AllQuery (v8::Arguments const& argv) {
@ -1610,6 +1610,121 @@ static v8::Handle<v8::Value> JS_AllQuery (v8::Arguments const& argv) {
return scope.Close(result);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief selects elements by example
////////////////////////////////////////////////////////////////////////////////
static v8::Handle<v8::Value> JS_ByExampleQuery (v8::Arguments const& argv) {
v8::HandleScope scope;
// extract the collection
v8::Handle<v8::Object> operand = argv.Holder();
v8::Handle<v8::Object> err;
TRI_vocbase_col_t const* collection = UseCollection(operand, &err);
if (collection == 0) {
return scope.Close(v8::ThrowException(err));
}
// handle various collection types
TRI_doc_collection_t* doc = collection->_collection;
if (doc->base._type != TRI_COL_TYPE_SIMPLE_DOCUMENT) {
ReleaseCollection(collection);
return scope.Close(v8::ThrowException(CreateErrorObject(TRI_ERROR_INTERNAL, "unknown collection type")));
}
TRI_sim_collection_t* sim = (TRI_sim_collection_t*) doc;
TRI_shaper_t* shaper = sim->base._shaper;
// extract example
if (argv.Length() == 0 || (argv.Length() % 2 == 1)) {
ReleaseCollection(collection);
return scope.Close(v8::ThrowException(
CreateErrorObject(TRI_ERROR_BAD_PARAMETER,
"usage: document(<path1>, <value1>, ...)")));
}
size_t n = argv.Length() / 2;
TRI_shape_pid_t* pids = (TRI_shape_pid_t*) TRI_Allocate(n * sizeof(TRI_shape_pid_t));
TRI_shaped_json_t** values = (TRI_shaped_json_t**) TRI_Allocate(n * sizeof(TRI_shaped_json_t*));
for (size_t i = 0; i < n; ++i) {
v8::Handle<v8::Value> key = argv[2 * i];
v8::Handle<v8::Value> val = argv[2 * i + 1];
v8::String::Utf8Value keyStr(key);
pids[i] = shaper->findAttributePathByName(shaper, *keyStr);
values[i] = TRI_ShapedJsonV8Object(val, shaper);
if (*keyStr == 0 || values[i] == 0) {
for (size_t j = 0; j < i; ++j) {
TRI_FreeShapedJson(values[i]);
}
TRI_Free(values);
TRI_Free(pids);
ReleaseCollection(collection);
if (*keyStr == 0) {
return scope.Close(v8::ThrowException(
CreateErrorObject(TRI_ERROR_BAD_PARAMETER,
"cannot convert attribute name to UTF8")));
}
else if (values[i] == 0) {
return scope.Close(v8::ThrowException(
CreateErrorObject(TRI_ERROR_BAD_PARAMETER,
"cannot convert value to JSON")));
}
assert(false);
}
}
// .............................................................................
// inside a read transaction
// .............................................................................
collection->_collection->beginRead(collection->_collection);
// find documents by example
TRI_vector_t filtered = TRI_SelectByExample(sim, n, pids, values);
// convert to list of shaped jsons
v8::Handle<v8::Array> result = v8::Array::New();
if (0 < filtered._length) {
TRI_barrier_t* barrier = TRI_CreateBarrierElement(&collection->_collection->_barrierList);
for (size_t j = 0; j < filtered._length; ++j) {
TRI_doc_mptr_t* mptr = (TRI_doc_mptr_t*) TRI_AtVector(&filtered, j);
v8::Handle<v8::Value> document = TRI_WrapShapedJson(collection, mptr, barrier);
result->Set(j, document);
}
}
collection->_collection->endRead(collection->_collection);
// .............................................................................
// outside a write transaction
// .............................................................................
ReleaseCollection(collection);
// free
for (size_t j = 0; j < n; ++j) {
TRI_FreeShapedJson(values[j]);
}
TRI_Free(values);
TRI_Free(pids);
return scope.Close(result);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief looks up all edges for a set of vertices
///
@ -5902,6 +6017,7 @@ void TRI_InitV8VocBridge (v8::Handle<v8::Context> context, TRI_vocbase_t* vocbas
// .............................................................................
v8::Handle<v8::String> AllFuncName = v8::Persistent<v8::String>::New(v8::String::New("ALL"));
v8::Handle<v8::String> ByExampleFuncName = v8::Persistent<v8::String>::New(v8::String::New("BY_EXAMPLE"));
v8::Handle<v8::String> NearFuncName = v8::Persistent<v8::String>::New(v8::String::New("NEAR"));
v8::Handle<v8::String> WithinFuncName = v8::Persistent<v8::String>::New(v8::String::New("WITHIN"));
@ -5913,12 +6029,6 @@ void TRI_InitV8VocBridge (v8::Handle<v8::Context> context, TRI_vocbase_t* vocbas
v8::Handle<v8::String> DropIndexFuncName = v8::Persistent<v8::String>::New(v8::String::New("dropIndex"));
v8::Handle<v8::String> EdgesFuncName = v8::Persistent<v8::String>::New(v8::String::New("edges"));
v8::Handle<v8::String> EnsureGeoIndexFuncName = v8::Persistent<v8::String>::New(v8::String::New("ensureGeoIndex"));
/*
oreste:
v8::Handle<v8::String> EnsureMultiHashIndexFuncName = v8::Persistent<v8::String>::New(v8::String::New("ensureMultiHashIndex"));
v8::Handle<v8::String> EnsureMultiSkiplistIndexFuncName = v8::Persistent<v8::String>::New(v8::String::New("ensureMultiSLIndex"));
v8::Handle<v8::String> EnsureSkiplistIndexFuncName = v8::Persistent<v8::String>::New(v8::String::New("ensureSLIndex"));
*/
v8::Handle<v8::String> EnsureHashIndexFuncName = v8::Persistent<v8::String>::New(v8::String::New("ensureHashIndex"));
v8::Handle<v8::String> EnsurePriorityQueueIndexFuncName = v8::Persistent<v8::String>::New(v8::String::New("ensurePQIndex"));
v8::Handle<v8::String> EnsureSkiplistFuncName = v8::Persistent<v8::String>::New(v8::String::New("ensureSkiplist"));
@ -6096,25 +6206,19 @@ void TRI_InitV8VocBridge (v8::Handle<v8::Context> context, TRI_vocbase_t* vocbas
v8g->VocbaseColTempl = v8::Persistent<v8::ObjectTemplate>::New(rt);
rt->Set(AllFuncName, v8::FunctionTemplate::New(JS_AllQuery));
rt->Set(ByExampleFuncName, v8::FunctionTemplate::New(JS_ByExampleQuery));
rt->Set(CountFuncName, v8::FunctionTemplate::New(JS_CountVocbaseCol));
rt->Set(DeleteFuncName, v8::FunctionTemplate::New(JS_DeleteVocbaseCol));
rt->Set(DocumentFuncName, v8::FunctionTemplate::New(JS_DocumentVocbaseCol));
rt->Set(DropFuncName, v8::FunctionTemplate::New(JS_DropVocbaseCol));
rt->Set(DropIndexFuncName, v8::FunctionTemplate::New(JS_DropIndexVocbaseCol));
rt->Set(EnsureGeoIndexFuncName, v8::FunctionTemplate::New(JS_EnsureGeoIndexVocbaseCol));
/* oreste:
rt->Set(EnsureMultiHashIndexFuncName, v8::FunctionTemplate::New(JS_EnsureMultiHashIndexVocbaseCol));
rt->Set(EnsureMultiSkiplistIndexFuncName, v8::FunctionTemplate::New(JS_EnsureMultiSkiplistIndexVocbaseCol));
rt->Set(EnsureSkiplistIndexFuncName, v8::FunctionTemplate::New(JS_EnsureSkiplistIndexVocbaseCol));
*/
rt->Set(EnsureHashIndexFuncName, v8::FunctionTemplate::New(JS_EnsureHashIndexVocbaseCol));
rt->Set(EnsurePriorityQueueIndexFuncName, v8::FunctionTemplate::New(JS_EnsurePriorityQueueIndexVocbaseCol));
rt->Set(EnsureSkiplistFuncName, v8::FunctionTemplate::New(JS_EnsureSkiplistVocbaseCol));
rt->Set(EnsureUniqueSkiplistFuncName, v8::FunctionTemplate::New(JS_EnsureUniqueSkiplistVocbaseCol));
rt->Set(EnsureUniqueConstraintFuncName, v8::FunctionTemplate::New(JS_EnsureUniqueConstraintVocbaseCol));
rt->Set(EnsureUniqueSkiplistFuncName, v8::FunctionTemplate::New(JS_EnsureUniqueSkiplistVocbaseCol));
rt->Set(FiguresFuncName, v8::FunctionTemplate::New(JS_FiguresVocbaseCol));
rt->Set(GetIndexesFuncName, v8::FunctionTemplate::New(JS_GetIndexesVocbaseCol));
rt->Set(LoadFuncName, v8::FunctionTemplate::New(JS_LoadVocbaseCol));
@ -6144,23 +6248,19 @@ void TRI_InitV8VocBridge (v8::Handle<v8::Context> context, TRI_vocbase_t* vocbas
rt->SetInternalFieldCount(SLOT_END); // FIXME
rt->Set(AllFuncName, v8::FunctionTemplate::New(JS_AllQuery));
rt->Set(ByExampleFuncName, v8::FunctionTemplate::New(JS_ByExampleQuery));
rt->Set(CountFuncName, v8::FunctionTemplate::New(JS_CountVocbaseCol));
rt->Set(DeleteFuncName, v8::FunctionTemplate::New(JS_DeleteVocbaseCol));
rt->Set(DocumentFuncName, v8::FunctionTemplate::New(JS_DocumentVocbaseCol));
rt->Set(DropFuncName, v8::FunctionTemplate::New(JS_DropVocbaseCol));
rt->Set(DropIndexFuncName, v8::FunctionTemplate::New(JS_DropIndexVocbaseCol));
rt->Set(EnsureGeoIndexFuncName, v8::FunctionTemplate::New(JS_EnsureGeoIndexVocbaseCol));
/* oreste
rt->Set(EnsureMultiHashIndexFuncName, v8::FunctionTemplate::New(JS_EnsureMultiHashIndexVocbaseCol));
rt->Set(EnsureMultiSkiplistIndexFuncName, v8::FunctionTemplate::New(JS_EnsureMultiSkiplistIndexVocbaseCol));
rt->Set(EnsureSkiplistIndexFuncName, v8::FunctionTemplate::New(JS_EnsureSkiplistIndexVocbaseCol));
*/
rt->Set(EnsureHashIndexFuncName, v8::FunctionTemplate::New(JS_EnsureHashIndexVocbaseCol));
rt->Set(EnsurePriorityQueueIndexFuncName, v8::FunctionTemplate::New(JS_EnsurePriorityQueueIndexVocbaseCol));
rt->Set(EnsureSkiplistFuncName, v8::FunctionTemplate::New(JS_EnsureSkiplistVocbaseCol));
rt->Set(EnsureUniqueConstraintFuncName, v8::FunctionTemplate::New(JS_EnsureUniqueConstraintVocbaseCol));
rt->Set(EnsureUniqueSkiplistFuncName, v8::FunctionTemplate::New(JS_EnsureUniqueSkiplistVocbaseCol));
rt->Set(FiguresFuncName, v8::FunctionTemplate::New(JS_FiguresVocbaseCol));
rt->Set(GetIndexesFuncName, v8::FunctionTemplate::New(JS_GetIndexesVocbaseCol));
rt->Set(LoadFuncName, v8::FunctionTemplate::New(JS_LoadVocbaseCol));

View File

@ -3058,6 +3058,144 @@ static TRI_index_t* CreateSkiplistIndexSimCollection (TRI_sim_collection_t* coll
}
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////
// -----------------------------------------------------------------------------
// --SECTION-- SELECT BY EXAMPLE QUERY
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// --SECTION-- private functions
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @addtogroup VocBase
/// @{
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief checks for match of an example
////////////////////////////////////////////////////////////////////////////////
static bool IsExampleMatch (TRI_shaper_t* shaper,
TRI_doc_mptr_t const* doc,
size_t len,
TRI_shape_pid_t* pids,
TRI_shaped_json_t** values) {
TRI_shape_access_t const* accessor;
TRI_shaped_json_t const* document;
TRI_shaped_json_t* example;
TRI_shaped_json_t result;
bool ok;
size_t i;
document = &doc->_document;
for (i = 0; i < len; ++i) {
example = values[i];
accessor = TRI_FindAccessorVocShaper(shaper, document->_sid, pids[i]);
if (accessor == NULL) {
LOG_TRACE("failed to get accessor for sid %lu and path %lu",
(unsigned long) document->_sid,
(unsigned long) pids[i]);
return false;
}
if (accessor->_shape == NULL) {
LOG_TRACE("expecting an array for path %lu",
(unsigned long) pids[i]);
return false;
}
if (accessor->_shape->_sid != example->_sid) {
LOG_TRACE("expecting sid %lu for path %lu, got sid %lu",
(unsigned long) example->_sid,
(unsigned long) pids[i],
(unsigned long) accessor->_shape->_sid);
return false;
}
ok = TRI_ExecuteShapeAccessor(accessor, document, &result);
if (! ok) {
LOG_TRACE("failed to get accessor for sid %lu and path %lu",
(unsigned long) document->_sid,
(unsigned long) pids[i]);
return false;
}
if (result._data.length != example->_data.length) {
LOG_TRACE("expecting length %lu, got length %lu for path %lu",
(unsigned long) result._data.length,
(unsigned long) example->_data.length,
(unsigned long) pids[i]);
return false;
}
if (memcmp(result._data.data, example->_data.data, example->_data.length) != 0) {
LOG_TRACE("data mismatch at path %lu",
(unsigned long) pids[i]);
return false;
}
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////
// -----------------------------------------------------------------------------
// --SECTION-- public functions
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @addtogroup VocBase
/// @{
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief executes a select-by-example query
////////////////////////////////////////////////////////////////////////////////
TRI_vector_t TRI_SelectByExample (TRI_sim_collection_t* sim,
size_t length,
TRI_shape_pid_t* pids,
TRI_shaped_json_t** values) {
TRI_shaper_t* shaper;
TRI_doc_mptr_t const** ptr;
TRI_doc_mptr_t const** end;
TRI_vector_t filtered;
// use filtered to hold copies of the master pointer
TRI_InitVector(&filtered, sizeof(TRI_doc_mptr_t));
// do a full scan
shaper = sim->base._shaper;
ptr = (TRI_doc_mptr_t const**) (sim->_primaryIndex._table);
end = (TRI_doc_mptr_t const**) (sim->_primaryIndex._table + sim->_primaryIndex._nrAlloc);
for (; ptr < end; ++ptr) {
if (*ptr) {
if (IsExampleMatch(shaper, *ptr, length, pids, values)) {
TRI_PushBackVector(&filtered, *ptr);
}
}
}
return filtered;
}
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////

View File

@ -502,6 +502,28 @@ struct TRI_index_s* TRI_EnsureSkiplistIndexSimCollection (TRI_sim_collection_t*
/// @}
////////////////////////////////////////////////////////////////////////////////
// -----------------------------------------------------------------------------
// --SECTION-- SELECT BY EXAMPLE QUERY
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @addtogroup VocBase
/// @{
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief executes a select-by-example query
////////////////////////////////////////////////////////////////////////////////
TRI_vector_t TRI_SelectByExample (TRI_sim_collection_t* sim,
size_t length,
TRI_shape_pid_t* pids,
TRI_shaped_json_t** values);
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////
#ifdef __cplusplus
}
#endif

View File

@ -684,12 +684,11 @@ SimpleQueryByExample.prototype.constructor = SimpleQueryByExample;
////////////////////////////////////////////////////////////////////////////////
/// @brief constructs a query-by-example for a collection
///
/// @FUN{byExample(@FA{example})}
/// @FUN{@FA{collection}.byExample(@FA{path1}, @FA{value1}, ...)}
///
/// Selects all documents of a collection that match the specified
/// @FA{example}. The example must be specified as an object, with the object
/// attributes being the search values. Allowed attribute types for searching
/// are numbers, strings, and boolean values.
/// example. The example must be specified as paths and values. Allowed
/// attribute types for searching are numbers, strings, and boolean values.
///
/// You can use @FN{toArray}, @FN{next}, @FN{nextRef}, or @FN{hasNext} to access
/// the result. The result can be limited using the @FN{skip} and @FN{limit}
@ -706,8 +705,8 @@ SimpleQueryByExample.prototype.constructor = SimpleQueryByExample;
/// @verbinclude simple19
////////////////////////////////////////////////////////////////////////////////
AvocadoCollection.prototype.byExample = function (example) {
return new SimpleQueryByExample(this, example);
AvocadoCollection.prototype.byExample = function () {
return new SimpleQueryByExample(this, arguments);
}
////////////////////////////////////////////////////////////////////////////////
@ -749,59 +748,13 @@ SimpleQueryByExample.prototype.execute = function () {
this._skip = 0;
}
var queryString = "SELECT c FROM `" + this._collection.name() + "` c";
var documents = this._collection.BY_EXAMPLE.apply(this._collection, this._example);
if (!(this._example instanceof Object)) {
throw "invalid example specification";
}
var found = false;
for (var i in this._example) {
if (!this._example.hasOwnProperty(i)) {
continue;
}
var typeString = typeof(this._example[i]);
if (typeString !== "number" && typeString !== "string" && typeString !== "boolean") {
throw "invalid example specification for key " + i;
}
if (found) {
queryString += "&& ";
}
else {
queryString += " WHERE ";
found = true;
}
queryString += "c.`" + i + "` == ";
if (typeString == "number") {
queryString += this._example[i];
}
else if (typeString == "string") {
queryString += QuoteJSONString(this._example[i]);
}
else if (typeString == "boolean") {
queryString += this._example[i];
}
}
var cursor = AQL_STATEMENT(queryString, undefined);
if (cursor instanceof AvocadoQueryError) {
throw cursor.message;
}
var documents = { "count" : cursor.count(), "total" : cursor.count(), "documents": [] };
while (cursor.hasNext()) {
documents.documents.push(cursor.next());
}
cursor.dispose();
this._execution = new SimpleQueryArray(documents.documents);
this._execution = new SimpleQueryArray(documents);
this._execution._skip = this._skip;
this._execution._limit = this._limit;
this._countQuery = documents.count;
this._countTotal = documents.total;
this._countQuery = documents.length;
this._countTotal = documents.length;
}
}