1
0
Fork 0

issue 526.3: update analyzer feature to store analyzer definitions in per-vocbase system collections (#8452)

* issue 526.3: update analyzer feature to store analyzer definitions in per-vocbase system collections

* address merge issues

* address another merge issue
This commit is contained in:
Vasiliy 2019-03-21 14:23:36 +03:00 committed by Andrey Abramov
parent 48ad71dc94
commit 405b60c2b7
58 changed files with 2317 additions and 600 deletions

View File

@ -374,10 +374,17 @@ bool iresearchAnalyzerLegacyAnalyzers( // upgrade task
std::string(vocbase.name()).append(2, ANALYZER_PREFIX_DELIM).append(entry.first);
auto& type = legacyAnalyzerType;
auto& properties = entry.second;
auto result = // result
analyzers->emplace(name, type, properties, legacyAnalyzerFeatures);
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
auto res = analyzers->emplace( // add analyzer
result, name, type, properties, legacyAnalyzerFeatures // args
);
if (!result.first) {
if (!res.ok()) {
LOG_TOPIC(WARN, arangodb::iresearch::TOPIC)
<< "failure while registering a legacy static analyzer '" << name << "' with vocbase '" << vocbase.name() << "': " << res.errorNumber() << " " << res.errorMessage();
success = false;
} else if (!result.first) {
LOG_TOPIC(WARN, arangodb::iresearch::TOPIC)
<< "failure while registering a legacy static analyzer '" << name << "' with vocbase '" << vocbase.name() << "'";
@ -620,6 +627,49 @@ IResearchAnalyzerFeature::IResearchAnalyzerFeature(arangodb::application_feature
);
}
/*static*/ bool IResearchAnalyzerFeature::canUse( // check permissions
irs::string_ref const& analyzer, // analyzer name
TRI_vocbase_t const& defaultVocbase, // fallback vocbase if not part of name
arangodb::auth::Level const& level // access level
) {
auto* ctx = arangodb::ExecContext::CURRENT;
if (!ctx) {
return true; // authentication not enabled
}
auto& staticAnalyzers = getStaticAnalyzers();
if (staticAnalyzers.find(irs::make_hashed_ref(analyzer, std::hash<irs::string_ref>())) != staticAnalyzers.end()) {
return true; // special case for singleton static analyzers (always allowed)
}
auto* sysDatabase = arangodb::application_features::ApplicationServer::lookupFeature< // find feature
arangodb::SystemDatabaseFeature // featue type
>();
auto sysVocbase = sysDatabase ? sysDatabase->use() : nullptr;
std::pair<irs::string_ref, irs::string_ref> split;
if (sysVocbase) {
split = splitAnalyzerName( // split analyzer name
arangodb::iresearch::IResearchAnalyzerFeature::normalize( // normalize
analyzer, defaultVocbase, *sysVocbase // args
)
);
} else {
split = splitAnalyzerName(analyzer);
}
// FIXME TODO remove temporary workaround once emplace(...) and all tests are updated
if (split.first.null()) {
return true;
}
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()*/
@ -757,6 +807,16 @@ std::pair<IResearchAnalyzerFeature::AnalyzerPool::ptr, bool> IResearchAnalyzerFe
return std::make_pair(AnalyzerPool::ptr(), false);
}
arangodb::Result IResearchAnalyzerFeature::emplace( // emplace an analyzer
EmplaceResult& result, // emplacement result on success (out-parameter)
irs::string_ref const& name, // analyzer name
irs::string_ref const& type, // analyzer type
irs::string_ref const& properties, // analyzer properties
irs::flags const& features /*= irs::flags::empty_instance()*/ // analyzer features
) {
return ensure(result, name, type, properties, features, true);
}
IResearchAnalyzerFeature::AnalyzerPool::ptr IResearchAnalyzerFeature::ensure( // get analyzer or placeholder
irs::string_ref const& name // analyzer name
) {
@ -768,6 +828,107 @@ IResearchAnalyzerFeature::AnalyzerPool::ptr IResearchAnalyzerFeature::ensure( //
: emplace(name, irs::string_ref::NIL, irs::string_ref::NIL, false).first;
}
arangodb::Result IResearchAnalyzerFeature::ensure( // ensure analyzer existence if possible
EmplaceResult& result, // emplacement result on success (out-param)
irs::string_ref const& name, // analyzer name
irs::string_ref const& type, // analyzer type
irs::string_ref const& properties, // analyzer properties
irs::flags const& features, // analyzer features
bool allowCreation
) {
try {
static const auto generator = []( // key + value generator
irs::hashed_string_ref const& key, // source key
AnalyzerPool::ptr const& value // source value
)->irs::hashed_string_ref {
auto pool = std::make_shared<AnalyzerPool>(key); // allocate pool
const_cast<AnalyzerPool::ptr&>(value) = pool; // lazy-instantiate pool to avoid allocation if pool is already present
return pool ? irs::hashed_string_ref(key.hash(), pool->name()) : key; // reuse hash but point ref at value in pool
};
WriteMutex mutex(_mutex);
SCOPED_LOCK(mutex);
auto itr = irs::map_utils::try_emplace_update_key( // emplace and update key
_analyzers, // destination
generator, // key generator
irs::make_hashed_ref(name, std::hash<irs::string_ref>()) // key
);
bool erase = itr.second; // an insertion took place
auto cleanup = irs::make_finally([&erase, this, &itr]()->void {
if (erase) {
_analyzers.erase(itr.first); // ensure no broken analyzers are left behind
}
});
auto pool = itr.first->second;
if (!pool) {
return arangodb::Result( // result
TRI_ERROR_BAD_PARAMETER, // code
std::string("failure creating an arangosearch analyzer instance for name '") + std::string(name) + "' type '" + std::string(type) + "' properties '" + std::string(properties) + "'"
);
}
// new pool creation
if (itr.second) {
if (!pool->init(type, properties, features)) {
return arangodb::Result( // result
TRI_ERROR_BAD_PARAMETER, // code
std::string("failure initializing an arangosearch analyzer instance for name '") + std::string(name) + "' type '" + std::string(type) + "' properties '" + std::string(properties) + "'"
);
}
if (!allowCreation) {
return arangodb::Result( // result
TRI_ERROR_BAD_PARAMETER, // code
std::string("forbidden implicit creation of an arangosearch analyzer instance for name '") + std::string(name) + "' type '" + std::string(type) + "' properties '" + std::string(properties) + "'"
);
}
// persist only on coordinator and single-server
auto res = arangodb::ServerState::instance()->isCoordinator() // coordinator
|| arangodb::ServerState::instance()->isSingleServer() // single-server
? storeAnalyzer(*pool) : arangodb::Result();
if (res.ok()) {
result = std::make_pair(pool, itr.second);
erase = false; // successful pool creation, cleanup not required
}
return res;
}
// pool exists but with different configuration
if (type != pool->type() // different type
|| properties != pool->properties() // different properties
|| features != pool->features() // different features
) {
return arangodb::Result( // result
TRI_ERROR_BAD_PARAMETER, // code
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()) + "'"
);
}
result = std::make_pair(pool, itr.second);
} catch (arangodb::basics::Exception const& e) {
return arangodb::Result( // result
e.code(), // code
std::string("caught exception while registering an arangosearch analizer name '") + std::string(name) + "' type '" + std::string(type) + "' properties '" + std::string(properties) + "': " + std::to_string(e.code()) + " " + e.what()
);
} catch (std::exception const& e) {
return arangodb::Result( // result
TRI_ERROR_INTERNAL, // code
std::string("caught exception while registering an arangosearch analizer name '") + std::string(name) + "' type '" + std::string(type) + "' properties '" + std::string(properties) + "': " + e.what()
);
} catch (...) {
return arangodb::Result( // result
TRI_ERROR_INTERNAL, // code
std::string("caught exception while registering an arangosearch analizer name '") + std::string(name) + "' type '" + std::string(type) + "' properties '" + std::string(properties) + "'"
);
}
return arangodb::Result();
}
size_t IResearchAnalyzerFeature::erase(irs::string_ref const& name) noexcept {
try {
WriteMutex mutex(_mutex);
@ -921,6 +1082,33 @@ IResearchAnalyzerFeature::AnalyzerPool::ptr IResearchAnalyzerFeature::get( // fi
return nullptr;
}
IResearchAnalyzerFeature::AnalyzerPool::ptr IResearchAnalyzerFeature::get( // find analyzer
irs::string_ref const& name, // analyzer name
irs::string_ref const& type, // analyzer type
irs::string_ref const& properties, // analyzer properties
irs::flags const& features // analyzer features
) {
EmplaceResult result;
auto res = ensure( // find and validate analyzer
result, // result
name, // analyzer name
type, // analyzer type
properties, // analyzer properties
features, // analyzer features
arangodb::ServerState::instance()->isDBServer() // create analyzer only if on db-server
);
if (!res.ok()) {
LOG_TOPIC(WARN, arangodb::iresearch::TOPIC)
<< "failure to get arangosearch analyzer name '" << name << "': " << res.errorNumber() << " " << res.errorMessage();
TRI_set_errno(TRI_ERROR_INTERNAL);
return nullptr;
}
return result.first;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief return a container of statically defined/initialized analyzers
////////////////////////////////////////////////////////////////////////////////
@ -1301,6 +1489,11 @@ bool IResearchAnalyzerFeature::loadConfiguration() {
auto split = splitAnalyzerName(name);
if (expandVocbasePrefix) {
// FIXME TODO remove temporary workaround once emplace(...) and all tests are updated
if (split.first.null()) {
return split.second;
}
if (split.first.null()) {
return std::string(activeVocbase.name()).append(2, ANALYZER_PREFIX_DELIM).append(split.second);
}
@ -1479,6 +1672,16 @@ arangodb::Result IResearchAnalyzerFeature::storeAnalyzer(AnalyzerPool& pool) {
);
}
auto* engine = arangodb::EngineSelectorFeature::ENGINE;
// do not allow persistence while in recovery
if (engine && engine->inRecovery()) {
return arangodb::Result( // result
TRI_ERROR_INTERNAL, // code
std::string("failure to persist arangosearch analyzer '") + pool.name()+ "' configuration while storage engine in recovery"
);
}
auto split = splitAnalyzerName(pool.name());
auto* vocbase = dbFeature->useDatabase(split.first);
@ -1601,7 +1804,7 @@ arangodb::Result IResearchAnalyzerFeature::storeAnalyzer(AnalyzerPool& pool) {
pool.setKey(getStringRef(key));
} catch (arangodb::basics::Exception const& e) {
return arangodb::Result( // result
TRI_ERROR_INTERNAL, // code
e.code(), // code
std::string("caught exception while persisting configuration for arangosearch analyzer name '") + pool.name() + "': " + std::to_string(e.code()) + " "+ e.what()
);
} catch (std::exception const& e) {

View File

@ -59,6 +59,7 @@ class IResearchAnalyzerFeature final : public arangodb::application_features::Ap
class AnalyzerPool : private irs::util::noncopyable {
public:
typedef std::shared_ptr<AnalyzerPool> ptr;
explicit AnalyzerPool(irs::string_ref const& name);
irs::flags const& features() const noexcept { return _features; }
irs::analysis::analyzer::ptr get() const noexcept; // nullptr == error creating analyzer
std::string const& name() const noexcept { return _name; }
@ -78,13 +79,11 @@ class IResearchAnalyzerFeature final : public arangodb::application_features::Ap
// AnalyzerBuilder::make(...))
std::string _config; // non-null type + non-null properties + key
irs::flags _features; // cached analyzer features
irs::string_ref _key; // the key of the persisted configuration for this
// pool, null == not persisted
irs::string_ref _key; // the key of the persisted configuration for this pool, null == static analyzer
std::string _name; // ArangoDB alias for an IResearch analyzer configuration
irs::string_ref _properties; // IResearch analyzer configuration
irs::string_ref _type; // IResearch analyzer name
explicit AnalyzerPool(irs::string_ref const& name);
bool init(irs::string_ref const& type, irs::string_ref const& properties,
irs::flags const& features = irs::flags::empty_instance());
void setKey(irs::string_ref const& type);
@ -100,11 +99,47 @@ class IResearchAnalyzerFeature final : public arangodb::application_features::Ap
arangodb::auth::Level const& level // access level
);
//////////////////////////////////////////////////////////////////////////////
/// @return analyzer with the given prefixed name (or unprefixed and resides
/// in defaultVocbase) is granted 'level' access
//////////////////////////////////////////////////////////////////////////////
static bool canUse( // check permissions
irs::string_ref const& analyzer, // analyzer name
TRI_vocbase_t const& defaultVocbase, // fallback vocbase if not part of name
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)
/// first - the emplaced pool
/// second - if an insertion of an new analyzer occured
/// @param name analyzer name (already normalized)
/// @param type the underlying IResearch analyzer type
/// @param properties the configuration for the underlying IResearch type
/// @param features the expected features the analyzer should produce
/// @param implicitCreation false == treat as error if creation is required
/// @return success
/// @note emplacement while inRecovery() will not allow adding new analyzers
/// valid because for existing links the analyzer definition should
/// already have been persisted and feature administration is not
/// allowed during recovery
//////////////////////////////////////////////////////////////////////////////
typedef std::pair<AnalyzerPool::ptr, bool> EmplaceResult;
arangodb::Result emplace( // emplace an analyzer
EmplaceResult& result, // emplacement result on success (out-param)
irs::string_ref const& name, // analyzer name
irs::string_ref const& type, // analyzer type
irs::string_ref const& properties, // analyzer properties
irs::flags const& features = irs::flags::empty_instance() // analyzer features
);
//////////////////////////////////////////////////////////////////////////////
/// @brief get analyzer or placeholder
/// before start() returns pool placeholder,
@ -112,6 +147,7 @@ class IResearchAnalyzerFeature final : public arangodb::application_features::Ap
/// after start() returns same as get(...)
/// @param name analyzer name (used verbatim)
//////////////////////////////////////////////////////////////////////////////
// FIXME TODO remove
AnalyzerPool::ptr ensure(irs::string_ref const& name);
//////////////////////////////////////////////////////////////////////////////
@ -126,6 +162,23 @@ class IResearchAnalyzerFeature final : public arangodb::application_features::Ap
//////////////////////////////////////////////////////////////////////////////
AnalyzerPool::ptr get(irs::string_ref const& name) const noexcept;
//////////////////////////////////////////////////////////////////////////////
/// @brief find analyzer
/// @param name analyzer name (already normalized)
/// @param type the underlying IResearch analyzer type
/// @param properties the configuration for the underlying IResearch type
/// @param features the expected features the analyzer should produce
/// @return analyzer matching the specified parameters or nullptr
/// @note will construct and cache the analyzer if missing only on db-server
/// persistence in the cases of inRecovery or !storage engine will fail
//////////////////////////////////////////////////////////////////////////////
AnalyzerPool::ptr get( // find analyzer
irs::string_ref const& name, // analyzer name
irs::string_ref const& type, // analyzer type
irs::string_ref const& properties, // analyzer properties
irs::flags const& features // analyzer features
);
static AnalyzerPool::ptr identity() noexcept; // the identity analyzer
static std::string const& name() noexcept;
@ -165,12 +218,42 @@ class IResearchAnalyzerFeature final : public arangodb::application_features::Ap
mutable irs::async_utils::read_write_mutex _mutex;
bool _started;
// FIXME TODO remove
std::pair<AnalyzerPool::ptr, bool> emplace(
irs::string_ref const& name, irs::string_ref const& type,
irs::string_ref const& properties, bool initAndPersist,
irs::flags const& features = irs::flags::empty_instance()) noexcept;
static Analyzers const& getStaticAnalyzers();
//////////////////////////////////////////////////////////////////////////////
/// @brief ensure an analyzer as per the specified parameters exists if
/// possible
/// @param result the result of the successful emplacement (out-param)
/// first - the emplaced pool
/// second - if an insertion of an new analyzer occured
/// @param name analyzer name (already normalized)
/// @param type the underlying IResearch analyzer type
/// @param properties the configuration for the underlying IResearch type
/// @param features the expected features the analyzer should produce
/// @param allowCreation false == treat as an error if creation is required
/// @return success
/// @note ensure while inRecovery() will not allow new analyzer persistance
/// valid because for existing links the analyzer definition should
/// already have been persisted and feature administration is not
/// allowed during recovery
/// @note for db-server analyzers are not persisted
/// valid because the authoritative analyzer source is from coordinators
//////////////////////////////////////////////////////////////////////////////
arangodb::Result ensure( // ensure analyzer existence if possible
EmplaceResult& result, // emplacement result on success (out-param)
irs::string_ref const& name, // analyzer name
irs::string_ref const& type, // analyzer type
irs::string_ref const& properties, // analyzer properties
irs::flags const& features, // analyzer features
bool allowCreation
);
bool loadConfiguration();
//////////////////////////////////////////////////////////////////////////////

View File

@ -52,6 +52,7 @@
#include "IResearchKludge.h"
#include "IResearchPrimaryKeyFilter.h"
#include "Logger/LogMacros.h"
#include "RestServer/SystemDatabaseFeature.h"
using namespace arangodb::iresearch;
@ -176,7 +177,22 @@ IResearchAnalyzerFeature::AnalyzerPool::ptr extractAnalyzerFromArg(
return nullptr;
}
analyzer = analyzerFeature->get(analyzerId);
if (ctx.trx) {
auto* sysDatabase = arangodb::application_features::ApplicationServer::lookupFeature< // find feature
arangodb::SystemDatabaseFeature // featue type
>();
auto sysVocbase = sysDatabase ? sysDatabase->use() : nullptr;
if (sysVocbase) {
analyzer = analyzerFeature->get( // get analyzer
arangodb::iresearch::IResearchAnalyzerFeature::normalize( // normalize
analyzerId, ctx.trx->vocbase(), *sysVocbase // args
)
);
}
} else {
analyzer = analyzerFeature->get(analyzerId); // verbatim
}
if (!analyzer) {
LOG_TOPIC(WARN, arangodb::iresearch::TOPIC)
@ -1956,4 +1972,4 @@ namespace iresearch {
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------

View File

@ -672,41 +672,40 @@ arangodb::Result IResearchLink::drop() {
bool exists;
// remove persisted data store directory if present
if (!_dataStore._path.exists_directory(exists) ||
(exists && !_dataStore._path.remove())) {
return arangodb::Result(TRI_ERROR_INTERNAL,
std::string(
"failed to remove arangosearch link '") +
std::to_string(id()) + "'");
if (!_dataStore._path.exists_directory(exists)
|| (exists && !_dataStore._path.remove())) {
return arangodb::Result(
TRI_ERROR_INTERNAL,
std::string("failed to remove arangosearch link '") + std::to_string(id()) + "'"
);
}
} catch (arangodb::basics::Exception& e) {
return arangodb::Result(
e.code(),
std::string("caught exception while removing arangosearch link '") +
std::to_string(id()) + "': " + e.what());
e.code(),
std::string("caught exception while removing arangosearch link '") + std::to_string(id()) + "': " + e.what()
);
} catch (std::exception const& e) {
return arangodb::Result(
TRI_ERROR_INTERNAL,
std::string("caught exception while removing arangosearch link '") +
std::to_string(id()) + "': " + e.what());
TRI_ERROR_INTERNAL,
std::string("caught exception while removing arangosearch link '") + std::to_string(id()) + "': " + e.what()
);
} catch (...) {
return arangodb::Result(
TRI_ERROR_INTERNAL,
std::string("caught exception while removing arangosearch link '") +
std::to_string(id()) + "'");
TRI_ERROR_INTERNAL,
std::string("caught exception while removing arangosearch link '") + std::to_string(id()) + "'"
);
}
return arangodb::Result();
}
bool IResearchLink::hasBatchInsert() const { return true; }
bool IResearchLink::hasSelectivityEstimate() const {
return false; // selectivity can only be determined per query since multiple
// fields are indexed
bool IResearchLink::hasBatchInsert() const {
return true;
}
TRI_idx_iid_t IResearchLink::id() const noexcept { return _id; }
bool IResearchLink::hasSelectivityEstimate() const {
return false; // selectivity can only be determined per query since multiple fields are indexed
}
arangodb::Result IResearchLink::init(
arangodb::velocypack::Slice const& definition,
@ -719,16 +718,19 @@ arangodb::Result IResearchLink::init(
std::string error;
IResearchLinkMeta meta;
if (!meta.init(definition, error)) {
if (!meta.init(definition, error, &(collection().vocbase()))) { // definition should already be normalized and analyzers created if required
return arangodb::Result(
TRI_ERROR_BAD_PARAMETER,
std::string("error parsing view link parameters from json: ") + error);
TRI_ERROR_BAD_PARAMETER,
std::string("error parsing view link parameters from json: ") + error
);
}
if (!definition.isObject() || !definition.get(StaticStrings::ViewIdField).isString()) {
return arangodb::Result(TRI_ERROR_ARANGO_DATA_SOURCE_NOT_FOUND,
std::string("error finding view for link '") +
std::to_string(_id) + "'");
if (!definition.isObject() // not object
|| !definition.get(StaticStrings::ViewIdField).isString()) {
return arangodb::Result( // result
TRI_ERROR_ARANGO_DATA_SOURCE_NOT_FOUND, // code
std::string("error finding view for link '") + std::to_string(_id) + "'" // message
);
}
auto viewId = definition.get(StaticStrings::ViewIdField).copyString();
@ -767,11 +769,25 @@ arangodb::Result IResearchLink::init(
viewId = view->guid(); // ensue that this is a GUID (required by
// operator==(IResearchView))
if (!view->emplace(_collection.id(), _collection.name(), definition)) {
return arangodb::Result(TRI_ERROR_INTERNAL,
std::string("failed to link with view '") + view->name() +
"' while initializing link '" +
std::to_string(_id) + "'");
arangodb::velocypack::Builder builder;
builder.openObject();
// FIXME TODO move this logic into IResearchViewCoordinator
if (!meta.json(builder, false)) { // generate user-visible definition
return arangodb::Result( // result
TRI_ERROR_INTERNAL, // code
std::string("failed to generate link definition while initializing link '") + std::to_string(_id) + "'"
);
}
builder.close();
if (!view->emplace(_collection.id(), _collection.name(), builder.slice())) {
return arangodb::Result( // result
TRI_ERROR_INTERNAL, // code
std::string("failed to link with view '") + view->name() + "' while initializing link '" + std::to_string(_id) + "'"
);
}
}
} else if (arangodb::ServerState::instance()->isDBServer()) { // db-server link
@ -1427,27 +1443,13 @@ bool IResearchLink::isSorted() const {
return false; // IResearch does not provide a fixed default sort order
}
bool IResearchLink::json(arangodb::velocypack::Builder& builder) const {
if (!builder.isOpenObject() || !_meta.json(builder)) {
return false;
}
builder.add(arangodb::StaticStrings::IndexId,
arangodb::velocypack::Value(std::to_string(_id)));
builder.add(arangodb::StaticStrings::IndexType,
arangodb::velocypack::Value(IResearchLinkHelper::type()));
builder.add(StaticStrings::ViewIdField, arangodb::velocypack::Value(_viewGuid));
return true;
}
void IResearchLink::load() {
// Note: this function is only used by RocksDB
}
bool IResearchLink::matchesDefinition(VPackSlice const& slice) const {
if (!slice.isObject() || !slice.hasKey(StaticStrings::ViewIdField)) {
return false; // slice has no view identifier field
return false; // slice has no view identifier field
}
auto viewId = slice.get(StaticStrings::ViewIdField);
@ -1455,27 +1457,26 @@ bool IResearchLink::matchesDefinition(VPackSlice const& slice) const {
// NOTE: below will not match if 'viewId' is 'id' or 'name',
// but ViewIdField should always contain GUID
if (!viewId.isString() || !viewId.isEqualString(_viewGuid)) {
return false; // IResearch View identifiers of current object and slice do
// not match
return false; // IResearch View identifiers of current object and slice do not match
}
IResearchLinkMeta other;
std::string errorField;
return other.init(slice, errorField) && _meta == other;
return other.init(slice, errorField, &(collection().vocbase())) // for db-server analyzer validation should have already apssed on coordinator (missing analyzer == no match)
&& _meta == other;
}
size_t IResearchLink::memory() const {
auto size = sizeof(IResearchLink); // includes empty members from parent
auto size = sizeof(IResearchLink); // includes empty members from parent
size += _meta.memory();
{
SCOPED_LOCK(_asyncSelf->mutex()); // '_dataStore' can be asynchronously modified
SCOPED_LOCK(_asyncSelf->mutex()); // '_dataStore' can be asynchronously modified
if (_dataStore) {
// FIXME TODO this is incorrect since '_storePersisted' is on disk and not
// in memory
// FIXME TODO this is incorrect since '_storePersisted' is on disk and not in memory
size += directoryMemory(*(_dataStore._directory), id());
size += _dataStore._path.native().size() * sizeof(irs::utf8_path::native_char_t);
}
@ -1484,6 +1485,30 @@ size_t IResearchLink::memory() const {
return size;
}
arangodb::Result IResearchLink::properties( // get link properties
arangodb::velocypack::Builder& builder, // output buffer
bool forPersistence // properties for persistance
) const {
if (!builder.isOpenObject() || !_meta.json(builder, forPersistence)) {
return arangodb::Result(TRI_ERROR_BAD_PARAMETER);
}
builder.add(
arangodb::StaticStrings::IndexId,
arangodb::velocypack::Value(std::to_string(_id))
);
builder.add(
arangodb::StaticStrings::IndexType,
arangodb::velocypack::Value(IResearchLinkHelper::type())
);
builder.add(
StaticStrings::ViewIdField,
arangodb::velocypack::Value(_viewGuid)
);
return arangodb::Result();
}
arangodb::Result IResearchLink::properties(IResearchViewMeta const& meta) {
SCOPED_LOCK(_asyncSelf->mutex()); // '_dataStore' can be asynchronously modified
@ -1747,4 +1772,4 @@ arangodb::Result IResearchLink::unload() {
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------

View File

@ -113,51 +113,55 @@ class IResearchLink {
////////////////////////////////////////////////////////////////////////////////
/// @brief called when the iResearch Link is dropped
////////////////////////////////////////////////////////////////////////////////
arangodb::Result drop(); // arangodb::Index override
arangodb::Result drop(); // arangodb::Index override
bool hasBatchInsert() const; // arangodb::Index override
bool hasSelectivityEstimate() const; // arangodb::Index override
bool hasBatchInsert() const; // arangodb::Index override
bool hasSelectivityEstimate() const; // arangodb::Index override
//////////////////////////////////////////////////////////////////////////////
/// @brief the identifier for this link
//////////////////////////////////////////////////////////////////////////////
TRI_idx_iid_t id() const noexcept { return _id; }
////////////////////////////////////////////////////////////////////////////////
/// @brief insert an ArangoDB document into an iResearch View using '_meta'
/// params
/// @brief insert an ArangoDB document into an iResearch View using '_meta' params
////////////////////////////////////////////////////////////////////////////////
arangodb::Result insert(arangodb::transaction::Methods& trx,
arangodb::LocalDocumentId const& documentId,
arangodb::velocypack::Slice const& doc,
arangodb::Index::OperationMode mode); // arangodb::Index override
bool isSorted() const; // arangodb::Index override
arangodb::Result insert( // insert document
arangodb::transaction::Methods& trx, // transaction
arangodb::LocalDocumentId const& documentId, // document identifier
arangodb::velocypack::Slice const& doc, // document
arangodb::Index::OperationMode mode // insert mode
); // arangodb::Index override
bool isHidden() const; // arangodb::Index override
////////////////////////////////////////////////////////////////////////////////
/// @brief the identifier for this link
////////////////////////////////////////////////////////////////////////////////
TRI_idx_iid_t id() const noexcept;
////////////////////////////////////////////////////////////////////////////////
/// @brief fill and return a jSON description of a IResearchLink object
/// elements are appended to an existing object
/// @return success or set TRI_set_errno(...) and return false
////////////////////////////////////////////////////////////////////////////////
bool json(arangodb::velocypack::Builder& builder) const;
bool isSorted() const; // arangodb::Index override
////////////////////////////////////////////////////////////////////////////////
/// @brief called when the iResearch Link is loaded into memory
////////////////////////////////////////////////////////////////////////////////
void load(); // arangodb::Index override
void load(); // arangodb::Index override
////////////////////////////////////////////////////////////////////////////////
/// @brief index comparator, used by the coordinator to detect if the specified
/// definition is the same as this link
////////////////////////////////////////////////////////////////////////////////
bool matchesDefinition(arangodb::velocypack::Slice const& slice) const; // arangodb::Index override
bool matchesDefinition( // matches
arangodb::velocypack::Slice const& slice // other definition
) const; // arangodb::Index override
////////////////////////////////////////////////////////////////////////////////
/// @brief amount of memory in bytes occupied by this iResearch Link
////////////////////////////////////////////////////////////////////////////////
size_t memory() const; // arangodb::Index override
size_t memory() const; // arangodb::Index override
//////////////////////////////////////////////////////////////////////////////
/// @brief fill and return a jSON description of a IResearchLink object
/// elements are appended to an existing object
//////////////////////////////////////////////////////////////////////////////
arangodb::Result properties( // get link properties
arangodb::velocypack::Builder& builder, // output buffer
bool forPersistence // properties for persistance
) const;
//////////////////////////////////////////////////////////////////////////////
/// @brief update runtine data processing properties (not persisted)

View File

@ -125,25 +125,27 @@ IResearchLinkCoordinator::IResearchLinkCoordinator(TRI_idx_iid_t id, LogicalColl
_sparse = true; // always sparse
}
void IResearchLinkCoordinator::toVelocyPack(
arangodb::velocypack::Builder& builder,
std::underlying_type<arangodb::Index::Serialize>::type flags) const {
void IResearchLinkCoordinator::toVelocyPack( // generate definition
arangodb::velocypack::Builder& builder, // destination buffer
std::underlying_type<arangodb::Index::Serialize>::type flags // definition flags
) const {
if (builder.isOpenObject()) {
THROW_ARANGO_EXCEPTION(
arangodb::Result(TRI_ERROR_BAD_PARAMETER,
std::string("failed to generate link definition for "
"arangosearch view Cluster link '") +
std::to_string(arangodb::Index::id()) + "'"));
THROW_ARANGO_EXCEPTION(arangodb::Result( // result
TRI_ERROR_BAD_PARAMETER, // code
std::string("failed to generate link definition for arangosearch view Cluster link '") + std::to_string(arangodb::Index::id()) + "'"
));
}
auto forPersistence = // definition for persistence
arangodb::Index::hasFlag(flags, arangodb::Index::Serialize::Internals);
builder.openObject();
if (!json(builder)) {
THROW_ARANGO_EXCEPTION(
arangodb::Result(TRI_ERROR_INTERNAL,
std::string("failed to generate link definition for "
"arangosearch view Cluster link '") +
std::to_string(arangodb::Index::id()) + "'"));
if (!properties(builder, forPersistence).ok()) {
THROW_ARANGO_EXCEPTION(arangodb::Result( // result
TRI_ERROR_INTERNAL, // code
std::string("failed to generate link definition for arangosearch view Cluster link '") + std::to_string(arangodb::Index::id()) + "'"
));
}
if (arangodb::Index::hasFlag(flags, arangodb::Index::Serialize::Figures)) {

View File

@ -97,7 +97,7 @@ class IResearchLinkCoordinator final : public arangodb::ClusterIndex, public IRe
/// @brief fill and return a JSON description of a IResearchLink object
/// @param withFigures output 'figures' section with e.g. memory size
////////////////////////////////////////////////////////////////////////////////
using Index::toVelocyPack; // for Index::toVelocyPack(bool, unsigned)
using Index::toVelocyPack; // for std::shared_ptr<Builder> Index::toVelocyPack(bool, Index::Serialize)
virtual void toVelocyPack(arangodb::velocypack::Builder& builder,
std::underlying_type<arangodb::Index::Serialize>::type flags) const override;

View File

@ -32,6 +32,8 @@
#include "Basics/StaticStrings.h"
#include "Logger/Logger.h"
#include "Logger/LogMacros.h"
#include "StorageEngine/EngineSelectorFeature.h"
#include "StorageEngine/StorageEngine.h"
#include "Transaction/Methods.h"
#include "Transaction/StandaloneContext.h"
#include "Utils/CollectionNameResolver.h"
@ -47,8 +49,39 @@ namespace {
////////////////////////////////////////////////////////////////////////////////
std::string const& LINK_TYPE = arangodb::iresearch::DATA_SOURCE_TYPE.name();
bool createLink(arangodb::LogicalCollection& collection, arangodb::LogicalView const& view,
arangodb::velocypack::Slice definition) {
arangodb::Result canUseAnalyzers( // validate
arangodb::iresearch::IResearchLinkMeta const& meta, // metadata
TRI_vocbase_t const& defaultVocbase // default vocbase
) {
for (auto& entry: meta._analyzers) {
if (entry // valid entry
&& !arangodb::iresearch::IResearchAnalyzerFeature::canUse(entry->name(), defaultVocbase, arangodb::auth::Level::RO)
) {
return arangodb::Result( // result
TRI_ERROR_FORBIDDEN, // code
std::string("read access is forbidden to arangosearch analyzer '") + entry->name() + "'"
);
}
}
for (auto& field: meta._fields) {
TRI_ASSERT(field.value().get()); // ensured by UniqueHeapInstance constructor
auto& entry = field.value();
auto res = canUseAnalyzers(*entry, defaultVocbase);
if (!res.ok()) {
return res;
}
}
return arangodb::Result();
}
bool createLink( // create link
arangodb::LogicalCollection& collection, // link collection
arangodb::LogicalView const& view, // link view
arangodb::velocypack::Slice definition // link definition
) {
bool isNew = false;
auto link = collection.createIndex(definition, isNew);
LOG_TOPIC_IF(DEBUG, arangodb::iresearch::TOPIC, link)
@ -57,21 +90,29 @@ bool createLink(arangodb::LogicalCollection& collection, arangodb::LogicalView c
return link && isNew;
}
bool createLink(arangodb::LogicalCollection& collection,
arangodb::iresearch::IResearchViewCoordinator const& view,
arangodb::velocypack::Slice definition) {
static const std::function<bool(irs::string_ref const& key)> acceptor =
[](irs::string_ref const& key) -> bool {
bool createLink( // create link
arangodb::LogicalCollection& collection, // link collection
arangodb::iresearch::IResearchViewCoordinator const& view, // link view
arangodb::velocypack::Slice definition // link definition
) {
static const std::function<bool(irs::string_ref const& key)> acceptor = [](
irs::string_ref const& key // json key
)->bool {
// ignored fields
return key != arangodb::StaticStrings::IndexType &&
key != arangodb::iresearch::StaticStrings::ViewIdField;
return key != arangodb::StaticStrings::IndexType // type field
&& key != arangodb::iresearch::StaticStrings::ViewIdField; // view id field
};
arangodb::velocypack::Builder builder;
builder.openObject();
builder.add(arangodb::StaticStrings::IndexType, arangodb::velocypack::Value(LINK_TYPE));
builder.add(arangodb::iresearch::StaticStrings::ViewIdField,
arangodb::velocypack::Value(view.guid()));
builder.add( // add
arangodb::StaticStrings::IndexType, // key
arangodb::velocypack::Value(LINK_TYPE) // value
);
builder.add( // add
arangodb::iresearch::StaticStrings::ViewIdField, // key
arangodb::velocypack::Value(view.guid()) // value
);
if (!arangodb::iresearch::mergeSliceSkipKeys(builder, definition, acceptor)) {
return false;
@ -81,41 +122,49 @@ bool createLink(arangodb::LogicalCollection& collection,
arangodb::velocypack::Builder tmp;
return arangodb::methods::Indexes::ensureIndex(&collection, builder.slice(), true, tmp)
.ok();
return arangodb::methods::Indexes::ensureIndex( // ensure index
&collection, builder.slice(), true, tmp // args
).ok();
}
template <typename ViewType>
bool dropLink(arangodb::LogicalCollection& collection,
arangodb::iresearch::IResearchLink const& link) {
// don't need to create an extra transaction inside
// arangodb::methods::Indexes::drop(...)
template<typename ViewType>
bool dropLink( // drop link
arangodb::LogicalCollection& collection, // link collection
arangodb::iresearch::IResearchLink const& link // link to drop
) {
// don't need to create an extra transaction inside arangodb::methods::Indexes::drop(...)
return collection.dropIndex(link.id());
}
template <>
bool dropLink<arangodb::iresearch::IResearchViewCoordinator>(
arangodb::LogicalCollection& collection, arangodb::iresearch::IResearchLink const& link) {
template<>
bool dropLink<arangodb::iresearch::IResearchViewCoordinator>( // drop link
arangodb::LogicalCollection& collection, // link collection
arangodb::iresearch::IResearchLink const& link // link to drop
) {
arangodb::velocypack::Builder builder;
builder.openObject();
builder.add(arangodb::StaticStrings::IndexId, arangodb::velocypack::Value(link.id()));
builder.add( // add
arangodb::StaticStrings::IndexId, // key
arangodb::velocypack::Value(link.id()) // value
);
builder.close();
return arangodb::methods::Indexes::drop(&collection, builder.slice()).ok();
}
template <typename ViewType>
arangodb::Result modifyLinks(std::unordered_set<TRI_voc_cid_t>& modified,
TRI_vocbase_t& vocbase, ViewType& view,
arangodb::velocypack::Slice const& links,
std::unordered_set<TRI_voc_cid_t> const& stale = {}) {
arangodb::Result modifyLinks( // modify links
std::unordered_set<TRI_voc_cid_t>& modified, // modified collection ids
ViewType& view, // modified view
arangodb::velocypack::Slice const& links, // modified link definitions
std::unordered_set<TRI_voc_cid_t> const& stale = {} // stale links
) {
if (!links.isObject()) {
return arangodb::Result(
TRI_ERROR_BAD_PARAMETER,
std::string(
"error parsing link parameters from json for arangosearch view '") +
view.name() + "'");
return arangodb::Result( // result
TRI_ERROR_BAD_PARAMETER, // code
std::string("error parsing link parameters from json for arangosearch view '") + view.name() + "'"
);
}
struct State {
@ -155,21 +204,42 @@ arangodb::Result modifyLinks(std::unordered_set<TRI_voc_cid_t>& modified,
linkModifications.emplace_back(collectionsToLock.size());
collectionsToLock.emplace_back(collectionName);
continue; // only removal requested
continue; // only removal requested
}
static const std::function<bool(irs::string_ref const& key)> acceptor =
[](irs::string_ref const& key) -> bool {
arangodb::velocypack::Builder normalized;
normalized.openObject();
auto res = arangodb::iresearch::IResearchLinkHelper::normalize( // normalize to validate analyzer definitions
normalized, link, true, view.vocbase() // args
);
if (!res.ok()) {
return res;
}
normalized.close();
link = normalized.slice(); // use normalized definition for index creation
static const std::function<bool(irs::string_ref const& key)> acceptor = [](
irs::string_ref const& key // json key
)->bool {
// ignored fields
return key != arangodb::StaticStrings::IndexType &&
key != arangodb::iresearch::StaticStrings::ViewIdField;
return key != arangodb::StaticStrings::IndexType // type field
&& key != arangodb::iresearch::StaticStrings::ViewIdField; // view id field
};
arangodb::velocypack::Builder namedJson;
namedJson.openObject();
namedJson.add(arangodb::StaticStrings::IndexType, arangodb::velocypack::Value(LINK_TYPE));
namedJson.add(arangodb::iresearch::StaticStrings::ViewIdField,
arangodb::velocypack::Value(view.guid()));
namedJson.add( // add
arangodb::StaticStrings::IndexType, // key
arangodb::velocypack::Value(LINK_TYPE) // value
);
namedJson.add( // add
arangodb::iresearch::StaticStrings::ViewIdField, // key
arangodb::velocypack::Value(view.guid()) // value
);
if (!arangodb::iresearch::mergeSliceSkipKeys(namedJson, link, acceptor)) {
return arangodb::Result(
@ -183,7 +253,7 @@ arangodb::Result modifyLinks(std::unordered_set<TRI_voc_cid_t>& modified,
std::string error;
arangodb::iresearch::IResearchLinkMeta linkMeta;
if (!linkMeta.init(namedJson.slice(), error)) { // analyzers in definition should already be normalized
if (!linkMeta.init(namedJson.slice(), error)) { // normalized and validated above via normalize(...)
return arangodb::Result(
TRI_ERROR_BAD_PARAMETER,
std::string("error parsing link parameters from json for arangosearch view '") + view.name() + "' collection '" + collectionName + "' error '" + error + "'"
@ -195,7 +265,8 @@ arangodb::Result modifyLinks(std::unordered_set<TRI_voc_cid_t>& modified,
linkDefinitions.emplace_back(std::move(namedJson), std::move(linkMeta));
}
auto trxCtx = arangodb::transaction::StandaloneContext::Create(vocbase);
auto trxCtx = // transaction context
arangodb::transaction::StandaloneContext::Create(view.vocbase());
// add removals for any 'stale' links not found in the 'links' definition
for (auto& id: stale) {
@ -212,17 +283,14 @@ arangodb::Result modifyLinks(std::unordered_set<TRI_voc_cid_t>& modified,
}
if (collectionsToLock.empty()) {
return arangodb::Result(); // nothing to update
return arangodb::Result(); // nothing to update
}
static std::vector<std::string> const EMPTY;
arangodb::ExecContextScope scope(
arangodb::ExecContext::superuser()); // required to remove links from
// non-RW collections
arangodb::ExecContextScope scope(arangodb::ExecContext::superuser()); // required to remove links from non-RW collections
{
std::unordered_set<TRI_voc_cid_t> collectionsToRemove; // track removal for potential reindex
std::unordered_set<TRI_voc_cid_t> collectionsToUpdate; // track reindex requests
std::unordered_set<TRI_voc_cid_t> collectionsToRemove; // track removal for potential reindex
std::unordered_set<TRI_voc_cid_t> collectionsToUpdate; // track reindex requests
// resolve corresponding collection and link
for (auto itr = linkModifications.begin(); itr != linkModifications.end();) {
@ -466,8 +534,8 @@ namespace iresearch {
IResearchLinkMeta lhsMeta;
IResearchLinkMeta rhsMeta;
return lhsMeta.init(lhs, errorField) // left side meta valid
&& rhsMeta.init(rhs, errorField) // right side meta valid
return lhsMeta.init(lhs, errorField) // left side meta valid (for db-server analyzer validation should have already apssed on coordinator)
&& rhsMeta.init(rhs, errorField) // right side meta valid (for db-server analyzer validation should have already apssed on coordinator)
&& lhsMeta == rhsMeta; // left meta equal right meta
}
@ -519,8 +587,6 @@ namespace iresearch {
bool isCreation, // definition for index creation
TRI_vocbase_t const& vocbase // index vocbase
) {
UNUSED(isCreation);
if (!normalized.isOpenObject()) {
return arangodb::Result(
TRI_ERROR_BAD_PARAMETER,
@ -531,17 +597,35 @@ namespace iresearch {
std::string error;
IResearchLinkMeta meta;
if (!meta.init(definition, error, IResearchLinkMeta::DEFAULT(), &vocbase)) {
// @note: implicit analyzer validation via IResearchLinkMeta done in 2 places:
// IResearchLinkHelper::normalize(...) if creating via collection API
// ::modifyLinks(...) (via call to normalize(...) prior to getting
// superuser) if creating via IResearchLinkHelper API
if (!meta.init(definition, error, &vocbase)) {
return arangodb::Result(
TRI_ERROR_BAD_PARAMETER,
std::string("error parsing arangosearch link parameters from json: ") + error
);
}
auto res = canUseAnalyzers(meta, vocbase); // same validation as in modifyLinks(...) for Views API
if (!res.ok()) {
return res;
}
normalized.add(
arangodb::StaticStrings::IndexType, arangodb::velocypack::Value(LINK_TYPE)
);
// copy over IResearch Link identifier
if (definition.hasKey(arangodb::StaticStrings::IndexId)) {
normalized.add( // preserve field
arangodb::StaticStrings::IndexId, // key
definition.get(arangodb::StaticStrings::IndexId) // value
);
}
// copy over IResearch View identifier
if (definition.hasKey(StaticStrings::ViewIdField)) {
normalized.add(
@ -549,7 +633,14 @@ namespace iresearch {
);
}
return meta.json(normalized)
if (definition.hasKey(arangodb::StaticStrings::IndexInBackground)) {
normalized.add( // preserve field
arangodb::StaticStrings::IndexInBackground, // key
definition.get(arangodb::StaticStrings::IndexInBackground) // value
);
}
return meta.json(normalized, isCreation) // 'isCreation' is set when forPersistence
? arangodb::Result()
: arangodb::Result(
TRI_ERROR_BAD_PARAMETER,
@ -610,18 +701,13 @@ namespace iresearch {
IResearchLinkMeta meta;
std::string errorField;
if (!linkDefinition.isNull() && !meta.init(linkDefinition, errorField)) {
return arangodb::Result(
TRI_ERROR_BAD_PARAMETER,
errorField.empty()
? (std::string(
"while validating arangosearch link definition, error: "
"invalid link definition for collection '") +
collectionName.copyString() + "': " + linkDefinition.toString())
: (std::string(
"while validating arangosearch link definition, error: "
"invalid link definition for collection '") +
collectionName.copyString() + "' error in attribute: " + errorField));
if (!linkDefinition.isNull() && !meta.init(linkDefinition, errorField)) { // for db-server analyzer validation should have already apssed on coordinator
return arangodb::Result( // result
TRI_ERROR_BAD_PARAMETER, // code
errorField.empty()
? (std::string("while validating arangosearch link definition, error: invalid link definition for collection '") + collectionName.copyString() + "': " + linkDefinition.toString())
: (std::string("while validating arangosearch link definition, error: invalid link definition for collection '") + collectionName.copyString() + "' error in attribute: " + errorField)
);
}
}
@ -649,55 +735,61 @@ namespace iresearch {
}
/*static*/ arangodb::Result IResearchLinkHelper::updateLinks(
std::unordered_set<TRI_voc_cid_t>& modified, TRI_vocbase_t& vocbase,
arangodb::LogicalView& view, arangodb::velocypack::Slice const& links,
std::unordered_set<TRI_voc_cid_t>& modified,
arangodb::LogicalView& view,
arangodb::velocypack::Slice const& links,
std::unordered_set<TRI_voc_cid_t> const& stale /*= {}*/
) {
LOG_TOPIC(TRACE, arangodb::iresearch::TOPIC)
<< "beginning IResearchLinkHelper::updateLinks";
try {
if (arangodb::ServerState::instance()->isCoordinator()) {
return modifyLinks<IResearchViewCoordinator>(modified, vocbase,
LogicalView::cast<IResearchViewCoordinator>(view),
links, stale);
return modifyLinks<IResearchViewCoordinator>( // modify
modified, // modified cids
LogicalView::cast<IResearchViewCoordinator>(view), // modified view
links, // link modifications
stale // stale links
);
}
return modifyLinks<IResearchView>(modified, vocbase,
LogicalView::cast<IResearchView>(view), links, stale);
return modifyLinks<IResearchView>( // modify
modified, // modified cids
LogicalView::cast<IResearchView>(view), // modified view
links, // link modifications
stale // stale links
);
} catch (arangodb::basics::Exception& e) {
LOG_TOPIC(WARN, arangodb::iresearch::TOPIC)
<< "caught exception while updating links for arangosearch view '"
<< view.name() << "': " << e.code() << " " << e.what();
<< "caught exception while updating links for arangosearch view '" << view.name() << "': " << e.code() << " " << e.what();
IR_LOG_EXCEPTION();
return arangodb::Result(
e.code(), std::string("error updating links for arangosearch view '") +
view.name() + "'");
e.code(),
std::string("error updating links for arangosearch view '") + view.name() + "'"
);
} catch (std::exception const& e) {
LOG_TOPIC(WARN, arangodb::iresearch::TOPIC)
<< "caught exception while updating links for arangosearch view '"
<< view.name() << "': " << e.what();
<< "caught exception while updating links for arangosearch view '" << view.name() << "': " << e.what();
IR_LOG_EXCEPTION();
return arangodb::Result(
TRI_ERROR_BAD_PARAMETER,
std::string("error updating links for arangosearch view '") +
view.name() + "'");
TRI_ERROR_BAD_PARAMETER,
std::string("error updating links for arangosearch view '") + view.name() + "'"
);
} catch (...) {
LOG_TOPIC(WARN, arangodb::iresearch::TOPIC)
<< "caught exception while updating links for arangosearch view '"
<< view.name() << "'";
<< "caught exception while updating links for arangosearch view '" << view.name() << "'";
IR_LOG_EXCEPTION();
return arangodb::Result(
TRI_ERROR_BAD_PARAMETER,
std::string("error updating links for arangosearch view '") +
view.name() + "'");
TRI_ERROR_BAD_PARAMETER,
std::string("error updating links for arangosearch view '") + view.name() + "'"
);
}
}
} // namespace iresearch
} // namespace arangodb
} // iresearch
} // arangodb
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE

View File

@ -56,24 +56,33 @@ struct IResearchLinkHelper {
/// @brief compare two link definitions for equivalience if used to create a
/// link instance
//////////////////////////////////////////////////////////////////////////////
static bool equal(arangodb::velocypack::Slice const& lhs,
arangodb::velocypack::Slice const& rhs);
static bool equal( // equal definition
arangodb::velocypack::Slice const& lhs, // left hand side
arangodb::velocypack::Slice const& rhs // right hand side
);
//////////////////////////////////////////////////////////////////////////////
/// @brief finds link between specified collection and view with the given id
//////////////////////////////////////////////////////////////////////////////
static std::shared_ptr<IResearchLink> find(arangodb::LogicalCollection const& collection,
TRI_idx_iid_t id);
static std::shared_ptr<IResearchLink> find( // find link
arangodb::LogicalCollection const& collection, // collection to search
TRI_idx_iid_t id // index to find
);
//////////////////////////////////////////////////////////////////////////////
/// @brief finds first link between specified collection and view
//////////////////////////////////////////////////////////////////////////////
static std::shared_ptr<IResearchLink> find(arangodb::LogicalCollection const& collection,
LogicalView const& view);
static std::shared_ptr<IResearchLink> find( // find link
arangodb::LogicalCollection const& collection, // collection to search
LogicalView const& view // link for view to find
);
//////////////////////////////////////////////////////////////////////////////
/// @brief validate and copy required fields from the 'definition' into
/// 'normalized'
/// @note missing analyzers will be created if exceuted on db-server
/// @note engine == nullptr then SEGFAULT in Methods constructor during insert
/// @note true == inRecovery() then AnalyzerFeature will not allow persistence
//////////////////////////////////////////////////////////////////////////////
static arangodb::Result normalize( // normalize definition
arangodb::velocypack::Builder& normalized, // normalized definition (out-param)
@ -94,34 +103,40 @@ struct IResearchLinkHelper {
/// * collection permissions
/// * valid link meta
//////////////////////////////////////////////////////////////////////////////
static arangodb::Result validateLinks(TRI_vocbase_t& vocbase,
arangodb::velocypack::Slice const& links);
static arangodb::Result validateLinks( // validate links
TRI_vocbase_t& vocbase, // link vocbase
arangodb::velocypack::Slice const& links // links to validate
);
//////////////////////////////////////////////////////////////////////////////
/// @brief visits all links in a collection
/// @return full visitation compleated
//////////////////////////////////////////////////////////////////////////////
static bool visit(arangodb::LogicalCollection const& collection,
std::function<bool(IResearchLink& link)> const& visitor);
static bool visit( // visit links
arangodb::LogicalCollection const& collection, // collection to visit
std::function<bool(IResearchLink& link)> const& visitor // visitor to call
);
//////////////////////////////////////////////////////////////////////////////
/// @brief updates the collections in 'vocbase' to match the specified
/// IResearchLink definitions
/// @param modified set of modified collection IDs
/// @param viewId the view to associate created links with
/// @param view the view to associate created links with
/// @param links the link modification definitions, null link == link removal
/// @param stale links to remove if there is no creation definition in 'links'
//////////////////////////////////////////////////////////////////////////////
static arangodb::Result updateLinks(std::unordered_set<TRI_voc_cid_t>& modified,
TRI_vocbase_t& vocbase, arangodb::LogicalView& view,
arangodb::velocypack::Slice const& links,
std::unordered_set<TRI_voc_cid_t> const& stale = {});
static arangodb::Result updateLinks( // update links
std::unordered_set<TRI_voc_cid_t>& modified, // odified cids
arangodb::LogicalView& view, // modified view
arangodb::velocypack::Slice const& links, // link definitions to apply
std::unordered_set<TRI_voc_cid_t> const& stale = {} //stale view links
);
private:
IResearchLinkHelper() = delete;
}; // IResearchLinkHelper
}; // IResearchLinkHelper
} // namespace iresearch
} // namespace arangodb
} // iresearch
} // arangodb
#endif // ARANGODB_IRESEARCH__IRESEARCH_LINK_HELPER_H
#endif // ARANGODB_IRESEARCH__IRESEARCH_LINK_HELPER_H

View File

@ -176,8 +176,8 @@ bool IResearchLinkMeta::operator!=(IResearchLinkMeta const& other) const noexcep
bool IResearchLinkMeta::init( // initialize meta
arangodb::velocypack::Slice const& slice, // definition
std::string& errorField, // field causing error (out-param)
IResearchLinkMeta const& defaults /*= DEFAULT()*/, // inherited defaults
TRI_vocbase_t const* defaultVocbase /*= nullptr*/, // fallback vocbase
IResearchLinkMeta const& defaults /*= DEFAULT()*/, // inherited defaults
Mask* mask /*= nullptr*/ // initialized fields (out-param)
) {
if (!slice.isObject()) {
@ -213,42 +213,157 @@ bool IResearchLinkMeta::init( // initialize meta
_analyzers.clear(); // reset to match read values exactly
for (arangodb::velocypack::ArrayIterator itr(field); itr.valid(); ++itr) {
auto key = *itr;
auto value = *itr;
if (!key.isString()) {
errorField = fieldName + "=>[" +
arangodb::basics::StringUtils::itoa(itr.index()) + "]";
if (value.isString()) {
auto name = value.copyString();
if (defaultVocbase) {
auto* sysDatabase = arangodb::application_features::ApplicationServer::lookupFeature< // find feature
arangodb::SystemDatabaseFeature // featue type
>();
auto sysVocbase = sysDatabase ? sysDatabase->use() : nullptr;
if (sysVocbase) {
name = IResearchAnalyzerFeature::normalize( // normalize
name, *defaultVocbase, *sysVocbase // args
);
}
}
auto analyzer = analyzers->get(name);
if (!analyzer) {
errorField = fieldName + "=>" + std::string(name);
return false;
}
// inserting two identical values for name is a poor-man's boost multiplier
_analyzers.emplace_back(analyzer);
continue; //process next analyzer
}
if (!value.isObject()) {
errorField = fieldName + "=>[" + std::to_string(itr.index()) + "]";
return false;
}
auto name = getStringRef(key);
IResearchAnalyzerFeature::AnalyzerPool::ptr analyzer;
std::string name;
// get analyzer or placeholder
if (defaultVocbase) {
auto* sysDatabase = arangodb::application_features::ApplicationServer::lookupFeature< // find feature
arangodb::SystemDatabaseFeature // featue type
>();
auto sysVocbase = sysDatabase ? sysDatabase->use() : nullptr;
{
// required string value
static const std::string subFieldName("name");
if (sysVocbase) {
analyzer = analyzers->ensure(IResearchAnalyzerFeature::normalize( // normalize
name, *defaultVocbase, *sysVocbase // args
));
if (!value.hasKey(subFieldName) // missing required filed
|| !value.get(subFieldName).isString()) {
errorField = fieldName + "=>[" + std::to_string(itr.index()) + "]=>" + subFieldName;
return false;
}
name = value.get(subFieldName).copyString();
if (defaultVocbase) {
auto* sysDatabase = arangodb::application_features::ApplicationServer::lookupFeature< // find feature
arangodb::SystemDatabaseFeature // featue type
>();
auto sysVocbase = sysDatabase ? sysDatabase->use() : nullptr;
if (sysVocbase) {
name = IResearchAnalyzerFeature::normalize( // normalize
name, *defaultVocbase, *sysVocbase // args
);
}
}
} else {
analyzer = analyzers->ensure(name); // verbatim (assume already normalized)
}
irs::string_ref type;
{
// required string value
static const std::string subFieldName("type");
if (!value.hasKey(subFieldName) // missing required filed
|| !value.get(subFieldName).isString()) {
errorField = fieldName + "=>[" + std::to_string(itr.index()) + "]=>" + subFieldName;
return false;
}
type = getStringRef(value.get(subFieldName));
}
irs::string_ref properties;
{
// optional string value
static const std::string subFieldName("properties");
if (value.hasKey(subFieldName)) {
auto subField = value.get(subFieldName);
if (!subField.isString()) {
errorField = fieldName + "=>[" + std::to_string(itr.index()) + "]=>" + subFieldName;
return false;
}
properties = getStringRef(subField);
}
}
irs::flags features;
{
// optional string list
static const std::string subFieldName("features");
if (value.hasKey(subFieldName)) {
auto subField = value.get(subFieldName);
if (!subField.isArray()) {
errorField = fieldName + "=>[" + std::to_string(itr.index()) + "]=>" + subFieldName;
return false;
}
for (arangodb::velocypack::ArrayIterator subItr(subField);
subItr.valid();
++subItr) {
auto subValue = *subItr;
if (!subValue.isString()) {
errorField = fieldName + "=>[" + std::to_string(itr.index()) + "]=>" + subFieldName + "=>[" + std::to_string(subItr.index()) + + "]";
return false;
}
auto featureName = getStringRef(subValue);
auto* feature = irs::attribute::type_id::get(featureName);
if (!feature) {
errorField = fieldName + "=>[" + std::to_string(itr.index()) + "]=>" + subFieldName + "=>" + std::string(featureName);
return false;
}
features.add(*feature);
}
}
}
auto analyzer = analyzers->get(name, type, properties, features); // get analyzer potentially creating it (e.g. on db-server)
if (!analyzer) {
errorField = fieldName + "=>" + std::string(name);
errorField = fieldName + "=>[" + std::to_string(itr.index()) + "]";
return false;
}
// inserting two identical values for name is a poor-man's boost
// multiplier
// inserting two identical values for name is a poor-man's boost multiplier
_analyzers.emplace_back(analyzer);
}
}
@ -375,7 +490,7 @@ bool IResearchLinkMeta::init( // initialize meta
std::string childErrorField;
if (!_fields[name]->init(value, errorField, subDefaults, defaultVocbase)) {
if (!_fields[name]->init(value, errorField, defaultVocbase, subDefaults)) {
errorField = fieldName + "=>" + name + "=>" + childErrorField;
return false;
@ -389,6 +504,7 @@ bool IResearchLinkMeta::init( // initialize meta
bool IResearchLinkMeta::json( // append meta jSON
arangodb::velocypack::Builder& builder, // output buffer (out-param)
bool writeAnalyzerDefinition, // output fill analyzer definition instead of just name
IResearchLinkMeta const* ignoreEqual /*= nullptr*/, // values to ignore if equal
TRI_vocbase_t const* defaultVocbase /*= nullptr*/, // fallback vocbase
Mask const* mask /*= nullptr*/ // values to ignore always
@ -408,6 +524,8 @@ bool IResearchLinkMeta::json( // append meta jSON
continue; // skip null analyzers
}
std::string name;
if (defaultVocbase) {
auto* sysDatabase = arangodb::application_features::ApplicationServer::lookupFeature< // find feature
arangodb::SystemDatabaseFeature // feature type
@ -418,14 +536,35 @@ bool IResearchLinkMeta::json( // append meta jSON
return false;
}
analyzersBuilder.add(arangodb::velocypack::Value(
IResearchAnalyzerFeature::normalize( // normalize
entry->name(), *defaultVocbase, *sysVocbase, false // args
) // normalized value
));
name = IResearchAnalyzerFeature::normalize( // normalize
entry->name(), *defaultVocbase, *sysVocbase, false // args
);
} else {
analyzersBuilder.add(arangodb::velocypack::Value(entry->name())); // verbatim (assume already normalized)
name = entry->name(); // verbatim (assume already normalized)
}
if (!writeAnalyzerDefinition) {
analyzersBuilder.add(arangodb::velocypack::Value(std::move(name)));
continue; // nothing else to output for analyzer
}
analyzersBuilder.openObject();
analyzersBuilder.add("name", arangodb::velocypack::Value(name));
analyzersBuilder.add("type", toValuePair(entry->type()));
analyzersBuilder.add("properties", toValuePair(entry->properties()));
analyzersBuilder.add(
"features", // key
arangodb::velocypack::Value(arangodb::velocypack::ValueType::Array) // value
);
for (auto& feature: entry->features()) {
TRI_ASSERT(feature); // has to be non-nullptr
analyzersBuilder.add(toValuePair(feature->name()));
}
analyzersBuilder.close();
analyzersBuilder.close();
}
analyzersBuilder.close();
@ -447,7 +586,7 @@ bool IResearchLinkMeta::json( // append meta jSON
arangodb::velocypack::Value(arangodb::velocypack::ValueType::Object)
);
if (!entry.value()->json(fieldsBuilder, &subDefaults, defaultVocbase, &fieldMask)) {
if (!entry.value()->json(fieldsBuilder, writeAnalyzerDefinition, &subDefaults, defaultVocbase, &fieldMask)) {
return false;
}

View File

@ -123,8 +123,8 @@ struct IResearchLinkMeta {
bool init( // initialize meta
arangodb::velocypack::Slice const& slice, // definition
std::string& errorField, // field causing error (out-param)
IResearchLinkMeta const& defaults = DEFAULT(), // inherited defaults
TRI_vocbase_t const* defaultVocbase = nullptr, // fallback vocbase
IResearchLinkMeta const& defaults = DEFAULT(), // inherited defaults
Mask* mask = nullptr // initialized fields (out-param)
);
@ -139,6 +139,7 @@ struct IResearchLinkMeta {
////////////////////////////////////////////////////////////////////////////////
bool json( // append meta jSON
arangodb::velocypack::Builder& builder, // output buffer (out-param)
bool writeAnalyzerDefinition, // output full analyzer definition instead of just name
IResearchLinkMeta const* ignoreEqual = nullptr, // values to ignore if equal
TRI_vocbase_t const* defaultVocbase = nullptr, // fallback vocbase
Mask const* mask = nullptr // values to ignore always

View File

@ -134,24 +134,27 @@ IResearchMMFilesLink::IResearchMMFilesLink(TRI_idx_iid_t iid,
return factory;
}
void IResearchMMFilesLink::toVelocyPack(arangodb::velocypack::Builder& builder,
std::underlying_type<arangodb::Index::Serialize>::type flags) const {
void IResearchMMFilesLink::toVelocyPack( // generate definition
arangodb::velocypack::Builder& builder, // destination buffer
std::underlying_type<arangodb::Index::Serialize>::type flags // definition flags
) const {
if (builder.isOpenObject()) {
THROW_ARANGO_EXCEPTION(
arangodb::Result(TRI_ERROR_BAD_PARAMETER,
std::string("failed to generate link definition for "
"arangosearch view MMFiles link '") +
std::to_string(arangodb::Index::id()) + "'"));
THROW_ARANGO_EXCEPTION(arangodb::Result( // result
TRI_ERROR_BAD_PARAMETER, // code
std::string("failed to generate link definition for arangosearch view MMFiles link '") + std::to_string(arangodb::Index::id()) + "'"
));
}
auto forPersistence = // definition for persistence
arangodb::Index::hasFlag(flags, arangodb::Index::Serialize::Internals);
builder.openObject();
if (!json(builder)) {
THROW_ARANGO_EXCEPTION(
arangodb::Result(TRI_ERROR_INTERNAL,
std::string("failed to generate link definition for "
"arangosearch view MMFiles link '") +
std::to_string(arangodb::Index::id()) + "'"));
if (!properties(builder, forPersistence).ok()) {
THROW_ARANGO_EXCEPTION(arangodb::Result( // result
TRI_ERROR_INTERNAL, // code
std::string("failed to generate link definition for arangosearch view MMFiles link '") + std::to_string(arangodb::Index::id()) + "'"
));
}
if (arangodb::Index::hasFlag(flags, arangodb::Index::Serialize::Figures)) {

View File

@ -107,7 +107,7 @@ class IResearchMMFilesLink final : public arangodb::MMFilesIndex, public IResear
/// @brief fill and return a JSON description of a IResearchLink object
/// @param withFigures output 'figures' section with e.g. memory size
////////////////////////////////////////////////////////////////////////////////
using Index::toVelocyPack; // for Index::toVelocyPack(bool, unsigned)
using Index::toVelocyPack; // for std::shared_ptr<Builder> Index::toVelocyPack(bool, Index::Serialize)
virtual void toVelocyPack(arangodb::velocypack::Builder& builder,
std::underlying_type<arangodb::Index::Serialize>::type) const override;

View File

@ -214,24 +214,27 @@ IResearchRocksDBLink::IResearchRocksDBLink(TRI_idx_iid_t iid,
return factory;
}
void IResearchRocksDBLink::toVelocyPack(arangodb::velocypack::Builder& builder,
std::underlying_type<arangodb::Index::Serialize>::type flags) const {
void IResearchRocksDBLink::toVelocyPack( // generate definition
arangodb::velocypack::Builder& builder, // destination buffer
std::underlying_type<arangodb::Index::Serialize>::type flags // definition flags
) const {
if (builder.isOpenObject()) {
THROW_ARANGO_EXCEPTION(
arangodb::Result(TRI_ERROR_BAD_PARAMETER,
std::string("failed to generate link definition for "
"arangosearch view RocksDB link '") +
std::to_string(arangodb::Index::id()) + "'"));
THROW_ARANGO_EXCEPTION(arangodb::Result( // result
TRI_ERROR_BAD_PARAMETER, // code
std::string("failed to generate link definition for arangosearch view RocksDB link '") + std::to_string(arangodb::Index::id()) + "'"
));
}
auto forPersistence = // definition for persistence
arangodb::Index::hasFlag(flags, arangodb::Index::Serialize::Internals);
builder.openObject();
if (!json(builder)) {
THROW_ARANGO_EXCEPTION(
arangodb::Result(TRI_ERROR_INTERNAL,
std::string("failed to generate link definition for "
"arangosearch view RocksDB link '") +
std::to_string(arangodb::Index::id()) + "'"));
if (!properties(builder, forPersistence).ok()) {
THROW_ARANGO_EXCEPTION(arangodb::Result( // result
TRI_ERROR_INTERNAL, // code
std::string("failed to generate link definition for arangosearch view RocksDB link '") + std::to_string(arangodb::Index::id()) + "'"
));
}
if (arangodb::Index::hasFlag(flags, arangodb::Index::Serialize::Internals)) {

View File

@ -101,7 +101,7 @@ class IResearchRocksDBLink final : public arangodb::RocksDBIndex, public IResear
/// @brief fill and return a JSON description of a IResearchLink object
/// @param withFigures output 'figures' section with e.g. memory size
////////////////////////////////////////////////////////////////////////////////
using Index::toVelocyPack; // for Index::toVelocyPack(bool, unsigned)
using Index::toVelocyPack; // for std::shared_ptr<Builder> Index::toVelocyPack(bool, Index::Serialize)
virtual void toVelocyPack(arangodb::velocypack::Builder& builder,
std::underlying_type<arangodb::Index::Serialize>::type flags) const override;

View File

@ -166,7 +166,7 @@ void ensureLink(arangodb::DatabaseFeature& db,
json.openObject();
if (!link->json(json)) {
if (!link->properties(json, true).ok()) { // link definition used for recreation and persistence
LOG_TOPIC(ERR, arangodb::iresearch::TOPIC)
<< "Failed to generate jSON definition for link '" << iid
<< "' to the collection '" << cid << "' in the database '" << dbId;

View File

@ -172,31 +172,24 @@ struct IResearchView::ViewFactory : public arangodb::ViewFactory {
try {
std::unordered_set<TRI_voc_cid_t> collections;
res = IResearchLinkHelper::updateLinks(collections, vocbase, *impl, links);
res = IResearchLinkHelper::updateLinks(collections, *impl, links);
if (!res.ok()) {
LOG_TOPIC(WARN, arangodb::iresearch::TOPIC)
<< "failed to create links while creating arangosearch view '"
<< impl->name() << "': " << res.errorNumber() << " " << res.errorMessage();
<< "failed to create links while creating arangosearch view '" << impl->name() << "': " << res.errorNumber() << " " << res.errorMessage();
}
} catch (arangodb::basics::Exception const& e) {
IR_LOG_EXCEPTION();
LOG_TOPIC(WARN, arangodb::iresearch::TOPIC)
<< "caught exception while creating links while creating "
"arangosearch view '"
<< impl->name() << "': " << e.code() << " " << e.what();
<< "caught exception while creating links while creating arangosearch view '" << impl->name() << "': " << e.code() << " " << e.what();
} catch (std::exception const& e) {
IR_LOG_EXCEPTION();
LOG_TOPIC(WARN, arangodb::iresearch::TOPIC)
<< "caught exception while creating links while creating "
"arangosearch view '"
<< impl->name() << "': " << e.what();
<< "caught exception while creating links while creating arangosearch view '" << impl->name() << "': " << e.what();
} catch (...) {
IR_LOG_EXCEPTION();
LOG_TOPIC(WARN, arangodb::iresearch::TOPIC)
<< "caught exception while creating links while creating "
"arangosearch view '"
<< impl->name() << "'";
<< "caught exception while creating links while creating arangosearch view '" << impl->name() << "'";
}
view = impl;
@ -433,7 +426,7 @@ arangodb::Result IResearchView::appendVelocyPackImpl( // append JSON
linkBuilder.openObject();
if (!link->json(linkBuilder)) {
if (!link->properties(linkBuilder, false).ok()) { // link definitions are not output if forPersistence
LOG_TOPIC(WARN, arangodb::iresearch::TOPIC)
<< "failed to generate json for arangosearch link '" << link->id() << "' while generating json for arangosearch view '" << name() << "'";
@ -544,24 +537,28 @@ arangodb::Result IResearchView::dropImpl() {
{
if (!_updateLinksLock.try_lock()) {
return arangodb::Result(
TRI_ERROR_FAILED, // FIXME use specific error code
std::string("failed to remove arangosearch view '") + name());
// FIXME use specific error code
return arangodb::Result( // result
TRI_ERROR_FAILED, //code
std::string("failed to remove arangosearch view '") + name() // message
);
}
ADOPT_SCOPED_LOCK_NAMED(_updateLinksLock, lock);
res = IResearchLinkHelper::updateLinks(collections, vocbase(), *this,
arangodb::velocypack::Slice::emptyObjectSlice(),
stale);
res = IResearchLinkHelper::updateLinks( // update links
collections, // modified collection ids
*this, // modified view
arangodb::velocypack::Slice::emptyObjectSlice(), // link definitions to apply
stale // stale links
);
}
if (!res.ok()) {
return arangodb::Result(
res.errorNumber(),
std::string(
"failed to remove links while removing arangosearch view '") +
name() + "': " + res.errorMessage());
return arangodb::Result( // result
res.errorNumber(), // code
std::string("failed to remove links while removing arangosearch view '") + name() + "': " + res.errorMessage()
);
}
}
@ -1036,71 +1033,68 @@ arangodb::Result IResearchView::updateProperties(arangodb::velocypack::Slice con
// ...........................................................................
// update links if requested (on a best-effort basis)
// indexing of collections is done in different threads so no locks can be
// held and rollback is not possible as a result it's also possible for
// links to be simultaneously modified via a different callflow (e.g. from
// collections)
// indexing of collections is done in different threads so no locks can be held and rollback is not possible
// as a result it's also possible for links to be simultaneously modified via a different callflow (e.g. from collections)
// ...........................................................................
std::unordered_set<TRI_voc_cid_t> collections;
if (partialUpdate) {
mtx.unlock(); // release lock
mtx.unlock(); // release lock
SCOPED_LOCK(_updateLinksLock);
return IResearchLinkHelper::updateLinks(collections, vocbase(), *this, links);
return IResearchLinkHelper::updateLinks(collections, *this, links);
}
std::unordered_set<TRI_voc_cid_t> stale;
for (auto& entry : _links) {
for (auto& entry: _links) {
stale.emplace(entry.first);
}
mtx.unlock(); // release lock
mtx.unlock(); // release lock
SCOPED_LOCK(_updateLinksLock);
return IResearchLinkHelper::updateLinks(collections, vocbase(), *this, links, stale);
return IResearchLinkHelper::updateLinks(collections, *this, links, stale);
} catch (arangodb::basics::Exception& e) {
LOG_TOPIC(WARN, iresearch::TOPIC)
<< "caught exception while updating properties for arangosearch view '"
<< name() << "': " << e.code() << " " << e.what();
<< "caught exception while updating properties for arangosearch view '" << name() << "': " << e.code() << " " << e.what();
IR_LOG_EXCEPTION();
return arangodb::Result(
e.code(),
std::string("error updating properties for arangosearch view '") +
name() + "'");
e.code(),
std::string("error updating properties for arangosearch view '") + name() + "'"
);
} catch (std::exception const& e) {
LOG_TOPIC(WARN, iresearch::TOPIC)
<< "caught exception while updating properties for arangosearch view '"
<< name() << "': " << e.what();
<< "caught exception while updating properties for arangosearch view '" << name() << "': " << e.what();
IR_LOG_EXCEPTION();
return arangodb::Result(
TRI_ERROR_BAD_PARAMETER,
std::string("error updating properties for arangosearch view '") +
name() + "'");
TRI_ERROR_BAD_PARAMETER,
std::string("error updating properties for arangosearch view '") + name() + "'"
);
} catch (...) {
LOG_TOPIC(WARN, iresearch::TOPIC)
<< "caught exception while updating properties for arangosearch view '"
<< name() << "'";
<< "caught exception while updating properties for arangosearch view '" << name() << "'";
IR_LOG_EXCEPTION();
return arangodb::Result(
TRI_ERROR_BAD_PARAMETER,
std::string("error updating properties for arangosearch view '") +
name() + "'");
TRI_ERROR_BAD_PARAMETER,
std::string("error updating properties for arangosearch view '") + name() + "'"
);
}
}
bool IResearchView::visitCollections(LogicalView::CollectionVisitor const& visitor) const {
ReadMutex mutex(_mutex); // '_links' can be asynchronously modified
bool IResearchView::visitCollections( // visit collections
LogicalView::CollectionVisitor const& visitor // visitor to call
) const {
ReadMutex mutex(_mutex); // '_links' can be asynchronously modified
SCOPED_LOCK(mutex);
for (auto& entry : _links) {
for (auto& entry: _links) {
if (!visitor(entry.first)) {
return false;
}

View File

@ -94,7 +94,7 @@ struct IResearchViewCoordinator::ViewFactory : public arangodb::ViewFactory {
try {
std::unordered_set<TRI_voc_cid_t> collections;
res = IResearchLinkHelper::updateLinks(collections, vocbase, *impl, links);
res = IResearchLinkHelper::updateLinks(collections, *impl, links);
if (!res.ok()) {
LOG_TOPIC(WARN, arangodb::iresearch::TOPIC)
@ -437,10 +437,12 @@ arangodb::Result IResearchViewCoordinator::properties(velocypack::Slice const& s
std::unordered_set<TRI_voc_cid_t> collections;
if (partialUpdate) {
return IResearchLinkHelper::updateLinks(collections, vocbase(), *this, links);
return IResearchLinkHelper::updateLinks(collections, *this, links);
}
return IResearchLinkHelper::updateLinks(collections, vocbase(), *this, links, currentCids);
return IResearchLinkHelper::updateLinks( // update links
collections, *this, links, currentCids // args
);
} catch (arangodb::basics::Exception& e) {
LOG_TOPIC(WARN, iresearch::TOPIC)
<< "caught exception while updating properties for arangosearch view '"
@ -507,7 +509,9 @@ Result IResearchViewCoordinator::dropImpl() {
}
std::unordered_set<TRI_voc_cid_t> collections;
auto res = IResearchLinkHelper::updateLinks(collections, vocbase(), *this,
auto res = IResearchLinkHelper::updateLinks( // update links
collections, // modified collections
*this, // view
arangodb::velocypack::Slice::emptyObjectSlice(),
currentCids);

View File

@ -105,7 +105,7 @@ bool optimizeSearchCondition(IResearchViewNode& viewNode, Query& query, Executio
auto const conditionValid =
!searchCondition.root() ||
FilterFactory::filter(nullptr,
{nullptr, nullptr, nullptr, nullptr, &viewNode.outVariable()},
{ query.trx(), nullptr, nullptr, nullptr, &viewNode.outVariable() },
*searchCondition.root());
if (!conditionValid) {
@ -308,4 +308,4 @@ void scatterViewInClusterRule(arangodb::aql::Optimizer* opt,
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------

View File

@ -23,16 +23,6 @@
#ifndef APPLICATION_FEATURES_DATABASE_FEATURE_H
#define APPLICATION_FEATURES_DATABASE_FEATURE_H 1
#if !defined(USE_CATCH_TESTS) && !defined(EXPAND_APPLICATION_FEATURES_DATABASE_FEATURE_H)
#define DO_EXPAND_APPLICATION_FEATURES_DATABASE_FEATURE_H(VAL) VAL ## 1
#define EXPAND_APPLICATION_FEATURES_DATABASE_FEATURE_H(VAL) DO_EXPAND_APPLICATION_FEATURES_DATABASE_FEATURE_H(VAL)
#if defined(TEST_VIRTUAL) && (EXPAND_APPLICATION_FEATURES_DATABASE_FEATURE_H(TEST_VIRTUAL) != 1)
#define USE_CATCH_TESTS
#endif
#undef EXPAND_APPLICATION_FEATURES_DATABASE_FEATURE_H
#undef DO_EXPAND_APPLICATION_FEATURES_DATABASE_FEATURE_H
#endif
#include "ApplicationFeatures/ApplicationFeature.h"
#include "Basics/DataProtector.h"
#include "Basics/Mutex.h"
@ -91,7 +81,7 @@ class DatabaseFeature : public application_features::ApplicationFeature {
void unprepare() override final;
// used by catch tests
#ifdef USE_CATCH_TESTS
#ifdef ARANGODB_USE_CATCH_TESTS
inline int loadDatabases(velocypack::Slice const& databases) {
return iterateDatabases(databases);
}

View File

@ -235,11 +235,6 @@ void methods::Upgrade::registerTasks() {
/*system*/ Flags::DATABASE_EXCEPT_SYSTEM,
/*cluster*/ Flags::CLUSTER_NONE | Flags::CLUSTER_COORDINATOR_GLOBAL,
/*database*/ DATABASE_INIT, &UpgradeTasks::addDefaultUserOther);
addTask("setupAnalyzers", "setup _iresearch_analyzers collection",
/*system*/ Flags::DATABASE_SYSTEM,
/*cluster*/ Flags::CLUSTER_NONE | Flags::CLUSTER_COORDINATOR_GLOBAL,
/*database*/ DATABASE_INIT | DATABASE_UPGRADE | DATABASE_EXISTING,
&UpgradeTasks::setupAnalyzers);
addTask("setupAqlFunctions", "setup _aqlfunctions collection",
/*system*/ Flags::DATABASE_ALL,
/*cluster*/ Flags::CLUSTER_NONE | Flags::CLUSTER_COORDINATOR_GLOBAL,

View File

@ -276,11 +276,6 @@ bool UpgradeTasks::addDefaultUserOther(TRI_vocbase_t& vocbase,
return true;
}
bool UpgradeTasks::setupAnalyzers(TRI_vocbase_t& vocbase,
arangodb::velocypack::Slice const& slice) {
return ::createSystemCollection(vocbase, "_iresearch_analyzers");
}
bool UpgradeTasks::setupAqlFunctions(TRI_vocbase_t& vocbase,
arangodb::velocypack::Slice const& slice) {
return ::createSystemCollection(vocbase, "_aqlfunctions");

View File

@ -38,7 +38,6 @@ struct UpgradeTasks {
static bool setupUsers(TRI_vocbase_t& vocbase, velocypack::Slice const& slice);
static bool createUsersIndex(TRI_vocbase_t& vocbase, velocypack::Slice const& slice);
static bool addDefaultUserOther(TRI_vocbase_t& vocbase, velocypack::Slice const& slice);
static bool setupAnalyzers(TRI_vocbase_t& vocbase, velocypack::Slice const& slice);
static bool setupAqlFunctions(TRI_vocbase_t& vocbase, velocypack::Slice const& slice);
static bool createFrontend(TRI_vocbase_t& vocbase, velocypack::Slice const& slice);
static bool setupQueues(TRI_vocbase_t& vocbase, velocypack::Slice const& slice);
@ -55,4 +54,4 @@ struct UpgradeTasks {
} // namespace methods
} // namespace arangodb
#endif
#endif

View File

@ -135,12 +135,6 @@ struct IResearchBlockMockSetup {
}
}
auto* analyzers =
arangodb::application_features::ApplicationServer::lookupFeature<arangodb::iresearch::IResearchAnalyzerFeature>();
analyzers->emplace("test_analyzer", "TestAnalyzer", "abc"); // cache analyzer
analyzers->emplace("test_csv_analyzer", "TestDelimAnalyzer", ","); // cache analyzer
auto* dbPathFeature =
arangodb::application_features::ApplicationServer::getFeature<arangodb::DatabasePathFeature>(
"DatabasePath");
@ -532,4 +526,4 @@ TEST_CASE("ExecutionBlockMockTestChain", "[iresearch]") {
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------

View File

@ -319,13 +319,6 @@ struct IResearchExpressionFilterSetup {
return params[0];
}});
auto* analyzers = arangodb::application_features::ApplicationServer::lookupFeature<
arangodb::iresearch::IResearchAnalyzerFeature
>();
analyzers->emplace("test_analyzer", "TestAnalyzer", "abc"); // cache analyzer
analyzers->emplace("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
}

View File

@ -137,6 +137,7 @@ struct Analyzer {
std::map<irs::string_ref, Analyzer>const& staticAnalyzers() {
static const std::map<irs::string_ref, Analyzer> analyzers = {
{ "identity", { "identity", irs::string_ref::NIL, { irs::frequency::type(), irs::norm::type() } } },
// FIXME TODO remove
{"text_de", { "text", "{ \"locale\": \"de.UTF-8\", \"ignored_words\": [ ] }", { irs::frequency::type(), irs::norm::type(), irs::position::type() } } },
{"text_en", { "text", "{ \"locale\": \"en.UTF-8\", \"ignored_words\": [ ] }", { irs::frequency::type(), irs::norm::type(), irs::position::type() } } },
{"text_es", { "text", "{ \"locale\": \"es.UTF-8\", \"ignored_words\": [ ] }", { irs::frequency::type(), irs::norm::type(), irs::position::type() } } },
@ -228,12 +229,18 @@ struct IResearchAnalyzerFeatureSetup {
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<Vocbase>(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 0, TRI_VOC_SYSTEM_DATABASE); // QueryRegistryFeature required for instantiation
features.emplace_back(new arangodb::SystemDatabaseFeature(server, system.get()), false); // required for IResearchAnalyzerFeature
features.emplace_back(new arangodb::V8DealerFeature(server), false); // required for DatabaseFeature::createDatabase(...)
features.emplace_back(new arangodb::aql::AqlFunctionFeature(server), true); // required for IResearchAnalyzerFeature
#if USE_ENTERPRISE
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);
}
@ -428,9 +435,11 @@ SECTION("test_auth") {
SECTION("test_emplace") {
// add valid
{
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
feature.start();
CHECK((false == !feature.emplace("test_analyzer0", "TestAnalyzer", "abc").first));
CHECK((true == feature.emplace(result, "test_analyzer0", "TestAnalyzer", "abc").ok()));
CHECK((false == !result.first));
auto pool = feature.get("test_analyzer0");
CHECK((false == !pool));
CHECK((irs::flags() == pool->features()));
@ -438,81 +447,115 @@ SECTION("test_emplace") {
// add duplicate valid (same name+type+properties)
{
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
feature.start();
CHECK((false == !feature.emplace("test_analyzer1", "TestAnalyzer", "abc", irs::flags{ TestAttribute::type() }).first));
CHECK((true == feature.emplace(result, "test_analyzer1", "TestAnalyzer", "abc", irs::flags{ TestAttribute::type() }).ok()));
CHECK((false == !result.first));
auto pool = feature.get("test_analyzer1");
CHECK((false == !pool));
CHECK((irs::flags({ TestAttribute::type() }) == pool->features()));
CHECK((false == !feature.emplace("test_analyzer1", "TestAnalyzer", "abc").first));
CHECK((true == feature.emplace(result, "test_analyzer1", "TestAnalyzer", "abc", irs::flags{ TestAttribute::type() }).ok()));
CHECK((false == !result.first));
CHECK((false == !feature.get("test_analyzer1")));
}
// add duplicate invalid (same name+type different properties)
{
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
feature.start();
CHECK((false == !feature.emplace("test_analyzer2", "TestAnalyzer", "abc").first));
CHECK((true == feature.emplace(result, "test_analyzer2", "TestAnalyzer", "abc").ok()));
CHECK((false == !result.first));
auto pool = feature.get("test_analyzer2");
CHECK((false == !pool));
CHECK((irs::flags() == pool->features()));
CHECK((true == !feature.emplace("test_analyzer2", "TestAnalyzer", "abcd").first));
CHECK((false == feature.emplace(result, "test_analyzer2", "TestAnalyzer", "abcd").ok()));
CHECK((false == !feature.get("test_analyzer2")));
}
// add duplicate invalid (same name+type+properties different features)
{
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
feature.start();
CHECK((true == feature.emplace(result, "test_analyzer2", "TestAnalyzer", "abc").ok()));
CHECK((false == !result.first));
auto pool = feature.get("test_analyzer2");
CHECK((false == !pool));
CHECK((irs::flags() == pool->features()));
CHECK((false == feature.emplace(result, "test_analyzer2", "TestAnalyzer", "abc", irs::flags{ TestAttribute::type() }).ok()));
CHECK((false == !feature.get("test_analyzer2")));
}
// add duplicate invalid (same name+properties different type)
{
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
feature.start();
CHECK((false == !feature.emplace("test_analyzer3", "TestAnalyzer", "abc").first));
CHECK((true == feature.emplace(result, "test_analyzer3", "TestAnalyzer", "abc").ok()));
CHECK((false == !result.first));
auto pool = feature.get("test_analyzer3");
CHECK((false == !pool));
CHECK((irs::flags() == pool->features()));
CHECK((true == !feature.emplace("test_analyzer3", "invalid", "abc").first));
CHECK((false == feature.emplace(result, "test_analyzer3", "invalid", "abc").ok()));
CHECK((false == !feature.get("test_analyzer3")));
}
// add invalid (instance creation failure)
{
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
feature.start();
CHECK((true == !feature.emplace("test_analyzer4", "TestAnalyzer", "").first));
CHECK((false == feature.emplace(result, "test_analyzer4", "TestAnalyzer", "").ok()));
CHECK((true == !feature.get("test_analyzer4")));
}
// add invalid (instance creation exception)
{
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
feature.start();
CHECK((true == !feature.emplace("test_analyzer5", "TestAnalyzer", irs::string_ref::NIL).first));
CHECK((false == feature.emplace(result, "test_analyzer5", "TestAnalyzer", irs::string_ref::NIL).ok()));
CHECK((true == !feature.get("test_analyzer5")));
}
// add invalid (not registred)
{
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
feature.start();
CHECK((true == !feature.emplace("test_analyzer6", "invalid", irs::string_ref::NIL).first));
CHECK((false == feature.emplace(result, "test_analyzer6", "invalid", irs::string_ref::NIL).ok()));
CHECK((true == !feature.get("test_analyzer6")));
}
// add invalid (feature not started)
{
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
CHECK((true == !feature.emplace("test_analyzer7", "TestAnalyzer", "abc").first));
auto pool = feature.ensure("test_analyzer");
REQUIRE((false == !pool));
CHECK((irs::flags::empty_instance() == pool->features()));
auto analyzer = pool->get();
CHECK((true == !analyzer));
CHECK((false == feature.emplace(result, "test_analyzer7", "invalid", irs::string_ref::NIL).ok()));
CHECK((true == !feature.get("test_analyzer7")));
}
// add valid inRecovery (failure)
{
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
feature.start();
auto before = StorageEngineMock::inRecoveryResult;
StorageEngineMock::inRecoveryResult = true;
auto restore = irs::make_finally([&before]()->void { StorageEngineMock::inRecoveryResult = before; });
CHECK((false == feature.emplace(result, "test_analyzer8", "TestAnalyzer", "abc").ok()));
CHECK((true == !feature.get("test_analyzer8")));
}
// add static analyzer
{
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
feature.start();
CHECK((false == !feature.emplace("identity", "identity", irs::string_ref::NIL).first));
CHECK((true == feature.emplace(result, "identity", "identity", irs::string_ref::NIL, irs::flags{ irs::frequency::type(), irs::norm::type() }).ok()));
CHECK((false == !result.first));
auto pool = feature.get("identity");
CHECK((false == !pool));
CHECK((irs::flags({irs::norm::type(), irs::frequency::type() }) == pool->features()));
@ -522,8 +565,10 @@ SECTION("test_emplace") {
// add static analyzer (feature not started)
{
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
CHECK((false == !feature.emplace("identity", "identity", irs::string_ref::NIL).first));
CHECK((true == feature.emplace(result, "identity", "identity", irs::string_ref::NIL, irs::flags{ irs::frequency::type(), irs::norm::type() }).ok()));
CHECK((false == !result.first));
auto pool = feature.get("identity");
CHECK((false == !pool));
CHECK((irs::flags({irs::norm::type(), irs::frequency::type() }) == pool->features()));
@ -587,6 +632,11 @@ SECTION("test_ensure") {
}
SECTION("test_get") {
auto* dbFeature = arangodb::application_features::ApplicationServer::lookupFeature<
arangodb::DatabaseFeature
>("Database");
REQUIRE((false == !dbFeature));
{
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
@ -617,10 +667,11 @@ SECTION("test_get") {
}
{
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
feature.start();
feature.emplace("test_analyzer", "TestAnalyzer", "abc");
REQUIRE((feature.emplace(result, "test_analyzer", "TestAnalyzer", "abc").ok()));
// get valid (started)
{
@ -645,6 +696,84 @@ SECTION("test_get") {
CHECK((false == !analyzer));
}
}
// get existing with parameter match
{
TRI_vocbase_t* vocbase;
REQUIRE((TRI_ERROR_NO_ERROR == dbFeature->createDatabase(1, "testVocbase", vocbase)));
auto dropDB = irs::make_finally([dbFeature]()->void { dbFeature->dropDatabase("testVocbase", true, true); });
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
REQUIRE((feature.emplace(result, "testVocbase::test_analyzer", "TestAnalyzer", "abc", { irs::frequency::type() } ).ok()));
CHECK((false == !feature.get("testVocbase::test_analyzer", "TestAnalyzer", "abc", { irs::frequency::type() })));
}
// get exisitng with type mismatch
{
TRI_vocbase_t* vocbase;
REQUIRE((TRI_ERROR_NO_ERROR == dbFeature->createDatabase(1, "testVocbase", vocbase)));
auto dropDB = irs::make_finally([dbFeature]()->void { dbFeature->dropDatabase("testVocbase", true, true); });
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
REQUIRE((feature.emplace(result, "testVocbase::test_analyzer", "TestAnalyzer", "abc", { irs::frequency::type() } ).ok()));
CHECK((true == !feature.get("testVocbase::test_analyzer", "identity", "abc", { irs::frequency::type() })));
}
// get existing with properties mismatch
{
TRI_vocbase_t* vocbase;
REQUIRE((TRI_ERROR_NO_ERROR == dbFeature->createDatabase(1, "testVocbase", vocbase)));
auto dropDB = irs::make_finally([dbFeature]()->void { dbFeature->dropDatabase("testVocbase", true, true); });
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
REQUIRE((feature.emplace(result, "testVocbase::test_analyzer", "TestAnalyzer", "abc", { irs::frequency::type() } ).ok()));
CHECK((true == !feature.get("testVocbase::test_analyzer", "TestAnalyzer", "abcd", { irs::frequency::type() })));
}
// get existing with features mismatch
{
TRI_vocbase_t* vocbase;
REQUIRE((TRI_ERROR_NO_ERROR == dbFeature->createDatabase(1, "testVocbase", vocbase)));
auto dropDB = irs::make_finally([dbFeature]()->void { dbFeature->dropDatabase("testVocbase", true, true); });
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
REQUIRE((feature.emplace(result, "testVocbase::test_analyzer", "TestAnalyzer", "abc", { irs::frequency::type() } ).ok()));
CHECK((true == !feature.get("testVocbase::test_analyzer", "TestAnalyzer", "abc", { irs::position::type() })));
}
// get missing (single-server)
{
TRI_vocbase_t* vocbase;
REQUIRE((TRI_ERROR_NO_ERROR == dbFeature->createDatabase(1, "testVocbase", vocbase)));
auto dropDB = irs::make_finally([dbFeature]()->void { dbFeature->dropDatabase("testVocbase", true, true); });
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
CHECK((true == !feature.get("testVocbase::test_analyzer", "TestAnalyzer", "abc", { irs::frequency::type() })));
}
// get missing (coordinator)
{
auto before = arangodb::ServerState::instance()->getRole();
arangodb::ServerState::instance()->setRole(arangodb::ServerState::ROLE_COORDINATOR);
auto restore = irs::make_finally([&before]()->void { arangodb::ServerState::instance()->setRole(before); });
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
CHECK((true == !feature.get("testVocbase::test_analyzer", "TestAnalyzer", "abc", { irs::frequency::type() })));
}
// get missing (db-server)
{
auto before = arangodb::ServerState::instance()->getRole();
arangodb::ServerState::instance()->setRole(arangodb::ServerState::ROLE_DBSERVER);
auto restore = irs::make_finally([&before]()->void { arangodb::ServerState::instance()->setRole(before); });
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
CHECK((feature.get("testVocbase::test_analyzer", "TestAnalyzer", "abc", { irs::frequency::type() })));
}
}
SECTION("test_identity") {
@ -713,28 +842,28 @@ SECTION("test_normalize") {
auto normalized = arangodb::iresearch::IResearchAnalyzerFeature::normalize(analyzer, active, system, true);
CHECK((std::string("identity") == normalized));
}
/* FIXME TODO uncomment once emplace(...) and all tests are updated
// normalize NIL (with prefix)
{
irs::string_ref analyzer = irs::string_ref::NIL;
auto normalized = arangodb::iresearch::IResearchAnalyzerFeature::normalize(analyzer, active, system, true);
CHECK((std::string("active::") == normalized));
}
*/
// normalize NIL (without prefix)
{
irs::string_ref analyzer = irs::string_ref::NIL;
auto normalized = arangodb::iresearch::IResearchAnalyzerFeature::normalize(analyzer, active, system, false);
CHECK((std::string("") == normalized));
}
/* FIXME TODO uncomment once emplace(...) and all tests are updated
// normalize EMPTY (with prefix)
{
irs::string_ref analyzer = irs::string_ref::EMPTY;
auto normalized = arangodb::iresearch::IResearchAnalyzerFeature::normalize(analyzer, active, system, true);
CHECK((std::string("active::") == normalized));
}
*/
// normalize EMPTY (without prefix)
{
irs::string_ref analyzer = irs::string_ref::EMPTY;
@ -769,14 +898,14 @@ SECTION("test_normalize") {
auto normalized = arangodb::iresearch::IResearchAnalyzerFeature::normalize(analyzer, active, system, false);
CHECK((std::string("::name") == normalized));
}
/* FIXME TODO uncomment once emplace(...) and all tests are updated
// normalize no-delimiter + name (with prefix)
{
irs::string_ref analyzer = "name";
auto normalized = arangodb::iresearch::IResearchAnalyzerFeature::normalize(analyzer, active, system, true);
CHECK((std::string("active::name") == normalized));
}
*/
// normalize no-delimiter + name (without prefix)
{
irs::string_ref analyzer = "name";
@ -1029,9 +1158,10 @@ SECTION("test_persistence") {
}
{
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
feature.start();
auto result = feature.emplace("valid", "identity", "abc");
CHECK((feature.emplace(result, "valid", "identity", "abc").ok()));
CHECK((result.first));
CHECK((result.second));
}
@ -1124,6 +1254,182 @@ SECTION("test_persistence") {
CHECK((expected.empty()));
}
}
// emplace on single-server (should persist)
{
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
CHECK((true == feature.emplace(result, "test_analyzerA", "TestAnalyzer", "abc").ok()));
CHECK((false == !result.first));
CHECK((false == !feature.get("test_analyzerA")));
CHECK((false == !s.system->lookupCollection("_iresearch_analyzers")));
arangodb::OperationOptions options;
arangodb::SingleCollectionTransaction trx(
arangodb::transaction::StandaloneContext::Create(*vocbase),
"_iresearch_analyzers",
arangodb::AccessMode::Type::WRITE
);
CHECK((trx.begin().ok()));
auto queryResult = trx.all("_iresearch_analyzers", 0, 2, options);
CHECK((true == queryResult.ok()));
auto slice = arangodb::velocypack::Slice(queryResult.buffer->data());
CHECK((slice.isArray() && 1 == slice.length()));
CHECK((trx.truncate("_iresearch_analyzers", options).ok()));
CHECK((trx.commit().ok()));
}
// emplace on coordinator (should persist)
{
auto before = arangodb::ServerState::instance()->getRole();
arangodb::ServerState::instance()->setRole(arangodb::ServerState::ROLE_COORDINATOR);
auto restore = irs::make_finally([&before]()->void { arangodb::ServerState::instance()->setRole(before); });
// create a new instance of an ApplicationServer and fill it with the required features
// cannot use the existing server since its features already have some state
std::shared_ptr<arangodb::application_features::ApplicationServer> originalServer(
arangodb::application_features::ApplicationServer::server,
[](arangodb::application_features::ApplicationServer* ptr)->void {
arangodb::application_features::ApplicationServer::server = ptr;
}
);
arangodb::application_features::ApplicationServer::server = nullptr; // avoid "ApplicationServer initialized twice"
arangodb::application_features::ApplicationServer server(nullptr, nullptr);
arangodb::iresearch::IResearchAnalyzerFeature* feature;
arangodb::DatabaseFeature* dbFeature;
arangodb::SystemDatabaseFeature* sysDatabase;
server.addFeature(new arangodb::ClusterFeature(server)); // required to create ClusterInfo instance
server.addFeature(dbFeature = new arangodb::DatabaseFeature(server)); // required for IResearchAnalyzerFeature::emplace(...)
server.addFeature(new arangodb::QueryRegistryFeature(server)); // required for constructing TRI_vocbase_t
server.addFeature(new arangodb::ShardingFeature(server)); // required for Collections::create(...)
server.addFeature(sysDatabase = new arangodb::SystemDatabaseFeature(server)); // required for IResearchAnalyzerFeature::start()
server.addFeature(new arangodb::UpgradeFeature(server, nullptr, {})); // required for upgrade tasks
server.addFeature(new arangodb::V8DealerFeature(server)); // required for DatabaseFeature::createDatabase(...)
server.addFeature(new arangodb::application_features::CommunicationFeaturePhase(server)); // required for SimpleHttpClient::doRequest()
server.addFeature(feature = new arangodb::iresearch::IResearchAnalyzerFeature(server)); // required for running upgrade task
// create system vocbase (before feature start)
{
auto const databases = arangodb::velocypack::Parser::fromJson(std::string("[ { \"name\": \"") + arangodb::StaticStrings::SystemDatabase + "\" } ]");
CHECK((TRI_ERROR_NO_ERROR == dbFeature->loadDatabases(databases->slice())));
sysDatabase->start(); // get system database from DatabaseFeature
}
auto system = sysDatabase->use();
server.getFeature<arangodb::ClusterFeature>("Cluster")->prepare(); // create ClusterInfo instance
server.getFeature<arangodb::ShardingFeature>("Sharding")->prepare(); // required for Collections::create(...), register sharding types
arangodb::AgencyCommManager::MANAGER->start(); // initialize agency
ClusterCommMock clusterComm;
auto scopedClusterComm = ClusterCommMock::setInstance(clusterComm);
auto* ci = arangodb::ClusterInfo::instance();
REQUIRE((nullptr != ci));
// simulate heartbeat thread
// (create dbserver in current) required by ClusterMethods::persistCollectionInAgency(...)
// (create collection in current) required by ClusterMethods::persistCollectionInAgency(...)
// (create dummy collection in plan to fill ClusterInfo::_shardServers) required by ClusterMethods::persistCollectionInAgency(...)
{
auto const srvPath = "/Current/DBServers";
auto const srvValue = arangodb::velocypack::Parser::fromJson("{ \"dbserver-key-does-not-matter\": \"dbserver-value-does-not-matter\" }");
CHECK(arangodb::AgencyComm().setValue(srvPath, srvValue->slice(), 0.0).successful());
auto const colPath = "/Current/Collections/_system/2"; // '2' must match what ClusterInfo generates for LogicalCollection::id() or collection creation request will never get executed (use 'collectionID' from ClusterInfo::createCollectionCoordinator(...) in stack trace)
auto const colValue = arangodb::velocypack::Parser::fromJson("{ \"same-as-dummy-shard-id\": { \"servers\": [ \"same-as-dummy-shard-server\" ] } }");
CHECK(arangodb::AgencyComm().setValue(colPath, colValue->slice(), 0.0).successful());
auto const dummyPath = "/Plan/Collections";
auto const dummyValue = arangodb::velocypack::Parser::fromJson("{ \"_system\": { \"collection-id-does-not-matter\": { \"name\": \"dummy\", \"shards\": { \"same-as-dummy-shard-id\": [ \"same-as-dummy-shard-server\" ] } } } }");
CHECK(arangodb::AgencyComm().setValue(dummyPath, dummyValue->slice(), 0.0).successful());
}
// insert response for expected extra analyzer
{
arangodb::ClusterCommResult response;
response.operationID = clusterComm.nextOperationId();
response.status = arangodb::ClusterCommOpStatus::CL_COMM_RECEIVED;
response.answer_code = arangodb::rest::ResponseCode::CREATED;
response.answer = std::make_shared<GeneralRequestMock>(*vocbase);
static_cast<GeneralRequestMock*>(response.answer.get())->_payload = *arangodb::velocypack::Parser::fromJson(std::string("{ \"_key\": \"") + std::to_string(response.operationID) + "\" }"); // unique arbitrary key
clusterComm._responses[0].emplace_back(std::move(response));
}
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
CHECK((true == feature->emplace(result, "_system::test_analyzerB", "TestAnalyzer", "abc").ok()));
CHECK((nullptr != ci->getCollection(system->name(), "_iresearch_analyzers")));
CHECK((1 == clusterComm._requests.size()));
auto& entry = *(clusterComm._requests.begin());
CHECK((entry.second._body));
auto body = arangodb::velocypack::Parser::fromJson(*(entry.second._body));
auto slice = body->slice();
CHECK((slice.isObject()));
CHECK((slice.get("name").isString()));
CHECK((std::string("test_analyzerB") == slice.get("name").copyString()));
}
// emplace on db-server(should not persist)
{
auto before = arangodb::ServerState::instance()->getRole();
arangodb::ServerState::instance()->setRole(arangodb::ServerState::ROLE_DBSERVER);
auto restore = irs::make_finally([&before]()->void { arangodb::ServerState::instance()->setRole(before); });
// create a new instance of an ApplicationServer and fill it with the required features
// cannot use the existing server since its features already have some state
std::shared_ptr<arangodb::application_features::ApplicationServer> originalServer(
arangodb::application_features::ApplicationServer::server,
[](arangodb::application_features::ApplicationServer* ptr)->void {
arangodb::application_features::ApplicationServer::server = ptr;
}
);
arangodb::application_features::ApplicationServer::server = nullptr; // avoid "ApplicationServer initialized twice"
arangodb::application_features::ApplicationServer server(nullptr, nullptr);
arangodb::iresearch::IResearchAnalyzerFeature* feature;
arangodb::DatabaseFeature* dbFeature;
arangodb::SystemDatabaseFeature* sysDatabase;
server.addFeature(new arangodb::ClusterFeature(server)); // required to create ClusterInfo instance
server.addFeature(dbFeature = new arangodb::DatabaseFeature(server)); // required for IResearchAnalyzerFeature::emplace(...)
server.addFeature(new arangodb::QueryRegistryFeature(server)); // required for constructing TRI_vocbase_t
server.addFeature(new arangodb::ShardingFeature(server)); // required for Collections::create(...)
server.addFeature(sysDatabase = new arangodb::SystemDatabaseFeature(server)); // required for IResearchAnalyzerFeature::start()
server.addFeature(new arangodb::UpgradeFeature(server, nullptr, {})); // required for upgrade tasks
server.addFeature(new arangodb::V8DealerFeature(server)); // required for DatabaseFeature::createDatabase(...)
server.addFeature(new arangodb::application_features::CommunicationFeaturePhase(server)); // required for SimpleHttpClient::doRequest()
server.addFeature(feature = new arangodb::iresearch::IResearchAnalyzerFeature(server)); // required for running upgrade task
// create system vocbase (before feature start)
{
auto const databases = arangodb::velocypack::Parser::fromJson(std::string("[ { \"name\": \"") + arangodb::StaticStrings::SystemDatabase + "\" } ]");
CHECK((TRI_ERROR_NO_ERROR == dbFeature->loadDatabases(databases->slice())));
sysDatabase->start(); // get system database from DatabaseFeature
}
auto system = sysDatabase->use();
server.getFeature<arangodb::ClusterFeature>("Cluster")->prepare(); // create ClusterInfo instance
server.getFeature<arangodb::ShardingFeature>("Sharding")->prepare(); // required for Collections::create(...), register sharding types
arangodb::AgencyCommManager::MANAGER->start(); // initialize agency
ClusterCommMock clusterComm;
auto scopedClusterComm = ClusterCommMock::setInstance(clusterComm);
auto* ci = arangodb::ClusterInfo::instance();
REQUIRE((nullptr != ci));
// simulate heartbeat thread
// (create dbserver in current) required by ClusterMethods::persistCollectionInAgency(...)
// (create collection in current) required by ClusterMethods::persistCollectionInAgency(...)
// (create dummy collection in plan to fill ClusterInfo::_shardServers) required by ClusterMethods::persistCollectionInAgency(...)
{
auto const srvPath = "/Current/DBServers";
auto const srvValue = arangodb::velocypack::Parser::fromJson("{ \"dbserver-key-does-not-matter\": \"dbserver-value-does-not-matter\" }");
CHECK(arangodb::AgencyComm().setValue(srvPath, srvValue->slice(), 0.0).successful());
auto const dummyPath = "/Plan/Collections";
auto const dummyValue = arangodb::velocypack::Parser::fromJson("{ \"_system\": { \"collection-id-does-not-matter\": { \"name\": \"dummy\", \"shards\": { \"same-as-dummy-shard-id\": [ \"same-as-dummy-shard-server\" ] } } } }");
CHECK(arangodb::AgencyComm().setValue(dummyPath, dummyValue->slice(), 0.0).successful());
}
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
CHECK((true == feature->emplace(result, "_system::test_analyzerC", "TestAnalyzer", "abc").ok()));
CHECK_THROWS((ci->getCollection(system->name(), "_iresearch_analyzers"))); // throws on missing collection, not ClusterInfo persisted
CHECK((true == !system->lookupCollection("_iresearch_analyzers"))); // not locally persisted
}
}
SECTION("test_remove") {
@ -1262,9 +1568,11 @@ SECTION("test_start") {
collection = vocbase->lookupCollection("_iresearch_analyzers");
CHECK((nullptr == collection));
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
feature.start();
CHECK((false == !feature.emplace("test_analyzer", "identity", "abc").first));
CHECK((true == feature.emplace(result, "test_analyzer", "identity", "abc").ok()));
CHECK((false == !result.first));
collection = vocbase->lookupCollection("_iresearch_analyzers");
CHECK((nullptr != collection));
}
@ -1303,9 +1611,11 @@ SECTION("test_start") {
collection = vocbase->lookupCollection("_iresearch_analyzers");
CHECK((nullptr == collection));
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
feature.start();
CHECK((false == !feature.emplace("test_analyzer", "identity", "abc").first));
CHECK((true == feature.emplace(result, "test_analyzer", "identity", "abc").ok()));
CHECK((false == !result.first));
collection = vocbase->lookupCollection("_iresearch_analyzers");
CHECK((nullptr != collection));
}
@ -1412,9 +1722,11 @@ SECTION("test_start") {
collection = vocbase->lookupCollection("_iresearch_analyzers");
CHECK((nullptr == collection));
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
feature.start();
CHECK((false == !feature.emplace("test_analyzer", "identity", "abc").first));
CHECK((true == feature.emplace(result, "test_analyzer", "identity", "abc").ok()));
CHECK((false == !result.first));
collection = vocbase->lookupCollection("_iresearch_analyzers");
CHECK((nullptr != collection));
}
@ -1450,9 +1762,11 @@ SECTION("test_start") {
collection = vocbase->lookupCollection("_iresearch_analyzers");
CHECK((nullptr == collection));
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
feature.start();
CHECK((false == !feature.emplace("test_analyzer", "identity", "abc").first));
CHECK((true == feature.emplace(result, "test_analyzer", "identity", "abc").ok()));
CHECK((false == !result.first));
collection = vocbase->lookupCollection("_iresearch_analyzers");
CHECK((nullptr != collection));
}
@ -1532,8 +1846,10 @@ SECTION("test_tokens") {
CHECK((nullptr != functions->byName("TOKENS")));
}
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
analyzers->start();
REQUIRE((false == !analyzers->emplace("test_analyzer", "TestAnalyzer", "abc").first));
REQUIRE((true == analyzers->emplace(result, "test_analyzer", "TestAnalyzer", "abc").ok()));
REQUIRE((false == !result.first));
// test tokenization
{
@ -2125,7 +2441,7 @@ SECTION("test_upgrade_static_legacy") {
}
sysDatabase->unprepare(); // unset system vocbase
CHECK_THROWS((!ci->getCollection(vocbase->name(), ANALYZER_COLLECTION_NAME))); // throws on missing collection
CHECK_THROWS((ci->getCollection(vocbase->name(), ANALYZER_COLLECTION_NAME))); // throws on missing collection
CHECK((arangodb::methods::Upgrade::clusterBootstrap(*vocbase).ok())); // run upgrade
CHECK((false == !ci->getCollection(vocbase->name(), ANALYZER_COLLECTION_NAME)));
CHECK((true == clusterComm._responses.empty()));
@ -2901,9 +3217,13 @@ SECTION("test_visit") {
feature.start();
CHECK((false == !feature.emplace("test_analyzer0", "TestAnalyzer", "abc0").first));
CHECK((false == !feature.emplace("test_analyzer1", "TestAnalyzer", "abc1").first));
CHECK((false == !feature.emplace("test_analyzer2", "TestAnalyzer", "abc2").first));
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
CHECK((true == feature.emplace(result, "test_analyzer0", "TestAnalyzer", "abc0").ok()));
CHECK((false == !result.first));
CHECK((true == feature.emplace(result, "test_analyzer1", "TestAnalyzer", "abc1").ok()));
CHECK((false == !result.first));
CHECK((true == feature.emplace(result, "test_analyzer2", "TestAnalyzer", "abc2").ok()));
CHECK((false == !result.first));
// full visitation
{
@ -2962,9 +3282,13 @@ SECTION("test_visit") {
// add database-prefixed analyzers
{
CHECK((false == !feature.emplace("vocbase2::test_analyzer3", "TestAnalyzer", "abc3").first));
CHECK((false == !feature.emplace("vocbase2::test_analyzer4", "TestAnalyzer", "abc4").first));
CHECK((false == !feature.emplace("vocbase1::test_analyzer5", "TestAnalyzer", "abc5").first));
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
CHECK((true == feature.emplace(result, "vocbase2::test_analyzer3", "TestAnalyzer", "abc3").ok()));
CHECK((false == !result.first));
CHECK((true == feature.emplace(result, "vocbase2::test_analyzer4", "TestAnalyzer", "abc4").ok()));
CHECK((false == !result.first));
CHECK((true == feature.emplace(result, "vocbase1::test_analyzer5", "TestAnalyzer", "abc5").ok()));
CHECK((false == !result.first));
}
// full visitation limited to a vocbase (empty)

View File

@ -172,12 +172,13 @@ struct IResearchDocumentSetup {
auto* analyzers = arangodb::application_features::ApplicationServer::lookupFeature<
arangodb::iresearch::IResearchAnalyzerFeature
>();
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
// ensure that there will be no exception on 'emplace'
InvalidAnalyzer::returnNullFromMake = false;
analyzers->emplace("iresearch-document-empty", "iresearch-document-empty", "en", irs::flags{ TestAttribute::type() }); // cache analyzer
analyzers->emplace("iresearch-document-invalid", "iresearch-document-invalid", "en", irs::flags{ TestAttribute::type() }); // cache analyzer
analyzers->emplace(result, "iresearch-document-empty", "iresearch-document-empty", "en", irs::flags{ TestAttribute::type() }); // cache analyzer
analyzers->emplace(result, "iresearch-document-invalid", "iresearch-document-invalid", "en", irs::flags{ TestAttribute::type() }); // cache analyzer
// suppress log messages since tests check error conditions
arangodb::LogTopic::setLogLevel(arangodb::iresearch::TOPIC.name(), arangodb::LogLevel::FATAL);
@ -1390,8 +1391,9 @@ SECTION("FieldIterator_nullptr_analyzer") {
// ensure that there will be no exception on 'emplace'
InvalidAnalyzer::returnNullFromMake = false;
analyzers.emplace("empty", "iresearch-document-empty", "en", irs::flags{TestAttribute::type()});
analyzers.emplace("invalid", "iresearch-document-invalid", "en", irs::flags{TestAttribute::type()});
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
analyzers.emplace(result, "empty", "iresearch-document-empty", "en", irs::flags{TestAttribute::type()});
analyzers.emplace(result, "invalid", "iresearch-document-invalid", "en", irs::flags{TestAttribute::type()});
}
// last analyzer invalid
@ -2014,4 +2016,4 @@ SECTION("test_rid_filter") {
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------

View File

@ -609,6 +609,7 @@ SECTION("test_upgrade0_1") {
REQUIRE((irs::utf8_path(dbPathFeature->directory()).mkdir()));
REQUIRE((arangodb::basics::VelocyPackHelper::velocyPackToFile(StorageEngineMock::versionFilenameResult, versionJson->slice(), false)));
s.engine.views.clear();
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
auto logicalCollection = vocbase.createCollection(collectionJson->slice());
REQUIRE((false == !logicalCollection));
@ -1091,4 +1092,4 @@ SECTION("test_async") {
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------

View File

@ -214,8 +214,9 @@ struct IResearchFilterSetup {
auto* analyzers = arangodb::application_features::ApplicationServer::lookupFeature<
arangodb::iresearch::IResearchAnalyzerFeature
>();
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
analyzers->emplace("test_analyzer", "TestCharAnalyzer", "abc"); // cache analyzer
analyzers->emplace(result, "test_analyzer", "TestCharAnalyzer", "abc"); // cache analyzer
}
~IResearchFilterSetup() {
@ -256,4 +257,4 @@ TEST_CASE("IResearchFilterTest", "[iresearch][iresearch-filter]") {
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------

View File

@ -146,12 +146,6 @@ struct IResearchFilterSetup {
return params[0];
}});
auto* analyzers = arangodb::application_features::ApplicationServer::lookupFeature<
arangodb::iresearch::IResearchAnalyzerFeature
>();
analyzers->emplace("test_analyzer", "TestCharAnalyzer", "abc"); // cache analyzer
// suppress log messages since tests check error conditions
arangodb::LogTopic::setLogLevel(arangodb::iresearch::TOPIC.name(), arangodb::LogLevel::FATAL);
irs::logger::output_le(iresearch::logger::IRL_FATAL, stderr);
@ -3639,4 +3633,4 @@ SECTION("BinaryAnd") {
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------

View File

@ -147,12 +147,6 @@ struct IResearchFilterSetup {
return params[0];
}});
auto* analyzers = arangodb::application_features::ApplicationServer::lookupFeature<
arangodb::iresearch::IResearchAnalyzerFeature
>();
analyzers->emplace("test_analyzer", "TestCharAnalyzer", "abc"); // cache analyzer
// suppress log messages since tests check error conditions
arangodb::LogTopic::setLogLevel(arangodb::iresearch::TOPIC.name(), arangodb::LogLevel::FATAL);
irs::logger::output_le(iresearch::logger::IRL_FATAL, stderr);
@ -3669,4 +3663,4 @@ SECTION("BinaryLT") {
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------

View File

@ -149,12 +149,6 @@ struct IResearchFilterSetup {
return params[0];
}});
auto* analyzers = arangodb::application_features::ApplicationServer::lookupFeature<
arangodb::iresearch::IResearchAnalyzerFeature
>();
analyzers->emplace("test_analyzer", "TestCharAnalyzer", "abc"); // cache analyzer
// suppress log messages since tests check error conditions
arangodb::LogTopic::setLogLevel(arangodb::iresearch::TOPIC.name(), arangodb::LogLevel::FATAL);
irs::logger::output_le(iresearch::logger::IRL_FATAL, stderr);
@ -2273,4 +2267,4 @@ SECTION("StartsWith") {
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------

View File

@ -146,12 +146,6 @@ struct IResearchFilterSetup {
return params[0];
}});
auto* analyzers = arangodb::application_features::ApplicationServer::lookupFeature<
arangodb::iresearch::IResearchAnalyzerFeature
>();
analyzers->emplace("test_analyzer", "TestCharAnalyzer", "abc"); // cache analyzer
// suppress log messages since tests check error conditions
arangodb::LogTopic::setLogLevel(arangodb::iresearch::TOPIC.name(), arangodb::LogLevel::FATAL);
irs::logger::output_le(iresearch::logger::IRL_FATAL, stderr);
@ -3951,4 +3945,4 @@ SECTION("BinaryNotIn") {
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------

View File

@ -182,9 +182,10 @@ struct IResearchIndexSetup {
auto* analyzers = arangodb::application_features::ApplicationServer::lookupFeature<
arangodb::iresearch::IResearchAnalyzerFeature
>();
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
analyzers->emplace("test_A", "TestInsertAnalyzer", "X"); // cache analyzer
analyzers->emplace("test_B", "TestInsertAnalyzer", "Y"); // cache analyzer
analyzers->emplace(result, "test_A", "TestInsertAnalyzer", "X"); // cache analyzer
analyzers->emplace(result, "test_B", "TestInsertAnalyzer", "Y"); // 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

View File

@ -30,6 +30,7 @@
#include "utils/utf8_path.hpp"
#include "Aql/AqlFunctionFeature.h"
#include "Aql/QueryRegistry.h"
#if USE_ENTERPRISE
#include "Enterprise/Ldap/LdapFeature.h"
@ -47,17 +48,20 @@
#include "Logger/Logger.h"
#include "Logger/LogTopic.h"
#include "MMFiles/MMFilesWalRecoverState.h"
#include "RestServer/AqlFeature.h"
#include "RestServer/DatabaseFeature.h"
#include "RestServer/DatabasePathFeature.h"
#include "RestServer/FlushFeature.h"
#include "RestServer/QueryRegistryFeature.h"
#include "RestServer/ServerIdFeature.h"
#include "RestServer/SystemDatabaseFeature.h"
#include "RestServer/TraverserEngineRegistryFeature.h"
#include "RestServer/ViewTypesFeature.h"
#include "Sharding/ShardingFeature.h"
#include "StorageEngine/EngineSelectorFeature.h"
#include "Transaction/Methods.h"
#include "Transaction/StandaloneContext.h"
#include "Utils/ExecContext.h"
#include "V8Server/V8DealerFeature.h"
#include "VocBase/KeyGenerator.h"
#include "VocBase/LogicalCollection.h"
@ -91,6 +95,7 @@ struct IResearchLinkSetup {
irs::logger::output_le(iresearch::logger::IRL_FATAL, stderr);
// setup required application features
features.emplace_back(new arangodb::AqlFeature(server), true); // required for UserManager::loadFromDB()
features.emplace_back(new arangodb::AuthenticationFeature(server), true);
features.emplace_back(new arangodb::DatabaseFeature(server), false);
features.emplace_back(new arangodb::ShardingFeature(server), false);
@ -99,6 +104,7 @@ struct IResearchLinkSetup {
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::TraverserEngineRegistryFeature(server), false); // required for AqlFeature::stop()
features.emplace_back(new arangodb::DatabasePathFeature(server), false);
features.emplace_back(new arangodb::aql::AqlFunctionFeature(server), true); // required for IResearchAnalyzerFeature
features.emplace_back(new arangodb::iresearch::IResearchAnalyzerFeature(server), true);
@ -133,6 +139,12 @@ struct IResearchLinkSetup {
TransactionStateMock::beginTransactionCount = 0;
TransactionStateMock::commitTransactionCount = 0;
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());
auto* dbPathFeature = arangodb::application_features::ApplicationServer::getFeature<arangodb::DatabasePathFeature>("DatabasePath");
arangodb::tests::setDatabasePath(*dbPathFeature); // ensure test data is stored in a unique directory
testFilesystemPath = dbPathFeature->directory();

View File

@ -22,19 +22,126 @@
////////////////////////////////////////////////////////////////////////////////
#include "catch.hpp"
#include "common.h"
#include "shared.hpp"
#include "Aql/QueryRegistry.h"
#include "Basics/files.h"
#include "Cluster/ClusterFeature.h"
#if USE_ENTERPRISE
#include "Enterprise/Ldap/LdapFeature.h"
#endif
#include "GeneralServer/AuthenticationFeature.h"
#include "IResearch/IResearchAnalyzerFeature.h"
#include "IResearch/IResearchCommon.h"
#include "IResearch/IResearchFeature.h"
#include "IResearch/IResearchLinkHelper.h"
#include "Mocks/StorageEngineMock.h"
#include "RestServer/AqlFeature.h"
#include "RestServer/DatabaseFeature.h"
#include "RestServer/DatabasePathFeature.h"
#include "RestServer/QueryRegistryFeature.h"
#include "RestServer/SystemDatabaseFeature.h"
#include "RestServer/TraverserEngineRegistryFeature.h"
#include "RestServer/ViewTypesFeature.h"
#include "StorageEngine/EngineSelectorFeature.h"
#include "Utils/ExecContext.h"
#include "utils/misc.hpp"
#include "V8Server/V8DealerFeature.h"
#include "velocypack/Parser.h"
#include "VocBase/LogicalCollection.h"
// -----------------------------------------------------------------------------
// --SECTION-- setup / tear-down
// -----------------------------------------------------------------------------
struct IResearchLinkHelperSetup {
IResearchLinkHelperSetup() {
StorageEngineMock engine;
arangodb::application_features::ApplicationServer server;
std::vector<std::pair<arangodb::application_features::ApplicationFeature*, bool>> features;
std::string testFilesystemPath;
IResearchLinkHelperSetup(): engine(server), server(nullptr, nullptr) {
arangodb::EngineSelectorFeature::ENGINE = &engine;
// suppress INFO {authentication} Authentication is turned on (system only), authentication for unix sockets is turned on
arangodb::LogTopic::setLogLevel(arangodb::Logger::AUTHENTICATION.name(), arangodb::LogLevel::WARN);
// suppress log messages since tests check error conditions
arangodb::LogTopic::setLogLevel(arangodb::iresearch::TOPIC.name(), arangodb::LogLevel::FATAL);
features.emplace_back(new arangodb::AqlFeature(server), true); // required for UserManager::loadFromDB()
features.emplace_back(new arangodb::AuthenticationFeature(server), false); // required for authentication tests
features.emplace_back(new arangodb::DatabaseFeature(server), false);
features.emplace_back(new arangodb::DatabasePathFeature(server), false); // required for IResearchLink::init(...)
features.emplace_back(new arangodb::QueryRegistryFeature(server), false); // required for constructing TRI_vocbase_t
features.emplace_back(new arangodb::SystemDatabaseFeature(server), false); // required by IResearchAnalyzerFeature::storeAnalyzer(...)
features.emplace_back(new arangodb::TraverserEngineRegistryFeature(server), false); // required for AqlFeature::stop()
features.emplace_back(new arangodb::V8DealerFeature(server), false); // required for DatabaseFeature::createDatabase(...)
features.emplace_back(new arangodb::ViewTypesFeature(server), false); // required for LogicalView::instantiate(...)
features.emplace_back(new arangodb::iresearch::IResearchAnalyzerFeature(server), false); // required for IResearchLinkMeta::init(...)
features.emplace_back(new arangodb::iresearch::IResearchFeature(server), false); // required for creating views of type 'iresearch'
#if USE_ENTERPRISE
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);
}
for (auto& f: features) {
f.first->prepare();
}
for (auto& f: features) {
if (f.second) {
f.first->start();
}
}
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());
auto* sysDatabase = arangodb::application_features::ApplicationServer::lookupFeature<
arangodb::SystemDatabaseFeature
>();
sysDatabase->start(); // load system database after loadDatabases()
auto* dbPathFeature = arangodb::application_features::ApplicationServer::getFeature<arangodb::DatabasePathFeature>("DatabasePath");
arangodb::tests::setDatabasePath(*dbPathFeature); // ensure test data is stored in a unique directory
testFilesystemPath = dbPathFeature->directory();
long systemError;
std::string systemErrorStr;
TRI_CreateDirectory(testFilesystemPath.c_str(), systemError, systemErrorStr);
}
~IResearchLinkHelperSetup() {
arangodb::application_features::ApplicationServer::server = nullptr;
for (auto& f: features) {
if (f.second) {
f.first->stop();
}
}
for (auto& f: features) {
f.first->unprepare();
}
arangodb::LogTopic::setLogLevel(arangodb::iresearch::TOPIC.name(), arangodb::LogLevel::DEFAULT);
arangodb::LogTopic::setLogLevel(arangodb::Logger::AUTHENTICATION.name(), arangodb::LogLevel::DEFAULT);
arangodb::EngineSelectorFeature::ENGINE = nullptr;
}
};
@ -132,6 +239,205 @@ SECTION("test_equals") {
}
}
SECTION("test_normalize") {
auto* analyzers = arangodb::application_features::ApplicationServer::lookupFeature<
arangodb::iresearch::IResearchAnalyzerFeature
>();
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
auto* sysDatabase = arangodb::application_features::ApplicationServer::lookupFeature<
arangodb::SystemDatabaseFeature
>();
auto sysVocbase = sysDatabase->use();
// create analyzer collection
{
static std::string const ANALYZER_COLLECTION_NAME("_iresearch_analyzers");
if (!sysVocbase->lookupCollection(ANALYZER_COLLECTION_NAME)) {
auto collectionJson = arangodb::velocypack::Parser::fromJson(std::string("{ \"name\": \"") + ANALYZER_COLLECTION_NAME + "\", \"isSystem\": true }");
auto logicalCollection = sysVocbase->createCollection(collectionJson->slice());
REQUIRE((false == !logicalCollection));
}
}
// analyzer single-server
{
auto json = arangodb::velocypack::Parser::fromJson("{ \"analyzers\": [ { \"name\": \"testAnalyzer0\", \"type\": \"identity\" } ] }");
arangodb::velocypack::Builder builder;
builder.openObject();
CHECK((false == arangodb::iresearch::IResearchLinkHelper::normalize(builder, json->slice(), false, *sysVocbase).ok()));
CHECK((true == !analyzers->get("testAnalyzer1")));
}
// analyzer single-server (inRecovery) fail persist in recovery
{
auto json = arangodb::velocypack::Parser::fromJson("{ \"analyzers\": [ { \"name\": \"testAnalyzer1\", \"type\": \"identity\" } ] }");
auto before = StorageEngineMock::inRecoveryResult;
StorageEngineMock::inRecoveryResult = true;
auto restore = irs::make_finally([&before]()->void { StorageEngineMock::inRecoveryResult = before; });
arangodb::velocypack::Builder builder;
builder.openObject();
CHECK((false == arangodb::iresearch::IResearchLinkHelper::normalize(builder, json->slice(), false, *sysVocbase).ok()));
CHECK((true == !analyzers->get("testAnalyzer2")));
}
// analyzer single-server (no engine) fail persist if not storage engine, else SEGFAULT in Methods(...)
{
auto json = arangodb::velocypack::Parser::fromJson("{ \"analyzers\": [ { \"name\": \"testAnalyzer2\", \"type\": \"identity\" } ] }");
auto* before = arangodb::EngineSelectorFeature::ENGINE;
arangodb::EngineSelectorFeature::ENGINE = nullptr;
auto restore = irs::make_finally([&before]()->void { arangodb::EngineSelectorFeature::ENGINE = before; });
arangodb::velocypack::Builder builder;
builder.openObject();
CHECK((false == arangodb::iresearch::IResearchLinkHelper::normalize(builder, json->slice(), false, *sysVocbase).ok()));
CHECK((true == !analyzers->get("testAnalyzer3")));
}
// analyzer coordinator
{
auto json = arangodb::velocypack::Parser::fromJson("{ \"analyzers\": [ { \"name\": \"testAnalyzer3\", \"type\": \"identity\" } ] }");
auto serverRoleBefore = arangodb::ServerState::instance()->getRole();
arangodb::ServerState::instance()->setRole(arangodb::ServerState::ROLE_COORDINATOR);
auto serverRoleRestore = irs::make_finally([&serverRoleBefore]()->void { arangodb::ServerState::instance()->setRole(serverRoleBefore); });
arangodb::velocypack::Builder builder;
builder.openObject();
CHECK((false == arangodb::iresearch::IResearchLinkHelper::normalize(builder, json->slice(), false, *sysVocbase).ok()));
CHECK((true == !analyzers->get("testAnalyzer4")));
}
// analyzer coordinator (inRecovery) fail persist in recovery
{
auto json = arangodb::velocypack::Parser::fromJson("{ \"analyzers\": [ { \"name\": \"testAnalyzer5\", \"type\": \"identity\" } ] }");
auto serverRoleBefore = arangodb::ServerState::instance()->getRole();
arangodb::ServerState::instance()->setRole(arangodb::ServerState::ROLE_COORDINATOR);
auto serverRoleRestore = irs::make_finally([&serverRoleBefore]()->void { arangodb::ServerState::instance()->setRole(serverRoleBefore); });
auto inRecoveryBefore = StorageEngineMock::inRecoveryResult;
StorageEngineMock::inRecoveryResult = true;
auto restore = irs::make_finally([&inRecoveryBefore]()->void { StorageEngineMock::inRecoveryResult = inRecoveryBefore; });
arangodb::velocypack::Builder builder;
builder.openObject();
CHECK((false == arangodb::iresearch::IResearchLinkHelper::normalize(builder, json->slice(), false, *sysVocbase).ok()));
CHECK((true == !analyzers->get("testAnalyzer5")));
}
// analyzer coordinator (no engine)
{
auto json = arangodb::velocypack::Parser::fromJson("{ \"analyzers\": [ { \"name\": \"testAnalyzer6\", \"type\": \"identity\" } ] }");
auto serverRoleBefore = arangodb::ServerState::instance()->getRole();
arangodb::ServerState::instance()->setRole(arangodb::ServerState::ROLE_COORDINATOR);
auto serverRoleRestore = irs::make_finally([&serverRoleBefore]()->void { arangodb::ServerState::instance()->setRole(serverRoleBefore); });
auto* engineBefore = arangodb::EngineSelectorFeature::ENGINE;
arangodb::EngineSelectorFeature::ENGINE = nullptr;
auto restore = irs::make_finally([&engineBefore]()->void { arangodb::EngineSelectorFeature::ENGINE = engineBefore; });
arangodb::velocypack::Builder builder;
builder.openObject();
CHECK((false == arangodb::iresearch::IResearchLinkHelper::normalize(builder, json->slice(), false, *sysVocbase).ok()));
CHECK((true == !analyzers->get("testAnalyzer6")));
}
// analyzer db-server
{
auto json = arangodb::velocypack::Parser::fromJson("{ \"analyzers\": [ { \"name\": \"testAnalyzer7\", \"type\": \"identity\" } ] }");
auto before = arangodb::ServerState::instance()->getRole();
arangodb::ServerState::instance()->setRole(arangodb::ServerState::ROLE_DBSERVER);
auto serverRoleRestore = irs::make_finally([&before]()->void { arangodb::ServerState::instance()->setRole(before); });
arangodb::velocypack::Builder builder;
builder.openObject();
CHECK((true == !analyzers->get("testAnalyzer7")));
CHECK((true == arangodb::iresearch::IResearchLinkHelper::normalize(builder, json->slice(), false, *sysVocbase).ok()));
CHECK((false == !analyzers->get("testAnalyzer7")));
}
// meta has analyzer which is not authorised
{
auto json = arangodb::velocypack::Parser::fromJson("{ \"type\": \"arangosearch\", \"view\": \"43\", \"analyzers\": [ \"::unAuthorsedAnalyzer\" ] }");
auto* analyzers = arangodb::application_features::ApplicationServer::lookupFeature<
arangodb::iresearch::IResearchAnalyzerFeature
>();
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
REQUIRE((analyzers->emplace(result, arangodb::StaticStrings::SystemDatabase + "::unAuthorsedAnalyzer", "identity", irs::string_ref::NIL).ok()));
REQUIRE((false == !result.first));
// not authorised
{
struct ExecContext: public arangodb::ExecContext {
ExecContext(): arangodb::ExecContext(arangodb::ExecContext::Type::Default, "", "", arangodb::auth::Level::NONE, arangodb::auth::Level::NONE) {}
} execContext;
arangodb::ExecContextScope execContextScope(&execContext);
auto* authFeature = arangodb::AuthenticationFeature::instance();
auto* userManager = authFeature->userManager();
arangodb::aql::QueryRegistry queryRegistry(0); // required for UserManager::loadFromDB()
userManager->setQueryRegistry(&queryRegistry);
auto resetUserManager = std::shared_ptr<arangodb::auth::UserManager>(userManager, [](arangodb::auth::UserManager* ptr)->void { ptr->removeAllUsers(); });
arangodb::velocypack::Builder builder;
builder.openObject();
CHECK((false == arangodb::iresearch::IResearchLinkHelper::normalize(builder, json->slice(), false, *sysVocbase).ok()));
}
// authorsed
{
arangodb::velocypack::Builder builder;
builder.openObject();
CHECK((true == arangodb::iresearch::IResearchLinkHelper::normalize(builder, json->slice(), false, *sysVocbase).ok()));
}
}
}
SECTION("test_updateLinks") {
// meta has analyzer which is not authorised
{
auto collectionJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testCollection\", \"id\": 101 }");
auto linkUpdateJson = arangodb::velocypack::Parser::fromJson("{ \"testCollection\": { \"type\": \"arangosearch\", \"view\": \"43\", \"analyzers\": [ \"::unAuthorsedAnalyzer\" ] } }");
auto viewCreateJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"id\": 43, \"type\": \"arangosearch\" }");
auto* analyzers = arangodb::application_features::ApplicationServer::lookupFeature<
arangodb::iresearch::IResearchAnalyzerFeature
>();
REQUIRE((nullptr != analyzers));
auto* dbFeature = arangodb::application_features::ApplicationServer::lookupFeature<
arangodb::DatabaseFeature
>("Database");
TRI_vocbase_t* vocbase;
REQUIRE((TRI_ERROR_NO_ERROR == dbFeature->createDatabase(1, "testVocbase", vocbase))); // required for IResearchAnalyzerFeature::emplace(...)
REQUIRE((nullptr != vocbase));
auto dropDB = irs::make_finally([dbFeature]()->void { dbFeature->dropDatabase("testVocbase", true, true); });
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
REQUIRE((analyzers->emplace(result, arangodb::StaticStrings::SystemDatabase + "::unAuthorsedAnalyzer", "identity", irs::string_ref::NIL).ok()));
REQUIRE((false == !result.first));
auto logicalCollection = vocbase->createCollection(collectionJson->slice());
REQUIRE((nullptr != logicalCollection));
auto logicalView = vocbase->createView(viewCreateJson->slice());
REQUIRE((false == !logicalView));
// not authorized
{
struct ExecContext: public arangodb::ExecContext {
ExecContext(): arangodb::ExecContext(arangodb::ExecContext::Type::Default, "", "", arangodb::auth::Level::NONE, arangodb::auth::Level::NONE) {}
} execContext;
arangodb::ExecContextScope execContextScope(&execContext);
auto* authFeature = arangodb::AuthenticationFeature::instance();
auto* userManager = authFeature->userManager();
arangodb::aql::QueryRegistry queryRegistry(0); // required for UserManager::loadFromDB()
userManager->setQueryRegistry(&queryRegistry);
auto resetUserManager = std::shared_ptr<arangodb::auth::UserManager>(userManager, [](arangodb::auth::UserManager* ptr)->void { ptr->removeAllUsers(); });
std::unordered_set<TRI_voc_cid_t> modified;
CHECK((0 == logicalCollection->getIndexes().size()));
CHECK((false == arangodb::iresearch::IResearchLinkHelper::updateLinks(modified, *logicalView, linkUpdateJson->slice()).ok()));
CHECK((0 == logicalCollection->getIndexes().size()));
}
// authorzed
{
std::unordered_set<TRI_voc_cid_t> modified;
CHECK((0 == logicalCollection->getIndexes().size()));
CHECK((true == arangodb::iresearch::IResearchLinkHelper::updateLinks(modified, *logicalView, linkUpdateJson->slice()).ok()));
CHECK((1 == logicalCollection->getIndexes().size()));
}
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate tests
////////////////////////////////////////////////////////////////////////////////

View File

@ -63,11 +63,12 @@
namespace {
struct TestAttribute: public irs::attribute {
struct TestAttributeZ: public irs::attribute {
DECLARE_ATTRIBUTE_TYPE();
};
DEFINE_ATTRIBUTE_TYPE(TestAttribute);
DEFINE_ATTRIBUTE_TYPE(TestAttributeZ);
REGISTER_ATTRIBUTE(TestAttributeZ);
class EmptyAnalyzer: public irs::analysis::analyzer {
public:
@ -82,7 +83,7 @@ public:
private:
irs::attribute_view _attrs;
TestAttribute _attr;
TestAttributeZ _attr;
};
DEFINE_ANALYZER_TYPE_NAMED(EmptyAnalyzer, "empty");
@ -153,9 +154,10 @@ struct IResearchLinkMetaSetup {
auto* analyzers = arangodb::application_features::ApplicationServer::lookupFeature<
arangodb::iresearch::IResearchAnalyzerFeature
>();
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
analyzers->emplace("empty", "empty", "en", irs::flags{ TestAttribute::type() }); // cache the 'empty' analyzer
analyzers->emplace("testVocbase::empty", "empty", "de", irs::flags{ TestAttribute::type() }); // cache the 'empty' analyzer for 'testVocbase'
analyzers->emplace(result, "empty", "empty", "en", irs::flags{ TestAttributeZ::type() }); // cache the 'empty' analyzer
analyzers->emplace(result, "testVocbase::empty", "empty", "de", irs::flags{ TestAttributeZ::type() }); // cache the 'empty' analyzer for 'testVocbase'
// suppress log messages since tests check error conditions
arangodb::LogTopic::setLogLevel(arangodb::iresearch::TOPIC.name(), arangodb::LogLevel::FATAL);
@ -228,7 +230,7 @@ SECTION("test_inheritDefaults") {
defaults._fields["abc"]->_fields["xyz"] = arangodb::iresearch::IResearchLinkMeta();
auto json = arangodb::velocypack::Parser::fromJson("{}");
CHECK(true == meta.init(json->slice(), tmpString, defaults));
CHECK(true == meta.init(json->slice(), tmpString, nullptr, defaults));
CHECK(1U == meta._fields.size());
for (auto& field: meta._fields) {
@ -290,7 +292,7 @@ SECTION("test_readDefaults") {
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
arangodb::iresearch::IResearchLinkMeta meta;
std::string tmpString;
CHECK((true == meta.init(json->slice(), tmpString, arangodb::iresearch::IResearchLinkMeta::DEFAULT(), &vocbase)));
CHECK((true == meta.init(json->slice(), tmpString, &vocbase)));
CHECK((true == meta._fields.empty()));
CHECK((false == meta._includeAllFields));
CHECK((false == meta._trackListPositions));
@ -361,7 +363,7 @@ SECTION("test_readCustomizedValues") {
CHECK(1U == actual._analyzers.size());
CHECK((*(actual._analyzers.begin())));
CHECK(("empty" == (*(actual._analyzers.begin()))->name()));
CHECK((irs::flags({TestAttribute::type()}) == (*(actual._analyzers.begin()))->features()));
CHECK((irs::flags({TestAttributeZ::type()}) == (*(actual._analyzers.begin()))->features()));
CHECK(false == !actual._analyzers.begin()->get());
} else if ("some" == fieldOverride.key()) {
CHECK(true == actual._fields.empty()); // not inherited
@ -372,7 +374,7 @@ SECTION("test_readCustomizedValues") {
auto itr = actual._analyzers.begin();
CHECK((*itr));
CHECK(("empty" == (*itr)->name()));
CHECK((irs::flags({TestAttribute::type()}) == (*itr)->features()));
CHECK((irs::flags({TestAttributeZ::type()}) == (*itr)->features()));
CHECK(false == !itr->get());
++itr;
CHECK((*itr));
@ -387,7 +389,7 @@ SECTION("test_readCustomizedValues") {
auto itr = actual._analyzers.begin();
CHECK((*itr));
CHECK(("empty" == (*itr)->name()));
CHECK((irs::flags({TestAttribute::type()}) == (*itr)->features()));
CHECK((irs::flags({TestAttributeZ::type()}) == (*itr)->features()));
CHECK(false == !itr->get());
++itr;
CHECK((*itr));
@ -406,7 +408,7 @@ SECTION("test_readCustomizedValues") {
auto itr = meta._analyzers.begin();
CHECK((*itr));
CHECK(("empty" == (*itr)->name()));
CHECK((irs::flags({TestAttribute::type()}) == (*itr)->features()));
CHECK((irs::flags({TestAttributeZ::type()}) == (*itr)->features()));
CHECK(false == !itr->get());
++itr;
CHECK((*itr));
@ -423,7 +425,7 @@ SECTION("test_readCustomizedValues") {
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
arangodb::iresearch::IResearchLinkMeta meta;
std::string tmpString;
CHECK((true == meta.init(json->slice(), tmpString, arangodb::iresearch::IResearchLinkMeta::DEFAULT(), &vocbase)));
CHECK((true == meta.init(json->slice(), tmpString, &vocbase)));
CHECK((3U == meta._fields.size()));
for (auto& field: meta._fields) {
@ -453,8 +455,10 @@ SECTION("test_readCustomizedValues") {
CHECK((arangodb::iresearch::ValueStorage::FULL == actual._storeValues));
CHECK((1U == actual._analyzers.size()));
CHECK((*(actual._analyzers.begin())));
/* FIXME TODO uncomment once emplace(...) and all tests are updated
CHECK(("testVocbase::empty" == (*(actual._analyzers.begin()))->name()));
CHECK((irs::flags({TestAttribute::type()}) == (*(actual._analyzers.begin()))->features()));
*/
CHECK((irs::flags({TestAttributeZ::type()}) == (*(actual._analyzers.begin()))->features()));
CHECK((false == !actual._analyzers.begin()->get()));
} else if ("some" == fieldOverride.key()) {
CHECK((true == actual._fields.empty())); // not inherited
@ -464,8 +468,10 @@ SECTION("test_readCustomizedValues") {
CHECK((2U == actual._analyzers.size()));
auto itr = actual._analyzers.begin();
CHECK((*itr));
/* FIXME TODO uncomment once emplace(...) and all tests are updated
CHECK(("testVocbase::empty" == (*itr)->name()));
CHECK((irs::flags({TestAttribute::type()}) == (*itr)->features()));
*/
CHECK((irs::flags({TestAttributeZ::type()}) == (*itr)->features()));
CHECK((false == !itr->get()));
++itr;
CHECK((*itr));
@ -479,8 +485,10 @@ SECTION("test_readCustomizedValues") {
CHECK((arangodb::iresearch::ValueStorage::FULL == actual._storeValues));
auto itr = actual._analyzers.begin();
CHECK((*itr));
/* FIXME TODO uncomment once emplace(...) and all tests are updated
CHECK(("testVocbase::empty" == (*itr)->name()));
CHECK((irs::flags({TestAttribute::type()}) == (*itr)->features()));
*/
CHECK((irs::flags({TestAttributeZ::type()}) == (*itr)->features()));
CHECK((false == !itr->get()));
++itr;
CHECK((*itr));
@ -498,8 +506,10 @@ SECTION("test_readCustomizedValues") {
CHECK((arangodb::iresearch::ValueStorage::FULL == meta._storeValues));
auto itr = meta._analyzers.begin();
CHECK((*itr));
/* FIXME TODO uncomment once emplace(...) and all tests are updated
CHECK(("testVocbase::empty" == (*itr)->name()));
CHECK((irs::flags({TestAttribute::type()}) == (*itr)->features()));
*/
CHECK((irs::flags({TestAttributeZ::type()}) == (*itr)->features()));
CHECK((false == !itr->get()));
++itr;
CHECK((*itr));
@ -510,14 +520,14 @@ SECTION("test_readCustomizedValues") {
}
SECTION("test_writeDefaults") {
// without active vobcase
// without active vobcase (not fullAnalyzerDefinition)
{
arangodb::iresearch::IResearchLinkMeta meta;
arangodb::velocypack::Builder builder;
arangodb::velocypack::Slice tmpSlice;
builder.openObject();
CHECK((true == meta.json(builder)));
CHECK((true == meta.json(builder, false)));
builder.close();
auto slice = builder.slice();
@ -541,7 +551,40 @@ SECTION("test_writeDefaults") {
));
}
// with active vocbase
// without active vobcase (with fullAnalyzerDefinition)
{
arangodb::iresearch::IResearchLinkMeta meta;
arangodb::velocypack::Builder builder;
arangodb::velocypack::Slice tmpSlice;
builder.openObject();
CHECK((true == meta.json(builder, true)));
builder.close();
auto slice = builder.slice();
CHECK((5U == slice.length()));
tmpSlice = slice.get("fields");
CHECK((true == tmpSlice.isObject() && 0 == tmpSlice.length()));
tmpSlice = slice.get("includeAllFields");
CHECK((true == tmpSlice.isBool() && false == tmpSlice.getBool()));
tmpSlice = slice.get("trackListPositions");
CHECK((true == tmpSlice.isBool() && false == tmpSlice.getBool()));
tmpSlice = slice.get("storeValues");
CHECK((true == tmpSlice.isString() && std::string("none") == tmpSlice.copyString()));
tmpSlice = slice.get("analyzers");
CHECK((
true == tmpSlice.isArray()
&& 1 == tmpSlice.length()
&& tmpSlice.at(0).isObject()
&& tmpSlice.at(0).get("name").isString() && std::string("identity") == tmpSlice.at(0).get("name").copyString()
&& tmpSlice.at(0).get("type").isString() && std::string("identity") == tmpSlice.at(0).get("type").copyString()
&& tmpSlice.at(0).get("properties").isString() && std::string("") == tmpSlice.at(0).get("properties").copyString()
&& tmpSlice.at(0).get("features").isArray() && 2 == tmpSlice.at(0).get("features").length() // frequency+norm
));
}
// with active vocbase (not fullAnalyzerDefinition)
{
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
arangodb::iresearch::IResearchLinkMeta meta;
@ -549,7 +592,7 @@ SECTION("test_writeDefaults") {
arangodb::velocypack::Slice tmpSlice;
builder.openObject();
CHECK((true == meta.json(builder, nullptr, &vocbase)));
CHECK((true == meta.json(builder, false, nullptr, &vocbase)));
builder.close();
auto slice = builder.slice();
@ -572,21 +615,55 @@ SECTION("test_writeDefaults") {
std::string("identity") == tmpSlice.at(0).copyString()
));
}
// with active vocbase (with fullAnalyzerDefinition)
{
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
arangodb::iresearch::IResearchLinkMeta meta;
arangodb::velocypack::Builder builder;
arangodb::velocypack::Slice tmpSlice;
builder.openObject();
CHECK((true == meta.json(builder, true, nullptr, &vocbase)));
builder.close();
auto slice = builder.slice();
CHECK((5U == slice.length()));
tmpSlice = slice.get("fields");
CHECK((true == tmpSlice.isObject() && 0 == tmpSlice.length()));
tmpSlice = slice.get("includeAllFields");
CHECK((true == tmpSlice.isBool() && false == tmpSlice.getBool()));
tmpSlice = slice.get("trackListPositions");
CHECK((true == tmpSlice.isBool() && false == tmpSlice.getBool()));
tmpSlice = slice.get("storeValues");
CHECK((true == tmpSlice.isString() && std::string("none") == tmpSlice.copyString()));
tmpSlice = slice.get("analyzers");
CHECK((
true == tmpSlice.isArray()
&& 1 == tmpSlice.length()
&& tmpSlice.at(0).isObject()
&& tmpSlice.at(0).get("name").isString() && std::string("identity") == tmpSlice.at(0).get("name").copyString()
&& tmpSlice.at(0).get("type").isString() && std::string("identity") == tmpSlice.at(0).get("type").copyString()
&& tmpSlice.at(0).get("properties").isString() && std::string("") == tmpSlice.at(0).get("properties").copyString()
&& tmpSlice.at(0).get("features").isArray() && 2 == tmpSlice.at(0).get("features").length() // frequency+norm
));
}
}
SECTION("test_writeCustomizedValues") {
arangodb::iresearch::IResearchAnalyzerFeature analyzers(s.server);
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult emplaceResult;
arangodb::iresearch::IResearchLinkMeta meta;
analyzers.emplace("identity", "identity", "");
analyzers.emplace("empty", "empty", "en");
analyzers.emplace(emplaceResult, "empty", "empty", "en", { irs::attribute::type_id::get("position") });
meta._includeAllFields = true;
meta._trackListPositions = true;
meta._storeValues = arangodb::iresearch::ValueStorage::FULL;
meta._analyzers.clear();
meta._analyzers.emplace_back(analyzers.ensure("identity"));
meta._analyzers.emplace_back(analyzers.ensure("empty"));
meta._analyzers.emplace_back(analyzers.get("identity"));
meta._analyzers.emplace_back(analyzers.get("empty"));
meta._fields["a"] = meta; // copy from meta
meta._fields["a"]->_fields.clear(); // do not inherit fields to match jSon inheritance
meta._fields["b"] = meta; // copy from meta
@ -609,13 +686,13 @@ SECTION("test_writeCustomizedValues") {
overrideAll._trackListPositions = false;
overrideAll._storeValues = arangodb::iresearch::ValueStorage::NONE;
overrideAll._analyzers.clear();
overrideAll._analyzers.emplace_back(analyzers.ensure("empty"));
overrideAll._analyzers.emplace_back(analyzers.get("empty"));
overrideSome._fields.clear(); // do not inherit fields to match jSon inheritance
overrideSome._trackListPositions = false;
overrideSome._storeValues = arangodb::iresearch::ValueStorage::ID;
overrideNone._fields.clear(); // do not inherit fields to match jSon inheritance
// without active vobcase
// without active vobcase (not fullAnalyzerDefinition)
{
std::unordered_set<std::string> expectedFields = { "a", "b", "c" };
std::unordered_set<std::string> expectedOverrides = { "default", "all", "some", "none" };
@ -624,7 +701,7 @@ SECTION("test_writeCustomizedValues") {
arangodb::velocypack::Slice tmpSlice;
builder.openObject();
CHECK((true == meta.json(builder)));
CHECK((true == meta.json(builder, false)));
builder.close();
auto slice = builder.slice();
@ -721,7 +798,128 @@ SECTION("test_writeCustomizedValues") {
CHECK((true == expectedAnalyzers.empty()));
}
// with active vocbase
// without active vobcase (with fullAnalyzerDefinition)
{
std::unordered_set<std::string> expectedFields = { "a", "b", "c" };
std::unordered_set<std::string> expectedOverrides = { "default", "all", "some", "none" };
std::set<std::pair<std::string, std::string>> expectedAnalyzers = {
{ "empty", "en" },
{ "identity", "" },
};
arangodb::velocypack::Builder builder;
arangodb::velocypack::Slice tmpSlice;
builder.openObject();
CHECK((true == meta.json(builder, true)));
builder.close();
auto slice = builder.slice();
CHECK((5U == slice.length()));
tmpSlice = slice.get("fields");
CHECK((true == tmpSlice.isObject() && 3 == tmpSlice.length()));
for (arangodb::velocypack::ObjectIterator itr(tmpSlice); itr.valid(); ++itr) {
auto key = itr.key();
auto value = itr.value();
CHECK((true == key.isString() && 1 == expectedFields.erase(key.copyString())));
CHECK((true == value.isObject()));
if (!value.hasKey("fields")) {
continue;
}
tmpSlice = value.get("fields");
for (arangodb::velocypack::ObjectIterator overrideItr(tmpSlice); overrideItr.valid(); ++overrideItr) {
auto fieldOverride = overrideItr.key();
auto sliceOverride = overrideItr.value();
CHECK((true == fieldOverride.isString() && sliceOverride.isObject()));
CHECK((1U == expectedOverrides.erase(fieldOverride.copyString())));
if ("default" == fieldOverride.copyString()) {
CHECK((4U == sliceOverride.length()));
tmpSlice = sliceOverride.get("includeAllFields");
CHECK((true == (false == tmpSlice.getBool())));
tmpSlice = sliceOverride.get("trackListPositions");
CHECK((true == (false == tmpSlice.getBool())));
tmpSlice = sliceOverride.get("storeValues");
CHECK((true == tmpSlice.isString() && std::string("none") == tmpSlice.copyString()));
tmpSlice = sliceOverride.get("analyzers");
CHECK((
true == tmpSlice.isArray()
&& 1 == tmpSlice.length()
&& tmpSlice.at(0).isObject()
&& tmpSlice.at(0).get("name").isString() && std::string("identity") == tmpSlice.at(0).get("name").copyString()
&& tmpSlice.at(0).get("type").isString() && std::string("identity") == tmpSlice.at(0).get("type").copyString()
&& tmpSlice.at(0).get("properties").isString() && std::string("") == tmpSlice.at(0).get("properties").copyString()
&& tmpSlice.at(0).get("features").isArray() && 2 == tmpSlice.at(0).get("features").length() // frequency+norm
));
} else if ("all" == fieldOverride.copyString()) {
std::unordered_set<std::string> expectedFields = { "x", "y" };
CHECK((5U == sliceOverride.length()));
tmpSlice = sliceOverride.get("fields");
CHECK((true == tmpSlice.isObject() && 2 == tmpSlice.length()));
for (arangodb::velocypack::ObjectIterator overrideFieldItr(tmpSlice); overrideFieldItr.valid(); ++overrideFieldItr) {
CHECK((true == overrideFieldItr.key().isString() && 1 == expectedFields.erase(overrideFieldItr.key().copyString())));
}
CHECK((true == expectedFields.empty()));
tmpSlice = sliceOverride.get("includeAllFields");
CHECK((true == tmpSlice.isBool() && false == tmpSlice.getBool()));
tmpSlice = sliceOverride.get("trackListPositions");
CHECK((true == tmpSlice.isBool() && false == tmpSlice.getBool()));
tmpSlice = sliceOverride.get("storeValues");
CHECK((true == tmpSlice.isString() && std::string("none") == tmpSlice.copyString()));
tmpSlice = sliceOverride.get("analyzers");
CHECK((
true == tmpSlice.isArray()
&& 1 == tmpSlice.length()
&& tmpSlice.at(0).isObject()
&& tmpSlice.at(0).get("name").isString() && std::string("empty") == tmpSlice.at(0).get("name").copyString()
&& tmpSlice.at(0).get("type").isString() && std::string("empty") == tmpSlice.at(0).get("type").copyString()
&& tmpSlice.at(0).get("properties").isString() && std::string("en") == tmpSlice.at(0).get("properties").copyString()
&& tmpSlice.at(0).get("features").isArray() && 1 == tmpSlice.at(0).get("features").length()
&& tmpSlice.at(0).get("features").at(0).isString() && std::string("position") == tmpSlice.at(0).get("features").at(0).copyString()
));
} else if ("some" == fieldOverride.copyString()) {
CHECK((2U == sliceOverride.length()));
tmpSlice = sliceOverride.get("trackListPositions");
CHECK((true == tmpSlice.isBool() && false == tmpSlice.getBool()));
tmpSlice = sliceOverride.get("storeValues");
CHECK((true == tmpSlice.isString() && std::string("id") == tmpSlice.copyString()));
} else if ("none" == fieldOverride.copyString()) {
CHECK((0U == sliceOverride.length()));
}
}
}
CHECK((true == expectedOverrides.empty()));
CHECK((true == expectedFields.empty()));
tmpSlice = slice.get("includeAllFields");
CHECK((true == tmpSlice.isBool() && true == tmpSlice.getBool()));
tmpSlice = slice.get("trackListPositions");
CHECK((true == tmpSlice.isBool() && true == tmpSlice.getBool()));
tmpSlice = slice.get("storeValues");
CHECK((true == tmpSlice.isString() && std::string("full") == tmpSlice.copyString()));
tmpSlice = slice.get("analyzers");
CHECK((true == tmpSlice.isArray() && 2 == tmpSlice.length()));
for (arangodb::velocypack::ArrayIterator analyzersItr(tmpSlice); analyzersItr.valid(); ++analyzersItr) {
auto value = *analyzersItr;
CHECK((
true == value.isObject()
&& value.hasKey("name") && value.get("name").isString()
&& value.hasKey("type") && value.get("type").isString()
&& value.hasKey("properties") && value.get("properties").isString()
&& value.hasKey("features") && value.get("features").isArray() && (1 == value.get("features").length() || 2 == value.get("features").length()) // empty/identity 1/2
&& 1 == expectedAnalyzers.erase(std::make_pair(value.get("name").copyString(), value.get("properties").copyString()))
));
}
CHECK((true == expectedAnalyzers.empty()));
}
// with active vocbase (no fullAnalyzerDefinition)
{
std::unordered_set<std::string> expectedFields = { "a", "b", "c" };
std::unordered_set<std::string> expectedOverrides = { "default", "all", "some", "none" };
@ -731,7 +929,7 @@ SECTION("test_writeCustomizedValues") {
arangodb::velocypack::Slice tmpSlice;
builder.openObject();
CHECK((true == meta.json(builder, nullptr, &vocbase)));
CHECK((true == meta.json(builder, false, nullptr, &vocbase)));
builder.close();
auto slice = builder.slice();
@ -827,6 +1025,128 @@ SECTION("test_writeCustomizedValues") {
CHECK((true == expectedAnalyzers.empty()));
}
// with active vocbase (with fullAnalyzerDefinition)
{
std::unordered_set<std::string> expectedFields = { "a", "b", "c" };
std::unordered_set<std::string> expectedOverrides = { "default", "all", "some", "none" };
std::set<std::pair<std::string, std::string>> expectedAnalyzers = {
{ "empty", "en" },
{ "identity", "" },
};
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
arangodb::velocypack::Builder builder;
arangodb::velocypack::Slice tmpSlice;
builder.openObject();
CHECK((true == meta.json(builder, true, nullptr, &vocbase)));
builder.close();
auto slice = builder.slice();
CHECK((5U == slice.length()));
tmpSlice = slice.get("fields");
CHECK((true == tmpSlice.isObject() && 3 == tmpSlice.length()));
for (arangodb::velocypack::ObjectIterator itr(tmpSlice); itr.valid(); ++itr) {
auto key = itr.key();
auto value = itr.value();
CHECK((true == key.isString() && 1 == expectedFields.erase(key.copyString())));
CHECK((true == value.isObject()));
if (!value.hasKey("fields")) {
continue;
}
tmpSlice = value.get("fields");
for (arangodb::velocypack::ObjectIterator overrideItr(tmpSlice); overrideItr.valid(); ++overrideItr) {
auto fieldOverride = overrideItr.key();
auto sliceOverride = overrideItr.value();
CHECK((true == fieldOverride.isString() && sliceOverride.isObject()));
CHECK((1U == expectedOverrides.erase(fieldOverride.copyString())));
if ("default" == fieldOverride.copyString()) {
CHECK((4U == sliceOverride.length()));
tmpSlice = sliceOverride.get("includeAllFields");
CHECK((true == (false == tmpSlice.getBool())));
tmpSlice = sliceOverride.get("trackListPositions");
CHECK((true == (false == tmpSlice.getBool())));
tmpSlice = sliceOverride.get("storeValues");
CHECK((true == tmpSlice.isString() && std::string("none") == tmpSlice.copyString()));
tmpSlice = sliceOverride.get("analyzers");
CHECK((
true == tmpSlice.isArray()
&& 1 == tmpSlice.length()
&& tmpSlice.at(0).isObject()
&& tmpSlice.at(0).get("name").isString() && std::string("identity") == tmpSlice.at(0).get("name").copyString()
&& tmpSlice.at(0).get("type").isString() && std::string("identity") == tmpSlice.at(0).get("type").copyString()
&& tmpSlice.at(0).get("properties").isString() && std::string("") == tmpSlice.at(0).get("properties").copyString()
&& tmpSlice.at(0).get("features").isArray() && 2 == tmpSlice.at(0).get("features").length() // frequency+norm
));
} else if ("all" == fieldOverride.copyString()) {
std::unordered_set<std::string> expectedFields = { "x", "y" };
CHECK((5U == sliceOverride.length()));
tmpSlice = sliceOverride.get("fields");
CHECK((true == tmpSlice.isObject() && 2 == tmpSlice.length()));
for (arangodb::velocypack::ObjectIterator overrideFieldItr(tmpSlice); overrideFieldItr.valid(); ++overrideFieldItr) {
CHECK((true == overrideFieldItr.key().isString() && 1 == expectedFields.erase(overrideFieldItr.key().copyString())));
}
CHECK((true == expectedFields.empty()));
tmpSlice = sliceOverride.get("includeAllFields");
CHECK((true == tmpSlice.isBool() && false == tmpSlice.getBool()));
tmpSlice = sliceOverride.get("trackListPositions");
CHECK((true == tmpSlice.isBool() && false == tmpSlice.getBool()));
tmpSlice = sliceOverride.get("storeValues");
CHECK((true == tmpSlice.isString() && std::string("none") == tmpSlice.copyString()));
tmpSlice = sliceOverride.get("analyzers");
CHECK((
true == tmpSlice.isArray()
&& 1 == tmpSlice.length()
&& tmpSlice.at(0).isObject()
&& tmpSlice.at(0).get("name").isString() && std::string("empty") == tmpSlice.at(0).get("name").copyString()
&& tmpSlice.at(0).get("type").isString() && std::string("empty") == tmpSlice.at(0).get("type").copyString()
&& tmpSlice.at(0).get("properties").isString() && std::string("en") == tmpSlice.at(0).get("properties").copyString()
&& tmpSlice.at(0).get("features").isArray() && 1 == tmpSlice.at(0).get("features").length()
&& tmpSlice.at(0).get("features").at(0).isString() && std::string("position") == tmpSlice.at(0).get("features").at(0).copyString()
));
} else if ("some" == fieldOverride.copyString()) {
CHECK((2U == sliceOverride.length()));
tmpSlice = sliceOverride.get("trackListPositions");
CHECK((true == tmpSlice.isBool() && false == tmpSlice.getBool()));
tmpSlice = sliceOverride.get("storeValues");
CHECK((true == tmpSlice.isString() && std::string("id") == tmpSlice.copyString()));
} else if ("none" == fieldOverride.copyString()) {
CHECK((0U == sliceOverride.length()));
}
}
}
CHECK((true == expectedOverrides.empty()));
CHECK((true == expectedFields.empty()));
tmpSlice = slice.get("includeAllFields");
CHECK((true == tmpSlice.isBool() && true == tmpSlice.getBool()));
tmpSlice = slice.get("trackListPositions");
CHECK((true == tmpSlice.isBool() && true == tmpSlice.getBool()));
tmpSlice = slice.get("storeValues");
CHECK((true == tmpSlice.isString() && std::string("full") == tmpSlice.copyString()));
tmpSlice = slice.get("analyzers");
CHECK((true == tmpSlice.isArray() && 2 == tmpSlice.length()));
for (arangodb::velocypack::ArrayIterator analyzersItr(tmpSlice); analyzersItr.valid(); ++analyzersItr) {
auto value = *analyzersItr;
CHECK((
true == value.isObject()
&& value.hasKey("name") && value.get("name").isString()
&& value.hasKey("type") && value.get("type").isString()
&& value.hasKey("properties") && value.get("properties").isString()
&& value.hasKey("features") && value.get("features").isArray() && (1 == value.get("features").length() || 2 == value.get("features").length()) // empty/identity 1/2
&& 1 == expectedAnalyzers.erase(std::make_pair(value.get("name").copyString(), value.get("properties").copyString()))
));
}
CHECK((true == expectedAnalyzers.empty()));
}
}
SECTION("test_readMaskAll") {
@ -841,7 +1161,7 @@ SECTION("test_readMaskAll") {
\"storeValues\": \"full\", \
\"analyzers\": [] \
}");
CHECK(true == meta.init(json->slice(), tmpString, arangodb::iresearch::IResearchLinkMeta::DEFAULT(), nullptr, &mask));
CHECK(true == meta.init(json->slice(), tmpString, nullptr, arangodb::iresearch::IResearchLinkMeta::DEFAULT(), &mask));
CHECK(true == mask._fields);
CHECK(true == mask._includeAllFields);
CHECK(true == mask._trackListPositions);
@ -855,7 +1175,7 @@ SECTION("test_readMaskNone") {
std::string tmpString;
auto json = arangodb::velocypack::Parser::fromJson("{}");
CHECK(true == meta.init(json->slice(), tmpString, arangodb::iresearch::IResearchLinkMeta::DEFAULT(), nullptr, &mask));
CHECK(true == meta.init(json->slice(), tmpString, nullptr, arangodb::iresearch::IResearchLinkMeta::DEFAULT(), &mask));
CHECK(false == mask._fields);
CHECK(false == mask._includeAllFields);
CHECK(false == mask._trackListPositions);
@ -864,36 +1184,308 @@ SECTION("test_readMaskNone") {
}
SECTION("test_writeMaskAll") {
arangodb::iresearch::IResearchLinkMeta meta;
arangodb::iresearch::IResearchLinkMeta::Mask mask(true);
arangodb::velocypack::Builder builder;
// not fullAnalyzerDefinition
{
arangodb::iresearch::IResearchLinkMeta meta;
arangodb::iresearch::IResearchLinkMeta::Mask mask(true);
arangodb::velocypack::Builder builder;
builder.openObject();
CHECK((true == meta.json(builder, nullptr, nullptr, &mask)));
builder.close();
builder.openObject();
CHECK((true == meta.json(builder, false, nullptr, nullptr, &mask)));
builder.close();
auto slice = builder.slice();
auto slice = builder.slice();
CHECK((5U == slice.length()));
CHECK(true == slice.hasKey("fields"));
CHECK(true == slice.hasKey("includeAllFields"));
CHECK(true == slice.hasKey("trackListPositions"));
CHECK(true == slice.hasKey("storeValues"));
CHECK(true == slice.hasKey("analyzers"));
CHECK((5U == slice.length()));
CHECK(true == slice.hasKey("fields"));
CHECK(true == slice.hasKey("includeAllFields"));
CHECK(true == slice.hasKey("trackListPositions"));
CHECK(true == slice.hasKey("storeValues"));
CHECK(true == slice.hasKey("analyzers"));
}
// with fullAnalyzerDefinition
{
arangodb::iresearch::IResearchLinkMeta meta;
arangodb::iresearch::IResearchLinkMeta::Mask mask(true);
arangodb::velocypack::Builder builder;
builder.openObject();
CHECK((true == meta.json(builder, true, nullptr, nullptr, &mask)));
builder.close();
auto slice = builder.slice();
CHECK((5U == slice.length()));
CHECK(true == slice.hasKey("fields"));
CHECK(true == slice.hasKey("includeAllFields"));
CHECK(true == slice.hasKey("trackListPositions"));
CHECK(true == slice.hasKey("storeValues"));
CHECK(true == slice.hasKey("analyzers"));
}
}
SECTION("test_writeMaskNone") {
arangodb::iresearch::IResearchLinkMeta meta;
arangodb::iresearch::IResearchLinkMeta::Mask mask(false);
arangodb::velocypack::Builder builder;
// not fullAnalyzerDefinition
{
arangodb::iresearch::IResearchLinkMeta meta;
arangodb::iresearch::IResearchLinkMeta::Mask mask(false);
arangodb::velocypack::Builder builder;
builder.openObject();
CHECK((true == meta.json(builder, nullptr, nullptr, &mask)));
builder.close();
builder.openObject();
CHECK((true == meta.json(builder, false, nullptr, nullptr, &mask)));
builder.close();
auto slice = builder.slice();
auto slice = builder.slice();
CHECK(0U == slice.length());
CHECK(0U == slice.length());
}
// with fullAnalyzerDefinition
{
arangodb::iresearch::IResearchLinkMeta meta;
arangodb::iresearch::IResearchLinkMeta::Mask mask(false);
arangodb::velocypack::Builder builder;
builder.openObject();
CHECK((true == meta.json(builder, true, nullptr, nullptr, &mask)));
builder.close();
auto slice = builder.slice();
CHECK(0U == slice.length());
}
}
SECTION("test_readAnalyzerDefinitions") {
// missing analyzer (name only)
{
auto json = arangodb::velocypack::Parser::fromJson("{ \
\"analyzers\": [ \"empty1\" ] \
}");
arangodb::iresearch::IResearchLinkMeta meta;
std::string errorField;
CHECK((false == meta.init(json->slice(), errorField)));
CHECK((std::string("analyzers=>empty1") == errorField));
}
// missing analyzer (name only) inRecovery
{
auto json = arangodb::velocypack::Parser::fromJson("{ \
\"analyzers\": [ \"empty1\" ] \
}");
auto before = StorageEngineMock::inRecoveryResult;
StorageEngineMock::inRecoveryResult = true;
auto restore = irs::make_finally([&before]()->void { StorageEngineMock::inRecoveryResult = before; });
arangodb::iresearch::IResearchLinkMeta meta;
std::string errorField;
CHECK((false == meta.init(json->slice(), errorField)));
CHECK((std::string("analyzers=>empty1") == errorField));
}
// missing analyzer (full) no name (fail) required
{
auto json = arangodb::velocypack::Parser::fromJson("{ \
\"analyzers\": [ { \"type\": \"empty\", \"properties\": \"ru\", \"features\": [ \"frequency\" ] } ] \
}");
arangodb::iresearch::IResearchLinkMeta meta;
std::string errorField;
CHECK((false == meta.init(json->slice(), errorField)));
CHECK((std::string("analyzers=>[0]=>name") == errorField));
}
// missing analyzer (full) no type (fail) required
{
auto json = arangodb::velocypack::Parser::fromJson("{ \
\"analyzers\": [ { \"name\": \"missing0\", \"properties\": \"ru\", \"features\": [ \"frequency\" ] } ] \
}");
arangodb::iresearch::IResearchLinkMeta meta;
std::string errorField;
CHECK((false == meta.init(json->slice(), errorField)));
CHECK((std::string("analyzers=>[0]=>type") == errorField));
}
// missing analyzer (full) analyzer creation not allowed (fail)
{
auto json = arangodb::velocypack::Parser::fromJson("{ \
\"analyzers\": [ { \"name\": \"missing0\", \"type\": \"empty\", \"properties\": \"ru\", \"features\": [ \"frequency\" ] } ] \
}");
arangodb::iresearch::IResearchLinkMeta meta;
std::string errorField;
CHECK((false == meta.init(json->slice(), errorField)));
CHECK((std::string("analyzers=>[0]") == errorField));
}
// missing analyzer (full) single-server
{
auto json = arangodb::velocypack::Parser::fromJson("{ \
\"analyzers\": [ { \"name\": \"missing0\", \"type\": \"empty\", \"properties\": \"ru\", \"features\": [ \"frequency\" ] } ] \
}");
arangodb::iresearch::IResearchLinkMeta meta;
std::string errorField;
CHECK((false == meta.init(json->slice(), errorField)));
CHECK((std::string("analyzers=>[0]") == errorField));
}
// missing analyzer (full) coordinator
{
auto before = arangodb::ServerState::instance()->getRole();
arangodb::ServerState::instance()->setRole(arangodb::ServerState::ROLE_COORDINATOR);
auto restore = irs::make_finally([&before]()->void { arangodb::ServerState::instance()->setRole(before); });
auto json = arangodb::velocypack::Parser::fromJson("{ \
\"analyzers\": [ { \"name\": \"missing1\", \"type\": \"empty\", \"properties\": \"ru\", \"features\": [ \"frequency\" ] } ] \
}");
arangodb::iresearch::IResearchLinkMeta meta;
std::string errorField;
CHECK((false == meta.init(json->slice(), errorField)));
CHECK((std::string("analyzers=>[0]") == errorField));
}
// missing analyzer (full) db-server
{
auto before = arangodb::ServerState::instance()->getRole();
arangodb::ServerState::instance()->setRole(arangodb::ServerState::ROLE_DBSERVER);
auto restore = irs::make_finally([&before]()->void { arangodb::ServerState::instance()->setRole(before); });
auto json = arangodb::velocypack::Parser::fromJson("{ \
\"analyzers\": [ { \"name\": \"missing2\", \"type\": \"empty\", \"properties\": \"ru\", \"features\": [ \"frequency\" ] } ] \
}");
arangodb::iresearch::IResearchLinkMeta meta;
std::string errorField;
CHECK((true == meta.init(json->slice(), errorField)));
CHECK((1 == meta._analyzers.size()));
CHECK((std::string("missing2") == meta._analyzers[0]->name()));
CHECK((std::string("empty") == meta._analyzers[0]->type()));
CHECK((std::string("ru") == meta._analyzers[0]->properties()));
CHECK((1 == meta._analyzers[0]->features().size()));
CHECK((true == meta._analyzers[0]->features().check(irs::frequency::type())));
}
// missing analyzer (full) inRecovery
{
auto json = arangodb::velocypack::Parser::fromJson("{ \
\"analyzers\": [ { \"name\": \"missing3\", \"type\": \"empty\", \"properties\": \"ru\", \"features\": [ \"frequency\" ] } ] \
}");
auto before = StorageEngineMock::inRecoveryResult;
StorageEngineMock::inRecoveryResult = true;
auto restore = irs::make_finally([&before]()->void { StorageEngineMock::inRecoveryResult = before; });
arangodb::iresearch::IResearchLinkMeta meta;
std::string errorField;
CHECK((false == meta.init(json->slice(), errorField)));
CHECK((std::string("analyzers=>[0]") == errorField));
}
// existing analyzer (name only)
{
auto json = arangodb::velocypack::Parser::fromJson("{ \
\"analyzers\": [ \"empty\" ] \
}");
arangodb::iresearch::IResearchLinkMeta meta;
std::string errorField;
CHECK((true == meta.init(json->slice(), errorField)));
CHECK((1 == meta._analyzers.size()));
CHECK((std::string("empty") == meta._analyzers[0]->name()));
CHECK((std::string("empty") == meta._analyzers[0]->type()));
CHECK((std::string("en") == meta._analyzers[0]->properties()));
CHECK((1 == meta._analyzers[0]->features().size()));
CHECK((true == meta._analyzers[0]->features().check(TestAttributeZ::type())));
}
// existing analyzer (name only) inRecovery
{
auto json = arangodb::velocypack::Parser::fromJson("{ \
\"analyzers\": [ \"empty\" ] \
}");
auto before = StorageEngineMock::inRecoveryResult;
StorageEngineMock::inRecoveryResult = true;
auto restore = irs::make_finally([&before]()->void { StorageEngineMock::inRecoveryResult = before; });
arangodb::iresearch::IResearchLinkMeta meta;
std::string errorField;
CHECK((true == meta.init(json->slice(), errorField)));
CHECK((1 == meta._analyzers.size()));
CHECK((std::string("empty") == meta._analyzers[0]->name()));
CHECK((std::string("empty") == meta._analyzers[0]->type()));
CHECK((std::string("en") == meta._analyzers[0]->properties()));
CHECK((1 == meta._analyzers[0]->features().size()));
CHECK((true == meta._analyzers[0]->features().check(TestAttributeZ::type())));
}
// existing analyzer (full) analyzer creation not allowed (passs)
{
auto json = arangodb::velocypack::Parser::fromJson("{ \
\"analyzers\": [ { \"name\": \"empty\", \"type\": \"empty\", \"properties\": \"en\", \"features\": [ \"TestAttributeZ\" ] } ] \
}");
arangodb::iresearch::IResearchLinkMeta meta;
std::string errorField;
CHECK((true == meta.init(json->slice(), errorField)));
CHECK((1 == meta._analyzers.size()));
CHECK((std::string("empty") == meta._analyzers[0]->name()));
CHECK((std::string("empty") == meta._analyzers[0]->type()));
CHECK((std::string("en") == meta._analyzers[0]->properties()));
CHECK((1 == meta._analyzers[0]->features().size()));
CHECK((true == meta._analyzers[0]->features().check(TestAttributeZ::type())));
}
// existing analyzer (full)
{
auto json = arangodb::velocypack::Parser::fromJson("{ \
\"analyzers\": [ { \"name\": \"empty\", \"type\": \"empty\", \"properties\": \"en\", \"features\": [ \"TestAttributeZ\" ] } ] \
}");
arangodb::iresearch::IResearchLinkMeta meta;
std::string errorField;
CHECK((true == meta.init(json->slice(), errorField)));
CHECK((1 == meta._analyzers.size()));
CHECK((std::string("empty") == meta._analyzers[0]->name()));
CHECK((std::string("empty") == meta._analyzers[0]->type()));
CHECK((std::string("en") == meta._analyzers[0]->properties()));
CHECK((1 == meta._analyzers[0]->features().size()));
CHECK((true == meta._analyzers[0]->features().check(TestAttributeZ::type())));
}
// existing analyzer (full) inRecovery
{
auto json = arangodb::velocypack::Parser::fromJson("{ \
\"analyzers\": [ { \"name\": \"empty\", \"type\": \"empty\", \"properties\": \"en\", \"features\": [ \"TestAttributeZ\" ] } ] \
}");
auto before = StorageEngineMock::inRecoveryResult;
StorageEngineMock::inRecoveryResult = true;
auto restore = irs::make_finally([&before]()->void { StorageEngineMock::inRecoveryResult = before; });
arangodb::iresearch::IResearchLinkMeta meta;
std::string errorField;
CHECK((true == meta.init(json->slice(), errorField)));
CHECK((1 == meta._analyzers.size()));
CHECK((std::string("empty") == meta._analyzers[0]->name()));
CHECK((std::string("empty") == meta._analyzers[0]->type()));
CHECK((std::string("en") == meta._analyzers[0]->properties()));
CHECK((1 == meta._analyzers[0]->features().size()));
CHECK((true == meta._analyzers[0]->features().check(TestAttributeZ::type())));
}
// existing analyzer (definition mismatch)
{
auto json = arangodb::velocypack::Parser::fromJson("{ \
\"analyzers\": [ { \"name\": \"empty\", \"type\": \"empty\", \"properties\": \"ru\", \"features\": [ \"TestAttributeZ\" ] } ] \
}");
arangodb::iresearch::IResearchLinkMeta meta;
std::string errorField;
CHECK((false == meta.init(json->slice(), errorField)));
CHECK((std::string("analyzers=>[0]") == errorField));
}
// existing analyzer (definition mismatch) inRecovery
{
auto json = arangodb::velocypack::Parser::fromJson("{ \
\"analyzers\": [ { \"name\": \"empty\", \"type\": \"empty\", \"properties\": \"ru\", \"features\": [ \"TestAttributeZ\" ] } ] \
}");
auto before = StorageEngineMock::inRecoveryResult;
StorageEngineMock::inRecoveryResult = true;
auto restore = irs::make_finally([&before]()->void { StorageEngineMock::inRecoveryResult = before; });
arangodb::iresearch::IResearchLinkMeta meta;
std::string errorField;
CHECK((false == meta.init(json->slice(), errorField)));
CHECK((std::string("analyzers=>[0]") == errorField));
}
}
////////////////////////////////////////////////////////////////////////////////

View File

@ -192,13 +192,6 @@ struct IResearchQuerySetup {
}
}
auto* analyzers = arangodb::application_features::ApplicationServer::lookupFeature<
arangodb::iresearch::IResearchAnalyzerFeature
>();
analyzers->emplace("test_analyzer", "TestAnalyzer", "abc"); // cache analyzer
analyzers->emplace("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
}
@ -241,4 +234,4 @@ TEST_CASE("IResearchQueryTest", "[iresearch][iresearch-query]") {
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------

View File

@ -129,13 +129,6 @@ struct IResearchQueryAggregateSetup {
}
}
auto* analyzers = arangodb::application_features::ApplicationServer::lookupFeature<
arangodb::iresearch::IResearchAnalyzerFeature
>();
analyzers->emplace("test_analyzer", "TestAnalyzer", "abc"); // cache analyzer
analyzers->emplace("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
}

View File

@ -133,9 +133,10 @@ struct IResearchQueryAndSetup {
auto* analyzers = arangodb::application_features::ApplicationServer::lookupFeature<
arangodb::iresearch::IResearchAnalyzerFeature
>();
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
analyzers->emplace("test_analyzer", "TestAnalyzer", "abc"); // cache analyzer
analyzers->emplace("test_csv_analyzer", "TestDelimAnalyzer", ","); // cache analyzer
analyzers->emplace(result, "test_analyzer", "TestAnalyzer", "abc"); // cache analyzer
analyzers->emplace(result, "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

View File

@ -131,13 +131,6 @@ struct IResearchQueryBooleanTermSetup {
}
}
auto* analyzers = arangodb::application_features::ApplicationServer::lookupFeature<
arangodb::iresearch::IResearchAnalyzerFeature
>();
analyzers->emplace("test_analyzer", "TestAnalyzer", "abc"); // cache analyzer
analyzers->emplace("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
}

View File

@ -133,8 +133,10 @@ struct IResearchQueryComplexBooleanSetup {
auto* analyzers = arangodb::application_features::ApplicationServer::lookupFeature<
arangodb::iresearch::IResearchAnalyzerFeature
>();
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
analyzers->emplace(
result,
"test_analyzer",
"TestAnalyzer",
"abc",
@ -142,6 +144,7 @@ struct IResearchQueryComplexBooleanSetup {
); // cache analyzer
analyzers->emplace(
result,
"test_csv_analyzer",
"TestDelimAnalyzer",
","

View File

@ -31,7 +31,9 @@
#endif
#include "Basics/files.h"
#include "Cluster/ClusterFeature.h"
#include "V8/v8-globals.h"
#include "V8Server/V8DealerFeature.h"
#include "VocBase/LogicalCollection.h"
#include "VocBase/LogicalView.h"
#include "VocBase/ManagedDocumentResult.h"
@ -98,6 +100,8 @@ struct IResearchQueryExistsSetup {
irs::logger::output_le(iresearch::logger::IRL_FATAL, stderr);
// setup required application features
features.emplace_back(new arangodb::SystemDatabaseFeature(server), true); // required by IResearchFilterFactory::extractAnalyzerFromArg(...)
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);
@ -118,6 +122,11 @@ struct IResearchQueryExistsSetup {
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);
}
@ -126,19 +135,18 @@ struct IResearchQueryExistsSetup {
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();
}
}
auto* analyzers = arangodb::application_features::ApplicationServer::lookupFeature<
arangodb::iresearch::IResearchAnalyzerFeature
>();
analyzers->emplace("test_analyzer", "TestAnalyzer", "abc"); // cache analyzer
analyzers->emplace("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
}
@ -150,7 +158,6 @@ struct IResearchQueryExistsSetup {
arangodb::LogTopic::setLogLevel(arangodb::Logger::FIXME.name(), arangodb::LogLevel::DEFAULT);
arangodb::LogTopic::setLogLevel(arangodb::Logger::AQL.name(), arangodb::LogLevel::DEFAULT);
arangodb::application_features::ApplicationServer::server = nullptr;
arangodb::EngineSelectorFeature::ENGINE = nullptr;
// destroy application features
for (auto& f : features) {
@ -164,6 +171,7 @@ struct IResearchQueryExistsSetup {
}
arangodb::LogTopic::setLogLevel(arangodb::Logger::AUTHENTICATION.name(), arangodb::LogLevel::DEFAULT);
arangodb::EngineSelectorFeature::ENGINE = nullptr;
}
}; // IResearchQuerySetup
@ -181,7 +189,21 @@ TEST_CASE("IResearchQueryTestExists", "[iresearch][iresearch-query]") {
IResearchQueryExistsSetup s;
UNUSED(s);
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
auto* analyzers = arangodb::application_features::ApplicationServer::lookupFeature<
arangodb::iresearch::IResearchAnalyzerFeature
>();
REQUIRE((nullptr != analyzers));
auto* dbFeature = arangodb::application_features::ApplicationServer::lookupFeature<
arangodb::DatabaseFeature
>("Database");
TRI_vocbase_t* vocbasePtr;
REQUIRE((TRI_ERROR_NO_ERROR == dbFeature->createDatabase(1, "testVocbase", vocbasePtr))); // required for IResearchAnalyzerFeature::emplace(...)
REQUIRE((nullptr != vocbasePtr));
auto& vocbase = *vocbasePtr;
//TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
REQUIRE((analyzers->emplace(result, "testVocbase::text_en", "text", "{ \"locale\": \"en.UTF-8\", \"ignored_words\": [ ] }", { irs::frequency::type(), irs::norm::type(), irs::position::type() }).ok()));
REQUIRE((false == !result.first));
std::vector<arangodb::velocypack::Builder> insertedDocs;
arangodb::LogicalView* view;

View File

@ -131,13 +131,6 @@ struct IResearchQueryInSetup {
}
}
auto* analyzers = arangodb::application_features::ApplicationServer::lookupFeature<
arangodb::iresearch::IResearchAnalyzerFeature
>();
analyzers->emplace("test_analyzer", "TestAnalyzer", "abc"); // cache analyzer
analyzers->emplace("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
}

View File

@ -180,9 +180,10 @@ struct IResearchQueryJoinSetup {
auto* analyzers = arangodb::application_features::ApplicationServer::lookupFeature<
arangodb::iresearch::IResearchAnalyzerFeature
>();
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
analyzers->emplace("test_analyzer", "TestAnalyzer", "abc"); // cache analyzer
analyzers->emplace("test_csv_analyzer", "TestDelimAnalyzer", ","); // cache analyzer
analyzers->emplace(result, "test_analyzer", "TestAnalyzer", "abc"); // cache analyzer
analyzers->emplace(result, "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

View File

@ -130,13 +130,6 @@ struct IResearchQueryNullTermSetup {
}
}
auto* analyzers = arangodb::application_features::ApplicationServer::lookupFeature<
arangodb::iresearch::IResearchAnalyzerFeature
>();
analyzers->emplace("test_analyzer", "TestAnalyzer", "abc"); // cache analyzer
analyzers->emplace("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
}

View File

@ -131,13 +131,6 @@ struct IResearchQueryNumericTermSetup {
}
}
auto* analyzers = arangodb::application_features::ApplicationServer::lookupFeature<
arangodb::iresearch::IResearchAnalyzerFeature
>();
analyzers->emplace("test_analyzer", "TestAnalyzer", "abc"); // cache analyzer
analyzers->emplace("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
}

View File

@ -161,13 +161,6 @@ struct IResearchQueryOptionsSetup {
return params[0];
}});
auto* analyzers = arangodb::application_features::ApplicationServer::lookupFeature<
arangodb::iresearch::IResearchAnalyzerFeature
>();
analyzers->emplace("test_analyzer", "TestAnalyzer", "abc"); // cache analyzer
analyzers->emplace("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
}

View File

@ -134,8 +134,10 @@ struct IResearchQueryOrSetup {
auto* analyzers = arangodb::application_features::ApplicationServer::lookupFeature<
arangodb::iresearch::IResearchAnalyzerFeature
>();
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
analyzers->emplace(
result,
"test_analyzer",
"TestAnalyzer",
"abc",
@ -143,6 +145,7 @@ struct IResearchQueryOrSetup {
); // cache analyzer
analyzers->emplace(
result,
"test_csv_analyzer",
"TestDelimAnalyzer",
","

View File

@ -133,8 +133,10 @@ struct IResearchQueryPhraseSetup {
auto* analyzers = arangodb::application_features::ApplicationServer::lookupFeature<
arangodb::iresearch::IResearchAnalyzerFeature
>();
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
analyzers->emplace(
result,
"test_analyzer",
"TestAnalyzer",
"abc",
@ -142,6 +144,7 @@ struct IResearchQueryPhraseSetup {
); // cache analyzer
analyzers->emplace(
result,
"test_csv_analyzer",
"TestDelimAnalyzer",
","

View File

@ -179,9 +179,10 @@ struct IResearchQueryScorerSetup {
auto* analyzers = arangodb::application_features::ApplicationServer::lookupFeature<
arangodb::iresearch::IResearchAnalyzerFeature
>();
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
analyzers->emplace("test_analyzer", "TestAnalyzer", "abc"); // cache analyzer
analyzers->emplace("test_csv_analyzer", "TestDelimAnalyzer", ","); // cache analyzer
analyzers->emplace(result, "test_analyzer", "TestAnalyzer", "abc"); // cache analyzer
analyzers->emplace(result, "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

View File

@ -130,13 +130,6 @@ struct IResearchQuerySelectAllSetup {
}
}
auto* analyzers = arangodb::application_features::ApplicationServer::lookupFeature<
arangodb::iresearch::IResearchAnalyzerFeature
>();
analyzers->emplace("test_analyzer", "TestAnalyzer", "abc"); // cache analyzer
analyzers->emplace("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
}

View File

@ -130,13 +130,6 @@ struct IResearchQueryStartsWithSetup {
}
}
auto* analyzers = arangodb::application_features::ApplicationServer::lookupFeature<
arangodb::iresearch::IResearchAnalyzerFeature
>();
analyzers->emplace("test_analyzer", "TestAnalyzer", "abc"); // cache analyzer
analyzers->emplace("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
}

View File

@ -161,13 +161,6 @@ struct IResearchQueryStringTermSetup {
return params[0];
}});
auto* analyzers = arangodb::application_features::ApplicationServer::lookupFeature<
arangodb::iresearch::IResearchAnalyzerFeature
>();
analyzers->emplace("test_analyzer", "TestAnalyzer", "abc"); // cache analyzer
analyzers->emplace("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
}

View File

@ -154,9 +154,10 @@ struct IResearchQueryTokensSetup {
auto* analyzers = arangodb::application_features::ApplicationServer::lookupFeature<
arangodb::iresearch::IResearchAnalyzerFeature
>();
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
analyzers->emplace("testVocbase::test_analyzer", "TestAnalyzer", "abc"); // cache analyzer
analyzers->emplace("testVocbase::test_csv_analyzer", "TestDelimAnalyzer", ","); // cache analyzer
analyzers->emplace(result, "test_analyzer", "TestAnalyzer", "abc"); // cache analyzer
analyzers->emplace(result, "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

View File

@ -130,13 +130,6 @@ struct IResearchQueryTraversalSetup {
}
}
auto* analyzers = arangodb::application_features::ApplicationServer::lookupFeature<
arangodb::iresearch::IResearchAnalyzerFeature
>();
analyzers->emplace("test_analyzer", "TestAnalyzer", "abc"); // cache analyzer
analyzers->emplace("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
}

View File

@ -129,13 +129,6 @@ struct IResearchQueryValueSetup {
}
}
auto* analyzers = arangodb::application_features::ApplicationServer::lookupFeature<
arangodb::iresearch::IResearchAnalyzerFeature
>();
analyzers->emplace("test_analyzer", "TestAnalyzer", "abc"); // cache analyzer
analyzers->emplace("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
}

View File

@ -122,6 +122,7 @@ struct IResearchViewDBServerSetup {
buildFeatureEntry(new arangodb::QueryRegistryFeature(server), false); // required for TRI_vocbase_t instantiation
buildFeatureEntry(new arangodb::ShardingFeature(server), false); // required for TRI_vocbase_t instantiation
buildFeatureEntry(new arangodb::ViewTypesFeature(server), false); // required for TRI_vocbase_t::createView(...)
buildFeatureEntry(new arangodb::iresearch::IResearchAnalyzerFeature(server), false); // required for IResearchLinkMeta::init(...)
buildFeatureEntry(new arangodb::iresearch::IResearchFeature(server), false); // required for instantiating IResearchView*
buildFeatureEntry(new arangodb::ClusterFeature(server), false);
buildFeatureEntry(new arangodb::V8DealerFeature(server), false);