1
0
Fork 0

issue 526.1: initial support for vocbase-prefixed analyzer names (#8319)

* issue 526.1: initial support for vocbase-prefixed analyzer names

* backport: add tests for new functionality, add temporary workaround for legacy analyzers

* address review comments

* fix typo
This commit is contained in:
Vasiliy 2019-03-06 15:13:19 +03:00 committed by Andrey Abramov
parent 8bcddf1498
commit e97cd8a0fb
31 changed files with 1596 additions and 502 deletions

View File

@ -91,9 +91,12 @@ struct DefaultIndexFactory : public arangodb::IndexTypeFactory {
return arangodb::Result();
}
arangodb::Result normalize(arangodb::velocypack::Builder& normalized,
arangodb::velocypack::Slice definition,
bool isCreation) const override {
virtual arangodb::Result normalize( // normalize definition
arangodb::velocypack::Builder& normalized, // normalized definition (out-param)
arangodb::velocypack::Slice definition, // source definition
bool isCreation, // definition for index creation
TRI_vocbase_t const& vocbase // index vocbase
) const override {
auto* clusterEngine =
static_cast<arangodb::ClusterEngine*>(arangodb::EngineSelectorFeature::ENGINE);
@ -111,7 +114,9 @@ struct DefaultIndexFactory : public arangodb::IndexTypeFactory {
"cannot find storage engine while normalizing index");
}
return engine->indexFactory().factory(_type).normalize(normalized, definition, isCreation);
return engine->indexFactory().factory(_type).normalize( // normalize definition
normalized, definition, isCreation, vocbase // args
);
}
};
@ -219,20 +224,27 @@ std::unordered_map<std::string, std::string> ClusterIndexFactory::indexAliases()
return ae->indexFactory().indexAliases();
}
Result ClusterIndexFactory::enhanceIndexDefinition(VPackSlice const definition,
VPackBuilder& normalized, bool isCreation,
bool isCoordinator) const {
Result ClusterIndexFactory::enhanceIndexDefinition( // normalize definition
velocypack::Slice const definition, // source definition
velocypack::Builder& normalized, // normalized definition (out-param)
bool isCreation, // definition for index creation
TRI_vocbase_t const& vocbase // index vocbase
) const {
auto* ce = static_cast<ClusterEngine*>(EngineSelectorFeature::ENGINE);
if (!ce) {
return TRI_ERROR_INTERNAL;
}
auto* ae = ce->actualEngine();
if (!ae) {
return TRI_ERROR_INTERNAL;
}
return ae->indexFactory().enhanceIndexDefinition(definition, normalized,
isCreation, isCoordinator);
return ae->indexFactory().enhanceIndexDefinition( // normalize definition
definition, normalized, isCreation, vocbase // args
);
}
void ClusterIndexFactory::fillSystemIndexes(arangodb::LogicalCollection& col,

View File

@ -31,15 +31,18 @@ class ClusterIndexFactory final : public IndexFactory {
public:
ClusterIndexFactory();
~ClusterIndexFactory() = default;
Result enhanceIndexDefinition( // normalize definition
velocypack::Slice const definition, // source definition
velocypack::Builder& normalized, // normalized definition (out-param)
bool isCreation, // definition for index creation
TRI_vocbase_t const& vocbase // index vocbase
) const override;
/// @brief index name aliases (e.g. "persistent" => "hash", "skiplist" => "hash")
/// used to display storage engine capabilities
std::unordered_map<std::string, std::string> indexAliases() const override;
Result enhanceIndexDefinition(velocypack::Slice const definition,
velocypack::Builder& normalized, bool isCreation,
bool isCoordinator) const override;
void fillSystemIndexes(arangodb::LogicalCollection& col,
std::vector<std::shared_ptr<arangodb::Index>>& systemIndexes) const override;
@ -50,4 +53,4 @@ class ClusterIndexFactory final : public IndexFactory {
} // namespace arangodb
#endif
#endif

View File

@ -46,6 +46,7 @@
#include "StorageEngine/EngineSelectorFeature.h"
#include "StorageEngine/StorageEngine.h"
#include "Transaction/StandaloneContext.h"
#include "Utils/ExecContext.h"
#include "Utils/OperationOptions.h"
#include "Utils/SingleCollectionTransaction.h"
#include "VelocyPackHelper.h"
@ -57,6 +58,7 @@
namespace {
static std::string const ANALYZER_COLLECTION_NAME("_iresearch_analyzers");
static char const ANALYZER_PREFIX_DELIM = ':'; // name prefix delimiter (2 chars)
static size_t const DEFAULT_POOL_SIZE = 8; // arbitrary value
static std::string const FEATURE_NAME("IResearchAnalyzer");
static irs::string_ref const IDENTITY_ANALYZER_NAME("identity");
@ -145,7 +147,24 @@ arangodb::aql::AqlValue aqlFnTokens(arangodb::aql::ExpressionContext* expression
return arangodb::aql::AqlValue();
}
auto pool = analyzers->get(name);
arangodb::iresearch::IResearchAnalyzerFeature::AnalyzerPool::ptr pool;
if (trx) {
auto* sysDatabase = arangodb::application_features::ApplicationServer::lookupFeature< // find feature
arangodb::SystemDatabaseFeature // featue type
>();
auto sysVocbase = sysDatabase ? sysDatabase->use() : nullptr;
if (sysVocbase) {
pool = analyzers->get( // get analyzer
arangodb::iresearch::IResearchAnalyzerFeature::normalize( // normalize
name, trx->vocbase(), *sysVocbase // args
)
);
}
} else {
pool = analyzers->get(name); // verbatim
}
if (!pool) {
LOG_TOPIC(WARN, arangodb::iresearch::TOPIC)
@ -274,6 +293,37 @@ arangodb::SystemDatabaseFeature::ptr getSystemDatabase() {
return database->use();
}
////////////////////////////////////////////////////////////////////////////////
/// @brief split the analyzer name into the vocbase part and analyzer part
/// @param name analyzer name
/// @return pair of first == vocbase name, second == analyzer name
/// EMPTY == system vocbase
/// NIL == unprefixed analyzer name, i.e. active vocbase
////////////////////////////////////////////////////////////////////////////////
std::pair<irs::string_ref, irs::string_ref> splitAnalyzerName( // split name
irs::string_ref const& analyzer // analyzer name
) noexcept {
// search for vocbase prefix ending with '::'
for (size_t i = 1, count = analyzer.size(); i < count; ++i) {
if (analyzer[i] == ANALYZER_PREFIX_DELIM // current is delim
&& analyzer[i - 1] == ANALYZER_PREFIX_DELIM // previous is also delim
) {
auto vocbase = i > 1 // non-empty prefix, +1 for first delimiter char
? irs::string_ref(analyzer.c_str(), i - 1) // -1 for the first ':' delimiter
: irs::string_ref::EMPTY
;
auto name = i < count - 1 // have suffix
? irs::string_ref(analyzer.c_str() + i + 1, count - i - 1) // +-1 for the suffix after '::'
: irs::string_ref::EMPTY // do not point after end of buffer
;
return std::make_pair(vocbase, name); // prefixed analyzer name
}
}
return std::make_pair(irs::string_ref::NIL, analyzer); // unprefixed analyzer name
}
typedef irs::async_utils::read_write_mutex::read_mutex ReadMutex;
typedef irs::async_utils::read_write_mutex::write_mutex WriteMutex;
@ -382,10 +432,6 @@ void IResearchAnalyzerFeature::AnalyzerPool::setKey(irs::string_ref const& key)
}
}
irs::flags const& IResearchAnalyzerFeature::AnalyzerPool::features() const noexcept {
return _features;
}
irs::analysis::analyzer::ptr IResearchAnalyzerFeature::AnalyzerPool::get() const noexcept {
try {
// FIXME do not use shared_ptr
@ -414,10 +460,6 @@ irs::analysis::analyzer::ptr IResearchAnalyzerFeature::AnalyzerPool::get() const
return nullptr;
}
std::string const& IResearchAnalyzerFeature::AnalyzerPool::name() const noexcept {
return _name;
}
IResearchAnalyzerFeature::IResearchAnalyzerFeature(arangodb::application_features::ApplicationServer& server)
: ApplicationFeature(server, IResearchAnalyzerFeature::name()),
_analyzers(getStaticAnalyzers()), // load static analyzers
@ -430,6 +472,18 @@ IResearchAnalyzerFeature::IResearchAnalyzerFeature(arangodb::application_feature
// containing the persisted configuration
}
/*static*/ bool IResearchAnalyzerFeature::canUse( // check permissions
TRI_vocbase_t const& vocbase, // analyzer vocbase
arangodb::auth::Level const& level // access level
) {
auto* ctx = arangodb::ExecContext::CURRENT;
return !ctx // authentication not enabled
|| (ctx->canUseDatabase(vocbase.name(), level) // can use vocbase
&& (ctx->canUseCollection(vocbase.name(), 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()*/
@ -559,13 +613,15 @@ std::pair<IResearchAnalyzerFeature::AnalyzerPool::ptr, bool> IResearchAnalyzerFe
return std::make_pair(AnalyzerPool::ptr(), false);
}
IResearchAnalyzerFeature::AnalyzerPool::ptr IResearchAnalyzerFeature::ensure(irs::string_ref const& name) {
IResearchAnalyzerFeature::AnalyzerPool::ptr IResearchAnalyzerFeature::ensure( // get analyzer or placeholder
irs::string_ref const& name // analyzer name
) {
// insert dummy (uninitialized) placeholders if this feature has not been
// started to break the dependency loop on DatabaseFeature
// placeholders will be loaded/validation during start()/loadConfiguration()
return _started
? get(name)
: emplace(name, irs::string_ref::NIL, irs::string_ref::NIL, false).first;
? get(name)
: emplace(name, irs::string_ref::NIL, irs::string_ref::NIL, false).first;
}
size_t IResearchAnalyzerFeature::erase(irs::string_ref const& name) noexcept {
@ -676,8 +732,9 @@ size_t IResearchAnalyzerFeature::erase(irs::string_ref const& name) noexcept {
return 0;
}
IResearchAnalyzerFeature::AnalyzerPool::ptr IResearchAnalyzerFeature::get(irs::string_ref const& name) const
noexcept {
IResearchAnalyzerFeature::AnalyzerPool::ptr IResearchAnalyzerFeature::get( // find analyzer
irs::string_ref const& name // analyzer name
) const noexcept {
try {
ReadMutex mutex(_mutex);
SCOPED_LOCK(mutex);
@ -1085,6 +1142,53 @@ bool IResearchAnalyzerFeature::loadConfiguration() {
return FEATURE_NAME;
}
/*static*/ std::string IResearchAnalyzerFeature::normalize( // normalize name
irs::string_ref const& name, // analyzer name
TRI_vocbase_t const& activeVocbase, // fallback vocbase if not part of name
TRI_vocbase_t const& systemVocbase, // the system vocbase for use with empty prefix
bool expandVocbasePrefix /*= true*/ // use full vocbase name as prefix for active/system v.s. EMPTY/'::'
) {
if (IDENTITY_ANALYZER_NAME == name) {
return name; // special case for the 'identity' analyzer which is a singleton
}
// FIXME TODO remove once lagacy analyzers will be added to each vocbase via an upgrade step and JavaScript tests updated accordingly
{
static std::unordered_set<std::string> legacyAnalyzers = {
"text_de", "text_en", "text_es", "text_fi", "text_fr", "text_it", "text_nl", "text_no", "text_pt", "text_ru", "text_sv", "text_zh"
};
if (legacyAnalyzers.find(name) != legacyAnalyzers.end()) {
return name;
}
}
auto split = splitAnalyzerName(name);
if (expandVocbasePrefix) {
if (split.first.null()) {
return std::string(activeVocbase.name()).append(2, ANALYZER_PREFIX_DELIM).append(split.second);
}
if (split.first.empty()) {
return std::string(systemVocbase.name()).append(2, ANALYZER_PREFIX_DELIM).append(split.second);
}
} else {
// .........................................................................
// normalize vocbase such that active vocbase takes precedence over system
// vocbase i.e. prefer NIL over EMPTY
// .........................................................................
if (split.first.null() || split.first == activeVocbase.name()) { // active vocbase
return split.second;
}
if (split.first.empty() || split.first == systemVocbase.name()) { // system vocbase
return std::string(2, ANALYZER_PREFIX_DELIM).append(split.second);
}
}
return name; // name prefixed with vocbase (or NIL)
}
void IResearchAnalyzerFeature::prepare() {
ApplicationFeature::prepare();
@ -1332,14 +1436,18 @@ bool IResearchAnalyzerFeature::storeConfiguration(AnalyzerPool& pool) {
return false;
}
bool IResearchAnalyzerFeature::visit(
std::function<bool(irs::string_ref const& name, irs::string_ref const& type,
irs::string_ref const& properties)> const& visitor) {
bool IResearchAnalyzerFeature::visit( // visit analyzers
VisitorType const& visitor, // visitor
TRI_vocbase_t const* vocbase /*= nullptr*/ // analyzers for vocbase
) {
ReadMutex mutex(_mutex);
SCOPED_LOCK(mutex);
for (auto& entry : _analyzers) {
if (entry.second && !visitor(entry.first, entry.second->_type, entry.second->_properties)) {
for (auto& entry: _analyzers) {
if (entry.second // have entry
&& (!vocbase || splitAnalyzerName(entry.first).first == vocbase->name()) // requested vocbase
&& !visitor(entry.first, entry.second->_type, entry.second->_properties) // termination request
) {
return false;
}
}
@ -1351,5 +1459,5 @@ bool IResearchAnalyzerFeature::visit(
} // namespace arangodb
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------

View File

@ -29,9 +29,11 @@
#include "utils/hash_utils.hpp"
#include "utils/object_pool.hpp"
#include "ApplicationFeatures/ApplicationFeature.h"
#include "Auth/Common.h"
#include "VocBase/voc-types.h"
#include "ApplicationFeatures/ApplicationFeature.h"
class TRI_vocbase_t; // forward declaration
namespace arangodb {
namespace transaction {
@ -57,13 +59,14 @@ class IResearchAnalyzerFeature final : public arangodb::application_features::Ap
class AnalyzerPool : private irs::util::noncopyable {
public:
typedef std::shared_ptr<AnalyzerPool> ptr;
irs::flags const& features() const noexcept;
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;
std::string const& name() const noexcept { return _name; }
irs::string_ref const& properties() const noexcept { return _properties; }
irs::string_ref const& type() const noexcept { return _type; }
private:
friend IResearchAnalyzerFeature; // required for calling
// AnalyzerPool::init()
friend class IResearchAnalyzerFeature; // required for calling AnalyzerPool::init()
// 'make(...)' method wrapper for irs::analysis::analyzer types
struct Builder {
@ -89,29 +92,74 @@ class IResearchAnalyzerFeature final : public arangodb::application_features::Ap
explicit IResearchAnalyzerFeature(arangodb::application_features::ApplicationServer& server);
//////////////////////////////////////////////////////////////////////////////
/// @return analyzers in the specified vocbase are granted 'level' access
//////////////////////////////////////////////////////////////////////////////
static bool canUse( // check permissions
TRI_vocbase_t const& vocbase, // analyzer vocbase
arangodb::auth::Level const& level // access level
);
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;
AnalyzerPool::ptr ensure(irs::string_ref const& name); // before start() returns pool placeholder, during start() all
// placeholders are initialized, after start() returns same as
// get(...)
//////////////////////////////////////////////////////////////////////////////
/// @brief get analyzer or placeholder
/// before start() returns pool placeholder,
/// during start() all placeholders are initialized,
/// after start() returns same as get(...)
/// @param name analyzer name (used verbatim)
//////////////////////////////////////////////////////////////////////////////
AnalyzerPool::ptr ensure(irs::string_ref const& name);
//////////////////////////////////////////////////////////////////////////////
/// @return number of analyzers removed
//////////////////////////////////////////////////////////////////////////////
size_t erase(irs::string_ref const& name) noexcept;
//////////////////////////////////////////////////////////////////////////////
/// @brief find analyzer
/// @param name analyzer name (used verbatim)
/// @return analyzer with the specified name or nullptr
//////////////////////////////////////////////////////////////////////////////
AnalyzerPool::ptr get(irs::string_ref const& name) const noexcept;
static AnalyzerPool::ptr identity() noexcept; // the identity analyzer
static std::string const& name() noexcept;
//////////////////////////////////////////////////////////////////////////////
/// @return normalized analyzer name, i.e. with vocbase prefix
//////////////////////////////////////////////////////////////////////////////
static std::string normalize( // normalize name
irs::string_ref const& name, // analyzer name
TRI_vocbase_t const& activeVocbase, // fallback vocbase if not part of name
TRI_vocbase_t const& systemVocbase, // the system vocbase for use with empty prefix
bool expandVocbasePrefix = true // use full vocbase name as prefix for active/system v.s. EMPTY/'::'
);
void prepare() override;
void start() override;
void stop() override;
bool visit(std::function<bool(irs::string_ref const& name, irs::string_ref const& type,
irs::string_ref const& properties)> const& visitor);
//////////////////////////////////////////////////////////////////////////////
/// @brief visit all analyzers for the specified vocbase
/// @param vocbase only visit analysers for this vocbase (nullptr == all)
/// @return visitation compleated fully
//////////////////////////////////////////////////////////////////////////////
typedef std::function<bool(irs::string_ref const& analyzer, irs::string_ref const& type, irs::string_ref const& properties)> VisitorType;
bool visit( // visit analyzers
VisitorType const& visitor, // visitor
TRI_vocbase_t const* vocbase = nullptr // analyzers for vocbase
);
private:
// map of caches of irs::analysis::analyzer pools indexed by analyzer name and
// their associated metas
typedef std::unordered_map<irs::hashed_string_ref, AnalyzerPool::ptr> Analyzers;
Analyzers _analyzers; // all analyzers known to this feature (including static)
Analyzers _analyzers; // all analyzers known to this feature (including static) (names are stored with expanded vocbase prefixes)
Analyzers _customAnalyzers; // user defined analyzers managed by this feature, a
// subset of '_analyzers' (used for removals)
mutable irs::async_utils::read_write_mutex _mutex;
@ -130,4 +178,4 @@ class IResearchAnalyzerFeature final : public arangodb::application_features::Ap
} // namespace iresearch
} // namespace arangodb
#endif
#endif

View File

@ -95,10 +95,15 @@ struct IResearchLinkCoordinator::IndexFactory : public arangodb::IndexTypeFactor
return arangodb::Result();
}
virtual arangodb::Result normalize(arangodb::velocypack::Builder& normalized,
arangodb::velocypack::Slice definition,
bool isCreation) const override {
return IResearchLinkHelper::normalize(normalized, definition, isCreation);
virtual arangodb::Result normalize( // normalize definition
arangodb::velocypack::Builder& normalized, // normalized definition (out-param)
arangodb::velocypack::Slice definition, // source definition
bool isCreation, // definition for index creation
TRI_vocbase_t const& vocbase // index vocbase
) const override {
return IResearchLinkHelper::normalize( // normalize
normalized, definition, isCreation, vocbase // args
);
}
};

View File

@ -173,10 +173,9 @@ arangodb::Result modifyLinks(std::unordered_set<TRI_voc_cid_t>& modified,
if (!arangodb::iresearch::mergeSliceSkipKeys(namedJson, link, acceptor)) {
return arangodb::Result(
TRI_ERROR_INTERNAL,
std::string("failed to update link definition with the view name "
"while updating arangosearch view '") +
view.name() + "' collection '" + collectionName + "'");
TRI_ERROR_INTERNAL,
std::string("failed to update link definition with the view name while updating arangosearch view '") + view.name() + "' collection '" + collectionName + "'"
);
}
namedJson.close();
@ -184,12 +183,11 @@ arangodb::Result modifyLinks(std::unordered_set<TRI_voc_cid_t>& modified,
std::string error;
arangodb::iresearch::IResearchLinkMeta linkMeta;
if (!linkMeta.init(namedJson.slice(), error)) {
return arangodb::Result(TRI_ERROR_BAD_PARAMETER,
std::string("error parsing link parameters from "
"json for arangosearch view '") +
view.name() + "' collection '" +
collectionName + "' error '" + error + "'");
if (!linkMeta.init(namedJson.slice(), error)) { // analyzers in definition should already be normalized
return arangodb::Result(
TRI_ERROR_BAD_PARAMETER,
std::string("error parsing link parameters from json for arangosearch view '") + view.name() + "' collection '" + collectionName + "' error '" + error + "'"
);
}
linkModifications.emplace_back(collectionsToLock.size(), linkDefinitions.size());
@ -200,14 +198,12 @@ arangodb::Result modifyLinks(std::unordered_set<TRI_voc_cid_t>& modified,
auto trxCtx = arangodb::transaction::StandaloneContext::Create(vocbase);
// add removals for any 'stale' links not found in the 'links' definition
for (auto& id : stale) {
for (auto& id: stale) {
if (!trxCtx->resolver().getCollection(id)) {
LOG_TOPIC(WARN, arangodb::iresearch::TOPIC)
<< "request for removal of a stale link to a missing collection '"
<< id << "', ignoring";
<< "request for removal of a stale link to a missing collection '" << id << "', ignoring";
continue; // skip adding removal requests to stale links to non-existant
// collections (already dropped)
continue; // skip adding removal requests to stale links to non-existent collections (already dropped)
}
linkModifications.emplace_back(collectionsToLock.size());
@ -517,38 +513,49 @@ namespace iresearch {
return nullptr;
}
/*static*/ arangodb::Result IResearchLinkHelper::normalize(arangodb::velocypack::Builder& normalized,
velocypack::Slice definition,
bool // isCreation
/*static*/ arangodb::Result IResearchLinkHelper::normalize( // normalize definition
arangodb::velocypack::Builder& normalized, // normalized definition (out-param)
arangodb::velocypack::Slice definition, // source definition
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,
std::string("invalid output buffer provided for arangosearch link "
"normalized definition generation"));
TRI_ERROR_BAD_PARAMETER,
std::string("invalid output buffer provided for arangosearch link normalized definition generation")
);
}
std::string error;
IResearchLinkMeta meta;
if (!meta.init(definition, error)) {
if (!meta.init(definition, error, IResearchLinkMeta::DEFAULT(), &vocbase)) {
return arangodb::Result(
TRI_ERROR_BAD_PARAMETER,
std::string("error parsing arangosearch link parameters from json: ") + error);
TRI_ERROR_BAD_PARAMETER,
std::string("error parsing arangosearch link parameters from json: ") + error
);
}
normalized.add(arangodb::StaticStrings::IndexType, arangodb::velocypack::Value(LINK_TYPE));
normalized.add(
arangodb::StaticStrings::IndexType, arangodb::velocypack::Value(LINK_TYPE)
);
// copy over IResearch View identifier
if (definition.hasKey(StaticStrings::ViewIdField)) {
normalized.add(StaticStrings::ViewIdField, definition.get(StaticStrings::ViewIdField));
normalized.add(
StaticStrings::ViewIdField, definition.get(StaticStrings::ViewIdField)
);
}
return meta.json(normalized)
? arangodb::Result()
: arangodb::Result(TRI_ERROR_BAD_PARAMETER,
std::string("error generating arangosearch "
"link normalized definition"));
? arangodb::Result()
: arangodb::Result(
TRI_ERROR_BAD_PARAMETER,
std::string("error generating arangosearch link normalized definition")
)
;
}
/*static*/ std::string const& IResearchLinkHelper::type() noexcept {

View File

@ -75,8 +75,12 @@ struct IResearchLinkHelper {
/// @brief validate and copy required fields from the 'definition' into
/// 'normalized'
//////////////////////////////////////////////////////////////////////////////
static arangodb::Result normalize(arangodb::velocypack::Builder& normalized,
velocypack::Slice definition, bool isCreation);
static arangodb::Result normalize( // normalize definition
arangodb::velocypack::Builder& normalized, // normalized definition (out-param)
arangodb::velocypack::Slice definition, // source definition
bool isCreation, // definition for index creation
TRI_vocbase_t const& vocbase // index vocbase
);
////////////////////////////////////////////////////////////////////////////////
/// @brief IResearch Link index type string value

View File

@ -30,6 +30,7 @@
#include "utils/locale_utils.hpp"
#include "Basics/StringUtils.h"
#include "RestServer/SystemDatabaseFeature.h"
#include "VelocyPackHelper.h"
#include "velocypack/Builder.h"
#include "velocypack/Iterator.h"
@ -172,10 +173,13 @@ bool IResearchLinkMeta::operator!=(IResearchLinkMeta const& other) const noexcep
return meta;
}
bool IResearchLinkMeta::init(arangodb::velocypack::Slice const& slice, std::string& errorField,
IResearchLinkMeta const& defaults /*= DEFAULT()*/,
Mask* mask /*= nullptr*/
) noexcept {
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
Mask* mask /*= nullptr*/ // initialized fields (out-param)
) {
if (!slice.isObject()) {
return false;
}
@ -195,8 +199,9 @@ bool IResearchLinkMeta::init(arangodb::velocypack::Slice const& slice, std::stri
if (!mask->_analyzers) {
_analyzers = defaults._analyzers;
} else {
auto* analyzers =
arangodb::application_features::ApplicationServer::lookupFeature<IResearchAnalyzerFeature>();
auto* analyzers = arangodb::application_features::ApplicationServer::lookupFeature< // find feature
IResearchAnalyzerFeature // featue type
>();
auto field = slice.get(fieldName);
if (!analyzers || !field.isArray()) {
@ -218,7 +223,23 @@ bool IResearchLinkMeta::init(arangodb::velocypack::Slice const& slice, std::stri
}
auto name = getStringRef(key);
auto analyzer = analyzers->ensure(name);
IResearchAnalyzerFeature::AnalyzerPool::ptr analyzer;
// 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;
if (sysVocbase) {
analyzer = analyzers->ensure(IResearchAnalyzerFeature::normalize( // normalize
name, *defaultVocbase, *sysVocbase // args
));
}
} else {
analyzer = analyzers->ensure(name); // verbatim (assume already normalized)
}
if (!analyzer) {
errorField = fieldName + "=>" + std::string(name);
@ -354,7 +375,7 @@ bool IResearchLinkMeta::init(arangodb::velocypack::Slice const& slice, std::stri
std::string childErrorField;
if (!_fields[name]->init(value, errorField, subDefaults)) {
if (!_fields[name]->init(value, errorField, subDefaults, defaultVocbase)) {
errorField = fieldName + "=>" + name + "=>" + childErrorField;
return false;
@ -366,10 +387,12 @@ bool IResearchLinkMeta::init(arangodb::velocypack::Slice const& slice, std::stri
return true;
}
bool IResearchLinkMeta::json(arangodb::velocypack::Builder& builder,
IResearchLinkMeta const* ignoreEqual /*= nullptr*/,
Mask const* mask /*= nullptr*/
) const {
bool IResearchLinkMeta::json( // append meta jSON
arangodb::velocypack::Builder& builder, // output buffer (out-param)
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
) const {
if (!builder.isOpenObject()) {
return false;
}
@ -381,8 +404,27 @@ bool IResearchLinkMeta::json(arangodb::velocypack::Builder& builder,
analyzersBuilder.openArray();
for (auto& entry : _analyzers) {
if (entry) { // skip null analyzers
analyzersBuilder.add(arangodb::velocypack::Value(entry->name()));
if (!entry) {
continue; // skip null analyzers
}
if (defaultVocbase) {
auto* sysDatabase = arangodb::application_features::ApplicationServer::lookupFeature< // find feature
arangodb::SystemDatabaseFeature // feature type
>();
auto sysVocbase = sysDatabase ? sysDatabase->use() : nullptr;
if (!sysVocbase) {
return false;
}
analyzersBuilder.add(arangodb::velocypack::Value(
IResearchAnalyzerFeature::normalize( // normalize
entry->name(), *defaultVocbase, *sysVocbase, false // args
) // normalized value
));
} else {
analyzersBuilder.add(arangodb::velocypack::Value(entry->name())); // verbatim (assume already normalized)
}
}
@ -392,29 +434,27 @@ bool IResearchLinkMeta::json(arangodb::velocypack::Builder& builder,
if (!mask || mask->_fields) { // fields are not inherited from parent
arangodb::velocypack::Builder fieldsBuilder;
Mask fieldMask(true); // output all non-matching fields
auto subDefaults = *this; // make modifable copy
{
arangodb::velocypack::ObjectBuilder fieldsBuilderWrapper(&fieldsBuilder);
arangodb::velocypack::Builder fieldBuilder;
Mask mask(true); // output all non-matching fields
auto subDefaults = *this;
subDefaults._fields.clear(); // do not inherit fields and overrides
// overrides from this field
subDefaults._fields.clear(); // do not inherit fields and overrides from this field
fieldsBuilder.openObject();
for (auto& entry : _fields) {
mask._fields = !entry.value()->_fields.empty(); // do not output empty fields on subobjects
fieldMask._fields = !entry.value()->_fields.empty(); // do not output empty fields on subobjects
fieldsBuilder.add( // add sub-object
entry.key(), // field name
arangodb::velocypack::Value(arangodb::velocypack::ValueType::Object)
);
if (!entry.value()->json(arangodb::velocypack::ObjectBuilder(&fieldBuilder),
&subDefaults, &mask)) {
return false;
}
if (!entry.value()->json(fieldsBuilder, &subDefaults, defaultVocbase, &fieldMask)) {
return false;
}
fieldsBuilderWrapper->add(entry.key(), fieldBuilder.slice());
fieldBuilder.clear();
fieldsBuilder.close();
}
}
fieldsBuilder.close();
builder.add("fields", fieldsBuilder.slice());
}
@ -452,13 +492,6 @@ bool IResearchLinkMeta::json(arangodb::velocypack::Builder& builder,
return true;
}
bool IResearchLinkMeta::json(arangodb::velocypack::ObjectBuilder const& builder,
IResearchLinkMeta const* ignoreEqual /*= nullptr*/,
Mask const* mask /*= nullptr*/
) const {
return builder.builder && json(*(builder.builder), ignoreEqual, mask);
}
size_t IResearchLinkMeta::memory() const noexcept {
auto size = sizeof(IResearchLinkMeta);
@ -477,5 +510,5 @@ size_t IResearchLinkMeta::memory() const noexcept {
} // namespace arangodb
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------

View File

@ -116,10 +116,17 @@ struct IResearchLinkMeta {
/// @brief initialize IResearchLinkMeta with values from a JSON description
/// return success or set 'errorField' to specific field with error
/// on failure state is undefined
/// @param defaultVocbase fallback vocbase for analyzer name normalization
/// nullptr == do not normalize
/// @param mask if set reflects which fields were initialized from JSON
////////////////////////////////////////////////////////////////////////////////
bool init(arangodb::velocypack::Slice const& slice, std::string& errorField,
IResearchLinkMeta const& defaults = DEFAULT(), Mask* mask = nullptr) noexcept;
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
Mask* mask = nullptr // initialized fields (out-param)
);
////////////////////////////////////////////////////////////////////////////////
/// @brief fill and return a JSON description of a IResearchLinkMeta object
@ -127,19 +134,15 @@ struct IResearchLinkMeta {
/// or (if 'mask' != nullptr) values in 'mask' that are set to false
/// elements are appended to an existing object
/// return success or set TRI_set_errno(...) and return false
/// @param defaultVocbase fallback vocbase for analyzer name normalization
/// nullptr == do not normalize
////////////////////////////////////////////////////////////////////////////////
bool json(arangodb::velocypack::Builder& builder,
IResearchLinkMeta const* ignoreEqual = nullptr, Mask const* mask = nullptr) const;
////////////////////////////////////////////////////////////////////////////////
/// @brief fill and return a JSON description of a IResearchLinkMeta object
/// do not fill values identical to ones available in 'ignoreEqual'
/// or (if 'mask' != nullptr) values in 'mask' that are set to false
/// elements are appended to an existing object
/// return success or set TRI_set_errno(...) and return false
////////////////////////////////////////////////////////////////////////////////
bool json(arangodb::velocypack::ObjectBuilder const& builder,
IResearchLinkMeta const* ignoreEqual = nullptr, Mask const* mask = nullptr) const;
bool json( // append meta jSON
arangodb::velocypack::Builder& builder, // output buffer (out-param)
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
) const;
////////////////////////////////////////////////////////////////////////////////
/// @brief amount of memory in bytes occupied by this iResearch Link meta
@ -150,4 +153,4 @@ struct IResearchLinkMeta {
} // namespace iresearch
} // namespace arangodb
#endif
#endif

View File

@ -107,10 +107,15 @@ struct IResearchMMFilesLink::IndexFactory : public arangodb::IndexTypeFactory {
return arangodb::Result();
}
virtual arangodb::Result normalize(arangodb::velocypack::Builder& normalized,
arangodb::velocypack::Slice definition,
bool isCreation) const override {
return IResearchLinkHelper::normalize(normalized, definition, isCreation);
virtual arangodb::Result normalize( // normalize definition
arangodb::velocypack::Builder& normalized, // normalized definition (out-param)
arangodb::velocypack::Slice definition, // source definition
bool isCreation, // definition for index creation
TRI_vocbase_t const& vocbase // index vocbase
) const override {
return IResearchLinkHelper::normalize( // normalize
normalized, definition, isCreation, vocbase // args
);
}
};
@ -178,5 +183,5 @@ bool IResearchMMFilesLink::isPersistent() const {
} // namespace arangodb
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------

View File

@ -18,8 +18,9 @@
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Andrey Abramov
/// @author Vasiliy Nabatchikov
/// @author Vasiliy Nabatchikov
////////////////////////////////////////////////////////////////////////////////
#include "Basics/Common.h" // required for RocksDBColumnFamily.h
#include "IResearchLinkHelper.h"
#include "IResearchView.h"
@ -185,10 +186,15 @@ struct IResearchRocksDBLink::IndexFactory : public arangodb::IndexTypeFactory {
return arangodb::Result();
}
virtual arangodb::Result normalize(arangodb::velocypack::Builder& normalized,
arangodb::velocypack::Slice definition,
bool isCreation) const override {
return IResearchLinkHelper::normalize(normalized, definition, isCreation);
virtual arangodb::Result normalize( // normalize definition
arangodb::velocypack::Builder& normalized, // normalized definition (out-param)
arangodb::velocypack::Slice definition, // source definition
bool isCreation, // definition for index creation
TRI_vocbase_t const& vocbase // index vocbase
) const override {
return IResearchLinkHelper::normalize( // normalize
normalized, definition, isCreation, vocbase // args
);
}
};
@ -250,4 +256,4 @@ void IResearchRocksDBLink::toVelocyPack(arangodb::velocypack::Builder& builder,
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------

View File

@ -610,13 +610,6 @@ bool IResearchViewMeta::json(arangodb::velocypack::Builder& builder,
return true;
}
bool IResearchViewMeta::json(arangodb::velocypack::ObjectBuilder const& builder,
IResearchViewMeta const* ignoreEqual /*= nullptr*/,
Mask const* mask /*= nullptr*/
) const {
return builder.builder && json(*(builder.builder), ignoreEqual, mask);
}
size_t IResearchViewMeta::memory() const {
auto size = sizeof(IResearchViewMeta);
@ -750,13 +743,6 @@ bool IResearchViewMetaState::json(arangodb::velocypack::Builder& builder,
return true;
}
bool IResearchViewMetaState::json(arangodb::velocypack::ObjectBuilder const& builder,
IResearchViewMetaState const* ignoreEqual /*= nullptr*/,
Mask const* mask /*= nullptr*/
) const {
return builder.builder && json(*(builder.builder), ignoreEqual, mask);
}
size_t IResearchViewMetaState::memory() const {
auto size = sizeof(IResearchViewMetaState);

View File

@ -139,16 +139,6 @@ struct IResearchViewMeta {
bool json(arangodb::velocypack::Builder& builder,
IResearchViewMeta const* ignoreEqual = nullptr, Mask const* mask = nullptr) const;
////////////////////////////////////////////////////////////////////////////////
/// @brief fill and return a JSON description of a IResearchViewMeta object
/// do not fill values identical to ones available in 'ignoreEqual'
/// or (if 'mask' != nullptr) values in 'mask' that are set to false
/// elements are appended to an existing object
/// return success or set TRI_set_errno(...) and return false
////////////////////////////////////////////////////////////////////////////////
bool json(arangodb::velocypack::ObjectBuilder const& builder,
IResearchViewMeta const* ignoreEqual = nullptr, Mask const* mask = nullptr) const;
////////////////////////////////////////////////////////////////////////////////
/// @brief amount of memory in bytes occupied by this iResearch Link meta
////////////////////////////////////////////////////////////////////////////////
@ -213,17 +203,6 @@ struct IResearchViewMetaState {
IResearchViewMetaState const* ignoreEqual = nullptr,
Mask const* mask = nullptr) const;
////////////////////////////////////////////////////////////////////////////////
/// @brief fill and return a JSON description of a IResearchViewMeta object
/// do not fill values identical to ones available in 'ignoreEqual'
/// or (if 'mask' != nullptr) values in 'mask' that are set to false
/// elements are appended to an existing object
/// return success or set TRI_set_errno(...) and return false
////////////////////////////////////////////////////////////////////////////////
bool json(arangodb::velocypack::ObjectBuilder const& builder,
IResearchViewMetaState const* ignoreEqual = nullptr,
Mask const* mask = nullptr) const;
////////////////////////////////////////////////////////////////////////////////
/// @brief amount of memory in bytes occupied by this iResearch Link meta
////////////////////////////////////////////////////////////////////////////////

View File

@ -56,9 +56,12 @@ struct InvalidIndexFactory : public arangodb::IndexTypeFactory {
definition.toString());
}
arangodb::Result normalize(arangodb::velocypack::Builder&,
arangodb::velocypack::Slice definition,
bool) const override {
arangodb::Result normalize( // normalize definition
arangodb::velocypack::Builder&, // normalized definition (out-param)
arangodb::velocypack::Slice definition, // source definition
bool, // definition for index creation
TRI_vocbase_t const& // index vocbase
) const override {
return arangodb::Result(
TRI_ERROR_BAD_PARAMETER,
std::string(
@ -185,10 +188,12 @@ Result IndexFactory::emplace(std::string const& type, IndexTypeFactory const& fa
return arangodb::Result();
}
Result IndexFactory::enhanceIndexDefinition(velocypack::Slice const definition,
velocypack::Builder& normalized,
bool isCreation, bool isCoordinator) const {
Result IndexFactory::enhanceIndexDefinition( // normalizze deefinition
velocypack::Slice const definition, // source definition
velocypack::Builder& normalized, // normalized definition (out-param)
bool isCreation, // definition for index creation
TRI_vocbase_t const& vocbase // index vocbase
) const {
auto type = definition.get(StaticStrings::IndexType);
if (!type.isString()) {
@ -197,7 +202,6 @@ Result IndexFactory::enhanceIndexDefinition(velocypack::Slice const definition,
auto& factory = IndexFactory::factory(type.copyString());
TRI_ASSERT(ServerState::instance()->isCoordinator() == isCoordinator);
TRI_ASSERT(normalized.isEmpty());
try {
@ -216,7 +220,7 @@ Result IndexFactory::enhanceIndexDefinition(velocypack::Slice const definition,
arangodb::velocypack::Value(std::to_string(id)));
}
return factory.normalize(normalized, definition, isCreation);
return factory.normalize(normalized, definition, isCreation, vocbase);
} catch (basics::Exception const& ex) {
return Result(ex.code(), ex.what());
} catch (std::exception const& ex) {

View File

@ -47,7 +47,7 @@ struct IndexTypeFactory {
/// index once instantiated
virtual bool equal(Index::IndexType type, velocypack::Slice const& lhs, velocypack::Slice const& rhs,
bool attributeOrderMatters) const;
virtual bool equal(velocypack::Slice const& lhs, velocypack::Slice const& rhs) const = 0;
/// @brief instantiate an Index definition
@ -56,9 +56,13 @@ struct IndexTypeFactory {
bool isClusterConstructor) const = 0;
/// @brief normalize an Index definition prior to instantiation/persistence
virtual Result normalize(velocypack::Builder& normalized,
velocypack::Slice definition, bool isCreation) const = 0;
virtual Result normalize( // normalize definition
velocypack::Builder& normalized, // normalized definition (out-param)
velocypack::Slice definition, // source definition
bool isCreation, // definition for index creation
TRI_vocbase_t const& vocbase // index vocbase
) const = 0;
/// @brief the order of attributes matters by default
virtual bool attributeOrderMatters() const {
// can be overridden by specific indexes
@ -73,9 +77,12 @@ class IndexFactory {
/// @return 'factory' for 'type' was added successfully
Result emplace(std::string const& type, IndexTypeFactory const& factory);
virtual Result enhanceIndexDefinition(velocypack::Slice const definition,
velocypack::Builder& normalized,
bool isCreation, bool isCoordinator) const;
virtual Result enhanceIndexDefinition( // normalizze definition
velocypack::Slice const definition, // source definition
velocypack::Builder& normalized, // normalized definition (out-param)
bool isCreation, // definition for index creation
TRI_vocbase_t const& vocbase // index vocbase
) const;
/// @return factory for the specified type or a failing placeholder if no such
/// type
@ -87,7 +94,7 @@ class IndexFactory {
/// @brief used to display storage engine capabilities
virtual std::vector<std::string> supportedIndexes() const;
/// @brief index name aliases (e.g. "persistent" => "hash", "skiplist" => "hash")
/// used to display storage engine capabilities
virtual std::unordered_map<std::string, std::string> indexAliases() const;
@ -153,4 +160,4 @@ class IndexFactory {
} // namespace arangodb
#endif
#endif

View File

@ -83,9 +83,12 @@ struct EdgeIndexFactory : public DefaultIndexFactory {
return arangodb::Result();
}
arangodb::Result normalize(arangodb::velocypack::Builder& normalized,
arangodb::velocypack::Slice definition,
bool isCreation) const override {
virtual arangodb::Result normalize( // normalize definition
arangodb::velocypack::Builder& normalized, // normalized definition (out-param)
arangodb::velocypack::Slice definition, // source definition
bool isCreation, // definition for index creation
TRI_vocbase_t const& vocbase // index vocbase
) const override {
if (isCreation) {
// creating these indexes yourself is forbidden
return TRI_ERROR_FORBIDDEN;
@ -112,9 +115,12 @@ struct FulltextIndexFactory : public DefaultIndexFactory {
return arangodb::Result();
}
arangodb::Result normalize(arangodb::velocypack::Builder& normalized,
arangodb::velocypack::Slice definition,
bool isCreation) const override {
virtual arangodb::Result normalize( // normalize definition
arangodb::velocypack::Builder& normalized, // normalized definition (out-param)
arangodb::velocypack::Slice definition, // source definition
bool isCreation, // definition for index creation
TRI_vocbase_t const& vocbase // index vocbase
) const override {
TRI_ASSERT(normalized.isOpenObject());
normalized.add(arangodb::StaticStrings::IndexType,
arangodb::velocypack::Value(
@ -138,9 +144,12 @@ struct GeoIndexFactory : public DefaultIndexFactory {
return arangodb::Result();
}
arangodb::Result normalize(arangodb::velocypack::Builder& normalized,
arangodb::velocypack::Slice definition,
bool isCreation) const override {
virtual arangodb::Result normalize( // normalize definition
arangodb::velocypack::Builder& normalized, // normalized definition (out-param)
arangodb::velocypack::Slice definition, // source definition
bool isCreation, // definition for index creation
TRI_vocbase_t const& vocbase // index vocbase
) const override {
TRI_ASSERT(normalized.isOpenObject());
normalized.add(arangodb::StaticStrings::IndexType,
arangodb::velocypack::Value(Index::oldtypeName(Index::TRI_IDX_TYPE_GEO_INDEX)));
@ -163,9 +172,12 @@ struct Geo1IndexFactory : public DefaultIndexFactory {
return arangodb::Result();
}
arangodb::Result normalize(arangodb::velocypack::Builder& normalized,
arangodb::velocypack::Slice definition,
bool isCreation) const override {
virtual arangodb::Result normalize( // normalize definition
arangodb::velocypack::Builder& normalized, // normalized definition (out-param)
arangodb::velocypack::Slice definition, // source definition
bool isCreation, // definition for index creation
TRI_vocbase_t const& vocbase // index vocbase
) const override {
TRI_ASSERT(normalized.isOpenObject());
normalized.add(arangodb::StaticStrings::IndexType,
arangodb::velocypack::Value(Index::oldtypeName(Index::TRI_IDX_TYPE_GEO_INDEX)));
@ -188,9 +200,12 @@ struct Geo2IndexFactory : public DefaultIndexFactory {
return arangodb::Result();
}
arangodb::Result normalize(arangodb::velocypack::Builder& normalized,
arangodb::velocypack::Slice definition,
bool isCreation) const override {
virtual arangodb::Result normalize( // normalize definition
arangodb::velocypack::Builder& normalized, // normalized definition (out-param)
arangodb::velocypack::Slice definition, // source definition
bool isCreation, // definition for index creation
TRI_vocbase_t const& vocbase // index vocbase
) const override {
TRI_ASSERT(normalized.isOpenObject());
normalized.add(arangodb::StaticStrings::IndexType,
arangodb::velocypack::Value(Index::oldtypeName(Index::TRI_IDX_TYPE_GEO_INDEX)));
@ -212,9 +227,12 @@ struct HashIndexFactory : public DefaultIndexFactory {
return arangodb::Result();
}
arangodb::Result normalize(arangodb::velocypack::Builder& normalized,
arangodb::velocypack::Slice definition,
bool isCreation) const override {
virtual arangodb::Result normalize( // normalize definition
arangodb::velocypack::Builder& normalized, // normalized definition (out-param)
arangodb::velocypack::Slice definition, // source definition
bool isCreation, // definition for index creation
TRI_vocbase_t const& vocbase // index vocbase
) const override {
TRI_ASSERT(normalized.isOpenObject());
normalized.add(arangodb::StaticStrings::IndexType,
arangodb::velocypack::Value(Index::oldtypeName(Index::TRI_IDX_TYPE_HASH_INDEX)));
@ -241,9 +259,12 @@ struct PersistentIndexFactory : public DefaultIndexFactory {
return arangodb::Result();
}
arangodb::Result normalize(arangodb::velocypack::Builder& normalized,
arangodb::velocypack::Slice definition,
bool isCreation) const override {
virtual arangodb::Result normalize( // normalize definition
arangodb::velocypack::Builder& normalized, // normalized definition (out-param)
arangodb::velocypack::Slice definition, // source definition
bool isCreation, // definition for index creation
TRI_vocbase_t const& vocbase // index vocbase
) const override {
TRI_ASSERT(normalized.isOpenObject());
normalized.add(arangodb::StaticStrings::IndexType,
arangodb::velocypack::Value(
@ -266,9 +287,12 @@ struct TtlIndexFactory : public DefaultIndexFactory {
return arangodb::Result();
}
arangodb::Result normalize(arangodb::velocypack::Builder& normalized,
arangodb::velocypack::Slice definition,
bool isCreation) const override {
virtual arangodb::Result normalize( // normalize definition
arangodb::velocypack::Builder& normalized, // normalized definition (out-param)
arangodb::velocypack::Slice definition, // source definition
bool isCreation, // definition for index creation
TRI_vocbase_t const& vocbase // index vocbase
) const override {
TRI_ASSERT(normalized.isOpenObject());
normalized.add(arangodb::StaticStrings::IndexType,
arangodb::velocypack::Value(Index::oldtypeName(Index::TRI_IDX_TYPE_TTL_INDEX)));
@ -296,9 +320,12 @@ struct PrimaryIndexFactory : public DefaultIndexFactory {
return arangodb::Result();
}
arangodb::Result normalize(arangodb::velocypack::Builder& normalized,
arangodb::velocypack::Slice definition,
bool isCreation) const override {
virtual arangodb::Result normalize( // normalize definition
arangodb::velocypack::Builder& normalized, // normalized definition (out-param)
arangodb::velocypack::Slice definition, // source definition
bool isCreation, // definition for index creation
TRI_vocbase_t const& vocbase // index vocbase
) const override {
if (isCreation) {
// creating these indexes yourself is forbidden
return TRI_ERROR_FORBIDDEN;
@ -326,9 +353,12 @@ struct SkiplistIndexFactory : public DefaultIndexFactory {
return arangodb::Result();
}
arangodb::Result normalize(arangodb::velocypack::Builder& normalized,
arangodb::velocypack::Slice definition,
bool isCreation) const override {
virtual arangodb::Result normalize( // normalize definition
arangodb::velocypack::Builder& normalized, // normalized definition (out-param)
arangodb::velocypack::Slice definition, // source definition
bool isCreation, // definition for index creation
TRI_vocbase_t const& vocbase // index vocbase
) const override {
TRI_ASSERT(normalized.isOpenObject());
normalized.add(arangodb::StaticStrings::IndexType,
arangodb::velocypack::Value(

View File

@ -37,16 +37,12 @@
namespace {
////////////////////////////////////////////////////////////////////////////////
/// @return the specified object is granted 'level' access
/// @return the specified vocbase is granted 'level' access
////////////////////////////////////////////////////////////////////////////////
bool canUse(arangodb::auth::Level level, TRI_vocbase_t const& vocbase,
std::string const* dataSource = nullptr // nullptr == validate only vocbase
) {
bool canUse(arangodb::auth::Level level, TRI_vocbase_t const& vocbase) {
auto* execCtx = arangodb::ExecContext::CURRENT;
return !execCtx ||
(execCtx->canUseDatabase(vocbase.name(), level) &&
(!dataSource || execCtx->canUseCollection(vocbase.name(), *dataSource, level)));
return !execCtx || execCtx->canUseDatabase(vocbase.name(), level);
}
} // namespace
@ -71,10 +67,7 @@ void RestViewHandler::getView(std::string const& nameOrId, bool detailed) {
// end of parameter parsing
// ...........................................................................
if (!canUse(auth::Level::RO,
view->vocbase())) { // as per
// https://github.com/arangodb/backlog/issues/459
// if (!canUse(auth::Level::RO, view->vocbase(), &view->name())) { // check
if (!view->canUse(auth::Level::RO)) { // check auth after ensuring that the view exists
// auth after ensuring that the view exists
generateError(
Result(TRI_ERROR_FORBIDDEN, "insufficient rights to get view"));
@ -284,11 +277,7 @@ void RestViewHandler::modifyView(bool partialUpdate) {
// end of parameter parsing
// .......................................................................
if (!canUse(auth::Level::RW,
view->vocbase())) { // as per
// https://github.com/arangodb/backlog/issues/459
// if (!canUse(auth::Level::RW, view->vocbase(), &view->name())) { //
// check auth after ensuring that the view exists
if (!view->canUse(auth::Level::RW)) { // check auth after ensuring that the view exists
generateError(
Result(TRI_ERROR_FORBIDDEN, "insufficient rights to rename view"));
@ -330,11 +319,7 @@ void RestViewHandler::modifyView(bool partialUpdate) {
// end of parameter parsing
// .........................................................................
if (!canUse(auth::Level::RW,
view->vocbase())) { // as per
// https://github.com/arangodb/backlog/issues/459
// if (!canUse(auth::Level::RW, view->vocbase(), &view->name())) { //
// check auth after ensuring that the view exists
if (!view->canUse(auth::Level::RW)) { // check auth after ensuring that the view exists
generateError(
Result(TRI_ERROR_FORBIDDEN, "insufficient rights to modify view"));
@ -421,11 +406,7 @@ void RestViewHandler::deleteView() {
// end of parameter parsing
// ...........................................................................
if (!canUse(auth::Level::RW,
view->vocbase())) { // as per
// https://github.com/arangodb/backlog/issues/459
// if (!canUse(auth::Level::RW, view->vocbase(), &view->name())) { // check
// auth after ensuring that the view exists
if (!view->canUse(auth::Level::RW)) { // check auth after ensuring that the view exists
generateError(
Result(TRI_ERROR_FORBIDDEN, "insufficient rights to drop view"));
@ -506,10 +487,7 @@ void RestViewHandler::getViews() {
for (auto view : views) {
if (view && (!excludeSystem || !view->system())) {
if (!canUse(auth::Level::RO,
view->vocbase())) { // as per
// https://github.com/arangodb/backlog/issues/459
// if (!canUse(auth::Level::RO, view->vocbase(), &view->name())) {
if (!view->canUse(auth::Level::RO)) { // check auth after ensuring that the view exists
continue; // skip views that are not authorized to be read
}

View File

@ -61,6 +61,12 @@ std::string const RestVocbaseBaseHandler::AGENCY_PATH = "/_api/agency";
std::string const RestVocbaseBaseHandler::AGENCY_PRIV_PATH =
"/_api/agency_priv";
////////////////////////////////////////////////////////////////////////////////
/// @brief analyzer path
////////////////////////////////////////////////////////////////////////////////
/*static*/ std::string const RestVocbaseBaseHandler::ANALYZER_PATH =
"/_api/analyzer";
////////////////////////////////////////////////////////////////////////////////
/// @brief batch path
////////////////////////////////////////////////////////////////////////////////

View File

@ -60,6 +60,11 @@ class RestVocbaseBaseHandler : public RestBaseHandler {
static std::string const AGENCY_PRIV_PATH;
//////////////////////////////////////////////////////////////////////////////
/// @brief analyzer path
//////////////////////////////////////////////////////////////////////////////
static std::string const ANALYZER_PATH;
//////////////////////////////////////////////////////////////////////////////
/// @brief batch path
//////////////////////////////////////////////////////////////////////////////
@ -401,4 +406,4 @@ class RestVocbaseBaseHandler : public RestBaseHandler {
} // namespace arangodb
#endif
#endif

View File

@ -88,9 +88,12 @@ struct EdgeIndexFactory : public DefaultIndexFactory {
return arangodb::Result();
}
arangodb::Result normalize(arangodb::velocypack::Builder& normalized,
arangodb::velocypack::Slice definition,
bool isCreation) const override {
virtual arangodb::Result normalize( // normalize definition
arangodb::velocypack::Builder& normalized, // normalized definition (out-param)
arangodb::velocypack::Slice definition, // source definition
bool isCreation, // definition for index creation
TRI_vocbase_t const& vocbase // index vocbase
) const override {
if (isCreation) {
// creating these indexes yourself is forbidden
return TRI_ERROR_FORBIDDEN;
@ -119,9 +122,12 @@ struct FulltextIndexFactory : public DefaultIndexFactory {
return arangodb::Result();
}
arangodb::Result normalize(arangodb::velocypack::Builder& normalized,
arangodb::velocypack::Slice definition,
bool isCreation) const override {
virtual arangodb::Result normalize( // normalize definition
arangodb::velocypack::Builder& normalized, // normalized definition (out-param)
arangodb::velocypack::Slice definition, // source definition
bool isCreation, // definition for index creation
TRI_vocbase_t const& vocbase // index vocbase
) const override {
TRI_ASSERT(normalized.isOpenObject());
normalized.add(arangodb::StaticStrings::IndexType,
arangodb::velocypack::Value(arangodb::Index::oldtypeName(
@ -152,9 +158,12 @@ struct GeoIndexFactory : public DefaultIndexFactory {
return arangodb::Result();
}
arangodb::Result normalize(arangodb::velocypack::Builder& normalized,
arangodb::velocypack::Slice definition,
bool isCreation) const override {
virtual arangodb::Result normalize( // normalize definition
arangodb::velocypack::Builder& normalized, // normalized definition (out-param)
arangodb::velocypack::Slice definition, // source definition
bool isCreation, // definition for index creation
TRI_vocbase_t const& vocbase // index vocbase
) const override {
TRI_ASSERT(normalized.isOpenObject());
normalized.add(arangodb::StaticStrings::IndexType,
arangodb::velocypack::Value(arangodb::Index::oldtypeName(
@ -184,9 +193,12 @@ struct Geo1IndexFactory : public DefaultIndexFactory {
return arangodb::Result();
}
arangodb::Result normalize(arangodb::velocypack::Builder& normalized,
arangodb::velocypack::Slice definition,
bool isCreation) const override {
virtual arangodb::Result normalize( // normalize definition
arangodb::velocypack::Builder& normalized, // normalized definition (out-param)
arangodb::velocypack::Slice definition, // source definition
bool isCreation, // definition for index creation
TRI_vocbase_t const& vocbase // index vocbase
) const override {
TRI_ASSERT(normalized.isOpenObject());
normalized.add(arangodb::StaticStrings::IndexType,
arangodb::velocypack::Value(arangodb::Index::oldtypeName(
@ -217,9 +229,12 @@ struct Geo2IndexFactory : public DefaultIndexFactory {
return arangodb::Result();
}
arangodb::Result normalize(arangodb::velocypack::Builder& normalized,
arangodb::velocypack::Slice definition,
bool isCreation) const override {
virtual arangodb::Result normalize( // normalize definition
arangodb::velocypack::Builder& normalized, // normalized definition (out-param)
arangodb::velocypack::Slice definition, // source definition
bool isCreation, // definition for index creation
TRI_vocbase_t const& vocbase // index vocbase
) const override {
TRI_ASSERT(normalized.isOpenObject());
normalized.add(arangodb::StaticStrings::IndexType,
arangodb::velocypack::Value(arangodb::Index::oldtypeName(
@ -248,9 +263,12 @@ struct SecondaryIndexFactory : public DefaultIndexFactory {
return arangodb::Result();
}
arangodb::Result normalize(arangodb::velocypack::Builder& normalized,
arangodb::velocypack::Slice definition,
bool isCreation) const override {
virtual arangodb::Result normalize( // normalize definition
arangodb::velocypack::Builder& normalized, // normalized definition (out-param)
arangodb::velocypack::Slice definition, // source definition
bool isCreation, // definition for index creation
TRI_vocbase_t const& vocbase // index vocbase
) const override {
TRI_ASSERT(normalized.isOpenObject());
normalized.add(arangodb::StaticStrings::IndexType,
arangodb::velocypack::Value(arangodb::Index::oldtypeName(type)));
@ -277,9 +295,12 @@ struct TtlIndexFactory : public DefaultIndexFactory {
return arangodb::Result();
}
arangodb::Result normalize(arangodb::velocypack::Builder& normalized,
arangodb::velocypack::Slice definition,
bool isCreation) const override {
virtual arangodb::Result normalize( // normalize definition
arangodb::velocypack::Builder& normalized, // normalized definition (out-param)
arangodb::velocypack::Slice definition, // source definition
bool isCreation, // definition for index creation
TRI_vocbase_t const& vocbase // index vocbase
) const override {
TRI_ASSERT(normalized.isOpenObject());
normalized.add(arangodb::StaticStrings::IndexType,
arangodb::velocypack::Value(arangodb::Index::oldtypeName(_type)));
@ -314,9 +335,12 @@ struct PrimaryIndexFactory : public DefaultIndexFactory {
return arangodb::Result();
}
arangodb::Result normalize(arangodb::velocypack::Builder& normalized,
arangodb::velocypack::Slice definition,
bool isCreation) const override {
virtual arangodb::Result normalize( // normalize definition
arangodb::velocypack::Builder& normalized, // normalized definition (out-param)
arangodb::velocypack::Slice definition, // source definition
bool isCreation, // definition for index creation
TRI_vocbase_t const& vocbase // index vocbase
) const override {
if (isCreation) {
// creating these indexes yourself is forbidden
return TRI_ERROR_FORBIDDEN;
@ -539,4 +563,4 @@ void RocksDBIndexFactory::prepareIndexes(
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------

View File

@ -42,16 +42,12 @@
namespace {
////////////////////////////////////////////////////////////////////////////////
/// @return the specified object is granted 'level' access
/// @return the specified vocbase is granted 'level' access
////////////////////////////////////////////////////////////////////////////////
bool canUse(arangodb::auth::Level level, TRI_vocbase_t const& vocbase,
std::string const* dataSource = nullptr // nullptr == validate only vocbase
) {
bool canUse(arangodb::auth::Level level, TRI_vocbase_t const& vocbase) {
auto* execCtx = arangodb::ExecContext::CURRENT;
return !execCtx ||
(execCtx->canUseDatabase(vocbase.name(), level) &&
(!dataSource || execCtx->canUseCollection(vocbase.name(), *dataSource, level)));
return !execCtx || execCtx->canUseDatabase(vocbase.name(), level);
}
////////////////////////////////////////////////////////////////////////////////
@ -260,11 +256,7 @@ static void JS_DropViewVocbase(v8::FunctionCallbackInfo<v8::Value> const& args)
auto view = CollectionNameResolver(vocbase).getView(name);
if (view) {
if (!canUse(auth::Level::RW,
vocbase)) { // as per
// https://github.com/arangodb/backlog/issues/459
// if (!canUse(auth::Level::RW, vocbase, &view->name())) { // check auth
// after ensuring that the view exists
if (!view->canUse(auth::Level::RW)) { // check auth after ensuring that the view exists
TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_ERROR_FORBIDDEN,
"insufficient rights to drop view");
}
@ -323,11 +315,7 @@ static void JS_DropViewVocbaseObj(v8::FunctionCallbackInfo<v8::Value> const& arg
// end of parameter parsing
// ...........................................................................
if (!canUse(auth::Level::RW,
view->vocbase())) { // as per
// https://github.com/arangodb/backlog/issues/459
// if (!canUse(auth::Level::RW, view->vocbase(), &view->name())) { // check
// auth after ensuring that the view exists
if (!view->canUse(auth::Level::RW)) { // check auth after ensuring that the view exists
TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_ERROR_FORBIDDEN,
"insufficient rights to drop view");
}
@ -373,10 +361,7 @@ static void JS_ViewVocbase(v8::FunctionCallbackInfo<v8::Value> const& args) {
// end of parameter parsing
// ...........................................................................
if (!canUse(auth::Level::RO,
vocbase)) { // as per https://github.com/arangodb/backlog/issues/459
// if (!canUse(auth::Level::RO, vocbase, &view->name())) { // check auth
// after ensuring that the view exists
if (!view->canUse(auth::Level::RO)) { // check auth after ensuring that the view exists
TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_ERROR_FORBIDDEN,
"insufficient rights to get view");
}
@ -450,10 +435,7 @@ static void JS_ViewsVocbase(v8::FunctionCallbackInfo<v8::Value> const& args) {
for (size_t i = 0; i < n; ++i) {
auto view = views[i];
if (!canUse(auth::Level::RO,
vocbase)) { // as per
// https://github.com/arangodb/backlog/issues/459
// if (!canUse(auth::Level::RO, vocbase, &view->name())) {
if (!view || !view->canUse(auth::Level::RO)) { // check auth after ensuring that the view exists
continue; // skip views that are not authorized to be read
}
@ -504,11 +486,7 @@ static void JS_NameViewVocbase(v8::FunctionCallbackInfo<v8::Value> const& args)
// end of parameter parsing
// ...........................................................................
if (!canUse(auth::Level::RO,
view->vocbase())) { // as per
// https://github.com/arangodb/backlog/issues/459
// if (!canUse(auth::Level::RO, view->vocbase(), &view->name())) { // check
// auth after ensuring that the view exists
if (!view->canUse(auth::Level::RO)) { // check auth after ensuring that the view exists
TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_ERROR_FORBIDDEN,
"insufficient rights to get view");
}
@ -564,11 +542,7 @@ static void JS_PropertiesViewVocbase(v8::FunctionCallbackInfo<v8::Value> const&
// end of parameter parsing
// ...........................................................................
if (!canUse(auth::Level::RW,
viewPtr->vocbase())) { // as per
// https://github.com/arangodb/backlog/issues/459
// if (!canUse(auth::Level::RW, viewPtr->vocbase(), &viewPtr->name())) {
// // check auth after ensuring that the view exists
if (!viewPtr->canUse(auth::Level::RW)) { // check auth after ensuring that the view exists
TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_ERROR_FORBIDDEN,
"insufficient rights to modify view");
}
@ -609,11 +583,7 @@ static void JS_PropertiesViewVocbase(v8::FunctionCallbackInfo<v8::Value> const&
// end of parameter parsing
// ...........................................................................
if (!canUse(auth::Level::RO,
view->vocbase())) { // as per
// https://github.com/arangodb/backlog/issues/459
// if (!canUse(auth::Level::RO, view->vocbase(), &view->name())) { // check
// auth after ensuring that the view exists
if (!view->canUse(auth::Level::RO)) { // check auth after ensuring that the view exists
TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_ERROR_FORBIDDEN,
"insufficient rights to get view");
}
@ -668,11 +638,7 @@ static void JS_RenameViewVocbase(v8::FunctionCallbackInfo<v8::Value> const& args
// end of parameter parsing
// ...........................................................................
if (!canUse(auth::Level::RW,
view->vocbase())) { // as per
// https://github.com/arangodb/backlog/issues/459
// if (!canUse(auth::Level::RW, view->vocbase(), &view->name())) { // check
// auth after ensuring that the view exists
if (!view->canUse(auth::Level::RW)) { // check auth after ensuring that the view exists
TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_ERROR_FORBIDDEN,
"insufficient rights to rename view");
}
@ -717,11 +683,7 @@ static void JS_TypeViewVocbase(v8::FunctionCallbackInfo<v8::Value> const& args)
// end of parameter parsing
// ...........................................................................
if (!canUse(auth::Level::RO,
view->vocbase())) { // as per
// https://github.com/arangodb/backlog/issues/459
// if (!canUse(auth::Level::RO, view->vocbase(), &view->name())) { // check
// auth after ensuring that the view exists
if (!view->canUse(auth::Level::RO)) { // check auth after ensuring that the view exists
TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_ERROR_FORBIDDEN,
"insufficient rights to get view");
}
@ -767,4 +729,4 @@ void TRI_InitV8Views(v8::Handle<v8::Context> context, TRI_vocbase_t* vocbase,
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------

View File

@ -31,6 +31,7 @@
#include "RestServer/ViewTypesFeature.h"
#include "StorageEngine/EngineSelectorFeature.h"
#include "StorageEngine/StorageEngine.h"
#include "Utils/ExecContext.h"
#include "VocBase/ticks.h"
#include "VocBase/vocbase.h"
@ -84,6 +85,20 @@ Result LogicalView::appendVelocyPack(velocypack::Builder& builder,
return appendVelocyPackImpl(builder, detailed, forPersistence);
}
bool LogicalView::canUse(arangodb::auth::Level const& level) {
auto* ctx = arangodb::ExecContext::CURRENT;
// as per https://github.com/arangodb/backlog/issues/459
return !ctx || ctx->canUseDatabase(vocbase().name(), level); // can use vocbase
/* FIXME TODO per-view authentication checks disabled as per https://github.com/arangodb/backlog/issues/459
return !ctx // authentication not enabled
|| (ctx->canUseDatabase(vocbase.name(), level) // can use vocbase
&& (ctx->canUseCollection(vocbase.name(), name(), level)) // can use view
);
*/
}
/*static*/ LogicalDataSource::Category const& LogicalView::category() noexcept {
static const Category category;

View File

@ -24,6 +24,7 @@
#ifndef ARANGOD_VOCBASE_LOGICAL_VIEW_H
#define ARANGOD_VOCBASE_LOGICAL_VIEW_H 1
#include "Auth/Common.h"
#include "Basics/Common.h"
#include "Basics/ReadWriteLock.h"
#include "Basics/Result.h"
@ -36,11 +37,15 @@
#include <velocypack/Buffer.h>
namespace arangodb {
namespace velocypack {
class Slice;
class Builder;
} // namespace velocypack
} // arangodb
namespace arangodb {
////////////////////////////////////////////////////////////////////////////////
/// @class LogicalView
@ -103,6 +108,11 @@ class LogicalView : public LogicalDataSource {
virtual Result appendVelocyPack(velocypack::Builder& builder, bool detailed,
bool forPersistence) const override final;
//////////////////////////////////////////////////////////////////////////////
/// @return the current view is granted 'level' access
//////////////////////////////////////////////////////////////////////////////
bool canUse(arangodb::auth::Level const& level);
//////////////////////////////////////////////////////////////////////////////
/// @brief the category representing a logical view
//////////////////////////////////////////////////////////////////////////////

View File

@ -330,17 +330,17 @@ Result Indexes::ensureIndex(LogicalCollection* collection, VPackSlice const& inp
}
}
TRI_ASSERT(collection);
VPackBuilder normalized;
StorageEngine* engine = EngineSelectorFeature::ENGINE;
auto res =
engine->indexFactory().enhanceIndexDefinition(input, normalized, create,
ServerState::instance()->isCoordinator());
auto res = engine->indexFactory().enhanceIndexDefinition( // normalize definition
input, normalized, create, collection->vocbase() // args
);
if (!res.ok()) {
return res;
}
TRI_ASSERT(collection);
auto const& dbname = collection->vocbase().name();
std::string const collname(collection->name());
VPackSlice indexDef = normalized.slice();

View File

@ -67,6 +67,7 @@ if (USE_IRESEARCH)
V8Server/v8-users-test.cpp
V8Server/v8-views-test.cpp
VocBase/LogicalDataSource-test.cpp
VocBase/LogicalView-test.cpp
VocBase/vocbase-test.cpp
)
endif ()

View File

@ -29,6 +29,7 @@
#include "analysis/token_attributes.hpp"
#include "Aql/AqlFunctionFeature.h"
#include "Aql/QueryRegistry.h"
#if USE_ENTERPRISE
#include "Enterprise/Ldap/LdapFeature.h"
@ -44,6 +45,7 @@
#include "Sharding/ShardingFeature.h"
#include "StorageEngine/EngineSelectorFeature.h"
#include "Transaction/StandaloneContext.h"
#include "Utils/ExecContext.h"
#include "Utils/OperationOptions.h"
#include "Utils/SingleCollectionTransaction.h"
#include "VocBase/KeyGenerator.h"
@ -254,6 +256,141 @@ TEST_CASE("IResearchAnalyzerFeatureTest", "[iresearch][iresearch-feature]") {
IResearchAnalyzerFeatureSetup s;
UNUSED(s);
SECTION("test_auth") {
// no ExecContext
{
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
CHECK((true == arangodb::iresearch::IResearchAnalyzerFeature::canUse(vocbase, arangodb::auth::Level::RW)));
}
// no vocbase read access
{
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
struct ExecContext: public arangodb::ExecContext {
ExecContext(): arangodb::ExecContext(
arangodb::ExecContext::Type::Default, "", "testVocbase", 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);
CHECK((false == arangodb::iresearch::IResearchAnalyzerFeature::canUse(vocbase, arangodb::auth::Level::RO)));
}
// no collection read access (vocbase read access, no user)
{
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
struct ExecContext: public arangodb::ExecContext {
ExecContext(): arangodb::ExecContext(
arangodb::ExecContext::Type::Default, "", "testVocbase", arangodb::auth::Level::NONE, arangodb::auth::Level::RO
) {}
} 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::auth::UserMap userMap; // empty map, no user -> no permissions
userManager->setAuthInfo(userMap); // set user map to avoid loading configuration from system database
CHECK((false == arangodb::iresearch::IResearchAnalyzerFeature::canUse(vocbase, arangodb::auth::Level::RO)));
}
// no collection read access (vocbase read access)
{
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
struct ExecContext: public arangodb::ExecContext {
ExecContext(): arangodb::ExecContext(
arangodb::ExecContext::Type::Default, "", "testVocbase", arangodb::auth::Level::NONE, arangodb::auth::Level::RO
) {}
} 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::auth::UserMap userMap;
auto& user = userMap.emplace("", arangodb::auth::User::newUser("", "", arangodb::auth::Source::LDAP)).first->second;
user.grantDatabase(vocbase.name(), arangodb::auth::Level::NONE); // system collections use vocbase auth level
userManager->setAuthInfo(userMap); // set user map to avoid loading configuration from system database
CHECK((false == arangodb::iresearch::IResearchAnalyzerFeature::canUse(vocbase, arangodb::auth::Level::RO)));
}
// no vocbase write access
{
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
struct ExecContext: public arangodb::ExecContext {
ExecContext(): arangodb::ExecContext(
arangodb::ExecContext::Type::Default, "", "testVocbase", arangodb::auth::Level::NONE, arangodb::auth::Level::RO
) {}
} 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::auth::UserMap userMap;
auto& user = userMap.emplace("", arangodb::auth::User::newUser("", "", arangodb::auth::Source::LDAP)).first->second;
user.grantDatabase(vocbase.name(), arangodb::auth::Level::RO); // system collections use vocbase auth level
userManager->setAuthInfo(userMap); // set user map to avoid loading configuration from system database
CHECK((true == arangodb::iresearch::IResearchAnalyzerFeature::canUse(vocbase, arangodb::auth::Level::RO)));
CHECK((false == arangodb::iresearch::IResearchAnalyzerFeature::canUse(vocbase, arangodb::auth::Level::RW)));
}
// no collection write access (vocbase write access)
{
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
struct ExecContext: public arangodb::ExecContext {
ExecContext(): arangodb::ExecContext(
arangodb::ExecContext::Type::Default, "", "testVocbase", arangodb::auth::Level::NONE, arangodb::auth::Level::RW
) {}
} 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::auth::UserMap userMap;
auto& user = userMap.emplace("", arangodb::auth::User::newUser("", "", arangodb::auth::Source::LDAP)).first->second;
user.grantDatabase(vocbase.name(), arangodb::auth::Level::RO); // system collections use vocbase auth level
userManager->setAuthInfo(userMap); // set user map to avoid loading configuration from system database
CHECK((true == arangodb::iresearch::IResearchAnalyzerFeature::canUse(vocbase, arangodb::auth::Level::RO)));
CHECK((false == arangodb::iresearch::IResearchAnalyzerFeature::canUse(vocbase, arangodb::auth::Level::RW)));
}
// collection write access
{
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
struct ExecContext: public arangodb::ExecContext {
ExecContext(): arangodb::ExecContext(
arangodb::ExecContext::Type::Default, "", "testVocbase", arangodb::auth::Level::NONE, arangodb::auth::Level::RW
) {}
} 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::auth::UserMap userMap;
auto& user = userMap.emplace("", arangodb::auth::User::newUser("", "", arangodb::auth::Source::LDAP)).first->second;
user.grantDatabase(vocbase.name(), arangodb::auth::Level::RW); // system collections use vocbase auth level
userManager->setAuthInfo(userMap); // set user map to avoid loading configuration from system database
CHECK((true == arangodb::iresearch::IResearchAnalyzerFeature::canUse(vocbase, arangodb::auth::Level::RW)));
}
}
SECTION("test_emplace") {
// add valid
{
@ -525,6 +662,151 @@ SECTION("test_identity") {
}
}
SECTION("test_normalize") {
TRI_vocbase_t active(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "active");
TRI_vocbase_t system(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "system");
// normalize 'identity' (with prefix)
{
irs::string_ref analyzer = "identity";
auto normalized = arangodb::iresearch::IResearchAnalyzerFeature::normalize(analyzer, active, system, true);
CHECK((std::string("identity") == normalized));
}
// normalize 'identity' (without prefix)
{
irs::string_ref analyzer = "identity";
auto normalized = arangodb::iresearch::IResearchAnalyzerFeature::normalize(analyzer, active, system, true);
CHECK((std::string("identity") == normalized));
}
// 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));
}
// 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;
auto normalized = arangodb::iresearch::IResearchAnalyzerFeature::normalize(analyzer, active, system, false);
CHECK((std::string("") == normalized));
}
// normalize delimiter (with prefix)
{
irs::string_ref analyzer = "::";
auto normalized = arangodb::iresearch::IResearchAnalyzerFeature::normalize(analyzer, active, system, true);
CHECK((std::string("system::") == normalized));
}
// normalize delimiter (without prefix)
{
irs::string_ref analyzer = "::";
auto normalized = arangodb::iresearch::IResearchAnalyzerFeature::normalize(analyzer, active, system, false);
CHECK((std::string("::") == normalized));
}
// normalize delimiter + name (with prefix)
{
irs::string_ref analyzer = "::name";
auto normalized = arangodb::iresearch::IResearchAnalyzerFeature::normalize(analyzer, active, system, true);
CHECK((std::string("system::name") == normalized));
}
// normalize delimiter + name (without prefix)
{
irs::string_ref analyzer = "::name";
auto normalized = arangodb::iresearch::IResearchAnalyzerFeature::normalize(analyzer, active, system, false);
CHECK((std::string("::name") == normalized));
}
// 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";
auto normalized = arangodb::iresearch::IResearchAnalyzerFeature::normalize(analyzer, active, system, false);
CHECK((std::string("name") == normalized));
}
// normalize system + delimiter (with prefix)
{
irs::string_ref analyzer = "system::";
auto normalized = arangodb::iresearch::IResearchAnalyzerFeature::normalize(analyzer, active, system, true);
CHECK((std::string("system::") == normalized));
}
// normalize system + delimiter (without prefix)
{
irs::string_ref analyzer = "system::";
auto normalized = arangodb::iresearch::IResearchAnalyzerFeature::normalize(analyzer, active, system, false);
CHECK((std::string("::") == normalized));
}
// normalize vocbase + delimiter (with prefix)
{
irs::string_ref analyzer = "active::";
auto normalized = arangodb::iresearch::IResearchAnalyzerFeature::normalize(analyzer, active, system, true);
CHECK((std::string("active::") == normalized));
}
// normalize vocbase + delimiter (without prefix)
{
irs::string_ref analyzer = "active::";
auto normalized = arangodb::iresearch::IResearchAnalyzerFeature::normalize(analyzer, active, system, false);
CHECK((std::string("") == normalized));
}
// normalize system + delimiter + name (with prefix)
{
irs::string_ref analyzer = "system::name";
auto normalized = arangodb::iresearch::IResearchAnalyzerFeature::normalize(analyzer, active, system, true);
CHECK((std::string("system::name") == normalized));
}
// normalize system + delimiter + name (without prefix)
{
irs::string_ref analyzer = "system::name";
auto normalized = arangodb::iresearch::IResearchAnalyzerFeature::normalize(analyzer, active, system, false);
CHECK((std::string("::name") == normalized));
}
// normalize vocbase + delimiter + name (with prefix)
{
irs::string_ref analyzer = "active::name";
auto normalized = arangodb::iresearch::IResearchAnalyzerFeature::normalize(analyzer, active, system, true);
CHECK((std::string("active::name") == normalized));
}
// normalize vocbase + delimiter + name (without prefix)
{
irs::string_ref analyzer = "active::name";
auto normalized = arangodb::iresearch::IResearchAnalyzerFeature::normalize(analyzer, active, system, false);
CHECK((std::string("name") == normalized));
}
}
SECTION("test_text_features") {
// test registered 'identity'
{
@ -1358,6 +1640,66 @@ SECTION("test_visit") {
CHECK((false == result));
CHECK((2 == expected.size()));
}
TRI_vocbase_t vocbaseEmpty(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "vocbaseEmpty");
TRI_vocbase_t vocbaseNonEmpty(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "vocbase0");
// add database-prefixed analyzers
{
CHECK((false == !feature.emplace("vocbase0::test_analyzer3", "TestAnalyzer", "abc3").first));
CHECK((false == !feature.emplace("vocbase0::test_analyzer4", "TestAnalyzer", "abc4").first));
CHECK((false == !feature.emplace("vocbase1::test_analyzer5", "TestAnalyzer", "abc5").first));
}
// full visitation limited to a vocbase (empty)
{
std::set<std::pair<irs::string_ref, irs::string_ref>> expected = {};
auto result = feature.visit(
[&expected](
irs::string_ref const& name,
irs::string_ref const& type,
irs::string_ref const& properties
)->bool {
if (staticAnalyzers().find(name) != staticAnalyzers().end()) {
return true; // skip static analyzers
}
CHECK((type == "TestAnalyzer"));
CHECK((1 == expected.erase(std::make_pair<irs::string_ref, irs::string_ref>(irs::string_ref(name), irs::string_ref(properties)))));
return true;
},
&vocbaseEmpty
);
CHECK((true == result));
CHECK((expected.empty()));
}
// full visitation limited to a vocbase (non-empty)
{
std::set<std::pair<irs::string_ref, irs::string_ref>> expected = {
std::make_pair<irs::string_ref, irs::string_ref>("vocbase0::test_analyzer3", "abc3"),
std::make_pair<irs::string_ref, irs::string_ref>("vocbase0::test_analyzer4", "abc4"),
};
auto result = feature.visit(
[&expected](
irs::string_ref const& name,
irs::string_ref const& type,
irs::string_ref const& properties
)->bool {
if (staticAnalyzers().find(name) != staticAnalyzers().end()) {
return true; // skip static analyzers
}
CHECK((type == "TestAnalyzer"));
CHECK((1 == expected.erase(std::make_pair<irs::string_ref, irs::string_ref>(irs::string_ref(name), irs::string_ref(properties)))));
return true;
},
&vocbaseNonEmpty
);
CHECK((true == result));
CHECK((expected.empty()));
}
}
////////////////////////////////////////////////////////////////////////////////
@ -1368,4 +1710,4 @@ SECTION("test_visit") {
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------

View File

@ -57,6 +57,7 @@
#include "RestServer/DatabaseFeature.h"
#include "RestServer/DatabasePathFeature.h"
#include "RestServer/QueryRegistryFeature.h"
#include "RestServer/SystemDatabaseFeature.h"
#include "RestServer/UpgradeFeature.h"
#include "RestServer/ViewTypesFeature.h"
#include "Sharding/ShardingFeature.h"
@ -399,13 +400,15 @@ SECTION("test_upgrade0_1") {
arangodb::application_features::ApplicationServer server(nullptr, nullptr);
arangodb::DatabaseFeature* database;
arangodb::iresearch::IResearchFeature feature(server);
server.addFeature(new arangodb::QueryRegistryFeature(server)); // required for constructing TRI_vocbase_t
TRI_vocbase_t system(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 0, TRI_VOC_SYSTEM_DATABASE);
server.addFeature(new arangodb::AuthenticationFeature(server)); // required for ClusterComm::instance()
server.addFeature(new arangodb::ClusterFeature(server)); // required to create ClusterInfo instance
server.addFeature(new arangodb::application_features::CommunicationFeaturePhase(server)); // required for SimpleHttpClient::doRequest()
server.addFeature(database = new arangodb::DatabaseFeature(server)); // required to skip IResearchView validation
server.addFeature(new arangodb::iresearch::IResearchAnalyzerFeature(server)); // required for restoring link analyzers
server.addFeature(new arangodb::QueryRegistryFeature(server)); // required for constructing TRI_vocbase_t
server.addFeature(new arangodb::ShardingFeature(server)); // required for LogicalCollection::LogicalCollection(...)
server.addFeature(new arangodb::SystemDatabaseFeature(server, &system)); // required for IResearchLinkHelper::normalize(...)
server.addFeature(new arangodb::UpgradeFeature(server, nullptr, {})); // required for upgrade tasks
server.addFeature(new arangodb::ViewTypesFeature(server)); // required for IResearchFeature::prepare()
@ -1088,4 +1091,4 @@ SECTION("test_async") {
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------

View File

@ -157,6 +157,7 @@ struct IResearchLinkMetaSetup {
>();
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'
// suppress log messages since tests check error conditions
arangodb::LogTopic::setLogLevel(arangodb::iresearch::TOPIC.name(), arangodb::LogLevel::FATAL);
@ -268,49 +269,69 @@ SECTION("test_inheritDefaults") {
}
SECTION("test_readDefaults") {
arangodb::iresearch::IResearchLinkMeta meta;
auto json = arangodb::velocypack::Parser::fromJson("{}");
std::string tmpString;
CHECK(true == meta.init(json->slice(), tmpString));
CHECK(true == meta._fields.empty());
CHECK(false == meta._includeAllFields);
CHECK(false == meta._trackListPositions);
CHECK((arangodb::iresearch::ValueStorage::NONE == meta._storeValues));
CHECK(1U == meta._analyzers.size());
CHECK((*(meta._analyzers.begin())));
CHECK(("identity" == (*(meta._analyzers.begin()))->name()));
CHECK((irs::flags({irs::norm::type(), irs::frequency::type()}) == (*(meta._analyzers.begin()))->features()));
// without active vobcase
{
arangodb::iresearch::IResearchLinkMeta meta;
std::string tmpString;
CHECK((true == meta.init(json->slice(), tmpString)));
CHECK((true == meta._fields.empty()));
CHECK((false == meta._includeAllFields));
CHECK((false == meta._trackListPositions));
CHECK((arangodb::iresearch::ValueStorage::NONE == meta._storeValues));
CHECK((1U == meta._analyzers.size()));
CHECK((*(meta._analyzers.begin())));
CHECK(("identity" == (*(meta._analyzers.begin()))->name()));
CHECK((irs::flags({irs::norm::type(), irs::frequency::type()}) == (*(meta._analyzers.begin()))->features()));
CHECK((false == !meta._analyzers.begin()->get()));
}
CHECK(false == !meta._analyzers.begin()->get());
// with active vocbase
{
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._fields.empty()));
CHECK((false == meta._includeAllFields));
CHECK((false == meta._trackListPositions));
CHECK((arangodb::iresearch::ValueStorage::NONE == meta._storeValues));
CHECK((1U == meta._analyzers.size()));
CHECK((*(meta._analyzers.begin())));
CHECK(("identity" == (*(meta._analyzers.begin()))->name()));
CHECK((irs::flags({irs::norm::type(), irs::frequency::type()}) == (*(meta._analyzers.begin()))->features()));
CHECK((false == !meta._analyzers.begin()->get()));
}
}
SECTION("test_readCustomizedValues") {
std::unordered_set<std::string> expectedFields = { "a", "b", "c" };
std::unordered_set<std::string> expectedOverrides = { "default", "all", "some", "none" };
std::unordered_set<std::string> expectedAnalyzers = { "empty", "identity" };
arangodb::iresearch::IResearchLinkMeta meta;
std::string tmpString;
{
auto json = arangodb::velocypack::Parser::fromJson("{ \
\"fields\": { \
\"a\": {}, \
\"b\": {}, \
\"c\": { \
\"fields\": { \
\"default\": { \"fields\": {}, \"includeAllFields\": false, \"trackListPositions\": false, \"storeValues\": \"none\", \"analyzers\": [ \"identity\" ] }, \
\"all\": { \"fields\": {\"d\": {}, \"e\": {}}, \"includeAllFields\": true, \"trackListPositions\": true, \"storeValues\": \"full\", \"analyzers\": [ \"empty\" ] }, \
\"some\": { \"trackListPositions\": true, \"storeValues\": \"id\" }, \
\"none\": {} \
} \
auto json = arangodb::velocypack::Parser::fromJson("{ \
\"fields\": { \
\"a\": {}, \
\"b\": {}, \
\"c\": { \
\"fields\": { \
\"default\": { \"fields\": {}, \"includeAllFields\": false, \"trackListPositions\": false, \"storeValues\": \"none\", \"analyzers\": [ \"identity\" ] }, \
\"all\": { \"fields\": {\"d\": {}, \"e\": {}}, \"includeAllFields\": true, \"trackListPositions\": true, \"storeValues\": \"full\", \"analyzers\": [ \"empty\" ] }, \
\"some\": { \"trackListPositions\": true, \"storeValues\": \"id\" }, \
\"none\": {} \
} \
}, \
\"includeAllFields\": true, \
\"trackListPositions\": true, \
\"storeValues\": \"full\", \
\"analyzers\": [ \"empty\", \"identity\" ] \
}");
} \
}, \
\"includeAllFields\": true, \
\"trackListPositions\": true, \
\"storeValues\": \"full\", \
\"analyzers\": [ \"empty\", \"identity\" ] \
}");
// without active vocbase
{
std::unordered_set<std::string> expectedFields = { "a", "b", "c" };
std::unordered_set<std::string> expectedOverrides = { "default", "all", "some", "none" };
std::unordered_set<std::string> expectedAnalyzers = { "empty", "identity" };
arangodb::iresearch::IResearchLinkMeta meta;
std::string tmpString;
CHECK(true == meta.init(json->slice(), tmpString));
CHECK(3U == meta._fields.size());
@ -395,34 +416,164 @@ SECTION("test_readCustomizedValues") {
CHECK((irs::flags({irs::norm::type(), irs::frequency::type()}) == (*itr)->features()));
CHECK(false == !itr->get());
}
// with active vocbase
{
std::unordered_set<std::string> expectedFields = { "a", "b", "c" };
std::unordered_set<std::string> expectedOverrides = { "default", "all", "some", "none" };
std::unordered_set<std::string> expectedAnalyzers = { "empty", "identity" };
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((3U == meta._fields.size()));
for (auto& field: meta._fields) {
CHECK((1U == expectedFields.erase(field.key())));
for (auto& fieldOverride: field.value()->_fields) {
auto& actual = *(fieldOverride.value());
CHECK((1U == expectedOverrides.erase(fieldOverride.key())));
if ("default" == fieldOverride.key()) {
CHECK((true == actual._fields.empty()));
CHECK((false == actual._includeAllFields));
CHECK((false == actual._trackListPositions));
CHECK((arangodb::iresearch::ValueStorage::NONE == actual._storeValues));
CHECK((1U == actual._analyzers.size()));
CHECK((*(actual._analyzers.begin())));
CHECK(("identity" == (*(actual._analyzers.begin()))->name()));
CHECK((irs::flags({irs::norm::type(), irs::frequency::type()}) == (*(actual._analyzers.begin()))->features()));
CHECK((false == !actual._analyzers.begin()->get()));
} else if ("all" == fieldOverride.key()) {
CHECK((2U == actual._fields.size()));
CHECK((true == (actual._fields.find("d") != actual._fields.end())));
CHECK((true == (actual._fields.find("e") != actual._fields.end())));
CHECK((true == actual._includeAllFields));
CHECK((true == actual._trackListPositions));
CHECK((arangodb::iresearch::ValueStorage::FULL == actual._storeValues));
CHECK((1U == actual._analyzers.size()));
CHECK((*(actual._analyzers.begin())));
CHECK(("testVocbase::empty" == (*(actual._analyzers.begin()))->name()));
CHECK((irs::flags({TestAttribute::type()}) == (*(actual._analyzers.begin()))->features()));
CHECK((false == !actual._analyzers.begin()->get()));
} else if ("some" == fieldOverride.key()) {
CHECK((true == actual._fields.empty())); // not inherited
CHECK((true == actual._includeAllFields)); // inherited
CHECK((true == actual._trackListPositions));
CHECK((arangodb::iresearch::ValueStorage::ID == actual._storeValues));
CHECK((2U == actual._analyzers.size()));
auto itr = actual._analyzers.begin();
CHECK((*itr));
CHECK(("testVocbase::empty" == (*itr)->name()));
CHECK((irs::flags({TestAttribute::type()}) == (*itr)->features()));
CHECK((false == !itr->get()));
++itr;
CHECK((*itr));
CHECK(("identity" == (*itr)->name()));
CHECK((irs::flags({irs::norm::type(), irs::frequency::type()}) == (*itr)->features()));
CHECK((false == !itr->get()));
} else if ("none" == fieldOverride.key()) {
CHECK((true == actual._fields.empty())); // not inherited
CHECK((true == actual._includeAllFields)); // inherited
CHECK((true == actual._trackListPositions)); // inherited
CHECK((arangodb::iresearch::ValueStorage::FULL == actual._storeValues));
auto itr = actual._analyzers.begin();
CHECK((*itr));
CHECK(("testVocbase::empty" == (*itr)->name()));
CHECK((irs::flags({TestAttribute::type()}) == (*itr)->features()));
CHECK((false == !itr->get()));
++itr;
CHECK((*itr));
CHECK(("identity" == (*itr)->name()));
CHECK((irs::flags({irs::norm::type(), irs::frequency::type()}) == (*itr)->features()));
CHECK((false == !itr->get()));
}
}
}
CHECK((true == expectedOverrides.empty()));
CHECK((true == expectedFields.empty()));
CHECK((true == meta._includeAllFields));
CHECK((true == meta._trackListPositions));
CHECK((arangodb::iresearch::ValueStorage::FULL == meta._storeValues));
auto itr = meta._analyzers.begin();
CHECK((*itr));
CHECK(("testVocbase::empty" == (*itr)->name()));
CHECK((irs::flags({TestAttribute::type()}) == (*itr)->features()));
CHECK((false == !itr->get()));
++itr;
CHECK((*itr));
CHECK(("identity" == (*itr)->name()));
CHECK((irs::flags({irs::norm::type(), irs::frequency::type()}) == (*itr)->features()));
CHECK((false == !itr->get()));
}
}
SECTION("test_writeDefaults") {
arangodb::iresearch::IResearchLinkMeta meta;
arangodb::velocypack::Builder builder;
arangodb::velocypack::Slice tmpSlice;
// without active vobcase
{
arangodb::iresearch::IResearchLinkMeta meta;
arangodb::velocypack::Builder builder;
arangodb::velocypack::Slice tmpSlice;
CHECK(true == meta.json(arangodb::velocypack::ObjectBuilder(&builder)));
builder.openObject();
CHECK((true == meta.json(builder)));
builder.close();
auto slice = builder.slice();
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).isString() &&
std::string("identity") == tmpSlice.at(0).copyString()
));
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).isString() &&
std::string("identity") == tmpSlice.at(0).copyString()
));
}
// with active vocbase
{
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, 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).isString() &&
std::string("identity") == tmpSlice.at(0).copyString()
));
}
}
SECTION("test_writeCustomizedValues") {
@ -466,106 +617,218 @@ SECTION("test_writeCustomizedValues") {
overrideSome._storeValues = arangodb::iresearch::ValueStorage::ID;
overrideNone._fields.clear(); // do not inherit fields to match jSon inheritance
std::unordered_set<std::string> expectedFields = { "a", "b", "c" };
std::unordered_set<std::string> expectedOverrides = { "default", "all", "some", "none" };
std::unordered_set<std::string> expectedAnalyzers = { "empty", "identity" };
arangodb::velocypack::Builder builder;
arangodb::velocypack::Slice tmpSlice;
// without active vobcase
{
std::unordered_set<std::string> expectedFields = { "a", "b", "c" };
std::unordered_set<std::string> expectedOverrides = { "default", "all", "some", "none" };
std::unordered_set<std::string> expectedAnalyzers = { "empty", "identity" };
arangodb::velocypack::Builder builder;
arangodb::velocypack::Slice tmpSlice;
CHECK(true == meta.json(arangodb::velocypack::ObjectBuilder(&builder)));
builder.openObject();
CHECK((true == meta.json(builder)));
builder.close();
auto slice = builder.slice();
auto slice = builder.slice();
CHECK((5U == slice.length()));
tmpSlice = slice.get("fields");
CHECK((true == tmpSlice.isObject() && 3 == tmpSlice.length()));
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());
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;
}
if (!value.hasKey("fields")) {
continue;
}
tmpSlice = value.get("fields");
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()));
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).isString() &&
std::string("identity") == tmpSlice.at(0).copyString()
));
} 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())));
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).isString() &&
std::string("identity") == tmpSlice.at(0).copyString()
));
} 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).isString() &&
std::string("empty") == tmpSlice.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 == 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).isString() &&
std::string("empty") == tmpSlice.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 key = *analyzersItr;
CHECK((true == key.isString() && 1 == expectedAnalyzers.erase(key.copyString())));
}
CHECK((true == expectedAnalyzers.empty()));
}
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()));
// with active vocbase
{
std::unordered_set<std::string> expectedFields = { "a", "b", "c" };
std::unordered_set<std::string> expectedOverrides = { "default", "all", "some", "none" };
std::unordered_set<std::string> expectedAnalyzers = { "empty", "identity" };
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
arangodb::velocypack::Builder builder;
arangodb::velocypack::Slice tmpSlice;
for (arangodb::velocypack::ArrayIterator analyzersItr(tmpSlice); analyzersItr.valid(); ++analyzersItr) {
auto key = *analyzersItr;
CHECK((true == key.isString() && 1 == expectedAnalyzers.erase(key.copyString())));
builder.openObject();
CHECK((true == meta.json(builder, 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).isString() &&
std::string("identity") == tmpSlice.at(0).copyString()
));
} 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).isString() &&
std::string("empty") == tmpSlice.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 key = *analyzersItr;
CHECK((true == key.isString() && 1 == expectedAnalyzers.erase(key.copyString())));
}
CHECK((true == expectedAnalyzers.empty()));
}
CHECK(true == expectedAnalyzers.empty());
}
SECTION("test_readMaskAll") {
@ -580,7 +843,7 @@ SECTION("test_readMaskAll") {
\"storeValues\": \"full\", \
\"analyzers\": [] \
}");
CHECK(true == meta.init(json->slice(), tmpString, arangodb::iresearch::IResearchLinkMeta::DEFAULT(), &mask));
CHECK(true == meta.init(json->slice(), tmpString, arangodb::iresearch::IResearchLinkMeta::DEFAULT(), nullptr, &mask));
CHECK(true == mask._fields);
CHECK(true == mask._includeAllFields);
CHECK(true == mask._trackListPositions);
@ -594,7 +857,7 @@ SECTION("test_readMaskNone") {
std::string tmpString;
auto json = arangodb::velocypack::Parser::fromJson("{}");
CHECK(true == meta.init(json->slice(), tmpString, arangodb::iresearch::IResearchLinkMeta::DEFAULT(), &mask));
CHECK(true == meta.init(json->slice(), tmpString, arangodb::iresearch::IResearchLinkMeta::DEFAULT(), nullptr, &mask));
CHECK(false == mask._fields);
CHECK(false == mask._includeAllFields);
CHECK(false == mask._trackListPositions);
@ -607,7 +870,9 @@ SECTION("test_writeMaskAll") {
arangodb::iresearch::IResearchLinkMeta::Mask mask(true);
arangodb::velocypack::Builder builder;
CHECK(true == meta.json(arangodb::velocypack::ObjectBuilder(&builder), nullptr, &mask));
builder.openObject();
CHECK((true == meta.json(builder, nullptr, nullptr, &mask)));
builder.close();
auto slice = builder.slice();
@ -624,7 +889,9 @@ SECTION("test_writeMaskNone") {
arangodb::iresearch::IResearchLinkMeta::Mask mask(false);
arangodb::velocypack::Builder builder;
CHECK(true == meta.json(arangodb::velocypack::ObjectBuilder(&builder), nullptr, &mask));
builder.openObject();
CHECK((true == meta.json(builder, nullptr, nullptr, &mask)));
builder.close();
auto slice = builder.slice();
@ -639,4 +906,4 @@ SECTION("test_writeMaskNone") {
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------

View File

@ -141,8 +141,8 @@ struct IResearchQueryTokensSetup {
arangodb::iresearch::IResearchAnalyzerFeature
>();
analyzers->emplace("test_analyzer", "TestAnalyzer", "abc"); // cache analyzer
analyzers->emplace("test_csv_analyzer", "TestDelimAnalyzer", ","); // cache analyzer
analyzers->emplace("testVocbase::test_analyzer", "TestAnalyzer", "abc"); // cache analyzer
analyzers->emplace("testVocbase::test_csv_analyzer", "TestDelimAnalyzer", ","); // cache analyzer
auto* dbPathFeature = arangodb::application_features::ApplicationServer::getFeature<arangodb::DatabasePathFeature>("DatabasePath");
arangodb::tests::setDatabasePath(*dbPathFeature); // ensure test data is stored in a unique directory

View File

@ -336,8 +336,10 @@ SECTION("test_writeCustomizedValues") {
arangodb::velocypack::Slice tmpSlice;
arangodb::velocypack::Slice tmpSlice2;
CHECK(true == meta.json(arangodb::velocypack::ObjectBuilder(&builder)));
CHECK((true == metaState.json(arangodb::velocypack::ObjectBuilder(&builder))));
builder.openObject();
CHECK((true == meta.json(builder)));
CHECK((true == metaState.json(builder)));
builder.close();
auto slice = builder.slice();
tmpSlice = slice.get("commitIntervalMsec");
@ -506,8 +508,10 @@ SECTION("test_writeMaskNone") {
arangodb::iresearch::IResearchViewMetaState::Mask maskState(false);
arangodb::velocypack::Builder builder;
CHECK(true == meta.json(arangodb::velocypack::ObjectBuilder(&builder), nullptr, &mask));
CHECK((true == metaState.json(arangodb::velocypack::ObjectBuilder(&builder), nullptr, &maskState)));
builder.openObject();
CHECK((true == meta.json(builder, nullptr, &mask)));
CHECK((true == metaState.json(builder, nullptr, &maskState)));
builder.close();
auto slice = builder.slice();

View File

@ -0,0 +1,237 @@
////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2019 ArangoDB GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Andrey Abramov
/// @author Vasiliy Nabatchikov
////////////////////////////////////////////////////////////////////////////////
#include "catch.hpp"
#include "../Mocks/StorageEngineMock.h"
#include "ApplicationFeatures/ApplicationServer.h"
#include "Aql/QueryRegistry.h"
#if USE_ENTERPRISE
#include "Enterprise/Ldap/LdapFeature.h"
#endif
#include "GeneralServer/AuthenticationFeature.h"
#include "RestServer/QueryRegistryFeature.h"
#include "RestServer/ViewTypesFeature.h"
#include "StorageEngine/EngineSelectorFeature.h"
#include "Utils/ExecContext.h"
#include "velocypack/Parser.h"
#include "VocBase/LogicalView.h"
#include "VocBase/vocbase.h"
namespace {
struct TestView: public arangodb::LogicalView {
arangodb::Result _appendVelocyPackResult;
arangodb::velocypack::Builder _properties;
TestView(TRI_vocbase_t& vocbase, arangodb::velocypack::Slice const& definition, uint64_t planVersion)
: arangodb::LogicalView(vocbase, definition, planVersion) {
}
virtual arangodb::Result appendVelocyPackImpl(arangodb::velocypack::Builder& builder, bool, bool) const override {
builder.add("properties", _properties.slice());
return _appendVelocyPackResult;
}
virtual arangodb::Result dropImpl() override { return arangodb::LogicalViewHelperStorageEngine::drop(*this); }
virtual void open() override {}
virtual arangodb::Result renameImpl(std::string const& oldName) override { return arangodb::LogicalViewHelperStorageEngine::rename(*this, oldName); }
virtual arangodb::Result properties(arangodb::velocypack::Slice const& properties, bool partialUpdate) override { _properties = arangodb::velocypack::Builder(properties); return arangodb::Result(); }
virtual bool visitCollections(CollectionVisitor const& visitor) const override { return true; }
};
struct ViewFactory: public arangodb::ViewFactory {
virtual arangodb::Result create(
arangodb::LogicalView::ptr& view,
TRI_vocbase_t& vocbase,
arangodb::velocypack::Slice const& definition
) const override {
view = vocbase.createView(definition);
return arangodb::Result();
}
virtual arangodb::Result instantiate(
arangodb::LogicalView::ptr& view,
TRI_vocbase_t& vocbase,
arangodb::velocypack::Slice const& definition,
uint64_t planVersion
) const override {
view = std::make_shared<TestView>(vocbase, definition, planVersion);
return arangodb::Result();
}
};
}
// -----------------------------------------------------------------------------
// --SECTION-- setup / tear-down
// -----------------------------------------------------------------------------
struct LogicalViewSetup {
StorageEngineMock engine;
arangodb::application_features::ApplicationServer server;
std::vector<std::pair<arangodb::application_features::ApplicationFeature*, bool>> features;
ViewFactory viewFactory;
LogicalViewSetup(): 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);
features.emplace_back(new arangodb::AuthenticationFeature(server), false); // required for ExecContext
features.emplace_back(new arangodb::QueryRegistryFeature(server), false); // required for TRI_vocbase_t
features.emplace_back(new arangodb::ViewTypesFeature(server), false); // required for LogicalView::create(...)
#if USE_ENTERPRISE
features.emplace_back(new arangodb::LdapFeature(server), false); // required for AuthenticationFeature with USE_ENTERPRISE
#endif
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* viewTypesFeature =
arangodb::application_features::ApplicationServer::lookupFeature<arangodb::ViewTypesFeature>();
viewTypesFeature->emplace(
arangodb::LogicalDataSource::Type::emplace(arangodb::velocypack::StringRef("testViewType")),
viewFactory
);
}
~LogicalViewSetup() {
arangodb::application_features::ApplicationServer::server = nullptr;
arangodb::EngineSelectorFeature::ENGINE = nullptr;
// destroy application features
for (auto& f: features) {
if (f.second) {
f.first->stop();
}
}
for (auto& f: features) {
f.first->unprepare();
}
arangodb::LogTopic::setLogLevel(arangodb::Logger::AUTHENTICATION.name(), arangodb::LogLevel::DEFAULT);
}
};
// -----------------------------------------------------------------------------
// --SECTION-- test suite
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief setup
////////////////////////////////////////////////////////////////////////////////
TEST_CASE("LogicalViewTest", "[vocbase]") {
LogicalViewSetup s;
(void)(s);
SECTION("test_auth") {
auto viewJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"type\": \"testViewType\" }");
// no ExecContext
{
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
auto logicalView = vocbase.createView(viewJson->slice());
CHECK((true == logicalView->canUse(arangodb::auth::Level::RW)));
}
// no read access
{
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
auto logicalView = vocbase.createView(viewJson->slice());
struct ExecContext: public arangodb::ExecContext {
ExecContext(): arangodb::ExecContext(
arangodb::ExecContext::Type::Default, "", "testVocbase", 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);
CHECK((false == logicalView->canUse(arangodb::auth::Level::RO)));
}
// no write access
{
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
auto logicalView = vocbase.createView(viewJson->slice());
struct ExecContext: public arangodb::ExecContext {
ExecContext(): arangodb::ExecContext(
arangodb::ExecContext::Type::Default, "", "testVocbase", arangodb::auth::Level::NONE, arangodb::auth::Level::RO
) {}
} 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);
CHECK((true == logicalView->canUse(arangodb::auth::Level::RO)));
CHECK((false == logicalView->canUse(arangodb::auth::Level::RW)));
}
// write access (view access is db access as per https://github.com/arangodb/backlog/issues/459)
{
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
auto logicalView = vocbase.createView(viewJson->slice());
struct ExecContext: public arangodb::ExecContext {
ExecContext(): arangodb::ExecContext(
arangodb::ExecContext::Type::Default, "", "testVocbase", arangodb::auth::Level::NONE, arangodb::auth::Level::RW
) {}
} 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);
CHECK((true == logicalView->canUse(arangodb::auth::Level::RO)));
CHECK((true == logicalView->canUse(arangodb::auth::Level::RW)));
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate tests
////////////////////////////////////////////////////////////////////////////////
}
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------