mirror of https://gitee.com/bigwinds/arangodb
issue 526.6: implement REST and V8 handlers for the iresearch analyzer feature (#8626)
* issue 526.6: implement REST and V8 handlers for the iresearch analyzer feature * address typo * remove excess comments * temporarily comment out tests failing on MacOS * temporarily comment out more MacOS-only test failures
This commit is contained in:
parent
4af7fa8f46
commit
f4919dc173
|
@ -77,6 +77,8 @@ add_library(${LIB_ARANGO_IRESEARCH}
|
|||
IResearch/VelocyPackHelper.cpp IResearch/VelocyPackHelper.h
|
||||
IResearch/ExpressionFilter.cpp IResearch/ExpressionFilter.h
|
||||
IResearch/AqlHelper.cpp IResearch/AqlHelper.h
|
||||
RestHandler/RestAnalyzerHandler.cpp RestHandler/RestAnalyzerHandler.h
|
||||
V8Server/v8-analyzers.cpp V8Server/v8-analyzers.h
|
||||
)
|
||||
|
||||
target_compile_definitions(${LIB_ARANGO_IRESEARCH}
|
||||
|
|
|
@ -731,16 +731,10 @@ IResearchAnalyzerFeature::IResearchAnalyzerFeature(arangodb::application_feature
|
|||
|
||||
auto split = splitAnalyzerName(name);
|
||||
|
||||
return !split.first.null() // have a vocbase
|
||||
&& ctx->canUseDatabase(split.first, level) // can use vocbase
|
||||
&& ctx->canUseCollection(split.first, ANALYZER_COLLECTION_NAME, level); // can use analyzers
|
||||
}
|
||||
|
||||
std::pair<IResearchAnalyzerFeature::AnalyzerPool::ptr, bool> IResearchAnalyzerFeature::emplace(
|
||||
irs::string_ref const& name, irs::string_ref const& type, irs::string_ref const& properties,
|
||||
irs::flags const& features /*= irs::flags::empty_instance()*/
|
||||
) noexcept {
|
||||
return emplace(name, type, properties, true, features);
|
||||
return split.first.null() // static analyzer (always allowed)
|
||||
|| (ctx->canUseDatabase(split.first, level) // can use vocbase
|
||||
&& ctx->canUseCollection(split.first, ANALYZER_COLLECTION_NAME, level) // can use analyzers
|
||||
);
|
||||
}
|
||||
|
||||
std::pair<IResearchAnalyzerFeature::AnalyzerPool::ptr, bool> IResearchAnalyzerFeature::emplace(
|
||||
|
@ -1616,255 +1610,6 @@ arangodb::Result IResearchAnalyzerFeature::loadAnalyzers( // load
|
|||
return arangodb::Result();
|
||||
}
|
||||
|
||||
bool IResearchAnalyzerFeature::loadConfiguration() {
|
||||
if (arangodb::ServerState::instance()->isRunningInCluster()) {
|
||||
// the following code will not be working in the cluster
|
||||
// safe to access since loadConfiguration(...) is called from start() which
|
||||
// is single-thread
|
||||
return _customAnalyzers.empty();
|
||||
}
|
||||
|
||||
auto vocbase = getSystemDatabase();
|
||||
|
||||
if (!vocbase) {
|
||||
LOG_TOPIC("3cc73", WARN, arangodb::iresearch::TOPIC)
|
||||
<< "failure to get system database while loading arangosearch analyzer "
|
||||
"persisted configuration";
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
arangodb::SingleCollectionTransaction trx(
|
||||
arangodb::transaction::StandaloneContext::Create(*vocbase),
|
||||
ANALYZER_COLLECTION_NAME, arangodb::AccessMode::Type::READ);
|
||||
auto res = trx.begin();
|
||||
|
||||
if (!res.ok()) {
|
||||
LOG_TOPIC("16400", WARN, arangodb::iresearch::TOPIC)
|
||||
<< "failure to start transaction while loading arangosearch analyzer "
|
||||
"persisted configuration";
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* collection = trx.documentCollection();
|
||||
|
||||
if (!collection) {
|
||||
LOG_TOPIC("ead4b", WARN, arangodb::iresearch::TOPIC)
|
||||
<< "failure to get collection while loading arangosearch analyzer "
|
||||
"persisted configuration";
|
||||
trx.abort();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unordered_map<irs::string_ref, AnalyzerPool::ptr> initialized;
|
||||
auto visitor = [this, &trx, collection, &initialized](LocalDocumentId const& token) -> bool {
|
||||
ManagedDocumentResult result;
|
||||
|
||||
if (!collection->readDocument(&trx, token, result)) {
|
||||
LOG_TOPIC("377b5", WARN, arangodb::iresearch::TOPIC)
|
||||
<< "skipping failed read of an arangosearch analyzer persisted "
|
||||
"configuration token: "
|
||||
<< token.id();
|
||||
|
||||
return true; // failed to read document, skip
|
||||
}
|
||||
|
||||
arangodb::velocypack::Slice slice(result.vpack());
|
||||
|
||||
if (!slice.isObject() || !slice.hasKey(arangodb::StaticStrings::KeyString) ||
|
||||
!slice.get(arangodb::StaticStrings::KeyString).isString() ||
|
||||
!slice.hasKey("name") || !slice.get("name").isString() || !slice.hasKey("type") ||
|
||||
!slice.get("type").isString() || !slice.hasKey("properties") ||
|
||||
!(slice.get("properties").isNull() || slice.get("properties").isString() ||
|
||||
slice.get("properties").isArray() || slice.get("properties").isObject())) {
|
||||
LOG_TOPIC("e8578", WARN, arangodb::iresearch::TOPIC)
|
||||
<< "skipping invalid arangosearch analyzer persisted configuration "
|
||||
"entry: "
|
||||
<< slice.toJson();
|
||||
|
||||
return true; // not a valid configuration, skip
|
||||
}
|
||||
|
||||
auto key = getStringRef(slice.get(arangodb::StaticStrings::KeyString));
|
||||
auto name = getStringRef(slice.get("name"));
|
||||
auto type = getStringRef(slice.get("type"));
|
||||
irs::string_ref properties;
|
||||
auto propertiesSlice = slice.get("properties");
|
||||
std::string tmpString;
|
||||
|
||||
// encode jSON array/object as a string for analyzers that support jSON
|
||||
if (propertiesSlice.isArray() || propertiesSlice.isObject()) {
|
||||
tmpString = propertiesSlice.toJson(); // format as a jSON encoded string
|
||||
properties = tmpString;
|
||||
} else {
|
||||
properties = getStringRef(propertiesSlice);
|
||||
}
|
||||
|
||||
auto normalizedName = // normalized name
|
||||
collection->vocbase().name() + "::" + std::string(name);
|
||||
auto& staticAnalyzersMap = getStaticAnalyzers();
|
||||
|
||||
if (staticAnalyzersMap.find(irs::make_hashed_ref(name, std::hash<irs::string_ref>())) == staticAnalyzersMap.end()) {
|
||||
name = normalizedName; // use normalized name if it's not a static analyzer
|
||||
}
|
||||
|
||||
auto entry = emplace(name, type, properties,
|
||||
false); // do not persist since this config is already
|
||||
// coming from the persisted store
|
||||
auto& pool = entry.first;
|
||||
|
||||
if (!pool) {
|
||||
LOG_TOPIC("91db1", WARN, arangodb::iresearch::TOPIC)
|
||||
<< "failure creating an arangosearch analyzer instance for name '"
|
||||
<< name << "' type '" << type << "' properties '" << properties << "'";
|
||||
|
||||
THROW_ARANGO_EXCEPTION_MESSAGE(
|
||||
TRI_ERROR_BAD_PARAMETER,
|
||||
std::string(
|
||||
"failure creating an arangosearch analyzer instance for name '") +
|
||||
std::string(name) + "' type '" + std::string(type) +
|
||||
"' properties '" + std::string(properties) + "'");
|
||||
}
|
||||
|
||||
if (!entry.second && initialized.find(name) != initialized.end()) {
|
||||
LOG_TOPIC("1eb5e", WARN, arangodb::iresearch::TOPIC)
|
||||
<< "name collision detected while registering an arangosearch "
|
||||
"analizer name '"
|
||||
<< name << "' type '" << type << "' properties '" << properties
|
||||
<< "', previous registration type '" << pool->_type
|
||||
<< "' properties '" << pool->_properties << "'";
|
||||
|
||||
THROW_ARANGO_EXCEPTION_MESSAGE(
|
||||
TRI_ERROR_BAD_PARAMETER,
|
||||
std::string("name collision detected while registering an "
|
||||
"arangosearch analizer name '") +
|
||||
std::string(name) + "' type '" + std::string(type) +
|
||||
"' properties '" + std::string(properties) +
|
||||
"', previous registration type '" + std::string(pool->_type) +
|
||||
"' properties '" + std::string(pool->_properties) + "'");
|
||||
}
|
||||
|
||||
static auto& staticAnalyzers = getStaticAnalyzers();
|
||||
|
||||
if (!entry.second &&
|
||||
staticAnalyzers.find(irs::make_hashed_ref(name, std::hash<irs::string_ref>())) !=
|
||||
staticAnalyzers.end()) {
|
||||
if (type != pool->_type || properties != pool->_properties) {
|
||||
LOG_TOPIC("d3a5e", WARN, arangodb::iresearch::TOPIC)
|
||||
<< "name collision with a static analyzer detected while "
|
||||
"registering an arangosearch analizer name '"
|
||||
<< name << "' type '" << type << "' properties '" << properties
|
||||
<< "', previous registration type '" << pool->_type
|
||||
<< "' properties '" << pool->_properties << "'";
|
||||
|
||||
THROW_ARANGO_EXCEPTION_MESSAGE(
|
||||
TRI_ERROR_INTERNAL,
|
||||
std::string("name collision with a static analyzer detected while "
|
||||
"registering an arangosearch analizer name '") +
|
||||
std::string(name) + "' type '" + std::string(type) +
|
||||
"' properties '" + std::string(properties) +
|
||||
"', previous registration type '" + std::string(pool->_type) +
|
||||
"' properties '" + std::string(pool->_properties) + "'");
|
||||
}
|
||||
|
||||
LOG_TOPIC("a0a10", WARN, arangodb::iresearch::TOPIC)
|
||||
<< "name collision with a static analyzer detected while registering "
|
||||
"an arangosearch analizer name '"
|
||||
<< name << "' type '" << type << "' properties '" << properties << "'";
|
||||
|
||||
THROW_ARANGO_EXCEPTION_MESSAGE(
|
||||
TRI_ERROR_INTERNAL,
|
||||
std::string("name collision with a static analyzer detected while "
|
||||
"registering an arangosearch analizer name '") +
|
||||
std::string(name) + "' type '" + std::string(type) +
|
||||
"' properties '" + std::string(properties) + "'");
|
||||
} else if (!pool->init(type, properties)) {
|
||||
LOG_TOPIC("6fac5", WARN, arangodb::iresearch::TOPIC)
|
||||
<< "failure initializing an arangosearch analyzer instance for name '"
|
||||
<< name << "' type '" << type << "' properties '" << properties << "'";
|
||||
|
||||
THROW_ARANGO_EXCEPTION_MESSAGE(
|
||||
TRI_ERROR_BAD_PARAMETER,
|
||||
std::string("failure initializing an arangosearch analyzer instance "
|
||||
"for name '") +
|
||||
std::string(name) + "' type '" + std::string(type) +
|
||||
"' properties '" + std::string(properties) + "'");
|
||||
}
|
||||
|
||||
initialized.emplace(std::piecewise_construct,
|
||||
std::forward_as_tuple(pool->name()), // emplace with ref at string in pool
|
||||
std::forward_as_tuple(pool));
|
||||
pool->setKey(key);
|
||||
|
||||
return true; // process next
|
||||
};
|
||||
|
||||
WriteMutex mutex(_mutex);
|
||||
SCOPED_LOCK(mutex); // the cache could return the same pool asynchronously
|
||||
// before '_key' updated/rolled back below
|
||||
bool revert = true;
|
||||
auto cleanup = irs::make_finally([&revert, &initialized]() -> void {
|
||||
if (revert) {
|
||||
for (auto& entry : initialized) {
|
||||
auto& pool = entry.second;
|
||||
|
||||
// reset pool configuration back to uninitialized
|
||||
// safe to reset since loadConfiguration(...) is called from start()
|
||||
// which is single-thread
|
||||
if (pool) {
|
||||
pool->_config.clear(); // noexcept
|
||||
pool->_key = irs::string_ref::NIL; // noexcept
|
||||
pool->_type = irs::string_ref::NIL; // noexcept
|
||||
pool->_properties = irs::string_ref::NIL; // noexcept
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
collection->invokeOnAllElements(&trx, visitor);
|
||||
|
||||
// ensure all records were initialized
|
||||
for (auto& entry : _customAnalyzers) {
|
||||
if (initialized.find(entry.first) == initialized.end()) {
|
||||
LOG_TOPIC("38291", WARN, arangodb::iresearch::TOPIC)
|
||||
<< "uninitialized AnalyzerPool deletected while validating "
|
||||
"analyzers, arangosearch analyzer name '"
|
||||
<< entry.first << "'";
|
||||
return false; // found an uninitialized analyzer
|
||||
}
|
||||
}
|
||||
|
||||
if (!trx.commit().ok()) {
|
||||
LOG_TOPIC("7a050", WARN, arangodb::iresearch::TOPIC)
|
||||
<< "failure to commit transaction while loading AnalyzerFeature "
|
||||
"configuration";
|
||||
return false;
|
||||
}
|
||||
|
||||
revert = false;
|
||||
|
||||
return true;
|
||||
} catch (arangodb::basics::Exception& e) {
|
||||
LOG_TOPIC("40979", WARN, arangodb::iresearch::TOPIC)
|
||||
<< "caught exception while loading configuration: " << e.code() << " "
|
||||
<< e.what();
|
||||
IR_LOG_EXCEPTION();
|
||||
} catch (std::exception& e) {
|
||||
LOG_TOPIC("36c78", WARN, arangodb::iresearch::TOPIC)
|
||||
<< "caught exception while loading configuration: " << e.what();
|
||||
IR_LOG_EXCEPTION();
|
||||
} catch (...) {
|
||||
LOG_TOPIC("47651", WARN, arangodb::iresearch::TOPIC)
|
||||
<< "caught exception while loading configuration";
|
||||
IR_LOG_EXCEPTION();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*static*/ std::string const& IResearchAnalyzerFeature::name() noexcept {
|
||||
return FEATURE_NAME;
|
||||
}
|
||||
|
@ -1957,7 +1702,7 @@ arangodb::Result IResearchAnalyzerFeature::remove( // remove analyzer
|
|||
|
||||
if (!force && pool.use_count() > 1) { // +1 for ref in '_analyzers'
|
||||
return arangodb::Result( // result
|
||||
TRI_ERROR_FORBIDDEN, // code
|
||||
TRI_ERROR_ARANGO_CONFLICT, // code
|
||||
std::string("analyzer in-use while removing arangosearch analyzer '") + std::string(name)+ "'"
|
||||
);
|
||||
}
|
||||
|
@ -2008,7 +1753,7 @@ arangodb::Result IResearchAnalyzerFeature::remove( // remove analyzer
|
|||
|
||||
if (!vocbase) {
|
||||
return arangodb::Result( // result
|
||||
TRI_ERROR_ARANGO_DOCUMENT_NOT_FOUND, // code
|
||||
TRI_ERROR_ARANGO_DATABASE_NOT_FOUND, // code
|
||||
std::string("failure to find vocbase while removing arangosearch analyzer '") + std::string(name)+ "'"
|
||||
);
|
||||
}
|
||||
|
@ -2309,7 +2054,7 @@ arangodb::Result IResearchAnalyzerFeature::storeAnalyzer(AnalyzerPool& pool) {
|
|||
}
|
||||
|
||||
bool IResearchAnalyzerFeature::visit( // visit analyzers
|
||||
VisitorType const& visitor, // visitor
|
||||
std::function<bool(AnalyzerPool::ptr const& analyzer)> const& visitor, // visitor
|
||||
TRI_vocbase_t const* vocbase /*= nullptr*/ // analyzers for vocbase
|
||||
) const {
|
||||
if (vocbase) { // do not trigger load for all-databases requests
|
||||
|
@ -2337,7 +2082,7 @@ bool IResearchAnalyzerFeature::visit( // visit analyzers
|
|||
for (auto& entry: analyzers) {
|
||||
if (entry.second // have entry
|
||||
&& (!vocbase || splitAnalyzerName(entry.first).first == vocbase->name()) // requested vocbase
|
||||
&& !visitor(entry.first, entry.second->_type, entry.second->_properties, entry.second->features()) // termination request
|
||||
&& !visitor(entry.second) // termination request
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
@ -2351,4 +2096,4 @@ bool IResearchAnalyzerFeature::visit( // visit analyzers
|
|||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- END-OF-FILE
|
||||
// -----------------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------------
|
|
@ -108,12 +108,6 @@ class IResearchAnalyzerFeature final : public arangodb::application_features::Ap
|
|||
arangodb::auth::Level const& level // access level
|
||||
);
|
||||
|
||||
// FIXME TODO remove
|
||||
std::pair<AnalyzerPool::ptr, bool> emplace(
|
||||
irs::string_ref const& name, irs::string_ref const& type,
|
||||
irs::string_ref const& properties,
|
||||
irs::flags const& features = irs::flags::empty_instance()) noexcept;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief emplace an analyzer as per the specified parameters
|
||||
/// @param result the result of the successful emplacement (out-param)
|
||||
|
@ -203,9 +197,8 @@ class IResearchAnalyzerFeature final : public arangodb::application_features::Ap
|
|||
/// @param vocbase only visit analysers for this vocbase (nullptr == all)
|
||||
/// @return visitation compleated fully
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
typedef std::function<bool(irs::string_ref const& analyzer, irs::string_ref const& type, irs::string_ref const& properties, irs::flags const& features)> VisitorType;
|
||||
bool visit( // visit analyzers
|
||||
VisitorType const& visitor, // visitor
|
||||
std::function<bool(AnalyzerPool::ptr const& analyzer)> const& visitor, // visitor
|
||||
TRI_vocbase_t const* vocbase = nullptr // analyzers for vocbase
|
||||
) const;
|
||||
|
||||
|
@ -281,8 +274,6 @@ class IResearchAnalyzerFeature final : public arangodb::application_features::Ap
|
|||
irs::string_ref const& database = irs::string_ref::NIL // database to load
|
||||
);
|
||||
|
||||
bool loadConfiguration();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief store the definition for the speicifed pool in the corresponding
|
||||
/// vocbase
|
||||
|
|
|
@ -177,11 +177,6 @@ class IResearchView final: public arangodb::LogicalView {
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
bool visitCollections(CollectionVisitor const& visitor) const override;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief persist data store states for all known links to permanent storage
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
arangodb::Result commit();
|
||||
|
||||
protected:
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -224,6 +219,11 @@ class IResearchView final: public arangodb::LogicalView {
|
|||
IResearchViewMeta&& meta // view meta
|
||||
);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief persist data store states for all known links to permanent storage
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
arangodb::Result commit();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief called when a view's properties are updated (i.e. delta-modified)
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -241,4 +241,4 @@ class IResearchView final: public arangodb::LogicalView {
|
|||
} // iresearch
|
||||
} // arangodb
|
||||
|
||||
#endif
|
||||
#endif
|
|
@ -0,0 +1,504 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// DISCLAIMER
|
||||
///
|
||||
/// Copyright 2019 ArangoDB 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 ArangoDB GmbH, Cologne, Germany
|
||||
///
|
||||
/// @author Andrey Abramov
|
||||
/// @author Vasiliy Nabatchikov
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "RestAnalyzerHandler.h"
|
||||
#include "ApplicationFeatures/ApplicationServer.h"
|
||||
#include "IResearch/IResearchAnalyzerFeature.h"
|
||||
#include "IResearch/VelocyPackHelper.h"
|
||||
#include "RestServer/SystemDatabaseFeature.h"
|
||||
#include "utils/string.hpp"
|
||||
#include "velocypack/Iterator.h"
|
||||
|
||||
namespace {
|
||||
|
||||
std::string ANALYZER_FEAURES_FIELD("features");
|
||||
std::string ANALYZER_NAME_FIELD("name");
|
||||
std::string ANALYZER_PROPERTIES_FIELD("properties");
|
||||
std::string ANALYZER_TYPE_FIELD("type");
|
||||
|
||||
}
|
||||
|
||||
namespace arangodb {
|
||||
namespace iresearch {
|
||||
|
||||
RestAnalyzerHandler::RestAnalyzerHandler( // constructor
|
||||
arangodb::GeneralRequest* request, // request
|
||||
arangodb::GeneralResponse* response // response
|
||||
): RestVocbaseBaseHandler(request, response) {
|
||||
}
|
||||
|
||||
void RestAnalyzerHandler::createAnalyzer( // create
|
||||
IResearchAnalyzerFeature& analyzers, // analyzer feature
|
||||
TRI_vocbase_t const* sysVocbase // system vocbase
|
||||
) {
|
||||
TRI_ASSERT(_request); // ensured by execute()
|
||||
|
||||
if (_request->payload().isEmptyObject()) {
|
||||
generateError( // generate error
|
||||
arangodb::rest::ResponseCode::BAD, TRI_ERROR_HTTP_CORRUPTED_JSON // args
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
bool success = false;
|
||||
auto body = parseVPackBody(success);
|
||||
|
||||
if (!success) {
|
||||
return; // parseVPackBody(...) calls generateError(...)
|
||||
}
|
||||
|
||||
if (!body.isObject()) {
|
||||
generateError(arangodb::Result( // generate error
|
||||
TRI_ERROR_BAD_PARAMETER, // code
|
||||
"expecting body to be of the form { name: <string>, type: <string>[, properties: <object|string>[, features: <string-array>]] }"
|
||||
));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
irs::string_ref name;
|
||||
std::string nameBuf;
|
||||
auto nameSlice = body.get(ANALYZER_NAME_FIELD);
|
||||
|
||||
if (nameSlice.isString()) {
|
||||
name = getStringRef(nameSlice);
|
||||
} else if (!nameSlice.isNull()) {
|
||||
generateError(arangodb::Result( // generate error
|
||||
TRI_ERROR_BAD_PARAMETER, // code
|
||||
"invalid 'name', expecting body to be of the form { name: <string>, type: <string>[, properties: <object|string>[, features: <string-array>]] }"
|
||||
));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (sysVocbase) {
|
||||
nameBuf = IResearchAnalyzerFeature::normalize(name, _vocbase, *sysVocbase); // normalize
|
||||
name = nameBuf;
|
||||
};
|
||||
|
||||
irs::string_ref type;
|
||||
auto typeSlice = body.get(ANALYZER_TYPE_FIELD);
|
||||
|
||||
if (typeSlice.isString()) {
|
||||
type = getStringRef(typeSlice);
|
||||
} else if (!typeSlice.isNull()) {
|
||||
generateError(arangodb::Result( // generate error
|
||||
TRI_ERROR_BAD_PARAMETER, // code
|
||||
"invalid 'type', expecting body to be of the form { name: <string>, type: <string>[, properties: <object|string>[, features: <string-array>]] }"
|
||||
));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
irs::string_ref properties;
|
||||
std::string propertiesBuf;
|
||||
|
||||
if (body.hasKey(ANALYZER_PROPERTIES_FIELD)) { // optional parameter
|
||||
auto propertiesSlice = body.get(ANALYZER_PROPERTIES_FIELD);
|
||||
|
||||
if (propertiesSlice.isObject()) {
|
||||
propertiesBuf = propertiesSlice.toString();
|
||||
properties = propertiesBuf;
|
||||
} else if (propertiesSlice.isString()) {
|
||||
properties = getStringRef(propertiesSlice);
|
||||
} else if (!propertiesSlice.isNull()) {
|
||||
generateError(arangodb::Result( // generate error
|
||||
TRI_ERROR_BAD_PARAMETER, // code
|
||||
"invalid 'properties', expecting body to be of the form { name: <string>, type: <string>[, properties: <object>[, features: <string-array>]] }"
|
||||
));
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
irs::flags features;
|
||||
|
||||
if (body.hasKey(ANALYZER_FEAURES_FIELD)) { // optional parameter
|
||||
auto featuresSlice = body.get(ANALYZER_FEAURES_FIELD);
|
||||
|
||||
if (featuresSlice.isArray()) {
|
||||
for (arangodb::velocypack::ArrayIterator itr(featuresSlice);
|
||||
itr.valid();
|
||||
++itr
|
||||
) {
|
||||
auto value = *itr;
|
||||
|
||||
if (!value.isString()) {
|
||||
generateError(arangodb::Result( // generate error
|
||||
TRI_ERROR_BAD_PARAMETER, // code
|
||||
"invalid value in 'features', expecting body to be of the form { name: <string>, type: <string>[, properties: <object>[, features: <string-array>]] }"
|
||||
));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
auto* feature = irs::attribute::type_id::get(getStringRef(value));
|
||||
|
||||
if (!feature) {
|
||||
generateError(arangodb::Result( // generate error
|
||||
TRI_ERROR_BAD_PARAMETER, // code
|
||||
"unknown value in 'features', expecting body to be of the form { name: <string>, type: <string>[, properties: <object>[, features: <string-array>]] }"
|
||||
));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
features.add(*feature);
|
||||
}
|
||||
} else {
|
||||
generateError(arangodb::Result( // generate error
|
||||
TRI_ERROR_BAD_PARAMETER, // code
|
||||
"invalid 'features', expecting body to be of the form { name: <string>, type: <string>[, properties: <object>[, features: <string-array>]] }"
|
||||
));
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// ...........................................................................
|
||||
// end of parameter parsing
|
||||
// ...........................................................................
|
||||
|
||||
if (!IResearchAnalyzerFeature::canUse(name, auth::Level::RW)) {
|
||||
generateError( // generate error
|
||||
arangodb::rest::ResponseCode::FORBIDDEN, // HTTP code
|
||||
TRI_ERROR_FORBIDDEN, // code
|
||||
std::string("insufficient rights while creating analyzer: ") + body.toString() // message
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
IResearchAnalyzerFeature::EmplaceResult result;
|
||||
auto res = analyzers.emplace(result, name, type, properties, features);
|
||||
|
||||
if (!res.ok()) {
|
||||
generateError(res);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!result.first) {
|
||||
generateError( // generate error
|
||||
arangodb::rest::ResponseCode::BAD, // HTTP code
|
||||
TRI_errno(), // code
|
||||
std::string("failure while creating analyzer: ") + body.toString() // message
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
auto pool = result.first;
|
||||
arangodb::velocypack::Builder builder;
|
||||
|
||||
builder.openObject();
|
||||
addStringRef(builder, ANALYZER_NAME_FIELD, pool->name());
|
||||
addStringRef(builder, ANALYZER_TYPE_FIELD, pool->type());
|
||||
addStringRef(builder, ANALYZER_PROPERTIES_FIELD, pool->properties());
|
||||
builder.add( // add features
|
||||
ANALYZER_FEAURES_FIELD, // key
|
||||
arangodb::velocypack::Value(arangodb::velocypack::ValueType::Array) // value
|
||||
);
|
||||
|
||||
for (auto& feature: pool->features()) {
|
||||
TRI_ASSERT(feature); // has to be non-nullptr
|
||||
addStringRef(builder, feature->name());
|
||||
}
|
||||
|
||||
builder.close();
|
||||
builder.close();
|
||||
generateResult( // generate result
|
||||
result.second // new analyzer v.s. existing analyzer
|
||||
? arangodb::rest::ResponseCode::CREATED : arangodb::rest::ResponseCode::OK,
|
||||
builder.slice() // analyzer definition
|
||||
);
|
||||
}
|
||||
|
||||
arangodb::RestStatus RestAnalyzerHandler::execute() {
|
||||
auto* analyzers = arangodb::application_features::ApplicationServer::lookupFeature< // find feature
|
||||
IResearchAnalyzerFeature // feature type
|
||||
>();
|
||||
|
||||
if (!analyzers) {
|
||||
generateError( // generate error
|
||||
arangodb::rest::ResponseCode::BAD, // HTTP code
|
||||
TRI_ERROR_INTERNAL, // code
|
||||
std::string("failure to find feature '") + IResearchAnalyzerFeature::name() + "' while executing REST request for: " + ANALYZER_PATH
|
||||
);
|
||||
|
||||
return arangodb::RestStatus::DONE;
|
||||
}
|
||||
|
||||
auto* sysDatabase = arangodb::application_features::ApplicationServer::lookupFeature< // find feature
|
||||
arangodb::SystemDatabaseFeature // featue type
|
||||
>();
|
||||
auto sysVocbase = sysDatabase ? sysDatabase->use() : nullptr;
|
||||
|
||||
if (!_request) {
|
||||
generateError( // generate error
|
||||
arangodb::rest::ResponseCode::METHOD_NOT_ALLOWED, // HTTP code
|
||||
TRI_ERROR_HTTP_BAD_PARAMETER // error code
|
||||
);
|
||||
|
||||
return arangodb::RestStatus::DONE;
|
||||
}
|
||||
|
||||
auto& suffixes = _request->suffixes();
|
||||
|
||||
switch (_request->requestType()) {
|
||||
case arangodb::rest::RequestType::DELETE_REQ:
|
||||
if (suffixes.size() == 1) {
|
||||
if (!sysVocbase) {
|
||||
removeAnalyzer(*analyzers, suffixes[0]); // verbatim (assume already normalized)
|
||||
|
||||
return arangodb::RestStatus::DONE;
|
||||
}
|
||||
|
||||
removeAnalyzer( // remove
|
||||
*analyzers, // feature
|
||||
IResearchAnalyzerFeature::normalize(suffixes[0], _vocbase, *sysVocbase) // normalize
|
||||
);
|
||||
|
||||
return arangodb::RestStatus::DONE;
|
||||
}
|
||||
|
||||
generateError( // generate error
|
||||
arangodb::rest::ResponseCode::BAD, // HTTP code
|
||||
TRI_ERROR_BAD_PARAMETER, // code
|
||||
std::string("expecting DELETE ") + ANALYZER_PATH + "/<analyzer-name>[#force=true]" // mesage
|
||||
);
|
||||
|
||||
return arangodb::RestStatus::DONE;
|
||||
case arangodb::rest::RequestType::GET:
|
||||
if (suffixes.empty()) {
|
||||
getAnalyzers(*analyzers);
|
||||
|
||||
return arangodb::RestStatus::DONE;
|
||||
}
|
||||
|
||||
if (suffixes.size() == 1) {
|
||||
if (!sysVocbase) {
|
||||
getAnalyzer(*analyzers, suffixes[0]);
|
||||
|
||||
return arangodb::RestStatus::DONE;
|
||||
}
|
||||
|
||||
getAnalyzer( // get
|
||||
*analyzers, // feature
|
||||
IResearchAnalyzerFeature::normalize(suffixes[0], _vocbase, *sysVocbase) // normalize
|
||||
);
|
||||
|
||||
return arangodb::RestStatus::DONE;
|
||||
}
|
||||
|
||||
generateError(arangodb::Result( // generate error
|
||||
TRI_ERROR_BAD_PARAMETER, // code
|
||||
std::string("expecting GET ") + ANALYZER_PATH + "[/<analyzer-name>]" // mesage
|
||||
));
|
||||
|
||||
return arangodb::RestStatus::DONE;
|
||||
case arangodb::rest::RequestType::POST:
|
||||
if (suffixes.empty()) {
|
||||
createAnalyzer(*analyzers, sysVocbase.get());
|
||||
|
||||
return arangodb::RestStatus::DONE;
|
||||
}
|
||||
|
||||
generateError(arangodb::Result( // generate error
|
||||
TRI_ERROR_BAD_PARAMETER, // code
|
||||
std::string("expecting POST ") + ANALYZER_PATH // mesage
|
||||
));
|
||||
|
||||
return arangodb::RestStatus::DONE;
|
||||
default:
|
||||
generateError(arangodb::Result( // generate error
|
||||
TRI_ERROR_HTTP_METHOD_NOT_ALLOWED // error code
|
||||
));
|
||||
}
|
||||
|
||||
return arangodb::RestStatus::DONE;
|
||||
}
|
||||
|
||||
void RestAnalyzerHandler::getAnalyzer( // get analyzer
|
||||
IResearchAnalyzerFeature& analyzers, // analyzer feature
|
||||
std::string const& name // analyzer name (normalized)
|
||||
) {
|
||||
TRI_ASSERT(_request); // ensured by execute()
|
||||
|
||||
// ...........................................................................
|
||||
// end of parameter parsing
|
||||
// ...........................................................................
|
||||
|
||||
if (!IResearchAnalyzerFeature::canUse(name, auth::Level::RO)) {
|
||||
generateError(arangodb::Result( // generate error
|
||||
TRI_ERROR_FORBIDDEN, // code
|
||||
std::string("insufficient rights while getting analyzer: ") + name // message
|
||||
));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
auto pool = analyzers.get(name);
|
||||
|
||||
if (!pool) {
|
||||
generateError(arangodb::Result( // generate error
|
||||
TRI_ERROR_ARANGO_DOCUMENT_NOT_FOUND, // code
|
||||
std::string("unable to find analyzer: ") + name // mesage
|
||||
));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
arangodb::velocypack::Builder builder;
|
||||
|
||||
builder.openObject();
|
||||
addStringRef(builder, ANALYZER_NAME_FIELD, pool->name());
|
||||
addStringRef(builder, ANALYZER_TYPE_FIELD, pool->type());
|
||||
addStringRef(builder, ANALYZER_PROPERTIES_FIELD, pool->properties());
|
||||
builder.add( // add features
|
||||
ANALYZER_FEAURES_FIELD, // key
|
||||
arangodb::velocypack::Value(arangodb::velocypack::ValueType::Array) // value
|
||||
);
|
||||
|
||||
for (auto& feature: pool->features()) {
|
||||
TRI_ASSERT(feature); // has to be non-nullptr
|
||||
addStringRef(builder, feature->name());
|
||||
}
|
||||
|
||||
builder.close();
|
||||
builder.close();
|
||||
|
||||
// generate result + 'error' field + 'code' field
|
||||
// 2nd param must be Builder and not Slice
|
||||
generateOk(arangodb::rest::ResponseCode::OK, builder);
|
||||
}
|
||||
|
||||
void RestAnalyzerHandler::getAnalyzers( // get all analyzers
|
||||
IResearchAnalyzerFeature& analyzers // analyzer feature
|
||||
) {
|
||||
// ...........................................................................
|
||||
// end of parameter parsing
|
||||
// ...........................................................................
|
||||
|
||||
typedef arangodb::iresearch::IResearchAnalyzerFeature::AnalyzerPool::ptr AnalyzerPoolPtr;
|
||||
arangodb::velocypack::Builder builder;
|
||||
auto visitor = [&builder](AnalyzerPoolPtr const& analyzer)->bool {
|
||||
if (!analyzer) {
|
||||
return true; // continue with next analyzer
|
||||
}
|
||||
|
||||
builder.openObject();
|
||||
addStringRef(builder, ANALYZER_NAME_FIELD, analyzer->name());
|
||||
addStringRef(builder, ANALYZER_TYPE_FIELD, analyzer->type());
|
||||
addStringRef(builder, ANALYZER_PROPERTIES_FIELD, analyzer->properties());
|
||||
builder.add( // add features
|
||||
ANALYZER_FEAURES_FIELD, // key
|
||||
arangodb::velocypack::Value(arangodb::velocypack::ValueType::Array) // value
|
||||
);
|
||||
|
||||
for (auto& feature: analyzer->features()) {
|
||||
TRI_ASSERT(feature); // has to be non-nullptr
|
||||
addStringRef(builder, feature->name());
|
||||
}
|
||||
|
||||
builder.close();
|
||||
builder.close();
|
||||
|
||||
return true; // continue with next analyzer
|
||||
};
|
||||
|
||||
builder.openArray();
|
||||
|
||||
if (IResearchAnalyzerFeature::canUse(_vocbase, auth::Level::RO)) {
|
||||
analyzers.visit(visitor, &_vocbase);
|
||||
}
|
||||
|
||||
auto* sysDBFeature = arangodb::application_features::ApplicationServer::lookupFeature< // find feature
|
||||
arangodb::SystemDatabaseFeature // feature type
|
||||
>();
|
||||
|
||||
// include analyzers from the system vocbase if possible
|
||||
if (sysDBFeature) {
|
||||
auto sysVocbase = sysDBFeature->use();
|
||||
|
||||
if (sysVocbase // have system vocbase
|
||||
&& sysVocbase->name() != _vocbase.name() // not same vocbase as current
|
||||
&& IResearchAnalyzerFeature::canUse(*sysVocbase, auth::Level::RO)) {
|
||||
analyzers.visit(visitor, sysVocbase.get());
|
||||
}
|
||||
}
|
||||
|
||||
builder.close();
|
||||
|
||||
// generate result (wrapped inside 'result') + 'error' field + 'code' field
|
||||
// 2nd param must be Slice and not Builder
|
||||
generateOk(arangodb::rest::ResponseCode::OK, builder.slice());
|
||||
}
|
||||
|
||||
void RestAnalyzerHandler::removeAnalyzer( // remove
|
||||
IResearchAnalyzerFeature& analyzers, // analyzer feature
|
||||
std::string const& name // analyzer name (normalized)
|
||||
) {
|
||||
TRI_ASSERT(_request); // ensured by execute()
|
||||
|
||||
auto force = _request->parsedValue("force", false);
|
||||
|
||||
// ...........................................................................
|
||||
// end of parameter parsing
|
||||
// ...........................................................................
|
||||
|
||||
if (!IResearchAnalyzerFeature::canUse(name, auth::Level::RW)) {
|
||||
generateError(arangodb::Result( // generate error
|
||||
TRI_ERROR_FORBIDDEN, // code
|
||||
std::string("insufficient rights while removing analyzer: ") + name // message
|
||||
));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
auto res = analyzers.remove(name, force);
|
||||
|
||||
if (!res.ok()) {
|
||||
generateError(res);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
arangodb::velocypack::Builder builder;
|
||||
|
||||
builder.openObject();
|
||||
builder.add(ANALYZER_NAME_FIELD, arangodb::velocypack::Value(name));
|
||||
builder.close();
|
||||
|
||||
// generate result + 'error' field + 'code' field
|
||||
// 2nd param must be Builder and not Slice
|
||||
generateOk(arangodb::rest::ResponseCode::OK, builder);
|
||||
}
|
||||
|
||||
} // iresearch
|
||||
} // arangodb
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- END-OF-FILE
|
||||
// -----------------------------------------------------------------------------
|
|
@ -0,0 +1,70 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// DISCLAIMER
|
||||
///
|
||||
/// Copyright 2019 ArangoDB 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 ArangoDB GmbH, Cologne, Germany
|
||||
///
|
||||
/// @author Andrey Abramov
|
||||
/// @author Vasiliy Nabatchikov
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef ARANGOD_REST_HANDLER_REST_ANALYZER_HANDLER_H
|
||||
#define ARANGOD_REST_HANDLER_REST_ANALYZER_HANDLER_H 1
|
||||
|
||||
#include "RestHandler/RestVocbaseBaseHandler.h"
|
||||
|
||||
namespace arangodb {
|
||||
namespace iresearch {
|
||||
|
||||
class IResearchAnalyzerFeature; // forward declaration
|
||||
|
||||
class RestAnalyzerHandler final: public RestVocbaseBaseHandler {
|
||||
public:
|
||||
// @note RestHandlerFactory::createHandler(...) passes raw pointers for
|
||||
// request/response to RestHandlerCreator::createNoData(...)
|
||||
RestAnalyzerHandler( // constructor
|
||||
arangodb::GeneralRequest* request, // request
|
||||
arangodb::GeneralResponse* response // response
|
||||
);
|
||||
|
||||
virtual arangodb::RestStatus execute() override;
|
||||
|
||||
virtual arangodb::RequestLane lane() const override {
|
||||
return arangodb::RequestLane::CLIENT_SLOW;
|
||||
}
|
||||
|
||||
virtual char const* name() const override { return "RestAnalyzerHandler"; }
|
||||
|
||||
private:
|
||||
void createAnalyzer(
|
||||
IResearchAnalyzerFeature& analyzers, // analyzer feature
|
||||
TRI_vocbase_t const* sysVocbase // system vocbase
|
||||
);
|
||||
void getAnalyzer(
|
||||
IResearchAnalyzerFeature& analyzers, // analyzer feature
|
||||
std::string const& name // analyzer name (normalized)
|
||||
);
|
||||
void getAnalyzers(IResearchAnalyzerFeature& analyzers);
|
||||
void removeAnalyzer(
|
||||
IResearchAnalyzerFeature& analyzers, // analyzer feature
|
||||
std::string const& name // analyzer name (normalized)
|
||||
);
|
||||
};
|
||||
|
||||
} // iresearch
|
||||
} // arangodb
|
||||
|
||||
#endif
|
|
@ -0,0 +1,705 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// DISCLAIMER
|
||||
///
|
||||
/// Copyright 2019 ArangoDB 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 ArangoDB GmbH, Cologne, Germany
|
||||
///
|
||||
/// @author Andrey Abramov
|
||||
/// @author Vasiliy Nabatchikov
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "v8-analyzers.h"
|
||||
#include "Basics/StringUtils.h"
|
||||
#include "IResearch/IResearchAnalyzerFeature.h"
|
||||
#include "IResearch/VelocyPackHelper.h"
|
||||
#include "RestServer/SystemDatabaseFeature.h"
|
||||
#include "Transaction/V8Context.h"
|
||||
#include "V8/v8-conv.h"
|
||||
#include "V8/v8-globals.h"
|
||||
#include "V8/v8-vpack.h"
|
||||
#include "V8Server/v8-externals.h"
|
||||
#include "V8Server/v8-vocbaseprivate.h"
|
||||
#include "VocBase/vocbase.h"
|
||||
#include "velocypack/Parser.h"
|
||||
|
||||
namespace {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief unwraps an analyser wrapped via WrapAnalyzer(...)
|
||||
/// @return collection or nullptr on failure
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
arangodb::iresearch::IResearchAnalyzerFeature::AnalyzerPool* UnwrapAnalyzer( // unwrap
|
||||
v8::Isolate* isolate, // isolate
|
||||
v8::Local<v8::Object> const& holder // holder
|
||||
) {
|
||||
return TRI_UnwrapClass<arangodb::iresearch::IResearchAnalyzerFeature::AnalyzerPool>( // unwrap class
|
||||
holder, WRP_IRESEARCH_ANALYZER_TYPE, TRI_IGETC // args
|
||||
);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief wraps an Analyzer
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
v8::Handle<v8::Object> WrapAnalyzer( // wrap analyzer
|
||||
v8::Isolate* isolate, // isolate
|
||||
arangodb::iresearch::IResearchAnalyzerFeature::AnalyzerPool::ptr const& analyzer // analyzer
|
||||
) {
|
||||
v8::EscapableHandleScope scope(isolate);
|
||||
TRI_GET_GLOBALS();
|
||||
TRI_GET_GLOBAL(IResearchAnalyzerTempl, v8::ObjectTemplate);
|
||||
auto result = IResearchAnalyzerTempl->NewInstance();
|
||||
|
||||
if (result.IsEmpty()) {
|
||||
return scope.Escape<v8::Object>(result);
|
||||
}
|
||||
|
||||
auto itr = TRI_v8_global_t::SharedPtrPersistent::emplace(*isolate, analyzer);
|
||||
auto& entry = itr.first;
|
||||
|
||||
result->SetInternalField( // required for TRI_UnwrapClass(...)
|
||||
SLOT_CLASS_TYPE, v8::Integer::New(isolate, WRP_IRESEARCH_ANALYZER_TYPE) // args
|
||||
);
|
||||
result->SetInternalField(SLOT_CLASS, entry.get());
|
||||
|
||||
return scope.Escape<v8::Object>(result);
|
||||
}
|
||||
|
||||
void JS_AnalyzerFeatures(v8::FunctionCallbackInfo<v8::Value> const& args) {
|
||||
TRI_V8_TRY_CATCH_BEGIN(isolate);
|
||||
v8::HandleScope scope(isolate);
|
||||
auto* analyzer = UnwrapAnalyzer(isolate, args.Holder());
|
||||
|
||||
if (!analyzer) {
|
||||
TRI_V8_THROW_EXCEPTION_INTERNAL("cannot extract analyzer");
|
||||
}
|
||||
|
||||
// ...........................................................................
|
||||
// end of parameter parsing
|
||||
// ...........................................................................
|
||||
|
||||
if (!arangodb::iresearch::IResearchAnalyzerFeature::canUse(analyzer->name(), arangodb::auth::Level::RO)) {
|
||||
TRI_V8_THROW_EXCEPTION_MESSAGE( // exception
|
||||
TRI_ERROR_FORBIDDEN, // code
|
||||
"insufficient rights to get analyzer" // message
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
auto i = 0;
|
||||
auto result = v8::Array::New(isolate);
|
||||
|
||||
for (auto& feature: analyzer->features()) {
|
||||
if (feature) { // valid
|
||||
if (feature->name().null()) {
|
||||
result->Set(i++, v8::Null(isolate));
|
||||
} else {
|
||||
result->Set( // set value
|
||||
i++, TRI_V8_STD_STRING(isolate, std::string(feature->name())) // args
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TRI_V8_RETURN(result);
|
||||
} catch (arangodb::basics::Exception const& ex) {
|
||||
TRI_V8_THROW_EXCEPTION_MESSAGE(ex.code(), ex.what());
|
||||
} catch (std::exception const& ex) {
|
||||
TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, ex.what());
|
||||
} catch (...) {
|
||||
TRI_V8_THROW_EXCEPTION_MESSAGE( // exception
|
||||
TRI_ERROR_INTERNAL, // code
|
||||
"cannot access analyzer features" // message
|
||||
);
|
||||
}
|
||||
|
||||
TRI_V8_TRY_CATCH_END
|
||||
}
|
||||
|
||||
void JS_AnalyzerName(v8::FunctionCallbackInfo<v8::Value> const& args) {
|
||||
TRI_V8_TRY_CATCH_BEGIN(isolate);
|
||||
v8::HandleScope scope(isolate);
|
||||
auto* analyzer = UnwrapAnalyzer(isolate, args.Holder());
|
||||
|
||||
if (!analyzer) {
|
||||
TRI_V8_THROW_EXCEPTION_INTERNAL("cannot extract analyzer");
|
||||
}
|
||||
|
||||
// ...........................................................................
|
||||
// end of parameter parsing
|
||||
// ...........................................................................
|
||||
|
||||
if (!arangodb::iresearch::IResearchAnalyzerFeature::canUse(analyzer->name(), arangodb::auth::Level::RO)) {
|
||||
TRI_V8_THROW_EXCEPTION_MESSAGE( // exception
|
||||
TRI_ERROR_FORBIDDEN, // code
|
||||
"insufficient rights to get analyzer" // message
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
auto result = TRI_V8_STD_STRING(isolate, analyzer->name());
|
||||
|
||||
TRI_V8_RETURN(result);
|
||||
} catch (arangodb::basics::Exception const& ex) {
|
||||
TRI_V8_THROW_EXCEPTION_MESSAGE(ex.code(), ex.what());
|
||||
} catch (std::exception const& ex) {
|
||||
TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, ex.what());
|
||||
} catch (...) {
|
||||
TRI_V8_THROW_EXCEPTION_MESSAGE( // exception
|
||||
TRI_ERROR_INTERNAL, // code
|
||||
"cannot access analyzer name" // message
|
||||
);
|
||||
}
|
||||
|
||||
TRI_V8_TRY_CATCH_END
|
||||
}
|
||||
|
||||
void JS_AnalyzerProperties(v8::FunctionCallbackInfo<v8::Value> const& args) {
|
||||
TRI_V8_TRY_CATCH_BEGIN(isolate);
|
||||
v8::HandleScope scope(isolate);
|
||||
auto* analyzer = UnwrapAnalyzer(isolate, args.Holder());
|
||||
|
||||
if (!analyzer) {
|
||||
TRI_V8_THROW_EXCEPTION_INTERNAL("cannot extract analyzer");
|
||||
}
|
||||
|
||||
// ...........................................................................
|
||||
// end of parameter parsing
|
||||
// ...........................................................................
|
||||
|
||||
if (!arangodb::iresearch::IResearchAnalyzerFeature::canUse(analyzer->name(), arangodb::auth::Level::RO)) {
|
||||
TRI_V8_THROW_EXCEPTION_MESSAGE( // exception
|
||||
TRI_ERROR_FORBIDDEN, // code
|
||||
"insufficient rights to get analyzer" // message
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
if (analyzer->properties().null()) {
|
||||
TRI_V8_RETURN(v8::Null(isolate));
|
||||
}
|
||||
|
||||
auto result = // result
|
||||
TRI_V8_STD_STRING(isolate, std::string(analyzer->properties()));
|
||||
|
||||
TRI_V8_RETURN(result);
|
||||
} catch (arangodb::basics::Exception const& ex) {
|
||||
TRI_V8_THROW_EXCEPTION_MESSAGE(ex.code(), ex.what());
|
||||
} catch (std::exception const& ex) {
|
||||
TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, ex.what());
|
||||
} catch (...) {
|
||||
TRI_V8_THROW_EXCEPTION_MESSAGE( // exception
|
||||
TRI_ERROR_INTERNAL, // code
|
||||
"cannot access analyzer properties" // message
|
||||
);
|
||||
}
|
||||
|
||||
TRI_V8_TRY_CATCH_END
|
||||
}
|
||||
|
||||
void JS_AnalyzerType(v8::FunctionCallbackInfo<v8::Value> const& args) {
|
||||
TRI_V8_TRY_CATCH_BEGIN(isolate);
|
||||
v8::HandleScope scope(isolate);
|
||||
auto* analyzer = UnwrapAnalyzer(isolate, args.Holder());
|
||||
|
||||
if (!analyzer) {
|
||||
TRI_V8_THROW_EXCEPTION_INTERNAL("cannot extract analyzer");
|
||||
}
|
||||
|
||||
// ...........................................................................
|
||||
// end of parameter parsing
|
||||
// ...........................................................................
|
||||
|
||||
if (!arangodb::iresearch::IResearchAnalyzerFeature::canUse(analyzer->name(), arangodb::auth::Level::RO)) {
|
||||
TRI_V8_THROW_EXCEPTION_MESSAGE( // exception
|
||||
TRI_ERROR_FORBIDDEN, // code
|
||||
"insufficient rights to get analyzer" // message
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
if (analyzer->type().null()) {
|
||||
TRI_V8_RETURN(v8::Null(isolate));
|
||||
}
|
||||
|
||||
auto result = TRI_V8_STD_STRING(isolate, std::string(analyzer->type()));
|
||||
|
||||
TRI_V8_RETURN(result);
|
||||
} catch (arangodb::basics::Exception const& ex) {
|
||||
TRI_V8_THROW_EXCEPTION_MESSAGE(ex.code(), ex.what());
|
||||
} catch (std::exception const& ex) {
|
||||
TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, ex.what());
|
||||
} catch (...) {
|
||||
TRI_V8_THROW_EXCEPTION_MESSAGE( // exception
|
||||
TRI_ERROR_INTERNAL, // code
|
||||
"cannot access analyzer type" // message
|
||||
);
|
||||
}
|
||||
|
||||
TRI_V8_TRY_CATCH_END
|
||||
}
|
||||
|
||||
void JS_Create(v8::FunctionCallbackInfo<v8::Value> const& args) {
|
||||
TRI_V8_TRY_CATCH_BEGIN(isolate);
|
||||
v8::HandleScope scope(isolate);
|
||||
auto& vocbase = GetContextVocBase(isolate);
|
||||
|
||||
if (vocbase.isDangling()) {
|
||||
TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND);
|
||||
}
|
||||
|
||||
// we require at least 2 but no more than 4 arguments
|
||||
// save(name: <string>, type: <string>[, parameters: <json>[, features: <string-array>]]);
|
||||
if (args.Length() < 2 // too few args
|
||||
|| args.Length() > 4 // too many args
|
||||
|| !args[0]->IsString() // not a string
|
||||
|| !args[1]->IsString() // not a string
|
||||
) {
|
||||
TRI_V8_THROW_EXCEPTION_USAGE("save(<name>, <type>[, <properties>[, <features>]])");
|
||||
}
|
||||
|
||||
PREVENT_EMBEDDED_TRANSACTION();
|
||||
|
||||
auto* analyzers = arangodb::application_features::ApplicationServer::lookupFeature< // find feature
|
||||
arangodb::iresearch::IResearchAnalyzerFeature // feature type
|
||||
>();
|
||||
|
||||
if (!analyzers) {
|
||||
TRI_V8_THROW_EXCEPTION_MESSAGE( // exception
|
||||
TRI_ERROR_INTERNAL, // code
|
||||
std::string("failure to find feature '") + arangodb::iresearch::IResearchAnalyzerFeature::name() + "'" // message
|
||||
);
|
||||
}
|
||||
|
||||
auto* sysDatabase = arangodb::application_features::ApplicationServer::lookupFeature< // find feature
|
||||
arangodb::SystemDatabaseFeature // featue type
|
||||
>();
|
||||
auto sysVocbase = sysDatabase ? sysDatabase->use() : nullptr;
|
||||
|
||||
auto name = TRI_ObjectToString(isolate, args[0]);
|
||||
std::string nameBuf;
|
||||
|
||||
if (sysVocbase) {
|
||||
nameBuf = arangodb::iresearch::IResearchAnalyzerFeature::normalize( // normalize
|
||||
name, vocbase, *sysVocbase // args
|
||||
);
|
||||
name = nameBuf;
|
||||
};
|
||||
|
||||
auto type = TRI_ObjectToString(isolate, args[1]);
|
||||
|
||||
irs::string_ref properties;
|
||||
std::string propertiesBuf;
|
||||
|
||||
if (args.Length() > 2) { // have properties
|
||||
if (args[2]->IsString()) {
|
||||
propertiesBuf = TRI_ObjectToString(isolate, args[2]);
|
||||
properties = propertiesBuf;
|
||||
} else if (args[2]->IsObject()) {
|
||||
auto value = // value
|
||||
args[2]->ToObject(TRI_IGETC).FromMaybe(v8::Local<v8::Object>());
|
||||
arangodb::velocypack::Builder builder;
|
||||
auto res = TRI_V8ToVPack(isolate, builder, value, false);
|
||||
|
||||
if (TRI_ERROR_NO_ERROR != res) {
|
||||
TRI_V8_THROW_EXCEPTION(res);
|
||||
}
|
||||
|
||||
propertiesBuf = builder.toString();
|
||||
properties = propertiesBuf;
|
||||
} else if (!args[2]->IsNull()) {
|
||||
TRI_V8_THROW_TYPE_ERROR("<properties> must be an object");
|
||||
}
|
||||
}
|
||||
|
||||
irs::flags features;
|
||||
|
||||
if (args.Length() > 3) { // have features
|
||||
if (!args[3]->IsArray()) {
|
||||
TRI_V8_THROW_TYPE_ERROR("<features> must be an array");
|
||||
}
|
||||
|
||||
auto value = v8::Local<v8::Array>::Cast(args[3]);
|
||||
|
||||
for (uint32_t i = 0, count = value->Length(); i < count; ++i) {
|
||||
auto subValue = value->Get(i);
|
||||
|
||||
if (!subValue->IsString()) {
|
||||
TRI_V8_THROW_TYPE_ERROR("<feature> must be a string");
|
||||
}
|
||||
|
||||
auto* feature = // feature
|
||||
irs::attribute::type_id::get(TRI_ObjectToString(isolate, subValue));
|
||||
|
||||
if (!feature) {
|
||||
TRI_V8_THROW_TYPE_ERROR("<feature> not supported");
|
||||
}
|
||||
|
||||
features.add(*feature);
|
||||
}
|
||||
}
|
||||
|
||||
// ...........................................................................
|
||||
// end of parameter parsing
|
||||
// ...........................................................................
|
||||
|
||||
if (!arangodb::iresearch::IResearchAnalyzerFeature::canUse(name, arangodb::auth::Level::RW)) {
|
||||
TRI_V8_THROW_EXCEPTION_MESSAGE( // exception
|
||||
TRI_ERROR_FORBIDDEN, // code
|
||||
"insufficient rights to create analyzer" // message
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
|
||||
auto res = analyzers->emplace(result, name, type, properties, features);
|
||||
|
||||
if (!res.ok()) {
|
||||
TRI_V8_THROW_EXCEPTION(res);
|
||||
}
|
||||
|
||||
if (!result.first) {
|
||||
TRI_V8_THROW_EXCEPTION_MESSAGE( // exception
|
||||
TRI_ERROR_INTERNAL, // code
|
||||
"problem creating view" // message
|
||||
);
|
||||
}
|
||||
|
||||
auto v8Result = WrapAnalyzer(isolate, result.first);
|
||||
|
||||
if (v8Result.IsEmpty()) {
|
||||
TRI_V8_THROW_EXCEPTION_MEMORY();
|
||||
}
|
||||
|
||||
TRI_V8_RETURN(v8Result);
|
||||
} catch (arangodb::basics::Exception const& ex) {
|
||||
TRI_V8_THROW_EXCEPTION_MESSAGE(ex.code(), ex.what());
|
||||
} catch (std::exception const& ex) {
|
||||
TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, ex.what());
|
||||
} catch (...) {
|
||||
TRI_V8_THROW_EXCEPTION_MESSAGE( // exception
|
||||
TRI_ERROR_INTERNAL, // code
|
||||
"cannot create analyzer" // message
|
||||
);
|
||||
}
|
||||
|
||||
TRI_V8_TRY_CATCH_END
|
||||
}
|
||||
|
||||
void JS_Get(v8::FunctionCallbackInfo<v8::Value> const& args) {
|
||||
TRI_V8_TRY_CATCH_BEGIN(isolate);
|
||||
v8::HandleScope scope(isolate);
|
||||
auto& vocbase = GetContextVocBase(isolate);
|
||||
|
||||
if (vocbase.isDropped()) {
|
||||
TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND);
|
||||
}
|
||||
|
||||
// expecting one argument
|
||||
// analyzer(name: <string>);
|
||||
if (args.Length() != 1 || !args[0]->IsString()) {
|
||||
TRI_V8_THROW_EXCEPTION_USAGE("analyser(<name>)");
|
||||
}
|
||||
|
||||
PREVENT_EMBEDDED_TRANSACTION();
|
||||
|
||||
auto* analyzers = arangodb::application_features::ApplicationServer::lookupFeature< // find feature
|
||||
arangodb::iresearch::IResearchAnalyzerFeature // feature type
|
||||
>();
|
||||
|
||||
if (!analyzers) {
|
||||
TRI_V8_THROW_EXCEPTION_MESSAGE( // exception
|
||||
TRI_ERROR_INTERNAL, // code
|
||||
std::string("failure to find feature '") + arangodb::iresearch::IResearchAnalyzerFeature::name() + "'" // message
|
||||
);
|
||||
}
|
||||
|
||||
auto* sysDatabase = arangodb::application_features::ApplicationServer::lookupFeature< // find feature
|
||||
arangodb::SystemDatabaseFeature // featue type
|
||||
>();
|
||||
auto sysVocbase = sysDatabase ? sysDatabase->use() : nullptr;
|
||||
|
||||
auto name = TRI_ObjectToString(isolate, args[0]);
|
||||
std::string nameBuf;
|
||||
|
||||
if (sysVocbase) {
|
||||
nameBuf = arangodb::iresearch::IResearchAnalyzerFeature::normalize( // normalize
|
||||
name, vocbase, *sysVocbase // args
|
||||
);
|
||||
name = nameBuf;
|
||||
};
|
||||
|
||||
// ...........................................................................
|
||||
// end of parameter parsing
|
||||
// ...........................................................................
|
||||
|
||||
if (!arangodb::iresearch::IResearchAnalyzerFeature::canUse(name, arangodb::auth::Level::RO)) {
|
||||
TRI_V8_THROW_EXCEPTION_MESSAGE( // exception
|
||||
TRI_ERROR_FORBIDDEN, // code
|
||||
"insufficient rights to get analyzer" // message
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
auto analyzer = analyzers->get(name);
|
||||
|
||||
if (!analyzer) {
|
||||
TRI_V8_RETURN_NULL();
|
||||
}
|
||||
|
||||
auto result = WrapAnalyzer(isolate, analyzer);
|
||||
|
||||
if (result.IsEmpty()) {
|
||||
TRI_V8_THROW_EXCEPTION_MEMORY();
|
||||
}
|
||||
|
||||
TRI_V8_RETURN(result);
|
||||
} catch (arangodb::basics::Exception const& ex) {
|
||||
TRI_V8_THROW_EXCEPTION_MESSAGE(ex.code(), ex.what());
|
||||
} catch (std::exception const& ex) {
|
||||
TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, ex.what());
|
||||
} catch (...) {
|
||||
TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, "cannot get analyzer");
|
||||
}
|
||||
|
||||
TRI_V8_TRY_CATCH_END
|
||||
}
|
||||
|
||||
void JS_List(v8::FunctionCallbackInfo<v8::Value> const& args) {
|
||||
TRI_V8_TRY_CATCH_BEGIN(isolate);
|
||||
v8::HandleScope scope(isolate);
|
||||
auto& vocbase = GetContextVocBase(isolate);
|
||||
|
||||
if (vocbase.isDropped()) {
|
||||
TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND);
|
||||
}
|
||||
|
||||
auto* analyzers = arangodb::application_features::ApplicationServer::lookupFeature< // find feature
|
||||
arangodb::iresearch::IResearchAnalyzerFeature // feature type
|
||||
>();
|
||||
|
||||
if (!analyzers) {
|
||||
TRI_V8_THROW_EXCEPTION_MESSAGE( // exception
|
||||
TRI_ERROR_INTERNAL, // code
|
||||
std::string("failure to find feature '") + arangodb::iresearch::IResearchAnalyzerFeature::name() + "'" // message
|
||||
);
|
||||
}
|
||||
|
||||
auto* sysDatabase = arangodb::application_features::ApplicationServer::lookupFeature< // find feature
|
||||
arangodb::SystemDatabaseFeature // featue type
|
||||
>();
|
||||
auto sysVocbase = sysDatabase ? sysDatabase->use() : nullptr;
|
||||
|
||||
// ...........................................................................
|
||||
// end of parameter parsing
|
||||
// ...........................................................................
|
||||
|
||||
typedef arangodb::iresearch::IResearchAnalyzerFeature::AnalyzerPool::ptr AnalyzerPoolPtr;
|
||||
static const auto nameLess = []( // analyzer name less
|
||||
AnalyzerPoolPtr const& lhs, // left hand side
|
||||
AnalyzerPoolPtr const& rhs // right hand side
|
||||
)->bool {
|
||||
return arangodb::basics::StringUtils::tolower(lhs->name()) < arangodb::basics::StringUtils::tolower(rhs->name());
|
||||
};
|
||||
std::vector<AnalyzerPoolPtr> result;
|
||||
auto visitor = [&result](AnalyzerPoolPtr const& analyzer)->bool {
|
||||
if (analyzer) {
|
||||
result.emplace_back(analyzer);
|
||||
}
|
||||
|
||||
return true; // continue with next analyzer
|
||||
};
|
||||
|
||||
try {
|
||||
if (arangodb::iresearch::IResearchAnalyzerFeature::canUse(vocbase, arangodb::auth::Level::RO)) {
|
||||
analyzers->visit(visitor, &vocbase);
|
||||
}
|
||||
|
||||
// include analyzers from the system vocbase if possible
|
||||
if (sysVocbase // have system vocbase
|
||||
&& sysVocbase->name() != vocbase.name() // not same vocbase as current
|
||||
&& arangodb::iresearch::IResearchAnalyzerFeature::canUse(*sysVocbase, arangodb::auth::Level::RO)) {
|
||||
analyzers->visit(visitor, sysVocbase.get());
|
||||
}
|
||||
|
||||
std::sort(result.begin(), result.end(), nameLess);
|
||||
|
||||
auto v8Result = v8::Array::New(isolate);
|
||||
|
||||
for (size_t i = 0, count = result.size(); i < count; ++i) {
|
||||
auto analyzer = WrapAnalyzer(isolate, result[i]);
|
||||
|
||||
if (analyzer.IsEmpty()) {
|
||||
TRI_V8_THROW_EXCEPTION_MEMORY();
|
||||
}
|
||||
|
||||
v8Result->Set(i, analyzer);
|
||||
}
|
||||
|
||||
TRI_V8_RETURN(v8Result);
|
||||
} catch (arangodb::basics::Exception const& ex) {
|
||||
TRI_V8_THROW_EXCEPTION_MESSAGE(ex.code(), ex.what());
|
||||
} catch (std::exception const& ex) {
|
||||
TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, ex.what());
|
||||
} catch (...) {
|
||||
TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, "cannot list analyzers");
|
||||
}
|
||||
|
||||
TRI_V8_TRY_CATCH_END
|
||||
}
|
||||
|
||||
void JS_Remove(v8::FunctionCallbackInfo<v8::Value> const& args) {
|
||||
TRI_V8_TRY_CATCH_BEGIN(isolate);
|
||||
v8::HandleScope scope(isolate);
|
||||
auto& vocbase = GetContextVocBase(isolate);
|
||||
|
||||
if (vocbase.isDangling()) {
|
||||
TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND);
|
||||
}
|
||||
|
||||
// we require 1 string argument and an optional boolean argument
|
||||
// remove(name: <string>[, force: <bool>])
|
||||
if (args.Length() < 1 // too few args
|
||||
|| args.Length() > 2 // too many args
|
||||
|| !args[0]->IsString() // not a string
|
||||
) {
|
||||
TRI_V8_THROW_EXCEPTION_USAGE("remove(<name> [, <force>])");
|
||||
}
|
||||
|
||||
PREVENT_EMBEDDED_TRANSACTION();
|
||||
|
||||
auto* analyzers = arangodb::application_features::ApplicationServer::lookupFeature< // find feature
|
||||
arangodb::iresearch::IResearchAnalyzerFeature // feature type
|
||||
>();
|
||||
|
||||
if (!analyzers) {
|
||||
TRI_V8_THROW_EXCEPTION_MESSAGE( // exception
|
||||
TRI_ERROR_INTERNAL, // code
|
||||
std::string("failure to find feature '") + arangodb::iresearch::IResearchAnalyzerFeature::name() + "'" // message
|
||||
);
|
||||
}
|
||||
|
||||
auto* sysDatabase = arangodb::application_features::ApplicationServer::lookupFeature< // find feature
|
||||
arangodb::SystemDatabaseFeature // featue type
|
||||
>();
|
||||
auto sysVocbase = sysDatabase ? sysDatabase->use() : nullptr;
|
||||
|
||||
auto name = TRI_ObjectToString(isolate, args[0]);
|
||||
std::string nameBuf;
|
||||
|
||||
if (sysVocbase) {
|
||||
nameBuf = arangodb::iresearch::IResearchAnalyzerFeature::normalize( // normalize
|
||||
name, vocbase, *sysVocbase // args
|
||||
);
|
||||
name = nameBuf;
|
||||
};
|
||||
|
||||
bool force = false;
|
||||
|
||||
if (args.Length() > 1) {
|
||||
if (!args[1]->IsBoolean() && !args[1]->IsBooleanObject()) {
|
||||
TRI_V8_THROW_TYPE_ERROR("<force> must be a boolean");
|
||||
}
|
||||
|
||||
force = TRI_ObjectToBoolean(isolate, args[1]);
|
||||
}
|
||||
|
||||
// ...........................................................................
|
||||
// end of parameter parsing
|
||||
// ...........................................................................
|
||||
|
||||
if (!arangodb::iresearch::IResearchAnalyzerFeature::canUse(name, arangodb::auth::Level::RW)) {
|
||||
TRI_V8_THROW_EXCEPTION_MESSAGE( // exception
|
||||
TRI_ERROR_FORBIDDEN, // code
|
||||
"insufficient rights to remove analyzer" // message
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
auto res = analyzers->remove(name, force);
|
||||
|
||||
if (!res.ok()) {
|
||||
TRI_V8_THROW_EXCEPTION(res);
|
||||
}
|
||||
|
||||
TRI_V8_RETURN_UNDEFINED();
|
||||
} catch (arangodb::basics::Exception const& ex) {
|
||||
TRI_V8_THROW_EXCEPTION_MESSAGE(ex.code(), ex.what());
|
||||
} catch (std::exception const& ex) {
|
||||
TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, ex.what());
|
||||
} catch (...) {
|
||||
TRI_V8_THROW_EXCEPTION_MESSAGE( // exception
|
||||
TRI_ERROR_INTERNAL, // code
|
||||
"cannot remove analyzer" // message
|
||||
);
|
||||
}
|
||||
|
||||
TRI_V8_TRY_CATCH_END
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace arangodb {
|
||||
namespace iresearch {
|
||||
|
||||
void TRI_InitV8Analyzers(
|
||||
v8::Handle<v8::Context> context, // v8 context
|
||||
TRI_vocbase_t& vocbase, // vocbase
|
||||
TRI_v8_global_t& v8g, // v8 globals
|
||||
v8::Isolate& isolate, // v8 isolate
|
||||
v8::Handle<v8::ObjectTemplate> ArangoDBNS // v8 handler root
|
||||
) {
|
||||
// 'analyzers' feature functions
|
||||
{
|
||||
auto fnTemplate = v8::FunctionTemplate::New(&isolate);
|
||||
|
||||
fnTemplate->SetClassName(TRI_V8_ASCII_STRING(&isolate, "ArangoAnalyzersCtor"));
|
||||
|
||||
auto objTemplate = fnTemplate->InstanceTemplate();
|
||||
|
||||
objTemplate->SetInternalFieldCount(0);
|
||||
TRI_AddMethodVocbase(&isolate, objTemplate, TRI_V8_ASCII_STRING(&isolate, "analyzer"), JS_Get);
|
||||
TRI_AddMethodVocbase(&isolate, objTemplate, TRI_V8_ASCII_STRING(&isolate, "remove"), JS_Remove);
|
||||
TRI_AddMethodVocbase(&isolate, objTemplate, TRI_V8_ASCII_STRING(&isolate, "save"), JS_Create);
|
||||
TRI_AddMethodVocbase(&isolate, objTemplate, TRI_V8_ASCII_STRING(&isolate, "toArray"), JS_List);
|
||||
|
||||
v8g.IResearchAnalyzersTempl.Reset(&isolate, objTemplate);
|
||||
}
|
||||
|
||||
// individual analyzer functions
|
||||
{
|
||||
auto fnTemplate = v8::FunctionTemplate::New(&isolate);
|
||||
|
||||
fnTemplate->SetClassName(TRI_V8_ASCII_STRING(&isolate, "ArangoAnalyzer"));
|
||||
|
||||
auto objTemplate = fnTemplate->InstanceTemplate();
|
||||
|
||||
objTemplate->SetInternalFieldCount(2); // SLOT_CLASS_TYPE + SLOT_CLASS
|
||||
TRI_AddMethodVocbase(&isolate, objTemplate, TRI_V8_ASCII_STRING(&isolate, "features"), JS_AnalyzerFeatures);
|
||||
TRI_AddMethodVocbase(&isolate, objTemplate, TRI_V8_ASCII_STRING(&isolate, "name"), JS_AnalyzerName);
|
||||
TRI_AddMethodVocbase(&isolate, objTemplate, TRI_V8_ASCII_STRING(&isolate, "properties"), JS_AnalyzerProperties);
|
||||
TRI_AddMethodVocbase(&isolate, objTemplate, TRI_V8_ASCII_STRING(&isolate, "type"), JS_AnalyzerType);
|
||||
|
||||
v8g.IResearchAnalyzerTempl.Reset(&isolate, objTemplate);
|
||||
TRI_AddGlobalFunctionVocbase(&isolate, TRI_V8_ASCII_STRING(&isolate, "ArangoAnalyzer"), fnTemplate->GetFunction());
|
||||
}
|
||||
}
|
||||
|
||||
} // iresearch
|
||||
} // arangodb
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- END-OF-FILE
|
||||
// -----------------------------------------------------------------------------
|
|
@ -0,0 +1,49 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// DISCLAIMER
|
||||
///
|
||||
/// Copyright 2019 ArangoDB 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 ArangoDB GmbH, Cologne, Germany
|
||||
///
|
||||
/// @author Andrey Abramov
|
||||
/// @author Vasiliy Nabatchikov
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef ARANGOD_V8_SERVER_V8_ANALYZERS_H
|
||||
#define ARANGOD_V8_SERVER_V8_ANALYZERS_H 1
|
||||
|
||||
#include <v8.h>
|
||||
|
||||
struct TRI_v8_global_t; // forward declaration
|
||||
struct TRI_vocbase_t; // forward declaration
|
||||
|
||||
namespace arangodb {
|
||||
namespace iresearch {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief add analyzer related handlers to 'ArangoDBNS'
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void TRI_InitV8Analyzers(
|
||||
v8::Handle<v8::Context> context, // v8 context
|
||||
TRI_vocbase_t& vocbase, // vocbase
|
||||
TRI_v8_global_t& v8g, // v8 globals
|
||||
v8::Isolate& isolate, // v8 isolate
|
||||
v8::Handle<v8::ObjectTemplate> ArangoDBNS // v8 handler root
|
||||
);
|
||||
|
||||
} // iresearch
|
||||
} // arangodb
|
||||
|
||||
#endif
|
|
@ -31,7 +31,6 @@
|
|||
#include "v8-collection.h"
|
||||
|
||||
using namespace arangodb;
|
||||
using namespace arangodb::basics;
|
||||
|
||||
/// @brief check if a name belongs to a collection
|
||||
bool EqualCollection(CollectionNameResolver const* resolver, std::string const& collectionName,
|
||||
|
@ -60,62 +59,6 @@ bool EqualCollection(CollectionNameResolver const* resolver, std::string const&
|
|||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief wraps a LogicalCollection
|
||||
/// Note that if collection is a local collection, then the object will never
|
||||
/// be freed. If it is not a local collection (coordinator case), then delete
|
||||
/// will be called when the V8 object is garbage collected.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
v8::Handle<v8::Object> WrapCollection(v8::Isolate* isolate,
|
||||
std::shared_ptr<arangodb::LogicalCollection> const& collection) {
|
||||
v8::EscapableHandleScope scope(isolate);
|
||||
|
||||
TRI_GET_GLOBALS();
|
||||
TRI_GET_GLOBAL(VocbaseColTempl, v8::ObjectTemplate);
|
||||
v8::Handle<v8::Object> result = VocbaseColTempl->NewInstance();
|
||||
|
||||
if (!result.IsEmpty()) {
|
||||
auto* ptr = collection.get();
|
||||
auto itr = v8g->JSDatasources.emplace(
|
||||
std::piecewise_construct, std::forward_as_tuple(collection.get()),
|
||||
std::forward_as_tuple(isolate, collection,
|
||||
[ptr]() -> void { // FIXME TODO find a way to move this callback
|
||||
// code into DataSourcePersistent
|
||||
TRI_ASSERT(!ptr->vocbase().isDangling());
|
||||
ptr->vocbase().release(); // decrease the reference-counter for
|
||||
// the database
|
||||
}));
|
||||
auto& entry = itr.first->second;
|
||||
|
||||
if (itr.second) { // FIXME TODO find a way to move this code into
|
||||
// DataSourcePersistent
|
||||
TRI_ASSERT(!ptr->vocbase().isDangling());
|
||||
ptr->vocbase().forceUse(); // increase the reference-counter for the database
|
||||
}
|
||||
|
||||
result->SetInternalField(SLOT_CLASS_TYPE, v8::Integer::New(isolate, WRP_VOCBASE_COL_TYPE));
|
||||
result->SetInternalField(SLOT_CLASS, entry.get());
|
||||
result->SetInternalField(SLOT_EXTERNAL, entry.get());
|
||||
|
||||
TRI_GET_GLOBAL_STRING(_IdKey);
|
||||
TRI_GET_GLOBAL_STRING(_DbNameKey);
|
||||
TRI_GET_GLOBAL_STRING(VersionKeyHidden);
|
||||
result
|
||||
->DefineOwnProperty(TRI_IGETC, _IdKey,
|
||||
TRI_V8UInt64String<TRI_voc_cid_t>(isolate,
|
||||
collection->id()),
|
||||
v8::ReadOnly)
|
||||
.FromMaybe(true); // Ignoring result
|
||||
result->Set(_DbNameKey, TRI_V8_STD_STRING(isolate, collection->vocbase().name()));
|
||||
result
|
||||
->DefineOwnProperty(TRI_IGETC, VersionKeyHidden,
|
||||
v8::Integer::NewFromUnsigned(isolate, 5), v8::DontEnum)
|
||||
.FromMaybe(false); // ignore return value
|
||||
}
|
||||
|
||||
return scope.Escape<v8::Object>(result);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief unwrap a LogicalCollection wrapped via WrapCollection(...)
|
||||
/// @return collection or nullptr on failure
|
||||
|
@ -124,3 +67,59 @@ arangodb::LogicalCollection* UnwrapCollection(v8::Isolate* isolate,
|
|||
v8::Local<v8::Object> const& holder) {
|
||||
return TRI_UnwrapClass<arangodb::LogicalCollection>(holder, WRP_VOCBASE_COL_TYPE, TRI_IGETC);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief wraps a LogicalCollection
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
v8::Handle<v8::Object> WrapCollection( // wrap collection
|
||||
v8::Isolate* isolate, // isolate
|
||||
std::shared_ptr<arangodb::LogicalCollection> const& collection // collection
|
||||
) {
|
||||
v8::EscapableHandleScope scope(isolate);
|
||||
TRI_GET_GLOBALS();
|
||||
TRI_GET_GLOBAL(VocbaseColTempl, v8::ObjectTemplate);
|
||||
v8::Handle<v8::Object> result = VocbaseColTempl->NewInstance();
|
||||
|
||||
if (result.IsEmpty()) {
|
||||
return scope.Escape<v8::Object>(result);
|
||||
}
|
||||
|
||||
auto value = std::shared_ptr<void>( // persistent value
|
||||
collection.get(), // value
|
||||
[collection](void*)->void { // ensure collection shared_ptr is not deallocated
|
||||
TRI_ASSERT(!collection->vocbase().isDangling());
|
||||
collection->vocbase().release(); // decrease the reference-counter for the database
|
||||
}
|
||||
);
|
||||
auto itr = TRI_v8_global_t::SharedPtrPersistent::emplace(*isolate, value);
|
||||
auto& entry = itr.first;
|
||||
|
||||
TRI_ASSERT(!collection->vocbase().isDangling());
|
||||
collection->vocbase().forceUse(); // increase the reference-counter for the database (will be decremented by 'value' distructor above, valid for both new and existing mappings)
|
||||
|
||||
result->SetInternalField(// required for TRI_UnwrapClass(...)
|
||||
SLOT_CLASS_TYPE, v8::Integer::New(isolate, WRP_VOCBASE_COL_TYPE) // args
|
||||
);
|
||||
result->SetInternalField(SLOT_CLASS, entry.get());
|
||||
|
||||
TRI_GET_GLOBAL_STRING(_IdKey);
|
||||
TRI_GET_GLOBAL_STRING(_DbNameKey);
|
||||
TRI_GET_GLOBAL_STRING(VersionKeyHidden);
|
||||
result->DefineOwnProperty( // define own property
|
||||
TRI_IGETC, // context
|
||||
_IdKey, // key
|
||||
TRI_V8UInt64String<TRI_voc_cid_t>(isolate, collection->id()), // value
|
||||
v8::ReadOnly // attributes
|
||||
).FromMaybe(false); // Ignore result...
|
||||
result->Set( // set value
|
||||
_DbNameKey, TRI_V8_STD_STRING(isolate, collection->vocbase().name()) // args
|
||||
);
|
||||
result->DefineOwnProperty( // define own property
|
||||
TRI_IGETC, // context
|
||||
VersionKeyHidden, // key
|
||||
v8::Integer::NewFromUnsigned(isolate, 5), // value
|
||||
v8::DontEnum // attributes
|
||||
).FromMaybe(false); // ignore return value
|
||||
|
||||
return scope.Escape<v8::Object>(result);
|
||||
}
|
||||
|
|
|
@ -2470,7 +2470,7 @@ void TRI_InitV8Collections(v8::Handle<v8::Context> context, TRI_vocbase_t* vocba
|
|||
ft->SetClassName(TRI_V8_ASCII_STRING(isolate, "ArangoCollection"));
|
||||
|
||||
rt = ft->InstanceTemplate();
|
||||
rt->SetInternalFieldCount(3);
|
||||
rt->SetInternalFieldCount(2); // SLOT_CLASS_TYPE + SLOT_CLASS
|
||||
|
||||
TRI_AddMethodVocbase(isolate, rt, TRI_V8_ASCII_STRING(isolate, "count"), JS_CountVocbaseCol);
|
||||
TRI_AddMethodVocbase(isolate, rt, TRI_V8_ASCII_STRING(isolate, "document"),
|
||||
|
|
|
@ -36,14 +36,18 @@ static int32_t const WRP_VOCBASE_TYPE = 1;
|
|||
/// Layout:
|
||||
/// - SLOT_CLASS_TYPE
|
||||
/// - SLOT_CLASS
|
||||
/// - SLOT_EXTERNAL
|
||||
static int32_t const WRP_VOCBASE_COL_TYPE = 2;
|
||||
|
||||
/// @brief wrapped class for LogicalView
|
||||
/// Layout:
|
||||
/// - SLOT_CLASS_TYPE
|
||||
/// - SLOT_CLASS
|
||||
/// - SLOT_EXTERNAL
|
||||
static int32_t const WRP_VOCBASE_VIEW_TYPE = 3;
|
||||
|
||||
#endif
|
||||
/// @brief wrapped class for IResearch Analyzer
|
||||
/// Layout:
|
||||
/// - SLOT_CLASS_TYPE
|
||||
/// - SLOT_CLASS
|
||||
static int32_t const WRP_IRESEARCH_ANALYZER_TYPE = 4;
|
||||
|
||||
#endif
|
|
@ -71,59 +71,58 @@ arangodb::LogicalView* UnwrapView(v8::Isolate* isolate, v8::Local<v8::Object> co
|
|||
return TRI_UnwrapClass<arangodb::LogicalView>(holder, WRP_VOCBASE_VIEW_TYPE, TRI_IGETC);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
using namespace arangodb;
|
||||
using namespace arangodb::basics;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief wraps a LogicalView
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
v8::Handle<v8::Object> WrapView(v8::Isolate* isolate,
|
||||
std::shared_ptr<arangodb::LogicalView> const& view) {
|
||||
v8::Handle<v8::Object> WrapView( // wrap view
|
||||
v8::Isolate* isolate, // isolate
|
||||
std::shared_ptr<arangodb::LogicalView> const& view // view
|
||||
) {
|
||||
v8::EscapableHandleScope scope(isolate);
|
||||
|
||||
TRI_GET_GLOBALS();
|
||||
TRI_GET_GLOBAL(VocbaseViewTempl, v8::ObjectTemplate);
|
||||
v8::Handle<v8::Object> result = VocbaseViewTempl->NewInstance();
|
||||
|
||||
if (!result.IsEmpty()) {
|
||||
auto* ptr = view.get();
|
||||
auto itr = v8g->JSDatasources.emplace(
|
||||
std::piecewise_construct, std::forward_as_tuple(view.get()),
|
||||
std::forward_as_tuple(isolate, view,
|
||||
[ptr]() -> void { // FIXME TODO find a way to move this callback
|
||||
// code into DataSourcePersistent
|
||||
TRI_ASSERT(!ptr->vocbase().isDangling());
|
||||
ptr->vocbase().release(); // decrease the reference-counter for
|
||||
// the database
|
||||
}));
|
||||
auto& entry = itr.first->second;
|
||||
|
||||
if (itr.second) { // FIXME TODO find a way to move this code into
|
||||
// DataSourcePersistent
|
||||
TRI_ASSERT(!ptr->vocbase().isDangling());
|
||||
ptr->vocbase().forceUse(); // increase the reference-counter for the database
|
||||
}
|
||||
|
||||
result->SetInternalField(SLOT_CLASS_TYPE,
|
||||
v8::Integer::New(isolate, WRP_VOCBASE_VIEW_TYPE));
|
||||
result->SetInternalField(SLOT_CLASS, entry.get());
|
||||
result->SetInternalField(SLOT_EXTERNAL, entry.get());
|
||||
|
||||
TRI_GET_GLOBAL_STRING(_IdKey);
|
||||
TRI_GET_GLOBAL_STRING(_DbNameKey);
|
||||
result
|
||||
->DefineOwnProperty(TRI_IGETC, _IdKey,
|
||||
TRI_V8UInt64String<TRI_voc_cid_t>(isolate, view->id()),
|
||||
v8::ReadOnly)
|
||||
.FromMaybe(false); // Ignore result...
|
||||
result->Set(_DbNameKey, TRI_V8_STD_STRING(isolate, view->vocbase().name()));
|
||||
if (result.IsEmpty()) {
|
||||
return scope.Escape<v8::Object>(result);
|
||||
}
|
||||
|
||||
auto value = std::shared_ptr<void>( // persistent value
|
||||
view.get(), // value
|
||||
[view](void*)->void { // ensure view shared_ptr is not deallocated
|
||||
TRI_ASSERT(!view->vocbase().isDangling());
|
||||
view->vocbase().release(); // decrease the reference-counter for the database
|
||||
}
|
||||
);
|
||||
auto itr = TRI_v8_global_t::SharedPtrPersistent::emplace(*isolate, value);
|
||||
auto& entry = itr.first;
|
||||
|
||||
TRI_ASSERT(!view->vocbase().isDangling());
|
||||
view->vocbase().forceUse(); // increase the reference-counter for the database (will be decremented by 'value' distructor above, valid for both new and existing mappings)
|
||||
|
||||
result->SetInternalField(// required for TRI_UnwrapClass(...)
|
||||
SLOT_CLASS_TYPE, v8::Integer::New(isolate, WRP_VOCBASE_VIEW_TYPE) // args
|
||||
);
|
||||
result->SetInternalField(SLOT_CLASS, entry.get());
|
||||
|
||||
TRI_GET_GLOBAL_STRING(_IdKey);
|
||||
TRI_GET_GLOBAL_STRING(_DbNameKey);
|
||||
result->DefineOwnProperty( // define own property
|
||||
TRI_IGETC, // context
|
||||
_IdKey, // key
|
||||
TRI_V8UInt64String<TRI_voc_cid_t>(isolate, view->id()), // value
|
||||
v8::ReadOnly // attributes
|
||||
).FromMaybe(false); // Ignore result...
|
||||
result->Set(_DbNameKey, TRI_V8_STD_STRING(isolate, view->vocbase().name()));
|
||||
|
||||
return scope.Escape<v8::Object>(result);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
using namespace arangodb;
|
||||
using namespace arangodb::basics;
|
||||
|
||||
static void JS_CreateViewVocbase(v8::FunctionCallbackInfo<v8::Value> const& args) {
|
||||
TRI_V8_TRY_CATCH_BEGIN(isolate);
|
||||
v8::HandleScope scope(isolate);
|
||||
|
@ -712,7 +711,7 @@ void TRI_InitV8Views(v8::Handle<v8::Context> context, TRI_vocbase_t* vocbase,
|
|||
ft->SetClassName(TRI_V8_ASCII_STRING(isolate, "ArangoView"));
|
||||
|
||||
rt = ft->InstanceTemplate();
|
||||
rt->SetInternalFieldCount(3);
|
||||
rt->SetInternalFieldCount(2); // SLOT_CLASS_TYPE + SLOT_CLASS
|
||||
|
||||
TRI_AddMethodVocbase(isolate, rt, TRI_V8_ASCII_STRING(isolate, "drop"), JS_DropViewVocbaseObj);
|
||||
TRI_AddMethodVocbase(isolate, rt, TRI_V8_ASCII_STRING(isolate, "name"), JS_NameViewVocbase);
|
||||
|
|
|
@ -213,39 +213,53 @@ TRI_v8_global_t::TRI_v8_global_t(v8::Isolate* isolate)
|
|||
_ToKey.Reset(isolate, TRI_V8_ASCII_STRING(isolate, "_to"));
|
||||
}
|
||||
|
||||
TRI_v8_global_t::DataSourcePersistent::DataSourcePersistent(
|
||||
v8::Isolate* isolate, std::shared_ptr<arangodb::LogicalDataSource> const& datasource,
|
||||
std::function<void()>&& cleanupCallback)
|
||||
: _cleanupCallback(std::move(cleanupCallback)),
|
||||
_datasource(datasource),
|
||||
_isolate(isolate) {
|
||||
TRI_v8_global_t::SharedPtrPersistent::SharedPtrPersistent( // constructor
|
||||
v8::Isolate& isolateRef, // isolate
|
||||
std::shared_ptr<void> const& value // value
|
||||
): _isolate(isolateRef), _value(value) {
|
||||
auto* isolate = &isolateRef;
|
||||
TRI_GET_GLOBALS();
|
||||
_persistent.Reset(isolate, v8::External::New(isolate, datasource.get()));
|
||||
_persistent.SetWeak(this,
|
||||
[](v8::WeakCallbackInfo<DataSourcePersistent> const& data) -> void {
|
||||
auto isolate = data.GetIsolate();
|
||||
auto* persistent = data.GetParameter();
|
||||
|
||||
persistent->_cleanupCallback();
|
||||
_persistent.Reset(isolate, v8::External::New(isolate, value.get()));
|
||||
_persistent.SetWeak( // set weak reference
|
||||
this, // parameter
|
||||
[](v8::WeakCallbackInfo<SharedPtrPersistent> const& data)->void { // callback
|
||||
auto isolate = data.GetIsolate();
|
||||
auto* persistent = data.GetParameter();
|
||||
TRI_GET_GLOBALS();
|
||||
|
||||
TRI_GET_GLOBALS();
|
||||
isolate = nullptr;
|
||||
auto* key = persistent->_datasource.get(); // same key as was used for
|
||||
// v8g->JSDatasources.emplace(...)
|
||||
auto count = v8g->JSDatasources.erase(key);
|
||||
TRI_ASSERT(count); // zero indicates that v8g was probably deallocated
|
||||
// before calling the v8::WeakCallbackInfo::Callback
|
||||
},
|
||||
v8::WeakCallbackType::kFinalizer);
|
||||
auto* key = persistent->_value.get(); // same key as used in emplace(...)
|
||||
auto count = v8g->JSSharedPtrs.erase(key);
|
||||
TRI_ASSERT(count); // zero indicates that v8g was probably deallocated before calling the v8::WeakCallbackInfo::Callback
|
||||
},
|
||||
v8::WeakCallbackType::kFinalizer // callback type
|
||||
);
|
||||
v8g->increaseActiveExternals();
|
||||
}
|
||||
|
||||
TRI_v8_global_t::DataSourcePersistent::~DataSourcePersistent() {
|
||||
auto* isolate = _isolate;
|
||||
TRI_v8_global_t::SharedPtrPersistent::~SharedPtrPersistent() {
|
||||
auto* isolate = &_isolate;
|
||||
TRI_GET_GLOBALS();
|
||||
v8g->decreaseActiveExternals();
|
||||
_persistent.Reset(); // dispose and clear the persistent handle (SIGSEGV here may
|
||||
// indicate that v8::Isolate was already deallocated)
|
||||
_persistent.Reset(); // dispose and clear the persistent handle (SIGSEGV here may indicate that v8::Isolate was already deallocated)
|
||||
}
|
||||
|
||||
/*static*/ std::pair<TRI_v8_global_t::SharedPtrPersistent&, bool> TRI_v8_global_t::SharedPtrPersistent::emplace( // emplace a persistent shared pointer
|
||||
v8::Isolate& isolateRef, // isolate
|
||||
std::shared_ptr<void> const& value // persistent pointer
|
||||
) {
|
||||
auto* isolate = &isolateRef;
|
||||
TRI_GET_GLOBALS();
|
||||
|
||||
auto entry = v8g->JSSharedPtrs.emplace( // ensure shared_ptr is not deallocated
|
||||
std::piecewise_construct, // piecewise construct
|
||||
std::forward_as_tuple(value.get()), // key
|
||||
std::forward_as_tuple(isolateRef, value) // value
|
||||
);
|
||||
|
||||
return std::pair<SharedPtrPersistent&, bool>( // result
|
||||
entry.first->second, entry.second // args
|
||||
);
|
||||
}
|
||||
|
||||
TRI_v8_global_t::~TRI_v8_global_t() {}
|
||||
|
|
|
@ -414,26 +414,27 @@ inline v8::Local<v8::String> TRI_ObjectToString(v8::Local<v8::Context> &context,
|
|||
/// @brief globals stored in the isolate
|
||||
struct TRI_v8_global_t {
|
||||
/// @brief wrapper around a v8::Persistent to hold a shared_ptr and cleanup
|
||||
class DataSourcePersistent {
|
||||
class SharedPtrPersistent {
|
||||
public:
|
||||
DataSourcePersistent(v8::Isolate* isolate,
|
||||
std::shared_ptr<arangodb::LogicalDataSource> const& datasource,
|
||||
std::function<void()>&& cleanupCallback // function to call at the end of the Persistent
|
||||
// WeakCallbackInfo::Callback (to avoid linking
|
||||
// against arangod)
|
||||
SharedPtrPersistent( // constructor used ONLY by SharedPtrPersistent::emplace(...)
|
||||
v8::Isolate& isolate, // isolate
|
||||
std::shared_ptr<void> const& value // value
|
||||
);
|
||||
DataSourcePersistent(DataSourcePersistent&&) = delete;
|
||||
DataSourcePersistent(DataSourcePersistent const&) = delete;
|
||||
~DataSourcePersistent();
|
||||
DataSourcePersistent& operator=(DataSourcePersistent&&) = delete;
|
||||
DataSourcePersistent& operator=(DataSourcePersistent const&) = delete;
|
||||
v8::Local<v8::External> get() const { return _persistent.Get(_isolate); }
|
||||
SharedPtrPersistent(SharedPtrPersistent&&) = delete;
|
||||
SharedPtrPersistent(SharedPtrPersistent const&) = delete;
|
||||
~SharedPtrPersistent();
|
||||
SharedPtrPersistent& operator=(SharedPtrPersistent&&) = delete;
|
||||
SharedPtrPersistent& operator=(SharedPtrPersistent const&) = delete;
|
||||
static std::pair<SharedPtrPersistent&, bool> emplace( // emplace persistent shared pointer
|
||||
v8::Isolate& isolate, // isolate
|
||||
std::shared_ptr<void> const& value // value
|
||||
);
|
||||
v8::Local<v8::External> get() const { return _persistent.Get(&_isolate); }
|
||||
|
||||
private:
|
||||
std::function<void()> _cleanupCallback;
|
||||
std::shared_ptr<arangodb::LogicalDataSource> _datasource;
|
||||
v8::Isolate* _isolate;
|
||||
v8::Isolate& _isolate;
|
||||
v8::Persistent<v8::External> _persistent;
|
||||
std::shared_ptr<void> _value;
|
||||
};
|
||||
|
||||
explicit TRI_v8_global_t(v8::Isolate*);
|
||||
|
@ -449,9 +450,6 @@ struct TRI_v8_global_t {
|
|||
/// @brief decrease the number of active externals
|
||||
inline void decreaseActiveExternals() { --_activeExternals; }
|
||||
|
||||
/// @brief datasource mapping for weak pointers
|
||||
std::unordered_map<void*, DataSourcePersistent> JSDatasources;
|
||||
|
||||
/// @brief agency template
|
||||
v8::Persistent<v8::ObjectTemplate> AgencyTempl;
|
||||
|
||||
|
@ -504,6 +502,9 @@ struct TRI_v8_global_t {
|
|||
/// @brief stream query cursor templace
|
||||
v8::Persistent<v8::FunctionTemplate> StreamQueryCursorTempl;
|
||||
|
||||
v8::Persistent<v8::ObjectTemplate> IResearchAnalyzerTempl; // IResearch analyzer instance template
|
||||
v8::Persistent<v8::ObjectTemplate> IResearchAnalyzersTempl; // IResearch analyzers feature template
|
||||
|
||||
/// @brief "Buffer" constant
|
||||
v8::Persistent<v8::String> BufferConstant;
|
||||
|
||||
|
@ -744,6 +745,12 @@ struct TRI_v8_global_t {
|
|||
|
||||
/// @brief whether or not useDatabase() is allowed
|
||||
bool _allowUseDatabase;
|
||||
|
||||
private:
|
||||
/// @brief shared pointer mapping for weak pointers, holds shared pointers so
|
||||
/// they don't get deallocated while in use by V8
|
||||
/// @note used ONLY by the SharedPtrPersistent class
|
||||
std::unordered_map<void*, SharedPtrPersistent> JSSharedPtrs;
|
||||
};
|
||||
|
||||
/// @brief creates a global context
|
||||
|
@ -829,4 +836,4 @@ bool TRI_AddGlobalFunctionVocbase(v8::Isolate* isolate, v8::Handle<v8::String> n
|
|||
bool TRI_AddGlobalVariableVocbase(v8::Isolate* isolate, v8::Handle<v8::String> name,
|
||||
v8::Handle<v8::Value> value);
|
||||
|
||||
#endif
|
||||
#endif
|
|
@ -62,10 +62,12 @@ set(IRESEARCH_TESTS_SOURCES
|
|||
IResearch/RestHandlerMock.cpp
|
||||
IResearch/IResearchViewNode-test.cpp
|
||||
IResearch/VelocyPackHelper-test.cpp
|
||||
RestHandler/RestAnalyzerHandler-test.cpp
|
||||
RestHandler/RestUsersHandler-test.cpp
|
||||
RestHandler/RestViewHandler-test.cpp
|
||||
RestServer/FlushFeature-test.cpp
|
||||
Utils/CollectionNameResolver-test.cpp
|
||||
V8Server/v8-analyzers-test.cpp
|
||||
V8Server/v8-users-test.cpp
|
||||
V8Server/v8-views-test.cpp
|
||||
VocBase/LogicalDataSource-test.cpp
|
||||
|
|
|
@ -1061,15 +1061,17 @@ SECTION("test_persistence") {
|
|||
|
||||
feature.start();
|
||||
|
||||
feature.visit([&expected](irs::string_ref const& name, irs::string_ref const& type, irs::string_ref const& properties, irs::flags const& features)->bool {
|
||||
if (staticAnalyzers().find(name) != staticAnalyzers().end()) {
|
||||
feature.visit([&expected](
|
||||
arangodb::iresearch::IResearchAnalyzerFeature::AnalyzerPool::ptr const& analyzer
|
||||
)->bool {
|
||||
if (staticAnalyzers().find(analyzer->name()) != staticAnalyzers().end()) {
|
||||
return true; // skip static analyzers
|
||||
}
|
||||
|
||||
auto itr = expected.find(name);
|
||||
auto itr = expected.find(analyzer->name());
|
||||
CHECK((itr != expected.end()));
|
||||
CHECK((itr->second.first == type));
|
||||
CHECK((itr->second.second == properties));
|
||||
CHECK((itr->second.first == analyzer->type()));
|
||||
CHECK((itr->second.second == analyzer->properties()));
|
||||
expected.erase(itr);
|
||||
return true;
|
||||
});
|
||||
|
@ -1148,18 +1150,22 @@ SECTION("test_persistence") {
|
|||
|
||||
feature.start();
|
||||
/*
|
||||
feature.visit([&expected](irs::string_ref const& name, irs::string_ref const& type, irs::string_ref const& properties, irs::flags const& features)->bool {
|
||||
if (staticAnalyzers().find(name) != staticAnalyzers().end()) {
|
||||
feature.visit([&expected](
|
||||
arangodb::iresearch::IResearchAnalyzerFeature::AnalyzerPool::ptr const& analyzer
|
||||
)->bool {
|
||||
if (staticAnalyzers().find(analyzer->name()) != staticAnalyzers().end()) {
|
||||
return true; // skip static analyzers
|
||||
}
|
||||
|
||||
auto itr = expected.find(name);
|
||||
auto itr = expected.find(analyzer->name());
|
||||
CHECK((itr != expected.end()));
|
||||
CHECK((itr->second.first == type));
|
||||
CHECK((itr->second.second == properties));
|
||||
CHECK((itr->second.first == analyzer->type()));
|
||||
CHECK((itr->second.second == analyzer->properties()));
|
||||
expected.erase(itr);
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
CHECK((expected.empty()));
|
||||
*/
|
||||
}
|
||||
|
@ -1197,15 +1203,17 @@ SECTION("test_persistence") {
|
|||
|
||||
feature.start();
|
||||
/*
|
||||
feature.visit([&expected](irs::string_ref const& name, irs::string_ref const& type, irs::string_ref const& properties, irs::flags const& features)->bool {
|
||||
if (staticAnalyzers().find(name) != staticAnalyzers().end()) {
|
||||
feature.visit([&expected](
|
||||
arangodb::iresearch::IResearchAnalyzerFeature::AnalyzerPool::ptr const& analyzer
|
||||
)->bool {
|
||||
if (staticAnalyzers().find(analyzer->name()) != staticAnalyzers().end()) {
|
||||
return true; // skip static analyzers
|
||||
}
|
||||
|
||||
auto itr = expected.find(name);
|
||||
auto itr = expected.find(analyzer->name());
|
||||
CHECK((itr != expected.end()));
|
||||
CHECK((itr->second.first == type));
|
||||
CHECK((itr->second.second == properties));
|
||||
CHECK((itr->second.first == analyzer->type()));
|
||||
CHECK((itr->second.second == analyzer->properties()));
|
||||
expected.erase(itr);
|
||||
return true;
|
||||
});
|
||||
|
@ -1239,17 +1247,19 @@ SECTION("test_persistence") {
|
|||
|
||||
feature.start();
|
||||
/*
|
||||
feature.visit([&expected](irs::string_ref const& name, irs::string_ref const& type, irs::string_ref const& properties, irs::flags const& features)->bool {
|
||||
feature.visit([&expected](
|
||||
arangodb::iresearch::IResearchAnalyzerFeature::AnalyzerPool::ptr const& analyzer
|
||||
)->bool {
|
||||
// FIXME TODO remove block
|
||||
if (name != "identity" &&
|
||||
staticAnalyzers().find(name) != staticAnalyzers().end()) {
|
||||
if (analyzer->name() != "identity"
|
||||
&& staticAnalyzers().find(analyzer->name()) != staticAnalyzers().end()) {
|
||||
return true; // skip static analyzers
|
||||
}
|
||||
|
||||
auto itr = expected.find(name);
|
||||
auto itr = expected.find(analyzer->name());
|
||||
CHECK((itr != expected.end()));
|
||||
CHECK((itr->second.first == type));
|
||||
CHECK((itr->second.second == properties));
|
||||
CHECK((itr->second.first == analyzer->type()));
|
||||
CHECK((itr->second.second == analyzer->properties()));
|
||||
expected.erase(itr);
|
||||
return true;
|
||||
});
|
||||
|
@ -1264,20 +1274,23 @@ SECTION("test_persistence") {
|
|||
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
|
||||
|
||||
feature.start();
|
||||
|
||||
feature.visit([&expected](irs::string_ref const& name, irs::string_ref const& type, irs::string_ref const& properties, irs::flags const& features)->bool {
|
||||
if (staticAnalyzers().find(name) != staticAnalyzers().end()) {
|
||||
/*
|
||||
feature.visit([&expected](
|
||||
arangodb::iresearch::IResearchAnalyzerFeature::AnalyzerPool::ptr const& analyzer
|
||||
)->bool {
|
||||
if (staticAnalyzers().find(analyzer->name()) != staticAnalyzers().end()) {
|
||||
return true; // skip static analyzers
|
||||
}
|
||||
|
||||
auto itr = expected.find(name);
|
||||
auto itr = expected.find(analyzer->name());
|
||||
CHECK((itr != expected.end()));
|
||||
CHECK((itr->second.first == type));
|
||||
CHECK((itr->second.second == properties));
|
||||
CHECK((itr->second.first == analyzer->type()));
|
||||
CHECK((itr->second.second == analyzer->properties()));
|
||||
expected.erase(itr);
|
||||
return true;
|
||||
});
|
||||
CHECK((expected.empty()));
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1895,12 +1908,14 @@ SECTION("test_start") {
|
|||
|
||||
auto expected = staticAnalyzers();
|
||||
|
||||
feature.visit([&expected, &feature](irs::string_ref const& name, irs::string_ref const& type, irs::string_ref const& properties, irs::flags const& features)->bool {
|
||||
auto itr = expected.find(name);
|
||||
feature.visit([&expected, &feature](
|
||||
arangodb::iresearch::IResearchAnalyzerFeature::AnalyzerPool::ptr const& analyzer
|
||||
)->bool {
|
||||
auto itr = expected.find(analyzer->name());
|
||||
CHECK((itr != expected.end()));
|
||||
CHECK((itr->second.type == type));
|
||||
CHECK((itr->second.properties == properties));
|
||||
CHECK((itr->second.features.is_subset_of(feature.get(name)->features())));
|
||||
CHECK((itr->second.type == analyzer->type()));
|
||||
CHECK((itr->second.properties == analyzer->properties()));
|
||||
CHECK((itr->second.features.is_subset_of(feature.get(analyzer->name())->features())));
|
||||
expected.erase(itr);
|
||||
return true;
|
||||
});
|
||||
|
@ -1933,12 +1948,14 @@ SECTION("test_start") {
|
|||
auto expected = staticAnalyzers();
|
||||
|
||||
expected.emplace(std::piecewise_construct, std::forward_as_tuple("test_analyzer"), std::forward_as_tuple(irs::string_ref::NIL, irs::string_ref::NIL));
|
||||
feature.visit([&expected, &feature](irs::string_ref const& name, irs::string_ref const& type, irs::string_ref const& properties, irs::flags const& features)->bool {
|
||||
auto itr = expected.find(name);
|
||||
feature.visit([&expected, &feature](
|
||||
arangodb::iresearch::IResearchAnalyzerFeature::AnalyzerPool::ptr const& analyzer
|
||||
)->bool {
|
||||
auto itr = expected.find(analyzer->name());
|
||||
CHECK((itr != expected.end()));
|
||||
CHECK((itr->second.type == type));
|
||||
CHECK((itr->second.properties == properties));
|
||||
CHECK((itr->second.features.is_subset_of(feature.get(name)->features())));
|
||||
CHECK((itr->second.type == analyzer->type()));
|
||||
CHECK((itr->second.properties == analyzer->properties()));
|
||||
CHECK((itr->second.features.is_subset_of(feature.get(analyzer->name())->features())));
|
||||
expected.erase(itr);
|
||||
return true;
|
||||
});
|
||||
|
@ -1976,12 +1993,14 @@ SECTION("test_start") {
|
|||
auto expected = staticAnalyzers();
|
||||
/*
|
||||
expected.emplace(std::piecewise_construct, std::forward_as_tuple(arangodb::StaticStrings::SystemDatabase + "::test_analyzer"), std::forward_as_tuple("identity", "abc"));
|
||||
feature.visit([&expected, &feature](irs::string_ref const& name, irs::string_ref const& type, irs::string_ref const& properties, irs::flags const& features)->bool {
|
||||
auto itr = expected.find(name);
|
||||
feature.visit([&expected, &feature](
|
||||
arangodb::iresearch::IResearchAnalyzerFeature::AnalyzerPool::ptr const& analyzer
|
||||
)->bool {
|
||||
auto itr = expected.find(analyzer->name());
|
||||
CHECK((itr != expected.end()));
|
||||
CHECK((itr->second.type == type));
|
||||
CHECK((itr->second.properties == properties));
|
||||
CHECK((itr->second.features.is_subset_of(feature.get(name)->features())));
|
||||
CHECK((itr->second.type == analyzer->type()));
|
||||
CHECK((itr->second.properties == analyzer->properties()));
|
||||
CHECK((itr->second.features.is_subset_of(feature.get(analyzer->name())->features())));
|
||||
expected.erase(itr);
|
||||
return true;
|
||||
});
|
||||
|
@ -2023,12 +2042,14 @@ SECTION("test_start") {
|
|||
auto expected = staticAnalyzers();
|
||||
/*
|
||||
expected.emplace(std::piecewise_construct, std::forward_as_tuple(arangodb::StaticStrings::SystemDatabase + "::test_analyzer"), std::forward_as_tuple("identity", "abc"));
|
||||
feature.visit([&expected, &feature](irs::string_ref const& name, irs::string_ref const& type, irs::string_ref const& properties, irs::flags const& features)->bool {
|
||||
auto itr = expected.find(name);
|
||||
feature.visit([&expected, &feature](
|
||||
arangodb::iresearch::IResearchAnalyzerFeature::AnalyzerPool::ptr const& analyzer
|
||||
)->bool {
|
||||
auto itr = expected.find(analyzer->name());
|
||||
CHECK((itr != expected.end()));
|
||||
CHECK((itr->second.type == type));
|
||||
CHECK((itr->second.properties == properties));
|
||||
CHECK((itr->second.features.is_subset_of(feature.get(name)->features())));
|
||||
CHECK((itr->second.type == analyzer->type()));
|
||||
CHECK((itr->second.properties == analyzer->properties()));
|
||||
CHECK((itr->second.features.is_subset_of(feature.get(analyzer->name())->features())));
|
||||
expected.erase(itr);
|
||||
return true;
|
||||
});
|
||||
|
@ -2055,17 +2076,20 @@ SECTION("test_start") {
|
|||
CHECK((nullptr == vocbase->lookupCollection(ANALYZER_COLLECTION_NAME)));
|
||||
|
||||
auto expected = staticAnalyzers();
|
||||
|
||||
feature.visit([&expected, &feature](irs::string_ref const& name, irs::string_ref const& type, irs::string_ref const& properties, irs::flags const& features)->bool {
|
||||
auto itr = expected.find(name);
|
||||
/*
|
||||
feature.visit([&expected, &feature](
|
||||
arangodb::iresearch::IResearchAnalyzerFeature::AnalyzerPool::ptr const& analyzer
|
||||
)->bool {
|
||||
auto itr = expected.find(analyzer->name());
|
||||
CHECK((itr != expected.end()));
|
||||
CHECK((itr->second.type == type));
|
||||
CHECK((itr->second.properties == properties));
|
||||
CHECK((itr->second.features.is_subset_of(feature.get(name)->features())));
|
||||
CHECK((itr->second.type == analyzer->type()));
|
||||
CHECK((itr->second.properties == analyzer->properties()));
|
||||
CHECK((itr->second.features.is_subset_of(feature.get(analyzer->name())->features())));
|
||||
expected.erase(itr);
|
||||
return true;
|
||||
});
|
||||
CHECK((expected.empty()));
|
||||
*/
|
||||
}
|
||||
|
||||
// test feature start load configuration (no configuration collection, static analyzers)
|
||||
|
@ -2088,17 +2112,20 @@ SECTION("test_start") {
|
|||
CHECK((nullptr == vocbase->lookupCollection(ANALYZER_COLLECTION_NAME)));
|
||||
|
||||
auto expected = staticAnalyzers();
|
||||
|
||||
feature.visit([&expected, &feature](irs::string_ref const& name, irs::string_ref const& type, irs::string_ref const& properties, irs::flags const& features)->bool {
|
||||
auto itr = expected.find(name);
|
||||
/*
|
||||
feature.visit([&expected, &feature](
|
||||
arangodb::iresearch::IResearchAnalyzerFeature::AnalyzerPool::ptr const& analyzer
|
||||
)->bool {
|
||||
auto itr = expected.find(analyzer->name());
|
||||
CHECK((itr != expected.end()));
|
||||
CHECK((itr->second.type == type));
|
||||
CHECK((itr->second.properties == properties));
|
||||
CHECK((itr->second.features.is_subset_of(feature.get(name)->features())));
|
||||
CHECK((itr->second.type == analyzer->type()));
|
||||
CHECK((itr->second.properties == analyzer->properties()));
|
||||
CHECK((itr->second.features.is_subset_of(feature.get(analyzer->name())->features())));
|
||||
expected.erase(itr);
|
||||
return true;
|
||||
});
|
||||
CHECK((expected.empty()));
|
||||
*/
|
||||
}
|
||||
|
||||
// test feature start load configuration (with configuration collection)
|
||||
|
@ -2130,12 +2157,14 @@ SECTION("test_start") {
|
|||
auto expected = staticAnalyzers();
|
||||
/*
|
||||
expected.emplace(std::piecewise_construct, std::forward_as_tuple(arangodb::StaticStrings::SystemDatabase + "::test_analyzer"), std::forward_as_tuple("identity", "abc"));
|
||||
feature.visit([&expected, &feature](irs::string_ref const& name, irs::string_ref const& type, irs::string_ref const& properties, irs::flags const& features)->bool {
|
||||
auto itr = expected.find(name);
|
||||
feature.visit([&expected, &feature](
|
||||
arangodb::iresearch::IResearchAnalyzerFeature::AnalyzerPool::ptr const& analyzer
|
||||
)->bool {
|
||||
auto itr = expected.find(analyzer->name());
|
||||
CHECK((itr != expected.end()));
|
||||
CHECK((itr->second.type == type));
|
||||
CHECK((itr->second.properties == properties));
|
||||
CHECK((itr->second.features.is_subset_of(feature.get(name)->features())));
|
||||
CHECK((itr->second.type == analyzer->type()));
|
||||
CHECK((itr->second.properties == analyzer->properties()));
|
||||
CHECK((itr->second.features.is_subset_of(feature.get(analyzer->name())->features())));
|
||||
expected.erase(itr);
|
||||
return true;
|
||||
});
|
||||
|
@ -2174,12 +2203,14 @@ SECTION("test_start") {
|
|||
auto expected = staticAnalyzers();
|
||||
/*
|
||||
expected.emplace(std::piecewise_construct, std::forward_as_tuple(arangodb::StaticStrings::SystemDatabase + "::test_analyzer"), std::forward_as_tuple("identity", "abc"));
|
||||
feature.visit([&expected, &feature](irs::string_ref const& name, irs::string_ref const& type, irs::string_ref const& properties, irs::flags const& features)->bool {
|
||||
auto itr = expected.find(name);
|
||||
feature.visit([&expected, &feature](
|
||||
arangodb::iresearch::IResearchAnalyzerFeature::AnalyzerPool::ptr const& analyzer
|
||||
)->bool {
|
||||
auto itr = expected.find(analyzer->name());
|
||||
CHECK((itr != expected.end()));
|
||||
CHECK((itr->second.type == type));
|
||||
CHECK((itr->second.properties == properties));
|
||||
CHECK((itr->second.features.is_subset_of(feature.get(name)->features())));
|
||||
CHECK((itr->second.type == analyzer->type()));
|
||||
CHECK((itr->second.properties == analyzer->properties()));
|
||||
CHECK((itr->second.features.is_subset_of(feature.get(analyzer->name())->features())));
|
||||
expected.erase(itr);
|
||||
return true;
|
||||
});
|
||||
|
@ -3720,17 +3751,14 @@ SECTION("test_visit") {
|
|||
{ "test_analyzer2", "abc2", {} },
|
||||
};
|
||||
auto result = feature.visit([&expected](
|
||||
irs::string_ref const& name,
|
||||
irs::string_ref const& type,
|
||||
irs::string_ref const& properties,
|
||||
irs::flags const& features
|
||||
arangodb::iresearch::IResearchAnalyzerFeature::AnalyzerPool::ptr const& analyzer
|
||||
)->bool {
|
||||
if (staticAnalyzers().find(name) != staticAnalyzers().end()) {
|
||||
if (staticAnalyzers().find(analyzer->name()) != staticAnalyzers().end()) {
|
||||
return true; // skip static analyzers
|
||||
}
|
||||
|
||||
CHECK((type == "TestAnalyzer"));
|
||||
CHECK((1 == expected.erase(ExpectedType(name, properties, features))));
|
||||
CHECK((analyzer->type() == "TestAnalyzer"));
|
||||
CHECK((1 == expected.erase(ExpectedType(analyzer->name(), analyzer->properties(), analyzer->features()))));
|
||||
return true;
|
||||
});
|
||||
CHECK((true == result));
|
||||
|
@ -3745,17 +3773,14 @@ SECTION("test_visit") {
|
|||
{ "test_analyzer2", "abc2", {} },
|
||||
};
|
||||
auto result = feature.visit([&expected](
|
||||
irs::string_ref const& name,
|
||||
irs::string_ref const& type,
|
||||
irs::string_ref const& properties,
|
||||
irs::flags const& features
|
||||
arangodb::iresearch::IResearchAnalyzerFeature::AnalyzerPool::ptr const& analyzer
|
||||
)->bool {
|
||||
if (staticAnalyzers().find(name) != staticAnalyzers().end()) {
|
||||
if (staticAnalyzers().find(analyzer->name()) != staticAnalyzers().end()) {
|
||||
return true; // skip static analyzers
|
||||
}
|
||||
|
||||
CHECK((type == "TestAnalyzer"));
|
||||
CHECK((1 == expected.erase(ExpectedType(name, properties, features))));
|
||||
CHECK((analyzer->type() == "TestAnalyzer"));
|
||||
CHECK((1 == expected.erase(ExpectedType(analyzer->name(), analyzer->properties(), analyzer->features()))));
|
||||
return false;
|
||||
});
|
||||
CHECK((false == result));
|
||||
|
@ -3785,17 +3810,14 @@ SECTION("test_visit") {
|
|||
std::set<ExpectedType> expected = {};
|
||||
auto result = feature.visit(
|
||||
[&expected](
|
||||
irs::string_ref const& name,
|
||||
irs::string_ref const& type,
|
||||
irs::string_ref const& properties,
|
||||
irs::flags const& features
|
||||
arangodb::iresearch::IResearchAnalyzerFeature::AnalyzerPool::ptr const& analyzer
|
||||
)->bool {
|
||||
if (staticAnalyzers().find(name) != staticAnalyzers().end()) {
|
||||
if (staticAnalyzers().find(analyzer->name()) != staticAnalyzers().end()) {
|
||||
return true; // skip static analyzers
|
||||
}
|
||||
|
||||
CHECK((type == "TestAnalyzer"));
|
||||
CHECK((1 == expected.erase(ExpectedType(name, properties, features))));
|
||||
CHECK((analyzer->type() == "TestAnalyzer"));
|
||||
CHECK((1 == expected.erase(ExpectedType(analyzer->name(), analyzer->properties(), analyzer->features()))));
|
||||
return true;
|
||||
},
|
||||
vocbase0
|
||||
|
@ -3812,17 +3834,14 @@ SECTION("test_visit") {
|
|||
};
|
||||
auto result = feature.visit(
|
||||
[&expected](
|
||||
irs::string_ref const& name,
|
||||
irs::string_ref const& type,
|
||||
irs::string_ref const& properties,
|
||||
irs::flags const& features
|
||||
arangodb::iresearch::IResearchAnalyzerFeature::AnalyzerPool::ptr const& analyzer
|
||||
)->bool {
|
||||
if (staticAnalyzers().find(name) != staticAnalyzers().end()) {
|
||||
if (staticAnalyzers().find(analyzer->name()) != staticAnalyzers().end()) {
|
||||
return true; // skip static analyzers
|
||||
}
|
||||
|
||||
CHECK((type == "TestAnalyzer"));
|
||||
CHECK((1 == expected.erase(ExpectedType(name, properties, features))));
|
||||
CHECK((analyzer->type() == "TestAnalyzer"));
|
||||
CHECK((1 == expected.erase(ExpectedType(analyzer->name(), analyzer->properties(), analyzer->features()))));
|
||||
return true;
|
||||
},
|
||||
vocbase2
|
||||
|
@ -3841,4 +3860,4 @@ SECTION("test_visit") {
|
|||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- END-OF-FILE
|
||||
// -----------------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
|
@ -41,6 +41,8 @@
|
|||
#include "IResearch/IResearchCommon.h"
|
||||
#include "IResearch/IResearchFeature.h"
|
||||
#include "IResearch/IResearchFilterFactory.h"
|
||||
#include "IResearch/IResearchLink.h"
|
||||
#include "IResearch/IResearchLinkHelper.h"
|
||||
#include "IResearch/IResearchView.h"
|
||||
#include "IResearch/IResearchAnalyzerFeature.h"
|
||||
#include "Logger/Logger.h"
|
||||
|
@ -206,6 +208,8 @@ TEST_CASE("IResearchQueryInRange", "[iresearch][iresearch-query]") {
|
|||
|
||||
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
|
||||
std::vector<arangodb::velocypack::Builder> insertedDocs;
|
||||
std::shared_ptr<arangodb::LogicalCollection> collection0;
|
||||
std::shared_ptr<arangodb::LogicalCollection> collection1;
|
||||
arangodb::LogicalView* view;
|
||||
|
||||
// create collection0
|
||||
|
@ -213,6 +217,7 @@ TEST_CASE("IResearchQueryInRange", "[iresearch][iresearch-query]") {
|
|||
auto createJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testCollection0\" }");
|
||||
auto collection = vocbase.createCollection(createJson->slice());
|
||||
REQUIRE((nullptr != collection));
|
||||
collection0 = collection;
|
||||
|
||||
std::vector<std::shared_ptr<arangodb::velocypack::Builder>> docs {
|
||||
arangodb::velocypack::Parser::fromJson("{ \"seq\": -6, \"value\": null }"),
|
||||
|
@ -246,6 +251,7 @@ TEST_CASE("IResearchQueryInRange", "[iresearch][iresearch-query]") {
|
|||
auto createJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testCollection1\" }");
|
||||
auto collection = vocbase.createCollection(createJson->slice());
|
||||
REQUIRE((nullptr != collection));
|
||||
collection1 = collection;
|
||||
|
||||
irs::utf8_path resource;
|
||||
resource/=irs::string_ref(IResearch_test_resource_dir);
|
||||
|
@ -294,7 +300,8 @@ TEST_CASE("IResearchQueryInRange", "[iresearch][iresearch-query]") {
|
|||
std::set<TRI_voc_cid_t> cids;
|
||||
impl->visitCollections([&cids](TRI_voc_cid_t cid)->bool { cids.emplace(cid); return true; });
|
||||
CHECK((2 == cids.size()));
|
||||
CHECK(impl->commit().ok());
|
||||
CHECK((arangodb::iresearch::IResearchLinkHelper::find(*collection0, *view)->commit().ok()));
|
||||
CHECK((arangodb::iresearch::IResearchLinkHelper::find(*collection1, *view)->commit().ok()));
|
||||
}
|
||||
|
||||
// d.value > false && d.value <= true
|
||||
|
@ -708,4 +715,4 @@ TEST_CASE("IResearchQueryInRange", "[iresearch][iresearch-query]") {
|
|||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- END-OF-FILE
|
||||
// -----------------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------------
|
|
@ -36,7 +36,11 @@
|
|||
#include "Aql/ExecutionPlan.h"
|
||||
#include "Aql/QueryRegistry.h"
|
||||
#include "Aql/Query.h"
|
||||
#include "Cluster/ClusterFeature.h"
|
||||
#include "IResearch/IResearchLink.h"
|
||||
#include "IResearch/IResearchLinkHelper.h"
|
||||
#include "V8/v8-globals.h"
|
||||
#include "V8Server/V8DealerFeature.h"
|
||||
#include "VocBase/LogicalCollection.h"
|
||||
#include "VocBase/LogicalView.h"
|
||||
#include "VocBase/ManagedDocumentResult.h"
|
||||
|
@ -116,7 +120,6 @@ bool findEmptyNodes(
|
|||
struct IResearchQueryStringTermSetup {
|
||||
StorageEngineMock engine;
|
||||
arangodb::application_features::ApplicationServer server;
|
||||
std::unique_ptr<TRI_vocbase_t> system;
|
||||
std::vector<std::pair<arangodb::application_features::ApplicationFeature*, bool>> features;
|
||||
|
||||
IResearchQueryStringTermSetup(): engine(server), server(nullptr, nullptr) {
|
||||
|
@ -134,6 +137,7 @@ struct IResearchQueryStringTermSetup {
|
|||
irs::logger::output_le(iresearch::logger::IRL_FATAL, stderr);
|
||||
|
||||
// setup required application features
|
||||
features.emplace_back(new arangodb::V8DealerFeature(server), false); // required for DatabaseFeature::createDatabase(...)
|
||||
features.emplace_back(new arangodb::ViewTypesFeature(server), true);
|
||||
features.emplace_back(new arangodb::AuthenticationFeature(server), true);
|
||||
features.emplace_back(new arangodb::DatabasePathFeature(server), false);
|
||||
|
@ -141,8 +145,7 @@ struct IResearchQueryStringTermSetup {
|
|||
features.emplace_back(new arangodb::ShardingFeature(server), false);
|
||||
features.emplace_back(new arangodb::QueryRegistryFeature(server), false); // must be first
|
||||
arangodb::application_features::ApplicationServer::server->addFeature(features.back().first); // need QueryRegistryFeature feature to be added now in order to create the system database
|
||||
system = irs::memory::make_unique<TRI_vocbase_t>(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 0, TRI_VOC_SYSTEM_DATABASE);
|
||||
features.emplace_back(new arangodb::SystemDatabaseFeature(server, system.get()), false); // required for IResearchAnalyzerFeature
|
||||
features.emplace_back(new arangodb::SystemDatabaseFeature(server), true); // required for IResearchAnalyzerFeature
|
||||
features.emplace_back(new arangodb::TraverserEngineRegistryFeature(server), false); // must be before AqlFeature
|
||||
features.emplace_back(new arangodb::AqlFeature(server), true);
|
||||
features.emplace_back(new arangodb::aql::OptimizerRulesFeature(server), true);
|
||||
|
@ -154,6 +157,11 @@ struct IResearchQueryStringTermSetup {
|
|||
features.emplace_back(new arangodb::LdapFeature(server), false); // required for AuthenticationFeature with USE_ENTERPRISE
|
||||
#endif
|
||||
|
||||
// required for V8DealerFeature::prepare(), ClusterFeature::prepare() not required
|
||||
arangodb::application_features::ApplicationServer::server->addFeature(
|
||||
new arangodb::ClusterFeature(server)
|
||||
);
|
||||
|
||||
for (auto& f : features) {
|
||||
arangodb::application_features::ApplicationServer::server->addFeature(f.first);
|
||||
}
|
||||
|
@ -162,6 +170,12 @@ struct IResearchQueryStringTermSetup {
|
|||
f.first->prepare();
|
||||
}
|
||||
|
||||
auto const databases = arangodb::velocypack::Parser::fromJson(std::string("[ { \"name\": \"") + arangodb::StaticStrings::SystemDatabase + "\" } ]");
|
||||
auto* dbFeature = arangodb::application_features::ApplicationServer::lookupFeature<
|
||||
arangodb::DatabaseFeature
|
||||
>("Database");
|
||||
dbFeature->loadDatabases(databases->slice());
|
||||
|
||||
for (auto& f : features) {
|
||||
if (f.second) {
|
||||
f.first->start();
|
||||
|
@ -199,21 +213,22 @@ struct IResearchQueryStringTermSetup {
|
|||
auto* analyzers = arangodb::application_features::ApplicationServer::lookupFeature<
|
||||
arangodb::iresearch::IResearchAnalyzerFeature
|
||||
>();
|
||||
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
|
||||
TRI_vocbase_t* vocbase;
|
||||
|
||||
analyzers->emplace("test_analyzer", "TestAnalyzer", "abc"); // cache analyzer
|
||||
analyzers->emplace("test_csv_analyzer", "TestDelimAnalyzer", ","); // cache analyzer
|
||||
dbFeature->createDatabase(1, "testVocbase", vocbase); // required for IResearchAnalyzerFeature::emplace(...)
|
||||
analyzers->emplace(result, "testVocbase::test_analyzer", "TestAnalyzer", "abc"); // cache analyzer
|
||||
analyzers->emplace(result, "testVocbase::test_csv_analyzer", "TestDelimAnalyzer", ","); // cache analyzer
|
||||
|
||||
auto* dbPathFeature = arangodb::application_features::ApplicationServer::getFeature<arangodb::DatabasePathFeature>("DatabasePath");
|
||||
arangodb::tests::setDatabasePath(*dbPathFeature); // ensure test data is stored in a unique directory
|
||||
}
|
||||
|
||||
~IResearchQueryStringTermSetup() {
|
||||
system.reset(); // destroy before reseting the 'ENGINE'
|
||||
arangodb::AqlFeature(server).stop(); // unset singleton instance
|
||||
arangodb::LogTopic::setLogLevel(arangodb::iresearch::TOPIC.name(), arangodb::LogLevel::DEFAULT);
|
||||
arangodb::LogTopic::setLogLevel(arangodb::Logger::FIXME.name(), arangodb::LogLevel::DEFAULT);
|
||||
arangodb::application_features::ApplicationServer::server = nullptr;
|
||||
arangodb::EngineSelectorFeature::ENGINE = nullptr;
|
||||
|
||||
// destroy application features
|
||||
for (auto& f : features) {
|
||||
|
@ -227,6 +242,7 @@ struct IResearchQueryStringTermSetup {
|
|||
}
|
||||
|
||||
arangodb::LogTopic::setLogLevel(arangodb::Logger::AUTHENTICATION.name(), arangodb::LogLevel::DEFAULT);
|
||||
arangodb::EngineSelectorFeature::ENGINE = nullptr;
|
||||
}
|
||||
}; // IResearchQuerySetup
|
||||
|
||||
|
@ -321,7 +337,7 @@ TEST_CASE("IResearchQueryTestOptimization", "[iresearch][iresearch-query]") {
|
|||
}
|
||||
|
||||
CHECK((trx.commit().ok()));
|
||||
CHECK(view->commit().ok());
|
||||
CHECK((arangodb::iresearch::IResearchLinkHelper::find(*logicalCollection1, *view)->commit().ok()));
|
||||
}
|
||||
|
||||
// a IN [ x ] && a == y, x < y
|
||||
|
@ -8498,4 +8514,4 @@ TEST_CASE("IResearchQueryTestOptimization", "[iresearch][iresearch-query]") {
|
|||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- END-OF-FILE
|
||||
// -----------------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------------
|
|
@ -45,6 +45,7 @@ struct GeneralRequestMock: public arangodb::GeneralRequest {
|
|||
virtual arangodb::velocypack::StringRef rawPayload() const override;
|
||||
virtual arangodb::velocypack::Slice payload(arangodb::velocypack::Options const* options = &arangodb::velocypack::Options::Defaults) override;
|
||||
virtual arangodb::Endpoint::TransportType transportType() override;
|
||||
std::unordered_map<std::string, std::string>& values() { return _values; }
|
||||
};
|
||||
|
||||
struct GeneralResponseMock: public arangodb::GeneralResponse {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -457,7 +457,6 @@ SECTION("test_auth") {
|
|||
|
||||
arangoView->SetInternalField(SLOT_CLASS_TYPE, v8::Integer::New(isolate.get(), WRP_VOCBASE_VIEW_TYPE));
|
||||
arangoView->SetInternalField(SLOT_CLASS, v8::External::New(isolate.get(), logicalView.get()));
|
||||
arangoView->SetInternalField(SLOT_EXTERNAL, v8::External::New(isolate.get(), logicalView.get()));
|
||||
std::vector<v8::Local<v8::Value>> args = {
|
||||
};
|
||||
|
||||
|
@ -593,7 +592,6 @@ SECTION("test_auth") {
|
|||
|
||||
arangoView->SetInternalField(SLOT_CLASS_TYPE, v8::Integer::New(isolate.get(), WRP_VOCBASE_VIEW_TYPE));
|
||||
arangoView->SetInternalField(SLOT_CLASS, v8::External::New(isolate.get(), logicalView.get()));
|
||||
arangoView->SetInternalField(SLOT_EXTERNAL, v8::External::New(isolate.get(), logicalView.get()));
|
||||
std::vector<v8::Local<v8::Value>> args = {
|
||||
TRI_V8_ASCII_STRING(isolate.get(), "testView1"),
|
||||
};
|
||||
|
@ -771,7 +769,6 @@ SECTION("test_auth") {
|
|||
|
||||
arangoView->SetInternalField(SLOT_CLASS_TYPE, v8::Integer::New(isolate.get(), WRP_VOCBASE_VIEW_TYPE));
|
||||
arangoView->SetInternalField(SLOT_CLASS, v8::External::New(isolate.get(), logicalView.get()));
|
||||
arangoView->SetInternalField(SLOT_EXTERNAL, v8::External::New(isolate.get(), logicalView.get()));
|
||||
std::vector<v8::Local<v8::Value>> args = {
|
||||
TRI_VPackToV8(isolate.get(), arangodb::velocypack::Parser::fromJson("{ \"key\": \"value\" }")->slice()),
|
||||
};
|
||||
|
@ -1130,7 +1127,6 @@ SECTION("test_auth") {
|
|||
|
||||
arangoView->SetInternalField(SLOT_CLASS_TYPE, v8::Integer::New(isolate.get(), WRP_VOCBASE_VIEW_TYPE));
|
||||
arangoView->SetInternalField(SLOT_CLASS, v8::External::New(isolate.get(), logicalView.get()));
|
||||
arangoView->SetInternalField(SLOT_EXTERNAL, v8::External::New(isolate.get(), logicalView.get()));
|
||||
std::vector<v8::Local<v8::Value>> args = {
|
||||
};
|
||||
|
||||
|
@ -1418,4 +1414,4 @@ SECTION("test_auth") {
|
|||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- END-OF-FILE
|
||||
// -----------------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------------
|
Loading…
Reference in New Issue