mirror of https://gitee.com/bigwinds/arangodb
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:
parent
8bcddf1498
commit
e97cd8a0fb
|
@ -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,
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
// -----------------------------------------------------------------------------
|
|
@ -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
|
|
@ -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
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
// -----------------------------------------------------------------------------
|
|
@ -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
|
|
@ -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
|
||||
// -----------------------------------------------------------------------------
|
|
@ -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
|
||||
// -----------------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------------
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
// -----------------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------------
|
|
@ -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
|
||||
// -----------------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------------
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 ()
|
||||
|
|
|
@ -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
|
||||
// -----------------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------------
|
|
@ -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
|
||||
// -----------------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------------
|
|
@ -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
|
||||
// -----------------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------------
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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
|
||||
// -----------------------------------------------------------------------------
|
Loading…
Reference in New Issue