mirror of https://gitee.com/bigwinds/arangodb
Merge branch '3.5' of https://github.com/arangodb/arangodb into 3.5.3
This commit is contained in:
commit
3da611dee0
28
CHANGELOG
28
CHANGELOG
|
@ -1,6 +1,34 @@
|
||||||
v3.5.3 (XXXX-XX-XX)
|
v3.5.3 (XXXX-XX-XX)
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
|
* Fixed UPSERT matching.
|
||||||
|
|
||||||
|
Empty objects in the `UPSERT { ... }` expression will not be omitted anymore:
|
||||||
|
|
||||||
|
db._collection("col").insert({ "find" : "me" });
|
||||||
|
db._query(` UPSERT { "find" : "me", "foo" : {} }
|
||||||
|
UPDATE { "foo" : "not gonna happen" }
|
||||||
|
INSERT { "find" : "me", "foo" : {} }
|
||||||
|
INTO col
|
||||||
|
`)
|
||||||
|
|
||||||
|
This will now correctly insert a document instead of updating the existing,
|
||||||
|
that only partially machtes the upsert-expression.
|
||||||
|
|
||||||
|
* Fixed undefined behaviour with creation of ArangoSearch links with custom
|
||||||
|
analyzers in cluster environment.
|
||||||
|
|
||||||
|
* Fixed internal issue #651: analyzer duplication in _analyzers collection.
|
||||||
|
|
||||||
|
* Fixed internal issue #4597: rebalanceShards API cannot work on any database
|
||||||
|
other than the _system database.
|
||||||
|
|
||||||
|
* Stop putting system services in _apps on single server, this has never
|
||||||
|
worked on cluster and was not needed.
|
||||||
|
|
||||||
|
* Fixed issue #10371: K_SHORTEST_PATHS LIMIT 1 can not return the shortest path.
|
||||||
|
Now the shortest path is returned as the first one in such queries.
|
||||||
|
|
||||||
* Improve killability of some types of cluster AQL queries. Previously, several
|
* Improve killability of some types of cluster AQL queries. Previously, several
|
||||||
cluster queries, especially those containing a `DistributeNode` in their
|
cluster queries, especially those containing a `DistributeNode` in their
|
||||||
execution plans, did not respond to a kill instruction.
|
execution plans, did not respond to a kill instruction.
|
||||||
|
|
|
@ -2665,7 +2665,6 @@ AstNode* Ast::makeConditionFromExample(AstNode const* node) {
|
||||||
TRI_ASSERT(object->type == NODE_TYPE_OBJECT);
|
TRI_ASSERT(object->type == NODE_TYPE_OBJECT);
|
||||||
|
|
||||||
auto const n = object->numMembers();
|
auto const n = object->numMembers();
|
||||||
|
|
||||||
for (size_t i = 0; i < n; ++i) {
|
for (size_t i = 0; i < n; ++i) {
|
||||||
auto member = object->getMember(i);
|
auto member = object->getMember(i);
|
||||||
|
|
||||||
|
@ -2680,8 +2679,8 @@ AstNode* Ast::makeConditionFromExample(AstNode const* node) {
|
||||||
|
|
||||||
auto value = member->getMember(0);
|
auto value = member->getMember(0);
|
||||||
|
|
||||||
if (value->type == NODE_TYPE_OBJECT) {
|
if (value->type == NODE_TYPE_OBJECT && value->numMembers() != 0) {
|
||||||
createCondition(value);
|
createCondition(value);
|
||||||
} else {
|
} else {
|
||||||
auto access = variable;
|
auto access = variable;
|
||||||
for (auto const& it : attributeParts) {
|
for (auto const& it : attributeParts) {
|
||||||
|
|
|
@ -55,6 +55,7 @@ class QueryString {
|
||||||
~QueryString() = default;
|
~QueryString() = default;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
std::string const& string() const noexcept { return _queryString; }
|
||||||
char const* data() const { return _queryString.data(); }
|
char const* data() const { return _queryString.data(); }
|
||||||
size_t size() const { return _queryString.size(); }
|
size_t size() const { return _queryString.size(); }
|
||||||
size_t length() const { return _queryString.size(); }
|
size_t length() const { return _queryString.size(); }
|
||||||
|
|
|
@ -70,27 +70,36 @@ bool KShortestPathsFinder::computeShortestPath(VertexRef const& start, VertexRef
|
||||||
std::unordered_set<VertexRef> const& forbiddenVertices,
|
std::unordered_set<VertexRef> const& forbiddenVertices,
|
||||||
std::unordered_set<Edge> const& forbiddenEdges,
|
std::unordered_set<Edge> const& forbiddenEdges,
|
||||||
Path& result) {
|
Path& result) {
|
||||||
bool found = false;
|
|
||||||
Ball left(start, FORWARD);
|
Ball left(start, FORWARD);
|
||||||
Ball right(end, BACKWARD);
|
Ball right(end, BACKWARD);
|
||||||
VertexRef join;
|
VertexRef join;
|
||||||
|
|
||||||
result.clear();
|
result.clear();
|
||||||
|
|
||||||
while (!left._frontier.empty() && !right._frontier.empty() && !found) {
|
auto currentBest = boost::optional<double>{};
|
||||||
|
|
||||||
|
// We will not improve anymore if we have found a best path and the smallest
|
||||||
|
// combined distance between left and right is bigger than that path
|
||||||
|
while (!left._frontier.empty() && !right._frontier.empty() &&
|
||||||
|
!(currentBest.has_value() &&
|
||||||
|
(left._closest + right._closest > currentBest.value()))) {
|
||||||
_options.isQueryKilledCallback();
|
_options.isQueryKilledCallback();
|
||||||
|
|
||||||
// Choose the smaller frontier to expand.
|
// Choose the smaller frontier to expand.
|
||||||
if (left._frontier.size() < right._frontier.size()) {
|
if (left._frontier.size() < right._frontier.size()) {
|
||||||
found = advanceFrontier(left, right, forbiddenVertices, forbiddenEdges, join);
|
advanceFrontier(left, right, forbiddenVertices, forbiddenEdges, join, currentBest);
|
||||||
} else {
|
} else {
|
||||||
found = advanceFrontier(right, left, forbiddenVertices, forbiddenEdges, join);
|
advanceFrontier(right, left, forbiddenVertices, forbiddenEdges, join, currentBest);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (found) {
|
|
||||||
|
if (currentBest.has_value()) {
|
||||||
reconstructPath(left, right, join, result);
|
reconstructPath(left, right, join, result);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
// No path found
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return found;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void KShortestPathsFinder::computeNeighbourhoodOfVertexCache(VertexRef vertex,
|
void KShortestPathsFinder::computeNeighbourhoodOfVertexCache(VertexRef vertex,
|
||||||
|
@ -178,10 +187,11 @@ void KShortestPathsFinder::computeNeighbourhoodOfVertex(VertexRef vertex, Direct
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool KShortestPathsFinder::advanceFrontier(Ball& source, Ball const& target,
|
void KShortestPathsFinder::advanceFrontier(Ball& source, Ball const& target,
|
||||||
std::unordered_set<VertexRef> const& forbiddenVertices,
|
std::unordered_set<VertexRef> const& forbiddenVertices,
|
||||||
std::unordered_set<Edge> const& forbiddenEdges,
|
std::unordered_set<Edge> const& forbiddenEdges,
|
||||||
VertexRef& join) {
|
VertexRef& join,
|
||||||
|
boost::optional<double>& currentBest) {
|
||||||
VertexRef vr;
|
VertexRef vr;
|
||||||
DijkstraInfo *v, *w;
|
DijkstraInfo *v, *w;
|
||||||
std::vector<Step>* neighbours;
|
std::vector<Step>* neighbours;
|
||||||
|
@ -190,7 +200,7 @@ bool KShortestPathsFinder::advanceFrontier(Ball& source, Ball const& target,
|
||||||
TRI_ASSERT(v != nullptr);
|
TRI_ASSERT(v != nullptr);
|
||||||
TRI_ASSERT(vr == v->_vertex);
|
TRI_ASSERT(vr == v->_vertex);
|
||||||
if (!success) {
|
if (!success) {
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
computeNeighbourhoodOfVertexCache(vr, source._direction, neighbours);
|
computeNeighbourhoodOfVertexCache(vr, source._direction, neighbours);
|
||||||
|
@ -217,14 +227,17 @@ bool KShortestPathsFinder::advanceFrontier(Ball& source, Ball const& target,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
v->_done = true;
|
v->_done = true;
|
||||||
|
source._closest = v->_weight;
|
||||||
|
|
||||||
w = target._frontier.find(v->_vertex);
|
w = target._frontier.find(v->_vertex);
|
||||||
if (w != nullptr && w->_done) {
|
if (w != nullptr && w->_done) {
|
||||||
join = v->_vertex;
|
// The total weight of the found path
|
||||||
return true;
|
double totalWeight = v->_weight + w->_weight;
|
||||||
|
if (!currentBest.has_value() || totalWeight < currentBest.value()) {
|
||||||
|
join = v->_vertex;
|
||||||
|
currentBest = v->_weight + w->_weight;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void KShortestPathsFinder::reconstructPath(Ball const& left, Ball const& right,
|
void KShortestPathsFinder::reconstructPath(Ball const& left, Ball const& right,
|
||||||
|
|
|
@ -34,6 +34,8 @@
|
||||||
|
|
||||||
#include <list>
|
#include <list>
|
||||||
|
|
||||||
|
#include <boost/optional.hpp>
|
||||||
|
|
||||||
namespace arangodb {
|
namespace arangodb {
|
||||||
|
|
||||||
namespace velocypack {
|
namespace velocypack {
|
||||||
|
@ -141,9 +143,9 @@ class KShortestPathsFinder : public ShortestPathFinder {
|
||||||
void setWeight(double weight) { _weight = weight; }
|
void setWeight(double weight) { _weight = weight; }
|
||||||
|
|
||||||
DijkstraInfo(VertexRef const& vertex, Edge const&& edge, VertexRef const& pred, double weight)
|
DijkstraInfo(VertexRef const& vertex, Edge const&& edge, VertexRef const& pred, double weight)
|
||||||
: _vertex(vertex), _edge(std::move(edge)), _pred(pred), _weight(weight), _done(false) {}
|
: _vertex(vertex), _edge(std::move(edge)), _pred(pred), _weight(weight), _done(false) {}
|
||||||
explicit DijkstraInfo(VertexRef const& vertex)
|
explicit DijkstraInfo(VertexRef const& vertex)
|
||||||
: _vertex(vertex), _weight(0), _done(true) {}
|
: _vertex(vertex), _weight(0), _done(true) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef ShortestPathPriorityQueue<VertexRef, DijkstraInfo, double> Frontier;
|
typedef ShortestPathPriorityQueue<VertexRef, DijkstraInfo, double> Frontier;
|
||||||
|
@ -154,10 +156,13 @@ class KShortestPathsFinder : public ShortestPathFinder {
|
||||||
VertexRef _centre;
|
VertexRef _centre;
|
||||||
Direction _direction;
|
Direction _direction;
|
||||||
Frontier _frontier;
|
Frontier _frontier;
|
||||||
|
// The distance of the last node that has been fully expanded
|
||||||
|
// from _centre
|
||||||
|
double _closest;
|
||||||
|
|
||||||
Ball() {}
|
Ball() {}
|
||||||
Ball(VertexRef const& centre, Direction direction)
|
Ball(VertexRef const& centre, Direction direction)
|
||||||
: _centre(centre), _direction(direction) {
|
: _centre(centre), _direction(direction), _closest(0) {
|
||||||
_frontier.insert(centre, std::make_unique<DijkstraInfo>(centre));
|
_frontier.insert(centre, std::make_unique<DijkstraInfo>(centre));
|
||||||
}
|
}
|
||||||
~Ball() {}
|
~Ball() {}
|
||||||
|
@ -193,7 +198,7 @@ class KShortestPathsFinder : public ShortestPathFinder {
|
||||||
std::vector<size_t> _paths;
|
std::vector<size_t> _paths;
|
||||||
|
|
||||||
explicit FoundVertex(VertexRef const& vertex)
|
explicit FoundVertex(VertexRef const& vertex)
|
||||||
: _vertex(vertex), _hasCachedOutNeighbours(false), _hasCachedInNeighbours(false) {}
|
: _vertex(vertex), _hasCachedOutNeighbours(false), _hasCachedInNeighbours(false) {}
|
||||||
};
|
};
|
||||||
// Contains the vertices that were found while searching
|
// Contains the vertices that were found while searching
|
||||||
// for a shortest path between start and end together with
|
// for a shortest path between start and end together with
|
||||||
|
@ -246,9 +251,10 @@ class KShortestPathsFinder : public ShortestPathFinder {
|
||||||
void computeNeighbourhoodOfVertex(VertexRef vertex, Direction direction,
|
void computeNeighbourhoodOfVertex(VertexRef vertex, Direction direction,
|
||||||
std::vector<Step>& steps);
|
std::vector<Step>& steps);
|
||||||
|
|
||||||
bool advanceFrontier(Ball& source, Ball const& target,
|
void advanceFrontier(Ball& source, Ball const& target,
|
||||||
std::unordered_set<VertexRef> const& forbiddenVertices,
|
std::unordered_set<VertexRef> const& forbiddenVertices,
|
||||||
std::unordered_set<Edge> const& forbiddenEdges, VertexRef& join);
|
std::unordered_set<Edge> const& forbiddenEdges,
|
||||||
|
VertexRef& join, boost::optional<double>& currentBest);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool _pathAvailable;
|
bool _pathAvailable;
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -70,15 +70,21 @@ class AnalyzerPool : private irs::util::noncopyable {
|
||||||
irs::string_ref const& type() const noexcept { return _type; }
|
irs::string_ref const& type() const noexcept { return _type; }
|
||||||
|
|
||||||
// definition to be stored in _analyzers collection or shown to the end user
|
// definition to be stored in _analyzers collection or shown to the end user
|
||||||
void toVelocyPack(arangodb::velocypack::Builder& builder,
|
void toVelocyPack(velocypack::Builder& builder,
|
||||||
bool forPersistence = false);
|
bool forPersistence = false);
|
||||||
|
|
||||||
// definition to be stored/shown in a link definition
|
// definition to be stored/shown in a link definition
|
||||||
void toVelocyPack(arangodb::velocypack::Builder& builder,
|
void toVelocyPack(velocypack::Builder& builder,
|
||||||
TRI_vocbase_t const* vocbase = nullptr);
|
TRI_vocbase_t const* vocbase = nullptr);
|
||||||
|
|
||||||
|
bool operator==(AnalyzerPool const& rhs) const;
|
||||||
|
bool operator!=(AnalyzerPool const& rhs) const {
|
||||||
|
return !(*this == rhs);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class IResearchAnalyzerFeature; // required for calling AnalyzerPool::init(...) and AnalyzerPool::setKey(...)
|
// required for calling AnalyzerPool::init(...) and AnalyzerPool::setKey(...)
|
||||||
|
friend class IResearchAnalyzerFeature;
|
||||||
|
|
||||||
// 'make(...)' method wrapper for irs::analysis::analyzer types
|
// 'make(...)' method wrapper for irs::analysis::analyzer types
|
||||||
struct Builder {
|
struct Builder {
|
||||||
|
@ -86,7 +92,7 @@ class AnalyzerPool : private irs::util::noncopyable {
|
||||||
DECLARE_FACTORY(irs::string_ref const& type, VPackSlice properties);
|
DECLARE_FACTORY(irs::string_ref const& type, VPackSlice properties);
|
||||||
};
|
};
|
||||||
|
|
||||||
void toVelocyPack(arangodb::velocypack::Builder& builder,
|
void toVelocyPack(velocypack::Builder& builder,
|
||||||
irs::string_ref const& name);
|
irs::string_ref const& name);
|
||||||
|
|
||||||
bool init(irs::string_ref const& type,
|
bool init(irs::string_ref const& type,
|
||||||
|
@ -94,14 +100,15 @@ class AnalyzerPool : private irs::util::noncopyable {
|
||||||
irs::flags const& features = irs::flags::empty_instance());
|
irs::flags const& features = irs::flags::empty_instance());
|
||||||
void setKey(irs::string_ref const& type);
|
void setKey(irs::string_ref const& type);
|
||||||
|
|
||||||
mutable irs::unbounded_object_pool<Builder> _cache; // cache of irs::analysis::analyzer (constructed via
|
mutable irs::unbounded_object_pool<Builder> _cache; // cache of irs::analysis::analyzer
|
||||||
// AnalyzerBuilder::make(...))
|
// (constructed via AnalyzerBuilder::make(...))
|
||||||
std::string _config; // non-null type + non-null properties + key
|
std::string _config; // non-null type + non-null properties + key
|
||||||
irs::flags _features; // cached analyzer features
|
irs::flags _features; // cached analyzer features
|
||||||
irs::string_ref _key; // the key of the persisted configuration for this pool, null == static analyzer
|
irs::string_ref _key; // the key of the persisted configuration for this pool,
|
||||||
std::string _name; // ArangoDB alias for an IResearch analyzer configuration
|
// null == static analyzer
|
||||||
|
std::string _name; // ArangoDB alias for an IResearch analyzer configuration
|
||||||
VPackSlice _properties; // IResearch analyzer configuration
|
VPackSlice _properties; // IResearch analyzer configuration
|
||||||
irs::string_ref _type; // IResearch analyzer name
|
irs::string_ref _type; // IResearch analyzer name
|
||||||
}; // AnalyzerPool
|
}; // AnalyzerPool
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -111,26 +118,95 @@ class AnalyzerPool : private irs::util::noncopyable {
|
||||||
/// invalidates all AnalyzerPool instances previously provided
|
/// invalidates all AnalyzerPool instances previously provided
|
||||||
/// by the deallocated feature instance
|
/// by the deallocated feature instance
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
class IResearchAnalyzerFeature final : public arangodb::application_features::ApplicationFeature {
|
class IResearchAnalyzerFeature final
|
||||||
|
: public application_features::ApplicationFeature {
|
||||||
public:
|
public:
|
||||||
explicit IResearchAnalyzerFeature(arangodb::application_features::ApplicationServer& server);
|
/// first == vocbase name, second == analyzer name
|
||||||
|
/// EMPTY == system vocbase
|
||||||
|
/// NIL == unprefixed analyzer name, i.e. active vocbase
|
||||||
|
using AnalyzerName = std::pair<irs::string_ref, irs::string_ref>;
|
||||||
|
|
||||||
|
explicit IResearchAnalyzerFeature(application_features::ApplicationServer& server);
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief check permissions
|
||||||
|
/// @param vocbase analyzer vocbase
|
||||||
|
/// @param level access level
|
||||||
/// @return analyzers in the specified vocbase are granted 'level' access
|
/// @return analyzers in the specified vocbase are granted 'level' access
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
static bool canUse( // check permissions
|
static bool canUse(TRI_vocbase_t const& vocbase, auth::Level const& level);
|
||||||
TRI_vocbase_t const& vocbase, // analyzer vocbase
|
|
||||||
arangodb::auth::Level const& level // access level
|
|
||||||
);
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief check permissions
|
||||||
|
/// @param name analyzer name (already normalized)
|
||||||
|
/// @param level access level
|
||||||
/// @return analyzer with the given prefixed name (or unprefixed and resides
|
/// @return analyzer with the given prefixed name (or unprefixed and resides
|
||||||
/// in defaultVocbase) is granted 'level' access
|
/// in defaultVocbase) is granted 'level' access
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
static bool canUse( // check permissions
|
static bool canUse(irs::string_ref const& name, auth::Level const& level);
|
||||||
irs::string_ref const& name, // analyzer name (already normalized)
|
|
||||||
arangodb::auth::Level const& level // access level
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
);
|
/// @brief create new analyzer pool
|
||||||
|
/// @param analyzer created analyzer
|
||||||
|
/// @param name analyzer name (already normalized)
|
||||||
|
/// @param type the underlying IResearch analyzer type
|
||||||
|
/// @param properties the configuration for the underlying IResearch type
|
||||||
|
/// @param features the expected features the analyzer should produce
|
||||||
|
/// @return success
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
static Result createAnalyzerPool(AnalyzerPool::ptr& analyzer,
|
||||||
|
irs::string_ref const& name,
|
||||||
|
irs::string_ref const& type,
|
||||||
|
VPackSlice const properties,
|
||||||
|
irs::flags const& features);
|
||||||
|
|
||||||
|
static AnalyzerPool::ptr identity() noexcept; // the identity analyzer
|
||||||
|
static std::string const& name() noexcept;
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @param name analyzer name
|
||||||
|
/// @param activeVocbase fallback vocbase if not part of name
|
||||||
|
/// @param systemVocbase the system vocbase for use with empty prefix
|
||||||
|
/// @param expandVocbasePrefix use full vocbase name as prefix for
|
||||||
|
/// active/system v.s. EMPTY/'::'
|
||||||
|
/// @return normalized analyzer name, i.e. with vocbase prefix
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
static std::string normalize(irs::string_ref const& name,
|
||||||
|
TRI_vocbase_t const& activeVocbase,
|
||||||
|
TRI_vocbase_t const& systemVocbase,
|
||||||
|
bool expandVocbasePrefix = true);
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @param name analyzer name (normalized)
|
||||||
|
/// @return vocbase prefix extracted from normalized analyzer name
|
||||||
|
/// EMPTY == system vocbase
|
||||||
|
/// NIL == analyzer name have had no db name prefix
|
||||||
|
/// @see analyzerReachableFromDb
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
static irs::string_ref extractVocbaseName(irs::string_ref const& name) noexcept {
|
||||||
|
return splitAnalyzerName(name).first;
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// Checks if analyzer db (identified by db name prefix extracted from analyzer
|
||||||
|
/// name) could be reached from specified db.
|
||||||
|
/// Properly handles special cases (e.g. NIL and EMPTY)
|
||||||
|
/// @param dbNameFromAnalyzer database name extracted from analyzer name
|
||||||
|
/// @param currentDbName database name to check against (should not be empty!)
|
||||||
|
/// @param forGetters check special case for getting analyzer (not creating/removing)
|
||||||
|
/// @return true if analyzer is reachable
|
||||||
|
static bool analyzerReachableFromDb(irs::string_ref const& dbNameFromAnalyzer,
|
||||||
|
irs::string_ref const& currentDbName,
|
||||||
|
bool forGetters = false) noexcept;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @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
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
static AnalyzerName splitAnalyzerName(irs::string_ref const& analyzer) noexcept;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief emplace an analyzer as per the specified parameters
|
/// @brief emplace an analyzer as per the specified parameters
|
||||||
|
@ -149,134 +225,70 @@ class IResearchAnalyzerFeature final : public arangodb::application_features::Ap
|
||||||
/// allowed during recovery
|
/// allowed during recovery
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
typedef std::pair<AnalyzerPool::ptr, bool> EmplaceResult;
|
typedef std::pair<AnalyzerPool::ptr, bool> EmplaceResult;
|
||||||
arangodb::Result emplace(
|
Result emplace(EmplaceResult& result,
|
||||||
EmplaceResult& result,
|
irs::string_ref const& name,
|
||||||
irs::string_ref const& name,
|
irs::string_ref const& type,
|
||||||
irs::string_ref const& type,
|
VPackSlice const properties,
|
||||||
VPackSlice const properties,
|
irs::flags const& features = irs::flags::empty_instance());
|
||||||
irs::flags const& features = irs::flags::empty_instance()) {
|
|
||||||
return ensure(result, name, type, properties, features, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief find analyzer
|
|
||||||
/// @param result the result of the successful emplacement (out-param)
|
|
||||||
/// first - the emplaced pool
|
|
||||||
/// second - if an insertion of an new analyzer occured
|
|
||||||
/// @param name analyzer name (already normalized)
|
|
||||||
/// @param type the underlying IResearch analyzer type
|
|
||||||
/// @param properties the configuration for the underlying IResearch type
|
|
||||||
/// @param features the expected features the analyzer should produce
|
|
||||||
/// @return analyzer matching the specified parameters or nullptr
|
|
||||||
/// @note will construct and cache the analyzer if missing only on db-server
|
|
||||||
/// persistence in the cases of inRecovery or !storage engine will fail
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
arangodb::Result get(
|
|
||||||
EmplaceResult& result,
|
|
||||||
irs::string_ref const& name,
|
|
||||||
irs::string_ref const& type,
|
|
||||||
VPackSlice const properties,
|
|
||||||
irs::flags const& features) {
|
|
||||||
return ensure(result, name, type, properties, features, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief find analyzer
|
/// @brief find analyzer
|
||||||
/// @param name analyzer name (already normalized)
|
/// @param name analyzer name (already normalized)
|
||||||
|
/// @param onlyCached check only locally cached analyzers
|
||||||
/// @return analyzer with the specified name or nullptr
|
/// @return analyzer with the specified name or nullptr
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
AnalyzerPool::ptr get( // find analyzer
|
AnalyzerPool::ptr get(irs::string_ref const& name,
|
||||||
irs::string_ref const& name, // analyzer name
|
bool onlyCached = false) const noexcept {
|
||||||
bool onlyCached = false // check only locally cached analyzers
|
return get(name, splitAnalyzerName(name), onlyCached);
|
||||||
) const noexcept;
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief find analyzer
|
/// @brief find analyzer
|
||||||
/// @return analyzer with the specified name or nullptr
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
AnalyzerPool::ptr get( // find analyzer
|
|
||||||
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 onlyCached = false // check only locally cached analyzers
|
|
||||||
) 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/'::'
|
|
||||||
);
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @param name analyzer name (normalized)
|
|
||||||
/// @return vocbase prefix extracted from normalized analyzer name
|
|
||||||
/// EMPTY == system vocbase
|
|
||||||
/// NIL == analyzer name have had no db name prefix
|
|
||||||
/// @see analyzerReachableFromDb
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
static irs::string_ref extractVocbaseName(irs::string_ref const& name) noexcept;
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// Checks if analyzer db (identified by db name prefix extracted from analyzer
|
|
||||||
/// name) could be reached from specified db.
|
|
||||||
/// Properly handles special cases (e.g. NIL and EMPTY)
|
|
||||||
/// @param dbNameFromAnalyzer database name extracted from analyzer name
|
|
||||||
/// @param currentDbName database name to check against (should not be empty!)
|
|
||||||
/// @param forGetters check special case for getting analyzer (not creating/removing)
|
|
||||||
/// @return true if analyzer is reachable
|
|
||||||
static bool analyzerReachableFromDb(irs::string_ref const& dbNameFromAnalyzer,
|
|
||||||
irs::string_ref const& currentDbName,
|
|
||||||
bool forGetters = false) noexcept;
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief split the analyzer name into the vocbase part and analyzer part
|
|
||||||
/// @param name analyzer name
|
/// @param name analyzer name
|
||||||
/// @return pair of first == vocbase name, second == analyzer name
|
/// @param activeVocbase fallback vocbase if not part of name
|
||||||
/// EMPTY == system vocbase
|
/// @param systemVocbase the system vocbase for use with empty prefix
|
||||||
/// NIL == unprefixed analyzer name, i.e. active vocbase
|
/// @param onlyCached check only locally cached analyzers
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
/// @return analyzer with the specified name or nullptr
|
||||||
static std::pair<irs::string_ref, irs::string_ref> splitAnalyzerName(irs::string_ref const& analyzer) noexcept;
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
AnalyzerPool::ptr get(irs::string_ref const& name,
|
||||||
void prepare() override;
|
TRI_vocbase_t const& activeVocbase,
|
||||||
|
TRI_vocbase_t const& systemVocbase,
|
||||||
|
bool onlyCached = false) const;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief remove the specified analyzer
|
/// @brief remove the specified analyzer
|
||||||
/// @param name analyzer name (already normalized)
|
/// @param name analyzer name (already normalized)
|
||||||
/// @param force remove even if the analyzer is actively referenced
|
/// @param force remove even if the analyzer is actively referenced
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
arangodb::Result remove(irs::string_ref const& name, bool force = false);
|
Result remove(irs::string_ref const& name, bool force = false);
|
||||||
|
|
||||||
void start() override;
|
|
||||||
void stop() override;
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief visit all analyzers for the specified vocbase
|
/// @brief visit all analyzers for the specified vocbase
|
||||||
/// @param vocbase only visit analysers for this vocbase (nullptr == static)
|
/// @param vocbase only visit analysers for this vocbase (nullptr == static)
|
||||||
/// @return visitation compleated fully
|
/// @return visitation compleated fully
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
bool visit(std::function<bool(AnalyzerPool::ptr const& analyzer)> const& visitor) const;
|
bool visit(std::function<bool(AnalyzerPool::ptr const&)> const& visitor) const;
|
||||||
bool visit(std::function<bool(AnalyzerPool::ptr const& analyzer)> const& visitor,
|
bool visit(std::function<bool(AnalyzerPool::ptr const&)> const& visitor,
|
||||||
TRI_vocbase_t const* vocbase) const;
|
TRI_vocbase_t const* vocbase) const;
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
// @brief removes analyzers for specified database from cache
|
/// @brief removes analyzers for specified database from cache
|
||||||
// @param vocbase database to invalidate analyzers
|
/// @param vocbase database to invalidate analyzers
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
void invalidate(const TRI_vocbase_t& vocbase);
|
void invalidate(const TRI_vocbase_t& vocbase);
|
||||||
|
|
||||||
|
virtual void prepare() override;
|
||||||
|
virtual void start() override;
|
||||||
|
virtual void stop() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// map of caches of irs::analysis::analyzer pools indexed by analyzer name and
|
// map of caches of irs::analysis::analyzer pools indexed by analyzer name and
|
||||||
// their associated metas
|
// their associated metas
|
||||||
typedef std::unordered_map<irs::hashed_string_ref, AnalyzerPool::ptr> Analyzers;
|
typedef std::unordered_map<irs::hashed_string_ref, AnalyzerPool::ptr> Analyzers;
|
||||||
|
|
||||||
Analyzers _analyzers; // all analyzers known to this feature (including static) (names are stored with expanded vocbase prefixes)
|
Analyzers _analyzers; // all analyzers known to this feature (including static)
|
||||||
|
// (names are stored with expanded vocbase prefixes)
|
||||||
std::unordered_map<std::string, std::chrono::system_clock::time_point> _lastLoad; // last time a database was loaded
|
std::unordered_map<std::string, std::chrono::system_clock::time_point> _lastLoad; // last time a database was loaded
|
||||||
mutable irs::async_utils::read_write_mutex _mutex; // for use with member '_analyzers', '_lastLoad'
|
mutable irs::async_utils::read_write_mutex _mutex; // for use with member '_analyzers', '_lastLoad'
|
||||||
|
|
||||||
|
@ -286,42 +298,17 @@ class IResearchAnalyzerFeature final : public arangodb::application_features::Ap
|
||||||
/// @brief validate analyzer parameters and emplace into map
|
/// @brief validate analyzer parameters and emplace into map
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
typedef std::pair<Analyzers::iterator, bool> EmplaceAnalyzerResult;
|
typedef std::pair<Analyzers::iterator, bool> EmplaceAnalyzerResult;
|
||||||
arangodb::Result emplaceAnalyzer( // emplace
|
Result emplaceAnalyzer( // emplace
|
||||||
EmplaceAnalyzerResult& result, // emplacement result on success (out-param)
|
EmplaceAnalyzerResult& result, // emplacement result on success (out-param)
|
||||||
arangodb::iresearch::IResearchAnalyzerFeature::Analyzers& analyzers, // analyzers
|
iresearch::IResearchAnalyzerFeature::Analyzers& analyzers, // analyzers
|
||||||
irs::string_ref const& name, // analyzer name
|
irs::string_ref const& name, // analyzer name
|
||||||
irs::string_ref const& type, // analyzer type
|
irs::string_ref const& type, // analyzer type
|
||||||
VPackSlice const properties, // analyzer properties
|
VPackSlice const properties, // analyzer properties
|
||||||
irs::flags const& features // analyzer features
|
irs::flags const& features // analyzer features
|
||||||
);
|
);
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
AnalyzerPool::ptr get(irs::string_ref const& normalizedName,
|
||||||
/// @brief ensure an analyzer as per the specified parameters exists if
|
AnalyzerName const& name, bool onlyCached) const noexcept;
|
||||||
/// possible
|
|
||||||
/// @param result the result of the successful emplacement (out-param)
|
|
||||||
/// first - the emplaced pool
|
|
||||||
/// second - if an insertion of an new analyzer occured
|
|
||||||
/// @param name analyzer name (already normalized)
|
|
||||||
/// @param type the underlying IResearch analyzer type
|
|
||||||
/// @param properties the configuration for the underlying IResearch type
|
|
||||||
/// @param features the expected features the analyzer should produce
|
|
||||||
/// @param isEmplace request coming from emplace(...)
|
|
||||||
/// @return success
|
|
||||||
/// @note ensure while inRecovery() will not allow new analyzer persistance
|
|
||||||
/// valid because for existing links the analyzer definition should
|
|
||||||
/// already have been persisted and feature administration is not
|
|
||||||
/// allowed during recovery
|
|
||||||
/// @note for db-server analyzers are not persisted
|
|
||||||
/// valid because the authoritative analyzer source is from coordinators
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
arangodb::Result ensure( // ensure analyzer existence if possible
|
|
||||||
EmplaceResult& result, // emplacement result on success (out-param)
|
|
||||||
irs::string_ref const& name, // analyzer name
|
|
||||||
irs::string_ref const& type, // analyzer type
|
|
||||||
VPackSlice const properties, // analyzer properties
|
|
||||||
irs::flags const& features, // analyzer features
|
|
||||||
bool isEmplace
|
|
||||||
);
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief load the analyzers for the specific database, analyzers read from
|
/// @brief load the analyzers for the specific database, analyzers read from
|
||||||
|
@ -330,9 +317,7 @@ class IResearchAnalyzerFeature final : public arangodb::application_features::Ap
|
||||||
/// @note on coordinator and db-server reload is also done if the database has
|
/// @note on coordinator and db-server reload is also done if the database has
|
||||||
/// not been reloaded in 'timeout' seconds
|
/// not been reloaded in 'timeout' seconds
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
arangodb::Result loadAnalyzers( // load analyzers
|
Result loadAnalyzers(irs::string_ref const& database = irs::string_ref::NIL);
|
||||||
irs::string_ref const& database = irs::string_ref::NIL // database to load
|
|
||||||
);
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
/// removes analyzers for database from feature cache
|
/// removes analyzers for database from feature cache
|
||||||
|
@ -345,7 +330,7 @@ class IResearchAnalyzerFeature final : public arangodb::application_features::Ap
|
||||||
/// vocbase
|
/// vocbase
|
||||||
/// @note on success will modify the '_key' of the pool
|
/// @note on success will modify the '_key' of the pool
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
arangodb::Result storeAnalyzer(AnalyzerPool& pool);
|
Result storeAnalyzer(AnalyzerPool& pool);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace iresearch
|
} // namespace iresearch
|
||||||
|
|
|
@ -158,8 +158,9 @@ inline bool keyFromSlice(VPackSlice keySlice, irs::string_ref& key) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool canHandleValue(std::string const& key, VPackSlice const& value,
|
inline bool canHandleValue(std::string const& key,
|
||||||
arangodb::iresearch::IResearchLinkMeta const& context) noexcept {
|
VPackSlice const& value,
|
||||||
|
arangodb::iresearch::FieldMeta const& context) noexcept {
|
||||||
switch (value.type()) {
|
switch (value.type()) {
|
||||||
case VPackValueType::None:
|
case VPackValueType::None:
|
||||||
case VPackValueType::Illegal:
|
case VPackValueType::Illegal:
|
||||||
|
@ -190,8 +191,9 @@ inline bool canHandleValue(std::string const& key, VPackSlice const& value,
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns 'context' in case if can't find the specified 'field'
|
// returns 'context' in case if can't find the specified 'field'
|
||||||
inline arangodb::iresearch::IResearchLinkMeta const* findMeta(
|
inline arangodb::iresearch::FieldMeta const* findMeta(
|
||||||
irs::string_ref const& key, arangodb::iresearch::IResearchLinkMeta const* context) {
|
irs::string_ref const& key,
|
||||||
|
arangodb::iresearch::FieldMeta const* context) {
|
||||||
TRI_ASSERT(context);
|
TRI_ASSERT(context);
|
||||||
|
|
||||||
auto const* meta = context->_fields.findPtr(key);
|
auto const* meta = context->_fields.findPtr(key);
|
||||||
|
@ -199,7 +201,7 @@ inline arangodb::iresearch::IResearchLinkMeta const* findMeta(
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool inObjectFiltered(std::string& buffer,
|
inline bool inObjectFiltered(std::string& buffer,
|
||||||
arangodb::iresearch::IResearchLinkMeta const*& context,
|
arangodb::iresearch::FieldMeta const*& context,
|
||||||
arangodb::iresearch::IteratorValue const& value) {
|
arangodb::iresearch::IteratorValue const& value) {
|
||||||
irs::string_ref key;
|
irs::string_ref key;
|
||||||
|
|
||||||
|
@ -220,7 +222,7 @@ inline bool inObjectFiltered(std::string& buffer,
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool inObject(std::string& buffer,
|
inline bool inObject(std::string& buffer,
|
||||||
arangodb::iresearch::IResearchLinkMeta const*& context,
|
arangodb::iresearch::FieldMeta const*& context,
|
||||||
arangodb::iresearch::IteratorValue const& value) {
|
arangodb::iresearch::IteratorValue const& value) {
|
||||||
irs::string_ref key;
|
irs::string_ref key;
|
||||||
|
|
||||||
|
@ -235,7 +237,7 @@ inline bool inObject(std::string& buffer,
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool inArrayOrdered(std::string& buffer,
|
inline bool inArrayOrdered(std::string& buffer,
|
||||||
arangodb::iresearch::IResearchLinkMeta const*& context,
|
arangodb::iresearch::FieldMeta const*& context,
|
||||||
arangodb::iresearch::IteratorValue const& value) {
|
arangodb::iresearch::IteratorValue const& value) {
|
||||||
buffer += arangodb::iresearch::NESTING_LIST_OFFSET_PREFIX;
|
buffer += arangodb::iresearch::NESTING_LIST_OFFSET_PREFIX;
|
||||||
append(buffer, value.pos);
|
append(buffer, value.pos);
|
||||||
|
@ -245,36 +247,36 @@ inline bool inArrayOrdered(std::string& buffer,
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool inArray(std::string& buffer,
|
inline bool inArray(std::string& buffer,
|
||||||
arangodb::iresearch::IResearchLinkMeta const*& context,
|
arangodb::iresearch::FieldMeta const*& context,
|
||||||
arangodb::iresearch::IteratorValue const& value) noexcept {
|
arangodb::iresearch::IteratorValue const& value) noexcept {
|
||||||
return canHandleValue(buffer, value.value, *context);
|
return canHandleValue(buffer, value.value, *context);
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef bool (*Filter)(std::string& buffer,
|
typedef bool (*Filter)(std::string& buffer,
|
||||||
arangodb::iresearch::IResearchLinkMeta const*& context,
|
arangodb::iresearch::FieldMeta const*& context,
|
||||||
arangodb::iresearch::IteratorValue const& value);
|
arangodb::iresearch::IteratorValue const& value);
|
||||||
|
|
||||||
Filter const valueAcceptors[] = {
|
Filter const valueAcceptors[] = {
|
||||||
&inObjectFiltered, // type == Object, nestListValues == false,
|
// type == Object, nestListValues == false, // includeAllValues == false
|
||||||
// includeAllValues == false
|
&inObjectFiltered,
|
||||||
&inObject, // type == Object, nestListValues == false, includeAllValues ==
|
// type == Object, nestListValues == false, includeAllValues == // true
|
||||||
// true
|
&inObject,
|
||||||
&inObjectFiltered, // type == Object, nestListValues == true ,
|
// type == Object, nestListValues == true , // includeAllValues == false
|
||||||
// includeAllValues == false
|
&inObjectFiltered,
|
||||||
&inObject, // type == Object, nestListValues == true , includeAllValues ==
|
// type == Object, nestListValues == true , includeAllValues == // true
|
||||||
// true
|
&inObject,
|
||||||
&inArray, // type == Array , nestListValues == flase, includeAllValues ==
|
// type == Array , nestListValues == flase, includeAllValues == // false
|
||||||
// false
|
&inArray,
|
||||||
&inArray, // type == Array , nestListValues == flase, includeAllValues ==
|
// type == Array , nestListValues == flase, includeAllValues == // true
|
||||||
// true
|
&inArray,
|
||||||
&inArrayOrdered, // type == Array , nestListValues == true,
|
// type == Array , nestListValues == true, // includeAllValues == false
|
||||||
// includeAllValues == false
|
&inArrayOrdered,
|
||||||
&inArrayOrdered // type == Array , nestListValues == true, includeAllValues
|
// type == Array , nestListValues == true, includeAllValues // == true
|
||||||
// == true
|
&inArrayOrdered
|
||||||
};
|
};
|
||||||
|
|
||||||
inline Filter getFilter(VPackSlice value,
|
inline Filter getFilter(VPackSlice value,
|
||||||
arangodb::iresearch::IResearchLinkMeta const& meta) noexcept {
|
arangodb::iresearch::FieldMeta const& meta) noexcept {
|
||||||
TRI_ASSERT(arangodb::iresearch::isArrayOrObject(value));
|
TRI_ASSERT(arangodb::iresearch::isArrayOrObject(value));
|
||||||
|
|
||||||
return valueAcceptors[4 * value.isArray() + 2 * meta._trackListPositions + meta._includeAllFields];
|
return valueAcceptors[4 * value.isArray() + 2 * meta._trackListPositions + meta._includeAllFields];
|
||||||
|
@ -292,7 +294,7 @@ namespace iresearch {
|
||||||
/*static*/ void Field::setPkValue(Field& field, LocalDocumentId::BaseType const& pk) {
|
/*static*/ void Field::setPkValue(Field& field, LocalDocumentId::BaseType const& pk) {
|
||||||
field._name = PK_COLUMN;
|
field._name = PK_COLUMN;
|
||||||
field._features = &irs::flags::empty_instance();
|
field._features = &irs::flags::empty_instance();
|
||||||
field._storeValues = ValueStorage::FULL;
|
field._storeValues = ValueStorage::VALUE;
|
||||||
field._value =
|
field._value =
|
||||||
irs::bytes_ref(reinterpret_cast<irs::byte_type const*>(&pk), sizeof(pk));
|
irs::bytes_ref(reinterpret_cast<irs::byte_type const*>(&pk), sizeof(pk));
|
||||||
field._analyzer = StringStreamPool.emplace().release(); // FIXME don't use shared_ptr
|
field._analyzer = StringStreamPool.emplace().release(); // FIXME don't use shared_ptr
|
||||||
|
@ -342,7 +344,7 @@ std::string& FieldIterator::valueBuffer() {
|
||||||
return *_valueBuffer;
|
return *_valueBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FieldIterator::reset(VPackSlice const& doc, IResearchLinkMeta const& linkMeta) {
|
void FieldIterator::reset(VPackSlice const& doc, FieldMeta const& linkMeta) {
|
||||||
// set surrogate analyzers
|
// set surrogate analyzers
|
||||||
_begin = nullptr;
|
_begin = nullptr;
|
||||||
_end = 1 + _begin;
|
_end = 1 + _begin;
|
||||||
|
@ -419,13 +421,11 @@ void FieldIterator::setNullValue(VPackSlice const value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FieldIterator::setStringValue(
|
bool FieldIterator::setStringValue(
|
||||||
arangodb::velocypack::Slice const value, // value
|
arangodb::velocypack::Slice const value,
|
||||||
IResearchLinkMeta::Analyzer const& valueAnalyzer // analyzer to use
|
FieldMeta::Analyzer const& valueAnalyzer) {
|
||||||
) {
|
|
||||||
TRI_ASSERT( // assert
|
TRI_ASSERT( // assert
|
||||||
(value.isCustom() && nameBuffer() == arangodb::StaticStrings::IdString) // custom string
|
(value.isCustom() && nameBuffer() == arangodb::StaticStrings::IdString) // custom string
|
||||||
|| value.isString() // verbatim string
|
|| value.isString()); // verbatim string
|
||||||
);
|
|
||||||
|
|
||||||
irs::string_ref valueRef;
|
irs::string_ref valueRef;
|
||||||
|
|
||||||
|
@ -483,7 +483,7 @@ bool FieldIterator::setStringValue(
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FieldIterator::pushAndSetValue(VPackSlice slice, IResearchLinkMeta const*& context) {
|
bool FieldIterator::pushAndSetValue(VPackSlice slice, FieldMeta const*& context) {
|
||||||
auto& name = nameBuffer();
|
auto& name = nameBuffer();
|
||||||
|
|
||||||
while (isArrayOrObject(slice)) {
|
while (isArrayOrObject(slice)) {
|
||||||
|
@ -522,7 +522,7 @@ bool FieldIterator::pushAndSetValue(VPackSlice slice, IResearchLinkMeta const*&
|
||||||
return setAttributeValue(*context);
|
return setAttributeValue(*context);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FieldIterator::setAttributeValue(IResearchLinkMeta const& context) {
|
bool FieldIterator::setAttributeValue(FieldMeta const& context) {
|
||||||
auto const value = topValue().value;
|
auto const value = topValue().value;
|
||||||
|
|
||||||
_value._storeValues = context._storeValues;
|
_value._storeValues = context._storeValues;
|
||||||
|
@ -580,7 +580,7 @@ void FieldIterator::next() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
IResearchLinkMeta const* context;
|
FieldMeta const* context;
|
||||||
|
|
||||||
auto& name = nameBuffer();
|
auto& name = nameBuffer();
|
||||||
|
|
||||||
|
@ -651,4 +651,4 @@ void FieldIterator::next() {
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// --SECTION-- END-OF-FILE
|
// --SECTION-- END-OF-FILE
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
|
@ -141,7 +141,7 @@ class FieldIterator : public std::iterator<std::forward_iterator_tag, Field cons
|
||||||
bool valid() const noexcept { return !_stack.empty(); }
|
bool valid() const noexcept { return !_stack.empty(); }
|
||||||
|
|
||||||
bool operator==(FieldIterator const& rhs) const noexcept {
|
bool operator==(FieldIterator const& rhs) const noexcept {
|
||||||
TRI_ASSERT(_trx == rhs._trx); // compatibility
|
TRI_ASSERT(_trx == rhs._trx); // compatibility
|
||||||
return _stack == rhs._stack;
|
return _stack == rhs._stack;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,27 +149,25 @@ class FieldIterator : public std::iterator<std::forward_iterator_tag, Field cons
|
||||||
return !(*this == rhs);
|
return !(*this == rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset( // reset
|
void reset(velocypack::Slice const& doc,
|
||||||
arangodb::velocypack::Slice const& doc, // doc
|
FieldMeta const& linkMeta);
|
||||||
IResearchLinkMeta const& linkMeta // link meta
|
|
||||||
);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef IResearchLinkMeta::Analyzer const* AnalyzerIterator;
|
typedef FieldMeta::Analyzer const* AnalyzerIterator;
|
||||||
|
|
||||||
typedef bool(*Filter)( // filter
|
typedef bool(*Filter)( // filter
|
||||||
std::string& buffer, // buffer
|
std::string& buffer, // buffer
|
||||||
IResearchLinkMeta const*& rootMeta, // root link meta
|
FieldMeta const*& rootMeta, // root link meta
|
||||||
IteratorValue const& value // value
|
IteratorValue const& value // value
|
||||||
);
|
);
|
||||||
|
|
||||||
struct Level {
|
struct Level {
|
||||||
Level( // constructor
|
Level(velocypack::Slice slice,
|
||||||
arangodb::velocypack::Slice slice, // slice
|
size_t nameLength,
|
||||||
size_t nameLength, // name length
|
FieldMeta const& meta,
|
||||||
IResearchLinkMeta const& meta, // link meta
|
Filter filter)
|
||||||
Filter filter // filter
|
: it(slice), nameLength(nameLength),
|
||||||
): it(slice), nameLength(nameLength), meta(&meta), filter(filter) {
|
meta(&meta), filter(filter) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator==(Level const& rhs) const noexcept { return it == rhs.it; }
|
bool operator==(Level const& rhs) const noexcept { return it == rhs.it; }
|
||||||
|
@ -178,7 +176,7 @@ class FieldIterator : public std::iterator<std::forward_iterator_tag, Field cons
|
||||||
|
|
||||||
Iterator it;
|
Iterator it;
|
||||||
size_t nameLength; // length of the name at the current level
|
size_t nameLength; // length of the name at the current level
|
||||||
IResearchLinkMeta const* meta; // metadata
|
FieldMeta const* meta; // metadata
|
||||||
Filter filter;
|
Filter filter;
|
||||||
}; // Level
|
}; // Level
|
||||||
|
|
||||||
|
@ -201,17 +199,17 @@ class FieldIterator : public std::iterator<std::forward_iterator_tag, Field cons
|
||||||
FieldIterator& operator=(FieldIterator const&) = delete;
|
FieldIterator& operator=(FieldIterator const&) = delete;
|
||||||
|
|
||||||
void next();
|
void next();
|
||||||
bool pushAndSetValue(arangodb::velocypack::Slice slice, IResearchLinkMeta const*& topMeta);
|
bool pushAndSetValue(arangodb::velocypack::Slice slice, FieldMeta const*& topMeta);
|
||||||
bool setAttributeValue(IResearchLinkMeta const& context);
|
bool setAttributeValue(FieldMeta const& context);
|
||||||
bool setStringValue( // set value
|
bool setStringValue( // set value
|
||||||
arangodb::velocypack::Slice const value, // value
|
arangodb::velocypack::Slice const value, // value
|
||||||
IResearchLinkMeta::Analyzer const& valueAnalyzer // analyzer to use
|
FieldMeta::Analyzer const& valueAnalyzer // analyzer to use
|
||||||
);
|
);
|
||||||
void setNullValue(VPackSlice const value);
|
void setNullValue(VPackSlice const value);
|
||||||
void setNumericValue(VPackSlice const value);
|
void setNumericValue(VPackSlice const value);
|
||||||
void setBoolValue(VPackSlice const value);
|
void setBoolValue(VPackSlice const value);
|
||||||
|
|
||||||
void resetAnalyzers(IResearchLinkMeta const& context) noexcept {
|
void resetAnalyzers(FieldMeta const& context) noexcept {
|
||||||
auto const& analyzers = context._analyzers;
|
auto const& analyzers = context._analyzers;
|
||||||
|
|
||||||
_begin = analyzers.data();
|
_begin = analyzers.data();
|
||||||
|
@ -253,4 +251,4 @@ struct DocumentPrimaryKey {
|
||||||
} // namespace iresearch
|
} // namespace iresearch
|
||||||
} // namespace arangodb
|
} // namespace arangodb
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -62,7 +62,7 @@ namespace {
|
||||||
|
|
||||||
struct FilterContext {
|
struct FilterContext {
|
||||||
FilterContext( // constructor
|
FilterContext( // constructor
|
||||||
arangodb::iresearch::IResearchLinkMeta::Analyzer const& analyzer, // analyzer
|
arangodb::iresearch::FieldMeta::Analyzer const& analyzer, // analyzer
|
||||||
irs::boost_t boost) noexcept
|
irs::boost_t boost) noexcept
|
||||||
: analyzer(analyzer), boost(boost) {
|
: analyzer(analyzer), boost(boost) {
|
||||||
TRI_ASSERT(analyzer._pool);
|
TRI_ASSERT(analyzer._pool);
|
||||||
|
@ -72,7 +72,7 @@ struct FilterContext {
|
||||||
FilterContext& operator=(FilterContext const&) = delete;
|
FilterContext& operator=(FilterContext const&) = delete;
|
||||||
|
|
||||||
// need shared_ptr since pool could be deleted from the feature
|
// need shared_ptr since pool could be deleted from the feature
|
||||||
arangodb::iresearch::IResearchLinkMeta::Analyzer const& analyzer;
|
arangodb::iresearch::FieldMeta::Analyzer const& analyzer;
|
||||||
irs::boost_t boost;
|
irs::boost_t boost;
|
||||||
}; // FilterContext
|
}; // FilterContext
|
||||||
|
|
||||||
|
@ -141,10 +141,10 @@ FORCE_INLINE void appendExpression(irs::boolean_filter& filter,
|
||||||
exprFilter.boost(filterCtx.boost);
|
exprFilter.boost(filterCtx.boost);
|
||||||
}
|
}
|
||||||
|
|
||||||
arangodb::iresearch::IResearchLinkMeta::Analyzer extractAnalyzerFromArg(
|
arangodb::iresearch::FieldMeta::Analyzer extractAnalyzerFromArg(
|
||||||
irs::boolean_filter const* filter, arangodb::aql::AstNode const* analyzerArg,
|
irs::boolean_filter const* filter, arangodb::aql::AstNode const* analyzerArg,
|
||||||
QueryContext const& ctx, size_t argIdx, irs::string_ref const& functionName) {
|
QueryContext const& ctx, size_t argIdx, irs::string_ref const& functionName) {
|
||||||
static const arangodb::iresearch::IResearchLinkMeta::Analyzer invalid( // invalid analyzer
|
static const arangodb::iresearch::FieldMeta::Analyzer invalid( // invalid analyzer
|
||||||
nullptr, ""
|
nullptr, ""
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -170,7 +170,7 @@ arangodb::iresearch::IResearchLinkMeta::Analyzer extractAnalyzerFromArg(
|
||||||
ScopedAqlValue analyzerValue(*analyzerArg);
|
ScopedAqlValue analyzerValue(*analyzerArg);
|
||||||
|
|
||||||
if (!filter && !analyzerValue.isConstant()) {
|
if (!filter && !analyzerValue.isConstant()) {
|
||||||
return arangodb::iresearch::IResearchLinkMeta::Analyzer();
|
return arangodb::iresearch::FieldMeta::Analyzer();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!analyzerValue.execute(ctx)) {
|
if (!analyzerValue.execute(ctx)) {
|
||||||
|
@ -196,7 +196,7 @@ arangodb::iresearch::IResearchLinkMeta::Analyzer extractAnalyzerFromArg(
|
||||||
return invalid;
|
return invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
arangodb::iresearch::IResearchLinkMeta::Analyzer result(nullptr, analyzerId);
|
arangodb::iresearch::FieldMeta::Analyzer result(nullptr, analyzerId);
|
||||||
auto& analyzer = result._pool;
|
auto& analyzer = result._pool;
|
||||||
auto& shortName = result._shortName;
|
auto& shortName = result._shortName;
|
||||||
|
|
||||||
|
@ -1134,7 +1134,7 @@ arangodb::Result fromFuncAnalyzer(irs::boolean_filter* filter, QueryContext cons
|
||||||
}
|
}
|
||||||
|
|
||||||
ScopedAqlValue analyzerId(*analyzerArg);
|
ScopedAqlValue analyzerId(*analyzerArg);
|
||||||
arangodb::iresearch::IResearchLinkMeta::Analyzer analyzerValue; // default analyzer
|
arangodb::iresearch::FieldMeta::Analyzer analyzerValue; // default analyzer
|
||||||
auto& analyzer = analyzerValue._pool;
|
auto& analyzer = analyzerValue._pool;
|
||||||
auto& shortName = analyzerValue._shortName;
|
auto& shortName = analyzerValue._shortName;
|
||||||
|
|
||||||
|
@ -1349,44 +1349,44 @@ arangodb::Result fromFuncExists(irs::boolean_filter* filter, QueryContext const&
|
||||||
arangodb::basics::StringUtils::tolowerInPlace(&strArg); // normalize user input
|
arangodb::basics::StringUtils::tolowerInPlace(&strArg); // normalize user input
|
||||||
irs::string_ref const TypeAnalyzer("analyzer");
|
irs::string_ref const TypeAnalyzer("analyzer");
|
||||||
|
|
||||||
typedef bool (*TypeHandler)(std::string&, arangodb::iresearch::IResearchLinkMeta::Analyzer const&);
|
typedef bool (*TypeHandler)(std::string&, arangodb::iresearch::FieldMeta::Analyzer const&);
|
||||||
|
|
||||||
static std::map<irs::string_ref, TypeHandler> const TypeHandlers{
|
static std::map<irs::string_ref, TypeHandler> const TypeHandlers{
|
||||||
// any string
|
// any string
|
||||||
{irs::string_ref("string"),
|
{irs::string_ref("string"),
|
||||||
[](std::string& name, arangodb::iresearch::IResearchLinkMeta::Analyzer const&)->bool {
|
[](std::string& name, arangodb::iresearch::FieldMeta::Analyzer const&)->bool {
|
||||||
kludge::mangleAnalyzer(name);
|
kludge::mangleAnalyzer(name);
|
||||||
return true; // a prefix match
|
return true; // a prefix match
|
||||||
}},
|
}},
|
||||||
// any non-string type
|
// any non-string type
|
||||||
{irs::string_ref("type"),
|
{irs::string_ref("type"),
|
||||||
[](std::string& name, arangodb::iresearch::IResearchLinkMeta::Analyzer const&)->bool {
|
[](std::string& name, arangodb::iresearch::FieldMeta::Analyzer const&)->bool {
|
||||||
kludge::mangleType(name);
|
kludge::mangleType(name);
|
||||||
return true; // a prefix match
|
return true; // a prefix match
|
||||||
}},
|
}},
|
||||||
// concrete analyzer from the context
|
// concrete analyzer from the context
|
||||||
{TypeAnalyzer,
|
{TypeAnalyzer,
|
||||||
[](std::string& name, arangodb::iresearch::IResearchLinkMeta::Analyzer const& analyzer)->bool {
|
[](std::string& name, arangodb::iresearch::FieldMeta::Analyzer const& analyzer)->bool {
|
||||||
kludge::mangleStringField(name, analyzer);
|
kludge::mangleStringField(name, analyzer);
|
||||||
return false; // not a prefix match
|
return false; // not a prefix match
|
||||||
}},
|
}},
|
||||||
{irs::string_ref("numeric"),
|
{irs::string_ref("numeric"),
|
||||||
[](std::string& name, arangodb::iresearch::IResearchLinkMeta::Analyzer const&)->bool {
|
[](std::string& name, arangodb::iresearch::FieldMeta::Analyzer const&)->bool {
|
||||||
kludge::mangleNumeric(name);
|
kludge::mangleNumeric(name);
|
||||||
return false; // not a prefix match
|
return false; // not a prefix match
|
||||||
}},
|
}},
|
||||||
{irs::string_ref("bool"),
|
{irs::string_ref("bool"),
|
||||||
[](std::string& name, arangodb::iresearch::IResearchLinkMeta::Analyzer const&)->bool {
|
[](std::string& name, arangodb::iresearch::FieldMeta::Analyzer const&)->bool {
|
||||||
kludge::mangleBool(name);
|
kludge::mangleBool(name);
|
||||||
return false; // not a prefix match
|
return false; // not a prefix match
|
||||||
}},
|
}},
|
||||||
{irs::string_ref("boolean"),
|
{irs::string_ref("boolean"),
|
||||||
[](std::string& name, arangodb::iresearch::IResearchLinkMeta::Analyzer const&)->bool {
|
[](std::string& name, arangodb::iresearch::FieldMeta::Analyzer const&)->bool {
|
||||||
kludge::mangleBool(name);
|
kludge::mangleBool(name);
|
||||||
return false; // not a prefix match
|
return false; // not a prefix match
|
||||||
}},
|
}},
|
||||||
{irs::string_ref("null"),
|
{irs::string_ref("null"),
|
||||||
[](std::string& name, arangodb::iresearch::IResearchLinkMeta::Analyzer const&)->bool {
|
[](std::string& name, arangodb::iresearch::FieldMeta::Analyzer const&)->bool {
|
||||||
kludge::mangleNull(name);
|
kludge::mangleNull(name);
|
||||||
return false; // not a prefix match
|
return false; // not a prefix match
|
||||||
}}};
|
}}};
|
||||||
|
@ -2082,7 +2082,7 @@ namespace iresearch {
|
||||||
arangodb::aql::AstNode const& node) {
|
arangodb::aql::AstNode const& node) {
|
||||||
// The analyzer is referenced in the FilterContext and used during the
|
// The analyzer is referenced in the FilterContext and used during the
|
||||||
// following ::filter() call, so may not be a temporary.
|
// following ::filter() call, so may not be a temporary.
|
||||||
IResearchLinkMeta::Analyzer analyzer = IResearchLinkMeta::Analyzer();
|
FieldMeta::Analyzer analyzer = FieldMeta::Analyzer();
|
||||||
FilterContext const filterCtx(analyzer, irs::no_boost());
|
FilterContext const filterCtx(analyzer, irs::no_boost());
|
||||||
|
|
||||||
return ::filter(filter, ctx, filterCtx, node);
|
return ::filter(filter, ctx, filterCtx, node);
|
||||||
|
|
|
@ -52,18 +52,16 @@ void mangleNumeric(std::string& name) {
|
||||||
name.append(NUMERIC_SUFFIX.c_str(), NUMERIC_SUFFIX.size());
|
name.append(NUMERIC_SUFFIX.c_str(), NUMERIC_SUFFIX.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
void mangleStringField( // mangle string field
|
void mangleStringField(
|
||||||
std::string& name, // field name
|
std::string& name,
|
||||||
arangodb::iresearch::IResearchLinkMeta::Analyzer const& analyzer // analyzer to apply
|
arangodb::iresearch::FieldMeta::Analyzer const& analyzer) {
|
||||||
) {
|
|
||||||
name += ANALYZER_DELIMITER;
|
name += ANALYZER_DELIMITER;
|
||||||
name += analyzer._shortName;
|
name += analyzer._shortName;
|
||||||
}
|
}
|
||||||
|
|
||||||
void demangleStringField( // demangle string field
|
void demangleStringField(
|
||||||
std::string& name, // field name
|
std::string& name,
|
||||||
arangodb::iresearch::IResearchLinkMeta::Analyzer const& analyzer // analyzer to apply
|
arangodb::iresearch::FieldMeta::Analyzer const& analyzer) {
|
||||||
) {
|
|
||||||
// +1 for preceding '\1'
|
// +1 for preceding '\1'
|
||||||
auto const suffixSize = 1 + analyzer._shortName.size();
|
auto const suffixSize = 1 + analyzer._shortName.size();
|
||||||
|
|
||||||
|
@ -77,4 +75,4 @@ void demangleStringField( // demangle string field
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// --SECTION-- END-OF-FILE
|
// --SECTION-- END-OF-FILE
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
|
@ -44,17 +44,16 @@ void mangleNull(std::string& name);
|
||||||
void mangleBool(std::string& name);
|
void mangleBool(std::string& name);
|
||||||
void mangleNumeric(std::string& name);
|
void mangleNumeric(std::string& name);
|
||||||
|
|
||||||
void mangleStringField( // mangle string field
|
void mangleStringField(
|
||||||
std::string& name, // field name
|
std::string& name,
|
||||||
arangodb::iresearch::IResearchLinkMeta::Analyzer const& analyzer // analyzer to apply
|
arangodb::iresearch::FieldMeta::Analyzer const& analyzer);
|
||||||
);
|
|
||||||
void demangleStringField( // demangle string field
|
void demangleStringField(
|
||||||
std::string& name, // field name
|
std::string& name,
|
||||||
arangodb::iresearch::IResearchLinkMeta::Analyzer const& analyzer // analyzer to apply
|
arangodb::iresearch::FieldMeta::Analyzer const& analyzer);
|
||||||
);
|
|
||||||
|
|
||||||
} // namespace kludge
|
} // namespace kludge
|
||||||
} // namespace iresearch
|
} // namespace iresearch
|
||||||
} // namespace arangodb
|
} // namespace arangodb
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -31,7 +31,6 @@
|
||||||
#include "Aql/QueryCache.h"
|
#include "Aql/QueryCache.h"
|
||||||
#include "Basics/LocalTaskQueue.h"
|
#include "Basics/LocalTaskQueue.h"
|
||||||
#include "Basics/StaticStrings.h"
|
#include "Basics/StaticStrings.h"
|
||||||
#include "Basics/VelocyPackHelper.h"
|
|
||||||
#include "Cluster/ClusterInfo.h"
|
#include "Cluster/ClusterInfo.h"
|
||||||
#include "MMFiles/MMFilesCollection.h"
|
#include "MMFiles/MMFilesCollection.h"
|
||||||
#include "RestServer/DatabaseFeature.h"
|
#include "RestServer/DatabaseFeature.h"
|
||||||
|
@ -1749,6 +1748,25 @@ Result IResearchLink::unload() {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief lookup referenced analyzer
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
AnalyzerPool::ptr IResearchLink::findAnalyzer(AnalyzerPool const& analyzer) const {
|
||||||
|
auto const it = _meta._analyzerDefinitions.find(irs::string_ref(analyzer.name()));
|
||||||
|
|
||||||
|
if (it == _meta._analyzerDefinitions.end()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto const pool = *it;
|
||||||
|
|
||||||
|
if (pool && analyzer == *pool) {
|
||||||
|
return pool;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace iresearch
|
} // namespace iresearch
|
||||||
} // namespace arangodb
|
} // namespace arangodb
|
||||||
|
|
||||||
|
|
|
@ -218,6 +218,11 @@ class IResearchLink {
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
Result unload();
|
Result unload();
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief lookup referenced analyzer
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
AnalyzerPool::ptr findAnalyzer(AnalyzerPool const& analyzer) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
typedef std::function<void(irs::directory&)> InitCallback;
|
typedef std::function<void(irs::directory&)> InitCallback;
|
||||||
|
|
||||||
|
|
|
@ -63,8 +63,8 @@ arangodb::Result canUseAnalyzers( // validate
|
||||||
>();
|
>();
|
||||||
auto sysVocbase = sysDatabase ? sysDatabase->use() : nullptr;
|
auto sysVocbase = sysDatabase ? sysDatabase->use() : nullptr;
|
||||||
|
|
||||||
for (auto& entry: meta._analyzers) {
|
for (auto& pool: meta._analyzerDefinitions) {
|
||||||
if (!entry._pool) {
|
if (!pool) {
|
||||||
continue; // skip invalid entries
|
continue; // skip invalid entries
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,35 +73,35 @@ arangodb::Result canUseAnalyzers( // validate
|
||||||
if (sysVocbase) {
|
if (sysVocbase) {
|
||||||
result = arangodb::iresearch::IResearchAnalyzerFeature::canUse( // validate
|
result = arangodb::iresearch::IResearchAnalyzerFeature::canUse( // validate
|
||||||
arangodb::iresearch::IResearchAnalyzerFeature::normalize( // normalize
|
arangodb::iresearch::IResearchAnalyzerFeature::normalize( // normalize
|
||||||
entry._pool->name(), defaultVocbase, *sysVocbase // args
|
pool->name(), defaultVocbase, *sysVocbase // args
|
||||||
), // analyzer
|
), // analyzer
|
||||||
arangodb::auth::Level::RO // auth level
|
arangodb::auth::Level::RO // auth level
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
result = arangodb::iresearch::IResearchAnalyzerFeature::canUse( // validate
|
result = arangodb::iresearch::IResearchAnalyzerFeature::canUse( // validate
|
||||||
entry._pool->name(), arangodb::auth::Level::RO // args
|
pool->name(), arangodb::auth::Level::RO // args
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
return arangodb::Result( // result
|
return {
|
||||||
TRI_ERROR_FORBIDDEN, // code
|
TRI_ERROR_FORBIDDEN,
|
||||||
std::string("read access is forbidden to arangosearch analyzer '") + entry._pool->name() + "'"
|
std::string("read access is forbidden to arangosearch analyzer '") + pool->name() + "'"
|
||||||
);
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& field: meta._fields) {
|
// for (auto& field: meta._fields) {
|
||||||
TRI_ASSERT(field.value().get()); // ensured by UniqueHeapInstance constructor
|
// TRI_ASSERT(field.value().get()); // ensured by UniqueHeapInstance constructor
|
||||||
auto& entry = field.value();
|
// auto& entry = field.value();
|
||||||
auto res = canUseAnalyzers(*entry, defaultVocbase);
|
// auto res = canUseAnalyzers(*entry, defaultVocbase);
|
||||||
|
//
|
||||||
|
// if (!res.ok()) {
|
||||||
|
// return res;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
if (!res.ok()) {
|
return {};
|
||||||
return res;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return arangodb::Result();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
arangodb::Result createLink( // create link
|
arangodb::Result createLink( // create link
|
||||||
|
@ -795,17 +795,22 @@ namespace iresearch {
|
||||||
// analyzer should be either from same database as view (and collection) or from system database
|
// analyzer should be either from same database as view (and collection) or from system database
|
||||||
{
|
{
|
||||||
const auto& currentVocbase = vocbase.name();
|
const auto& currentVocbase = vocbase.name();
|
||||||
for (const auto& analyzer : meta._analyzers) {
|
for (const auto& analyzer : meta._analyzerDefinitions) {
|
||||||
TRI_ASSERT(analyzer._pool); // should be checked in meta init
|
TRI_ASSERT(analyzer); // should be checked in meta init
|
||||||
if (ADB_UNLIKELY(!analyzer._pool)) {
|
if (ADB_UNLIKELY(!analyzer)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
auto analyzerVocbase = IResearchAnalyzerFeature::extractVocbaseName(analyzer._pool->name());
|
auto* pool = analyzer.get();
|
||||||
|
auto analyzerVocbase = IResearchAnalyzerFeature::extractVocbaseName(pool->name());
|
||||||
|
|
||||||
if (!IResearchAnalyzerFeature::analyzerReachableFromDb(analyzerVocbase, currentVocbase, true)) {
|
if (!IResearchAnalyzerFeature::analyzerReachableFromDb(analyzerVocbase, currentVocbase, true)) {
|
||||||
return arangodb::Result(
|
return {
|
||||||
TRI_ERROR_BAD_PARAMETER,
|
TRI_ERROR_BAD_PARAMETER,
|
||||||
std::string("Analyzer '").append(analyzer._pool->name())
|
std::string("Analyzer '")
|
||||||
.append("' is not accessible from database '").append(currentVocbase).append("'"));
|
.append(pool->name())
|
||||||
|
.append("' is not accessible from database '")
|
||||||
|
.append(currentVocbase).append("'")
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
#include "Cluster/ServerState.h"
|
#include "Cluster/ServerState.h"
|
||||||
#include "RestServer/SystemDatabaseFeature.h"
|
#include "RestServer/SystemDatabaseFeature.h"
|
||||||
#include "VelocyPackHelper.h"
|
#include "VelocyPackHelper.h"
|
||||||
|
#include "Basics/VelocyPackHelper.h"
|
||||||
#include "velocypack/Builder.h"
|
#include "velocypack/Builder.h"
|
||||||
#include "velocypack/Iterator.h"
|
#include "velocypack/Iterator.h"
|
||||||
#include "IResearchLinkMeta.h"
|
#include "IResearchLinkMeta.h"
|
||||||
|
@ -39,8 +40,8 @@
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
bool equalAnalyzers(arangodb::iresearch::IResearchLinkMeta::Analyzers const& lhs,
|
bool equalAnalyzers(std::vector<arangodb::iresearch::FieldMeta::Analyzer> const& lhs,
|
||||||
arangodb::iresearch::IResearchLinkMeta::Analyzers const& rhs) noexcept {
|
std::vector<arangodb::iresearch::FieldMeta::Analyzer> const& rhs) noexcept {
|
||||||
if (lhs.size() != rhs.size()) {
|
if (lhs.size() != rhs.size()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -70,32 +71,41 @@ bool equalAnalyzers(arangodb::iresearch::IResearchLinkMeta::Analyzers const& lhs
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
bool operator<(arangodb::iresearch::FieldMeta::Analyzer const& lhs,
|
||||||
|
irs::string_ref const& rhs) noexcept {
|
||||||
|
return lhs._pool->name() < rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator<(irs::string_ref const& lhs,
|
||||||
|
arangodb::iresearch::FieldMeta::Analyzer const& rhs) noexcept {
|
||||||
|
return lhs < rhs._pool->name();
|
||||||
|
}
|
||||||
|
|
||||||
namespace arangodb {
|
namespace arangodb {
|
||||||
namespace iresearch {
|
namespace iresearch {
|
||||||
|
|
||||||
IResearchLinkMeta::Analyzer::Analyzer()
|
// -----------------------------------------------------------------------------
|
||||||
|
// --SECTION-- FieldMeta::Analyzer
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
FieldMeta::Analyzer::Analyzer()
|
||||||
: _pool(IResearchAnalyzerFeature::identity()) {
|
: _pool(IResearchAnalyzerFeature::identity()) {
|
||||||
if (_pool) {
|
if (_pool) {
|
||||||
_shortName = _pool->name(); // static analyzers are used verbatim
|
_shortName = _pool->name(); // static analyzers are used verbatim
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
IResearchLinkMeta::Mask::Mask(bool mask /*= false*/) noexcept
|
// -----------------------------------------------------------------------------
|
||||||
: _analyzerDefinitions(mask),
|
// --SECTION-- IResearchLinkMeta::FieldMeta
|
||||||
_analyzers(mask),
|
// -----------------------------------------------------------------------------
|
||||||
_fields(mask),
|
|
||||||
_includeAllFields(mask),
|
/*static*/ const FieldMeta& FieldMeta::DEFAULT() {
|
||||||
_trackListPositions(mask),
|
static const FieldMeta meta;
|
||||||
_storeValues(mask),
|
|
||||||
_sort(mask) {
|
return meta;
|
||||||
}
|
}
|
||||||
|
|
||||||
IResearchLinkMeta::IResearchLinkMeta()
|
FieldMeta::FieldMeta() {
|
||||||
: //_fields(<empty>), // no fields to index by default
|
|
||||||
_includeAllFields(false), // true to match all encountered fields, false
|
|
||||||
// match only fields in '_fields'
|
|
||||||
_trackListPositions(false), // treat '_trackListPositions' as SQL-IN
|
|
||||||
_storeValues(ValueStorage::NONE) { // do not track values at all
|
|
||||||
Analyzer analyzer; // identity analyzer
|
Analyzer analyzer; // identity analyzer
|
||||||
|
|
||||||
// identity-only tokenization
|
// identity-only tokenization
|
||||||
|
@ -104,16 +114,16 @@ IResearchLinkMeta::IResearchLinkMeta()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IResearchLinkMeta::operator==(IResearchLinkMeta const& other) const noexcept {
|
bool FieldMeta::operator==(FieldMeta const& rhs) const noexcept {
|
||||||
if (!equalAnalyzers(_analyzers, other._analyzers)) {
|
if (!equalAnalyzers(_analyzers, rhs._analyzers)) {
|
||||||
return false; // values do not match
|
return false; // values do not match
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_fields.size() != other._fields.size()) {
|
if (_fields.size() != rhs._fields.size()) {
|
||||||
return false; // values do not match
|
return false; // values do not match
|
||||||
}
|
}
|
||||||
|
|
||||||
auto itr = other._fields.begin();
|
auto itr = rhs._fields.begin();
|
||||||
|
|
||||||
for (auto& entry : _fields) {
|
for (auto& entry : _fields) {
|
||||||
if (itr.key() != entry.key() || itr.value() != entry.value()) {
|
if (itr.key() != entry.key() || itr.value() != entry.value()) {
|
||||||
|
@ -123,27 +133,423 @@ bool IResearchLinkMeta::operator==(IResearchLinkMeta const& other) const noexcep
|
||||||
++itr;
|
++itr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_includeAllFields != other._includeAllFields) {
|
if (_includeAllFields != rhs._includeAllFields) {
|
||||||
return false; // values do not match
|
return false; // values do not match
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_trackListPositions != other._trackListPositions) {
|
if (_trackListPositions != rhs._trackListPositions) {
|
||||||
return false; // values do not match
|
return false; // values do not match
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_storeValues != other._storeValues) {
|
if (_storeValues != rhs._storeValues) {
|
||||||
return false; // values do not match
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_sort != other._sort) {
|
|
||||||
return false; // values do not match
|
return false; // values do not match
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IResearchLinkMeta::operator!=(IResearchLinkMeta const& other) const noexcept {
|
bool FieldMeta::init(velocypack::Slice const& slice,
|
||||||
return !(*this == other);
|
std::string& errorField,
|
||||||
|
TRI_vocbase_t const* defaultVocbase /*= nullptr*/,
|
||||||
|
FieldMeta const& defaults /*= DEFAULT()*/,
|
||||||
|
Mask* mask /*= nullptr*/,
|
||||||
|
std::set<AnalyzerPool::ptr, AnalyzerComparer>* referencedAnalyzers /*= nullptr*/) {
|
||||||
|
if (!slice.isObject()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Mask tmpMask;
|
||||||
|
|
||||||
|
if (!mask) {
|
||||||
|
mask = &tmpMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// optional string list
|
||||||
|
static const std::string fieldName("analyzers");
|
||||||
|
|
||||||
|
mask->_analyzers = slice.hasKey(fieldName);
|
||||||
|
|
||||||
|
if (!mask->_analyzers) {
|
||||||
|
_analyzers = defaults._analyzers;
|
||||||
|
} else {
|
||||||
|
auto* analyzers = application_features::ApplicationServer::lookupFeature< // find feature
|
||||||
|
IResearchAnalyzerFeature // feature type
|
||||||
|
>();
|
||||||
|
auto* sysDatabase = application_features::ApplicationServer::lookupFeature< // find feature
|
||||||
|
SystemDatabaseFeature // feature type
|
||||||
|
>();
|
||||||
|
auto field = slice.get(fieldName);
|
||||||
|
|
||||||
|
if (!field.isArray()) {
|
||||||
|
errorField = fieldName;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_analyzers.clear(); // reset to match read values exactly
|
||||||
|
std::unordered_set<irs::string_ref> uniqueGuard; // deduplicate analyzers
|
||||||
|
|
||||||
|
for (velocypack::ArrayIterator itr(field); itr.valid(); ++itr) {
|
||||||
|
auto value = *itr;
|
||||||
|
|
||||||
|
if (!value.isString()) {
|
||||||
|
errorField = fieldName + "[" + std::to_string(itr.index()) + "]";
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto name = value.copyString();
|
||||||
|
auto shortName = name;
|
||||||
|
|
||||||
|
if (defaultVocbase) {
|
||||||
|
auto sysVocbase = sysDatabase ? sysDatabase->use() : nullptr;
|
||||||
|
|
||||||
|
if (sysVocbase) {
|
||||||
|
name = IResearchAnalyzerFeature::normalize(
|
||||||
|
name, *defaultVocbase, *sysVocbase);
|
||||||
|
shortName = IResearchAnalyzerFeature::normalize(
|
||||||
|
name, *defaultVocbase, *sysVocbase, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AnalyzerPool::ptr analyzer;
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
|
if (referencedAnalyzers) {
|
||||||
|
auto it = referencedAnalyzers->find(irs::string_ref(name));
|
||||||
|
|
||||||
|
if (it != referencedAnalyzers->end()) {
|
||||||
|
analyzer = *it;
|
||||||
|
found = static_cast<bool>(analyzer);
|
||||||
|
|
||||||
|
if (ADB_UNLIKELY(!found)) {
|
||||||
|
TRI_ASSERT(false); // should not happen
|
||||||
|
referencedAnalyzers->erase(it); // remove null analyzer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found && analyzers) {
|
||||||
|
// for cluster only check cache to avoid ClusterInfo locking issues
|
||||||
|
// analyzer should have been populated via 'analyzerDefinitions' above
|
||||||
|
analyzer = analyzers->get(name, ServerState::instance()->isClusterRole());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!analyzer) {
|
||||||
|
errorField = fieldName + "." + value.copyString(); // original (non-normalized) 'name' value
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found && referencedAnalyzers) {
|
||||||
|
// save in referencedAnalyzers
|
||||||
|
referencedAnalyzers->emplace(analyzer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// avoid adding same analyzer twice
|
||||||
|
if (uniqueGuard.emplace(analyzer->name()).second) {
|
||||||
|
_analyzers.emplace_back(analyzer, std::move(shortName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// optional bool
|
||||||
|
static const std::string fieldName("includeAllFields");
|
||||||
|
|
||||||
|
mask->_includeAllFields = slice.hasKey(fieldName);
|
||||||
|
|
||||||
|
if (!mask->_includeAllFields) {
|
||||||
|
_includeAllFields = defaults._includeAllFields;
|
||||||
|
} else {
|
||||||
|
auto field = slice.get(fieldName);
|
||||||
|
|
||||||
|
if (!field.isBool()) {
|
||||||
|
errorField = fieldName;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_includeAllFields = field.getBool();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// optional bool
|
||||||
|
static const std::string fieldName("trackListPositions");
|
||||||
|
|
||||||
|
mask->_trackListPositions = slice.hasKey(fieldName);
|
||||||
|
|
||||||
|
if (!mask->_trackListPositions) {
|
||||||
|
_trackListPositions = defaults._trackListPositions;
|
||||||
|
} else {
|
||||||
|
auto field = slice.get(fieldName);
|
||||||
|
|
||||||
|
if (!field.isBool()) {
|
||||||
|
errorField = fieldName;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_trackListPositions = field.getBool();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// optional string enum
|
||||||
|
static const std::string fieldName("storeValues");
|
||||||
|
|
||||||
|
mask->_storeValues = slice.hasKey(fieldName);
|
||||||
|
|
||||||
|
if (!mask->_storeValues) {
|
||||||
|
_storeValues = defaults._storeValues;
|
||||||
|
} else {
|
||||||
|
auto field = slice.get(fieldName);
|
||||||
|
|
||||||
|
if (!field.isString()) {
|
||||||
|
errorField = fieldName;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const std::unordered_map<std::string, ValueStorage> policies = {
|
||||||
|
{"none", ValueStorage::NONE},
|
||||||
|
{"id", ValueStorage::ID},
|
||||||
|
{"value", ValueStorage::VALUE}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto name = field.copyString();
|
||||||
|
auto itr = policies.find(name);
|
||||||
|
|
||||||
|
if (itr == policies.end()) {
|
||||||
|
errorField = fieldName + "." + name;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_storeValues = itr->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// .............................................................................
|
||||||
|
// process fields last since children inherit from parent
|
||||||
|
// .............................................................................
|
||||||
|
|
||||||
|
{
|
||||||
|
// optional string map<name, overrides>
|
||||||
|
static const std::string fieldName("fields");
|
||||||
|
|
||||||
|
mask->_fields = slice.hasKey(fieldName);
|
||||||
|
|
||||||
|
if (!mask->_fields) {
|
||||||
|
_fields = defaults._fields;
|
||||||
|
} else {
|
||||||
|
auto field = slice.get(fieldName);
|
||||||
|
|
||||||
|
if (!field.isObject()) {
|
||||||
|
errorField = fieldName;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto subDefaults = *this;
|
||||||
|
|
||||||
|
subDefaults._fields.clear(); // do not inherit fields and overrides from this field
|
||||||
|
_fields.clear(); // reset to match either defaults or read values exactly
|
||||||
|
|
||||||
|
for (velocypack::ObjectIterator itr(field); itr.valid(); ++itr) {
|
||||||
|
auto key = itr.key();
|
||||||
|
auto value = itr.value();
|
||||||
|
|
||||||
|
if (!key.isString()) {
|
||||||
|
errorField = fieldName + "[" +
|
||||||
|
basics::StringUtils::itoa(itr.index()) + "]";
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto name = key.copyString();
|
||||||
|
|
||||||
|
if (!value.isObject()) {
|
||||||
|
errorField = fieldName + "." + name;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string childErrorField;
|
||||||
|
|
||||||
|
if (!_fields[name]->init(value, childErrorField, defaultVocbase,
|
||||||
|
subDefaults, nullptr, referencedAnalyzers)) {
|
||||||
|
errorField = fieldName + "." + name + "." + childErrorField;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FieldMeta::json(velocypack::Builder& builder,
|
||||||
|
FieldMeta const* ignoreEqual /*= nullptr*/,
|
||||||
|
TRI_vocbase_t const* defaultVocbase /*= nullptr*/,
|
||||||
|
Mask const* mask /*= nullptr*/) const {
|
||||||
|
if (!builder.isOpenObject()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<std::string, AnalyzerPool::ptr> analyzers;
|
||||||
|
|
||||||
|
if ((!ignoreEqual || !equalAnalyzers(_analyzers, ignoreEqual->_analyzers)) &&
|
||||||
|
(!mask || mask->_analyzers)) {
|
||||||
|
velocypack::Builder analyzersBuilder;
|
||||||
|
|
||||||
|
analyzersBuilder.openArray();
|
||||||
|
|
||||||
|
for (auto& entry : _analyzers) {
|
||||||
|
if (!entry._pool) {
|
||||||
|
continue; // skip null analyzers
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string name;
|
||||||
|
|
||||||
|
if (defaultVocbase) {
|
||||||
|
auto* sysDatabase = application_features::ApplicationServer::lookupFeature< // find feature
|
||||||
|
SystemDatabaseFeature // feature type
|
||||||
|
>();
|
||||||
|
auto sysVocbase = sysDatabase ? sysDatabase->use() : nullptr;
|
||||||
|
|
||||||
|
if (!sysVocbase) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @note: DBServerAgencySync::getLocalCollections(...) generates
|
||||||
|
// 'forPersistence' definitions that are then compared in
|
||||||
|
// Maintenance.cpp:compareIndexes(...) via
|
||||||
|
// arangodb::Index::Compare(...) without access to
|
||||||
|
// 'defaultVocbase', hence the generated definitions must not
|
||||||
|
// rely on 'defaultVocbase'
|
||||||
|
// hence must use 'expandVocbasePrefix==true' if
|
||||||
|
// 'writeAnalyzerDefinition==true' for normalize
|
||||||
|
// for 'writeAnalyzerDefinition==false' must use
|
||||||
|
// 'expandVocbasePrefix==false' so that dump/restore an restore
|
||||||
|
// definitions into differently named databases
|
||||||
|
name = IResearchAnalyzerFeature::normalize( // normalize
|
||||||
|
entry._pool->name(), // analyzer name
|
||||||
|
*defaultVocbase, // active vocbase
|
||||||
|
*sysVocbase, // system vocbase
|
||||||
|
false // expand vocbase prefix
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
name = entry._pool->name(); // verbatim (assume already normalized)
|
||||||
|
}
|
||||||
|
|
||||||
|
analyzers.emplace(name, entry._pool);
|
||||||
|
analyzersBuilder.add(velocypack::Value(std::move(name)));
|
||||||
|
}
|
||||||
|
|
||||||
|
analyzersBuilder.close();
|
||||||
|
builder.add("analyzers", analyzersBuilder.slice());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mask || mask->_fields) { // fields are not inherited from parent
|
||||||
|
velocypack::Builder fieldsBuilder;
|
||||||
|
Mask fieldMask(true); // output all non-matching fields
|
||||||
|
auto subDefaults = *this; // make modifable copy
|
||||||
|
|
||||||
|
subDefaults._fields.clear(); // do not inherit fields and overrides from this field
|
||||||
|
fieldsBuilder.openObject();
|
||||||
|
|
||||||
|
for (auto& entry : _fields) {
|
||||||
|
fieldMask._fields = !entry.value()->_fields.empty(); // do not output empty fields on subobjects
|
||||||
|
fieldsBuilder.add( // add sub-object
|
||||||
|
entry.key(), // field name
|
||||||
|
velocypack::Value(velocypack::ValueType::Object)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!entry.value()->json(fieldsBuilder, &subDefaults, defaultVocbase, &fieldMask)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldsBuilder.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldsBuilder.close();
|
||||||
|
builder.add("fields", fieldsBuilder.slice());
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((!ignoreEqual || _includeAllFields != ignoreEqual->_includeAllFields) &&
|
||||||
|
(!mask || mask->_includeAllFields)) {
|
||||||
|
builder.add("includeAllFields", velocypack::Value(_includeAllFields));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((!ignoreEqual || _trackListPositions != ignoreEqual->_trackListPositions) &&
|
||||||
|
(!mask || mask->_trackListPositions)) {
|
||||||
|
builder.add("trackListPositions", velocypack::Value(_trackListPositions));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((!ignoreEqual || _storeValues != ignoreEqual->_storeValues) &&
|
||||||
|
(!mask || mask->_storeValues)) {
|
||||||
|
static_assert(adjacencyChecker<ValueStorage>::checkAdjacency<ValueStorage::VALUE, ValueStorage::ID, ValueStorage::NONE>(),
|
||||||
|
"Values are not adjacent");
|
||||||
|
|
||||||
|
static const std::string policies[]{
|
||||||
|
"none", // ValueStorage::NONE
|
||||||
|
"id", // ValueStorage::ID
|
||||||
|
"value" // ValueStorage::VALUE
|
||||||
|
};
|
||||||
|
|
||||||
|
auto const policyIdx =
|
||||||
|
static_cast<std::underlying_type<ValueStorage>::type>(_storeValues);
|
||||||
|
|
||||||
|
if (policyIdx >= IRESEARCH_COUNTOF(policies)) {
|
||||||
|
return false; // unsupported value storage policy
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.add("storeValues", velocypack::Value(policies[policyIdx]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t FieldMeta::memory() const noexcept {
|
||||||
|
auto size = sizeof(FieldMeta);
|
||||||
|
|
||||||
|
size += _analyzers.size() * sizeof(decltype(_analyzers)::value_type);
|
||||||
|
size += _fields.size() * sizeof(decltype(_fields)::value_type);
|
||||||
|
|
||||||
|
for (auto& entry : _fields) {
|
||||||
|
size += entry.key().size();
|
||||||
|
size += entry.value()->memory();
|
||||||
|
}
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// --SECTION-- IResearchLinkMeta
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
IResearchLinkMeta::IResearchLinkMeta() {
|
||||||
|
// add default analyzers
|
||||||
|
for (auto& analyzer : _analyzers) {
|
||||||
|
_analyzerDefinitions.emplace(analyzer._pool);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IResearchLinkMeta::operator==(IResearchLinkMeta const& other) const noexcept {
|
||||||
|
if (FieldMeta::operator!=(other)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_sort != other._sort) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*static*/ const IResearchLinkMeta& IResearchLinkMeta::DEFAULT() {
|
/*static*/ const IResearchLinkMeta& IResearchLinkMeta::DEFAULT() {
|
||||||
|
@ -152,14 +558,12 @@ bool IResearchLinkMeta::operator!=(IResearchLinkMeta const& other) const noexcep
|
||||||
return meta;
|
return meta;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IResearchLinkMeta::init( // initialize meta
|
bool IResearchLinkMeta::init(velocypack::Slice const& slice,
|
||||||
arangodb::velocypack::Slice const& slice, // definition
|
bool readAnalyzerDefinition,
|
||||||
bool readAnalyzerDefinition, // allow analyzer definitions
|
std::string& errorField,
|
||||||
std::string& errorField, // field causing error (out-param)
|
TRI_vocbase_t const* defaultVocbase /*= nullptr*/,
|
||||||
TRI_vocbase_t const* defaultVocbase /*= nullptr*/, // fallback vocbase
|
FieldMeta const& defaults /*= DEFAULT()*/,
|
||||||
IResearchLinkMeta const& defaults /*= DEFAULT()*/, // inherited defaults
|
Mask* mask /*= nullptr*/) {
|
||||||
Mask* mask /*= nullptr*/ // initialized fields (out-param)
|
|
||||||
) {
|
|
||||||
if (!slice.isObject()) {
|
if (!slice.isObject()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -185,6 +589,9 @@ bool IResearchLinkMeta::init( // initialize meta
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
// clear existing definitions
|
||||||
|
_analyzerDefinitions.clear();
|
||||||
|
|
||||||
// optional object list
|
// optional object list
|
||||||
static const std::string fieldName("analyzerDefinitions");
|
static const std::string fieldName("analyzerDefinitions");
|
||||||
|
|
||||||
|
@ -193,11 +600,11 @@ bool IResearchLinkMeta::init( // initialize meta
|
||||||
// load analyzer definitions if requested (used on cluster)
|
// load analyzer definitions if requested (used on cluster)
|
||||||
// @note must load definitions before loading 'analyzers' to ensure presence
|
// @note must load definitions before loading 'analyzers' to ensure presence
|
||||||
if (readAnalyzerDefinition && mask->_analyzerDefinitions) {
|
if (readAnalyzerDefinition && mask->_analyzerDefinitions) {
|
||||||
auto* analyzers = arangodb::application_features::ApplicationServer::lookupFeature< // find feature
|
auto* analyzers = application_features::ApplicationServer::lookupFeature< // find feature
|
||||||
IResearchAnalyzerFeature // featue type
|
IResearchAnalyzerFeature // featue type
|
||||||
>();
|
>();
|
||||||
auto* sysDatabase = arangodb::application_features::ApplicationServer::lookupFeature< // find feature
|
auto* sysDatabase = application_features::ApplicationServer::lookupFeature< // find feature
|
||||||
arangodb::SystemDatabaseFeature // featue type
|
SystemDatabaseFeature // featue type
|
||||||
>();
|
>();
|
||||||
auto field = slice.get(fieldName);
|
auto field = slice.get(fieldName);
|
||||||
|
|
||||||
|
@ -207,7 +614,7 @@ bool IResearchLinkMeta::init( // initialize meta
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (arangodb::velocypack::ArrayIterator itr(field); itr.valid(); ++itr) {
|
for (velocypack::ArrayIterator itr(field); itr.valid(); ++itr) {
|
||||||
auto value = *itr;
|
auto value = *itr;
|
||||||
|
|
||||||
if (!value.isObject()) {
|
if (!value.isObject()) {
|
||||||
|
@ -292,7 +699,7 @@ bool IResearchLinkMeta::init( // initialize meta
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (arangodb::velocypack::ArrayIterator subItr(subField);
|
for (velocypack::ArrayIterator subItr(subField);
|
||||||
subItr.valid();
|
subItr.valid();
|
||||||
++subItr) {
|
++subItr) {
|
||||||
auto subValue = *subItr;
|
auto subValue = *subItr;
|
||||||
|
@ -317,226 +724,32 @@ bool IResearchLinkMeta::init( // initialize meta
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// get analyzer potentially creating it (e.g. on cluster)
|
AnalyzerPool::ptr analyzer;
|
||||||
// @note do not use emplace(...) since it'll trigger loadAnalyzers(...)
|
auto const res = IResearchAnalyzerFeature::createAnalyzerPool(analyzer, name, type, properties, features);
|
||||||
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult emplaceResult;
|
|
||||||
auto const res = analyzers->get(emplaceResult, name, type, properties, features);
|
if (res.fail() || !analyzer) {
|
||||||
if (!res.ok()) {
|
|
||||||
errorField = fieldName + "[" + std::to_string(itr.index()) + "]";
|
errorField = fieldName + "[" + std::to_string(itr.index()) + "]";
|
||||||
|
|
||||||
return false;
|
if (res.fail()) {
|
||||||
}
|
errorField.append(": ").append(res.errorMessage());
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
// optional string list
|
|
||||||
static const std::string fieldName("analyzers");
|
|
||||||
|
|
||||||
mask->_analyzers = slice.hasKey(fieldName);
|
|
||||||
|
|
||||||
if (!mask->_analyzers) {
|
|
||||||
_analyzers = defaults._analyzers;
|
|
||||||
} else {
|
|
||||||
auto* analyzers = arangodb::application_features::ApplicationServer::lookupFeature< // find feature
|
|
||||||
IResearchAnalyzerFeature // feature type
|
|
||||||
>();
|
|
||||||
auto* sysDatabase = arangodb::application_features::ApplicationServer::lookupFeature< // find feature
|
|
||||||
arangodb::SystemDatabaseFeature // feature type
|
|
||||||
>();
|
|
||||||
auto field = slice.get(fieldName);
|
|
||||||
|
|
||||||
if (!analyzers || !field.isArray()) {
|
|
||||||
errorField = fieldName;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
_analyzers.clear(); // reset to match read values exactly
|
|
||||||
std::unordered_set<irs::string_ref> uniqueGuard;
|
|
||||||
|
|
||||||
for (arangodb::velocypack::ArrayIterator itr(field); itr.valid(); ++itr) {
|
|
||||||
auto value = *itr;
|
|
||||||
|
|
||||||
if (!value.isString()) {
|
|
||||||
errorField = fieldName + "[" + std::to_string(itr.index()) + "]";
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto name = value.copyString();
|
|
||||||
auto shortName = name;
|
|
||||||
|
|
||||||
if (defaultVocbase) {
|
|
||||||
auto sysVocbase = sysDatabase ? sysDatabase->use() : nullptr;
|
|
||||||
|
|
||||||
if (sysVocbase) {
|
|
||||||
name = IResearchAnalyzerFeature::normalize(
|
|
||||||
name, *defaultVocbase, *sysVocbase);
|
|
||||||
shortName = IResearchAnalyzerFeature::normalize(
|
|
||||||
name, *defaultVocbase, *sysVocbase, false);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// for cluster only check cache to avoid ClusterInfo locking issues
|
|
||||||
// analyzer should have been populated via 'analyzerDefinitions' above
|
|
||||||
auto analyzer = analyzers->get(name, arangodb::ServerState::instance()->isClusterRole());
|
|
||||||
|
|
||||||
if (!analyzer) {
|
|
||||||
errorField = fieldName + "." + value.copyString(); // original (non-normalized) 'name' value
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// avoid adding same analyzer twice
|
|
||||||
if (uniqueGuard.emplace(analyzer->name()).second)
|
|
||||||
_analyzers.emplace_back(analyzer, std::move(shortName));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
// optional bool
|
|
||||||
static const std::string fieldName("includeAllFields");
|
|
||||||
|
|
||||||
mask->_includeAllFields = slice.hasKey(fieldName);
|
|
||||||
|
|
||||||
if (!mask->_includeAllFields) {
|
|
||||||
_includeAllFields = defaults._includeAllFields;
|
|
||||||
} else {
|
|
||||||
auto field = slice.get(fieldName);
|
|
||||||
|
|
||||||
if (!field.isBool()) {
|
|
||||||
errorField = fieldName;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
_includeAllFields = field.getBool();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
// optional bool
|
|
||||||
static const std::string fieldName("trackListPositions");
|
|
||||||
|
|
||||||
mask->_trackListPositions = slice.hasKey(fieldName);
|
|
||||||
|
|
||||||
if (!mask->_trackListPositions) {
|
|
||||||
_trackListPositions = defaults._trackListPositions;
|
|
||||||
} else {
|
|
||||||
auto field = slice.get(fieldName);
|
|
||||||
|
|
||||||
if (!field.isBool()) {
|
|
||||||
errorField = fieldName;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
_trackListPositions = field.getBool();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
// optional string enum
|
|
||||||
static const std::string fieldName("storeValues");
|
|
||||||
|
|
||||||
mask->_storeValues = slice.hasKey(fieldName);
|
|
||||||
|
|
||||||
if (!mask->_storeValues) {
|
|
||||||
_storeValues = defaults._storeValues;
|
|
||||||
} else {
|
|
||||||
auto field = slice.get(fieldName);
|
|
||||||
|
|
||||||
if (!field.isString()) {
|
|
||||||
errorField = fieldName;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const std::unordered_map<std::string, ValueStorage> policies = {
|
|
||||||
{"none", ValueStorage::NONE}, {"id", ValueStorage::ID}, {"full", ValueStorage::FULL}};
|
|
||||||
auto name = field.copyString();
|
|
||||||
auto itr = policies.find(name);
|
|
||||||
|
|
||||||
if (itr == policies.end()) {
|
|
||||||
errorField = fieldName + "." + name;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
_storeValues = itr->second;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// .............................................................................
|
|
||||||
// process fields last since children inherit from parent
|
|
||||||
// .............................................................................
|
|
||||||
|
|
||||||
{
|
|
||||||
// optional string map<name, overrides>
|
|
||||||
static const std::string fieldName("fields");
|
|
||||||
|
|
||||||
mask->_fields = slice.hasKey(fieldName);
|
|
||||||
|
|
||||||
if (!mask->_fields) {
|
|
||||||
_fields = defaults._fields;
|
|
||||||
} else {
|
|
||||||
auto field = slice.get(fieldName);
|
|
||||||
|
|
||||||
if (!field.isObject()) {
|
|
||||||
errorField = fieldName;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto subDefaults = *this;
|
|
||||||
|
|
||||||
subDefaults._fields.clear(); // do not inherit fields and overrides from this field
|
|
||||||
_fields.clear(); // reset to match either defaults or read values exactly
|
|
||||||
|
|
||||||
for (arangodb::velocypack::ObjectIterator itr(field); itr.valid(); ++itr) {
|
|
||||||
auto key = itr.key();
|
|
||||||
auto value = itr.value();
|
|
||||||
|
|
||||||
if (!key.isString()) {
|
|
||||||
errorField = fieldName + "[" +
|
|
||||||
arangodb::basics::StringUtils::itoa(itr.index()) + "]";
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto name = key.copyString();
|
_analyzerDefinitions.emplace(analyzer);
|
||||||
|
|
||||||
if (!value.isObject()) {
|
|
||||||
errorField = fieldName + "." + name;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string childErrorField;
|
|
||||||
|
|
||||||
// false == do not read 'analyzerDefinitions' from child elements
|
|
||||||
if (!_fields[name]->init(value, false, childErrorField, defaultVocbase, subDefaults)) {
|
|
||||||
errorField = fieldName + "." + name + "." + childErrorField;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return FieldMeta::init(slice, errorField, defaultVocbase, defaults, mask, &_analyzerDefinitions);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IResearchLinkMeta::json( // append meta jSON
|
bool IResearchLinkMeta::json(velocypack::Builder& builder,
|
||||||
arangodb::velocypack::Builder& builder, // output buffer (out-param)
|
bool writeAnalyzerDefinition,
|
||||||
bool writeAnalyzerDefinition, // output fill analyzer definition instead of just name
|
IResearchLinkMeta const* ignoreEqual /*= nullptr*/,
|
||||||
IResearchLinkMeta const* ignoreEqual /*= nullptr*/, // values to ignore if equal
|
TRI_vocbase_t const* defaultVocbase /*= nullptr*/,
|
||||||
TRI_vocbase_t const* defaultVocbase /*= nullptr*/, // fallback vocbase
|
Mask const* mask /*= nullptr*/) const {
|
||||||
Mask const* mask /*= nullptr*/, // values to ignore always
|
|
||||||
std::map<std::string, AnalyzerPool::ptr>* usedAnalyzers /*= nullptr*/ // append analyzers used in definition
|
|
||||||
) const {
|
|
||||||
if (!builder.isOpenObject()) {
|
if (!builder.isOpenObject()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -550,154 +763,29 @@ bool IResearchLinkMeta::json( // append meta jSON
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::map<std::string, AnalyzerPool::ptr> analyzers;
|
|
||||||
|
|
||||||
if ((!ignoreEqual || !equalAnalyzers(_analyzers, ignoreEqual->_analyzers)) &&
|
|
||||||
(!mask || mask->_analyzers)) {
|
|
||||||
arangodb::velocypack::Builder analyzersBuilder;
|
|
||||||
|
|
||||||
analyzersBuilder.openArray();
|
|
||||||
|
|
||||||
for (auto& entry : _analyzers) {
|
|
||||||
if (!entry._pool) {
|
|
||||||
continue; // skip null analyzers
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string name;
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// @note: DBServerAgencySync::getLocalCollections(...) generates
|
|
||||||
// 'forPersistence' definitions that are then compared in
|
|
||||||
// Maintenance.cpp:compareIndexes(...) via
|
|
||||||
// arangodb::Index::Compare(...) without access to
|
|
||||||
// 'defaultVocbase', hence the generated definitions must not
|
|
||||||
// rely on 'defaultVocbase'
|
|
||||||
// hence must use 'expandVocbasePrefix==true' if
|
|
||||||
// 'writeAnalyzerDefinition==true' for normalize
|
|
||||||
// for 'writeAnalyzerDefinition==false' must use
|
|
||||||
// 'expandVocbasePrefix==false' so that dump/restore an restore
|
|
||||||
// definitions into differently named databases
|
|
||||||
name = IResearchAnalyzerFeature::normalize( // normalize
|
|
||||||
entry._pool->name(), // analyzer name
|
|
||||||
*defaultVocbase, // active vocbase
|
|
||||||
*sysVocbase, // system vocbase
|
|
||||||
false // expand vocbase prefix
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
name = entry._pool->name(); // verbatim (assume already normalized)
|
|
||||||
}
|
|
||||||
|
|
||||||
analyzers.emplace(name, entry._pool);
|
|
||||||
analyzersBuilder.add(arangodb::velocypack::Value(std::move(name)));
|
|
||||||
}
|
|
||||||
|
|
||||||
analyzersBuilder.close();
|
|
||||||
builder.add("analyzers", analyzersBuilder.slice());
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
subDefaults._fields.clear(); // do not inherit fields and overrides from this field
|
|
||||||
fieldsBuilder.openObject();
|
|
||||||
fieldMask._analyzerDefinitions = false; // do not output analyzer definitions in children
|
|
||||||
|
|
||||||
for (auto& entry : _fields) {
|
|
||||||
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(fieldsBuilder, writeAnalyzerDefinition, &subDefaults, defaultVocbase, &fieldMask, &analyzers)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
fieldsBuilder.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
fieldsBuilder.close();
|
|
||||||
builder.add("fields", fieldsBuilder.slice());
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((!ignoreEqual || _includeAllFields != ignoreEqual->_includeAllFields) &&
|
|
||||||
(!mask || mask->_includeAllFields)) {
|
|
||||||
builder.add("includeAllFields", arangodb::velocypack::Value(_includeAllFields));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((!ignoreEqual || _trackListPositions != ignoreEqual->_trackListPositions) &&
|
|
||||||
(!mask || mask->_trackListPositions)) {
|
|
||||||
builder.add("trackListPositions", arangodb::velocypack::Value(_trackListPositions));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((!ignoreEqual || _storeValues != ignoreEqual->_storeValues) &&
|
|
||||||
(!mask || mask->_storeValues)) {
|
|
||||||
static_assert(adjacencyChecker<ValueStorage>::checkAdjacency<ValueStorage::FULL, ValueStorage::ID, ValueStorage::NONE>(),
|
|
||||||
"Values are not adjacent");
|
|
||||||
|
|
||||||
static const std::string policies[]{
|
|
||||||
"none", // ValueStorage::NONE
|
|
||||||
"id", // ValueStorage::ID
|
|
||||||
"full" // ValueStorage::FULL
|
|
||||||
};
|
|
||||||
|
|
||||||
auto const policyIdx =
|
|
||||||
static_cast<std::underlying_type<ValueStorage>::type>(_storeValues);
|
|
||||||
|
|
||||||
if (policyIdx >= IRESEARCH_COUNTOF(policies)) {
|
|
||||||
return false; // unsupported value storage policy
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.add("storeValues", arangodb::velocypack::Value(policies[policyIdx]));
|
|
||||||
}
|
|
||||||
|
|
||||||
// output definitions if 'writeAnalyzerDefinition' requested and not maked
|
// output definitions if 'writeAnalyzerDefinition' requested and not maked
|
||||||
// this should be the case for the default top-most call
|
// this should be the case for the default top-most call
|
||||||
if (writeAnalyzerDefinition && (!mask || mask->_analyzerDefinitions)) {
|
if (writeAnalyzerDefinition && (!mask || mask->_analyzerDefinitions)) {
|
||||||
VPackArrayBuilder arrayScope(&builder, "analyzerDefinitions");
|
VPackArrayBuilder arrayScope(&builder, "analyzerDefinitions");
|
||||||
|
|
||||||
for (auto& entry: analyzers) {
|
for (auto& entry: _analyzerDefinitions) {
|
||||||
TRI_ASSERT(entry.second); // ensured by emplace into 'analyzers' above
|
TRI_ASSERT(entry); // ensured by emplace into 'analyzers' above
|
||||||
entry.second->toVelocyPack(builder, defaultVocbase);
|
entry->toVelocyPack(builder, defaultVocbase);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (usedAnalyzers) {
|
return FieldMeta::json(builder, ignoreEqual, defaultVocbase, mask);
|
||||||
usedAnalyzers->insert(analyzers.begin(), analyzers.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t IResearchLinkMeta::memory() const noexcept {
|
size_t IResearchLinkMeta::memory() const noexcept {
|
||||||
auto size = sizeof(IResearchLinkMeta);
|
auto size = sizeof(IResearchLinkMeta);
|
||||||
|
|
||||||
size += _analyzers.size() * sizeof(decltype(_analyzers)::value_type);
|
size += _analyzers.size() * sizeof(decltype(_analyzers)::value_type);
|
||||||
size += _fields.size() * sizeof(decltype(_fields)::value_type);
|
|
||||||
size += _sort.memory();
|
size += _sort.memory();
|
||||||
|
size += FieldMeta::memory();
|
||||||
for (auto& entry : _fields) {
|
|
||||||
size += entry.key().size();
|
|
||||||
size += entry.value()->memory();
|
|
||||||
}
|
|
||||||
|
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace iresearch
|
} // namespace iresearch
|
||||||
} // namespace arangodb
|
} // namespace arangodb
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
// --SECTION-- END-OF-FILE
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
|
|
|
@ -48,58 +48,147 @@ class Slice; // forward declarations
|
||||||
namespace arangodb {
|
namespace arangodb {
|
||||||
namespace iresearch {
|
namespace iresearch {
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
// --SECTION-- public
|
|
||||||
// types
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief enum of possible ways to store values in the view
|
/// @brief enum of possible ways to store values in the view
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
enum class ValueStorage : uint32_t {
|
enum class ValueStorage : uint32_t {
|
||||||
NONE = 0, // do not store values in the view
|
NONE = 0, // do not store values in the view
|
||||||
ID, // only store value existance
|
ID, // only store value existance
|
||||||
FULL, // store full value in the view
|
VALUE // store full value in the view
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
struct FieldMeta {
|
||||||
/// @brief metadata describing how to process a field in a collection
|
// can't use FieldMeta as value type since it's incomplete type so far
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
typedef UnorderedRefKeyMap<char, UniqueHeapInstance<FieldMeta>> Fields;
|
||||||
struct IResearchLinkMeta {
|
|
||||||
struct Analyzer {
|
struct Analyzer {
|
||||||
AnalyzerPool::ptr _pool;
|
|
||||||
std::string _shortName; // vocbase-dependent short analyzer name
|
|
||||||
Analyzer(); // identity analyzer
|
Analyzer(); // identity analyzer
|
||||||
Analyzer( // constructor
|
Analyzer(AnalyzerPool::ptr const& pool,
|
||||||
AnalyzerPool::ptr const& pool, // pool
|
std::string&& shortName) noexcept
|
||||||
std::string&& shortName // short name (cached for use during insert(...))
|
: _pool(pool),
|
||||||
) noexcept: _pool(pool), _shortName(std::move(shortName)) {}
|
_shortName(std::move(shortName)) {
|
||||||
|
}
|
||||||
operator bool() const noexcept { return false == !_pool; }
|
operator bool() const noexcept { return false == !_pool; }
|
||||||
|
|
||||||
|
AnalyzerPool::ptr _pool;
|
||||||
|
std::string _shortName; // vocbase-independent short analyzer name
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct AnalyzerComparer {
|
||||||
|
using is_transparent = void;
|
||||||
|
|
||||||
|
bool operator()(AnalyzerPool::ptr const& lhs, AnalyzerPool::ptr const& rhs) const noexcept {
|
||||||
|
return lhs->name() < rhs->name();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator()(AnalyzerPool::ptr const& lhs, irs::string_ref const& rhs) const noexcept {
|
||||||
|
return lhs->name() < rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator()(irs::string_ref const& lhs, AnalyzerPool::ptr const& rhs) const noexcept {
|
||||||
|
return lhs < rhs->name();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
struct Mask {
|
struct Mask {
|
||||||
bool _analyzerDefinitions;
|
explicit Mask(bool mask = false) noexcept
|
||||||
|
: _analyzers(mask),
|
||||||
|
_fields(mask),
|
||||||
|
_includeAllFields(mask),
|
||||||
|
_trackListPositions(mask),
|
||||||
|
_storeValues(mask) {
|
||||||
|
}
|
||||||
|
|
||||||
bool _analyzers;
|
bool _analyzers;
|
||||||
bool _fields;
|
bool _fields;
|
||||||
bool _includeAllFields;
|
bool _includeAllFields;
|
||||||
bool _trackListPositions;
|
bool _trackListPositions;
|
||||||
bool _storeValues;
|
bool _storeValues;
|
||||||
bool _sort;
|
|
||||||
explicit Mask(bool mask = false) noexcept;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::vector<Analyzer> Analyzers;
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief return default IResearchLinkMeta values
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
static const FieldMeta& DEFAULT();
|
||||||
|
|
||||||
// can't use IResearchLinkMeta as value type since it's incomplete type so far
|
FieldMeta();
|
||||||
typedef UnorderedRefKeyMap<char, UniqueHeapInstance<IResearchLinkMeta>> Fields;
|
FieldMeta(FieldMeta const&) = default;
|
||||||
|
FieldMeta(FieldMeta&&) = default;
|
||||||
|
|
||||||
IResearchViewSort _sort; // sort condition associated with the link
|
FieldMeta& operator=(FieldMeta const&) = default;
|
||||||
Analyzers _analyzers; // analyzers to apply to every field
|
FieldMeta& operator=(FieldMeta&&) = default;
|
||||||
|
|
||||||
|
bool operator==(FieldMeta const& rhs) const noexcept;
|
||||||
|
bool operator!=(FieldMeta const& rhs) const noexcept {
|
||||||
|
return !(*this == rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief initialize FieldMeta with values from a JSON description
|
||||||
|
/// return success or set 'errorField' to specific field with error
|
||||||
|
/// on failure state is undefined
|
||||||
|
/// @param slice input definition
|
||||||
|
/// @param errorField field causing error (out-param)
|
||||||
|
/// @param defaultVocbase fallback vocbase for analyzer name normalization
|
||||||
|
/// nullptr == do not normalize
|
||||||
|
/// @param defaults inherited defaults
|
||||||
|
/// @param mask if set reflects which fields were initialized from JSON
|
||||||
|
/// @param analyzers analyzers referenced in this link
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
bool init(velocypack::Slice const& slice,
|
||||||
|
std::string& errorField,
|
||||||
|
TRI_vocbase_t const* defaultVocbase = nullptr,
|
||||||
|
FieldMeta const& defaults = DEFAULT(),
|
||||||
|
Mask* mask = nullptr,
|
||||||
|
std::set<AnalyzerPool::ptr, AnalyzerComparer>* analyzers = nullptr);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief fill and return a JSON description of a FieldMeta 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
|
||||||
|
/// @param builder output buffer
|
||||||
|
/// @param defaultVocbase fallback vocbase for analyzer name normalization
|
||||||
|
/// nullptr == do not normalize
|
||||||
|
/// @param ignoreEqual values to ignore if equal
|
||||||
|
/// @param defaultVocbase fallback vocbase
|
||||||
|
/// @param mask if set reflects which fields were initialized from JSON
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
bool json(arangodb::velocypack::Builder& builder,
|
||||||
|
FieldMeta const* ignoreEqual = nullptr,
|
||||||
|
TRI_vocbase_t const* defaultVocbase = nullptr,
|
||||||
|
Mask const* mask = nullptr) const;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief amount of memory in bytes occupied by this FieldMeta
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
size_t memory() const noexcept;
|
||||||
|
|
||||||
|
std::vector<Analyzer> _analyzers; // analyzers to apply to every field
|
||||||
Fields _fields; // explicit list of fields to be indexed with optional overrides
|
Fields _fields; // explicit list of fields to be indexed with optional overrides
|
||||||
bool _includeAllFields; // include all fields or only fields listed in
|
ValueStorage _storeValues{ ValueStorage::NONE }; // how values should be stored inside the view
|
||||||
// '_fields'
|
bool _includeAllFields{ false }; // include all fields or only fields listed in '_fields'
|
||||||
bool _trackListPositions; // append relative offset in list to attribute name
|
bool _trackListPositions{ false }; // append relative offset in list to attribute name (as opposed to without offset)
|
||||||
// (as opposed to without offset)
|
};
|
||||||
ValueStorage _storeValues; // how values should be stored inside the view
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief metadata describing how to process a field in a collection
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
struct IResearchLinkMeta : public FieldMeta {
|
||||||
|
struct Mask : public FieldMeta::Mask {
|
||||||
|
explicit Mask(bool mask = false) noexcept
|
||||||
|
: FieldMeta::Mask(mask),
|
||||||
|
_analyzerDefinitions(mask),
|
||||||
|
_sort(mask) {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _analyzerDefinitions;
|
||||||
|
bool _sort;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::set<AnalyzerPool::ptr, FieldMeta::AnalyzerComparer> _analyzerDefinitions;
|
||||||
|
IResearchViewSort _sort; // sort condition associated with the link
|
||||||
// NOTE: if adding fields don't forget to modify the comparison operator !!!
|
// NOTE: if adding fields don't forget to modify the comparison operator !!!
|
||||||
// NOTE: if adding fields don't forget to modify IResearchLinkMeta::Mask !!!
|
// NOTE: if adding fields don't forget to modify IResearchLinkMeta::Mask !!!
|
||||||
// NOTE: if adding fields don't forget to modify IResearchLinkMeta::Mask constructor !!!
|
// NOTE: if adding fields don't forget to modify IResearchLinkMeta::Mask constructor !!!
|
||||||
|
@ -114,8 +203,10 @@ struct IResearchLinkMeta {
|
||||||
IResearchLinkMeta& operator=(IResearchLinkMeta&& other) = default;
|
IResearchLinkMeta& operator=(IResearchLinkMeta&& other) = default;
|
||||||
IResearchLinkMeta& operator=(IResearchLinkMeta const& other) = default;
|
IResearchLinkMeta& operator=(IResearchLinkMeta const& other) = default;
|
||||||
|
|
||||||
bool operator==(IResearchLinkMeta const& other) const noexcept;
|
bool operator==(IResearchLinkMeta const& rhs) const noexcept;
|
||||||
bool operator!=(IResearchLinkMeta const& other) const noexcept;
|
bool operator!=(IResearchLinkMeta const& rhs) const noexcept {
|
||||||
|
return !(*this == rhs);
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief return default IResearchLinkMeta values
|
/// @brief return default IResearchLinkMeta values
|
||||||
|
@ -126,18 +217,21 @@ struct IResearchLinkMeta {
|
||||||
/// @brief initialize IResearchLinkMeta with values from a JSON description
|
/// @brief initialize IResearchLinkMeta with values from a JSON description
|
||||||
/// return success or set 'errorField' to specific field with error
|
/// return success or set 'errorField' to specific field with error
|
||||||
/// on failure state is undefined
|
/// on failure state is undefined
|
||||||
|
/// @param slice definition
|
||||||
|
/// @param readAnalyzerDefinition allow reading analyzer definitions instead
|
||||||
|
/// of just name
|
||||||
|
/// @param erroField field causing error (out-param)
|
||||||
/// @param defaultVocbase fallback vocbase for analyzer name normalization
|
/// @param defaultVocbase fallback vocbase for analyzer name normalization
|
||||||
/// nullptr == do not normalize
|
/// nullptr == do not normalize
|
||||||
|
/// @param defaults inherited defaults
|
||||||
/// @param mask if set reflects which fields were initialized from JSON
|
/// @param mask if set reflects which fields were initialized from JSON
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
bool init( // initialize meta
|
bool init(velocypack::Slice const& slice,
|
||||||
arangodb::velocypack::Slice const& slice, // definition
|
bool readAnalyzerDefinition,
|
||||||
bool readAnalyzerDefinition, // allow reading analyzer definitions instead of just name
|
std::string& errorField,
|
||||||
std::string& errorField, // field causing error (out-param)
|
TRI_vocbase_t const* defaultVocbase = nullptr,
|
||||||
TRI_vocbase_t const* defaultVocbase = nullptr, // fallback vocbase
|
FieldMeta const& defaults = DEFAULT(),
|
||||||
IResearchLinkMeta const& defaults = DEFAULT(), // inherited defaults
|
Mask* mask = nullptr);
|
||||||
Mask* mask = nullptr // initialized fields (out-param)
|
|
||||||
);
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief fill and return a JSON description of a IResearchLinkMeta object
|
/// @brief fill and return a JSON description of a IResearchLinkMeta object
|
||||||
|
@ -145,21 +239,21 @@ struct IResearchLinkMeta {
|
||||||
/// or (if 'mask' != nullptr) values in 'mask' that are set to false
|
/// or (if 'mask' != nullptr) values in 'mask' that are set to false
|
||||||
/// elements are appended to an existing object
|
/// elements are appended to an existing object
|
||||||
/// return success or set TRI_set_errno(...) and return false
|
/// return success or set TRI_set_errno(...) and return false
|
||||||
|
/// @param builder output buffer (out-param)
|
||||||
|
/// @param writeAnalyzerDefinition output full analyzer definition instead of just name
|
||||||
|
/// @param ignoreEqual values to ignore if equal
|
||||||
/// @param defaultVocbase fallback vocbase for analyzer name normalization
|
/// @param defaultVocbase fallback vocbase for analyzer name normalization
|
||||||
/// nullptr == do not normalize
|
/// nullptr == do not normalize
|
||||||
/// @param usedAnalyzers add to this map analyzers used in meta,
|
/// @param mask if set reflects which fields were initialized from JSON
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
bool json( // append meta jSON
|
bool json(velocypack::Builder& builder,
|
||||||
arangodb::velocypack::Builder& builder, // output buffer (out-param)
|
bool writeAnalyzerDefinition,
|
||||||
bool writeAnalyzerDefinition, // output full analyzer definition instead of just name
|
IResearchLinkMeta const* ignoreEqual = nullptr,
|
||||||
IResearchLinkMeta const* ignoreEqual = nullptr, // values to ignore if equal
|
TRI_vocbase_t const* defaultVocbase = nullptr,
|
||||||
TRI_vocbase_t const* defaultVocbase = nullptr, // fallback vocbase
|
Mask const* mask = nullptr) const;
|
||||||
Mask const* mask = nullptr, // values to ignore always
|
|
||||||
std::map<std::string, AnalyzerPool::ptr>* usedAnalyzers = nullptr // append analyzers used in definition
|
|
||||||
) const;
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief amount of memory in bytes occupied by this iResearch Link meta
|
/// @brief amount of memory in bytes occupied by this IResearchLinkMeta
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
size_t memory() const noexcept;
|
size_t memory() const noexcept;
|
||||||
}; // IResearchLinkMeta
|
}; // IResearchLinkMeta
|
||||||
|
|
|
@ -640,6 +640,8 @@ Result parseResponse(velocypack::Builder& builder,
|
||||||
velocypack::Parser parser(builder);
|
velocypack::Parser parser(builder);
|
||||||
parser.parse(response->getBody().begin(), response->getBody().length());
|
parser.parse(response->getBody().begin(), response->getBody().length());
|
||||||
return Result();
|
return Result();
|
||||||
|
} catch (VPackException const& e) {
|
||||||
|
return Result(TRI_ERROR_REPLICATION_INVALID_RESPONSE, e.what());
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
return Result(TRI_ERROR_REPLICATION_INVALID_RESPONSE);
|
return Result(TRI_ERROR_REPLICATION_INVALID_RESPONSE);
|
||||||
}
|
}
|
||||||
|
|
|
@ -228,7 +228,7 @@ Result syncChunkRocksDB(DatabaseInitialSyncer& syncer, SingleCollectionTransacti
|
||||||
if (r.fail()) {
|
if (r.fail()) {
|
||||||
return Result(TRI_ERROR_REPLICATION_INVALID_RESPONSE,
|
return Result(TRI_ERROR_REPLICATION_INVALID_RESPONSE,
|
||||||
std::string("got invalid response from master at ") +
|
std::string("got invalid response from master at ") +
|
||||||
syncer._state.master.endpoint + ": response is no array");
|
syncer._state.master.endpoint + ": " + r.errorMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
VPackSlice const responseBody = builder.slice();
|
VPackSlice const responseBody = builder.slice();
|
||||||
|
@ -425,7 +425,7 @@ Result syncChunkRocksDB(DatabaseInitialSyncer& syncer, SingleCollectionTransacti
|
||||||
return Result(TRI_ERROR_REPLICATION_INVALID_RESPONSE,
|
return Result(TRI_ERROR_REPLICATION_INVALID_RESPONSE,
|
||||||
std::string("got invalid response from master at ") +
|
std::string("got invalid response from master at ") +
|
||||||
syncer._state.master.endpoint +
|
syncer._state.master.endpoint +
|
||||||
": response is no array");
|
": " + r.errorMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
VPackSlice const slice = docsBuilder->slice();
|
VPackSlice const slice = docsBuilder->slice();
|
||||||
|
@ -613,7 +613,7 @@ Result handleSyncKeysRocksDB(DatabaseInitialSyncer& syncer,
|
||||||
if (r.fail()) {
|
if (r.fail()) {
|
||||||
return Result(TRI_ERROR_REPLICATION_INVALID_RESPONSE,
|
return Result(TRI_ERROR_REPLICATION_INVALID_RESPONSE,
|
||||||
std::string("got invalid response from master at ") +
|
std::string("got invalid response from master at ") +
|
||||||
syncer._state.master.endpoint + ": response is no array");
|
syncer._state.master.endpoint + ": " + r.errorMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
VPackSlice const chunkSlice = builder.slice();
|
VPackSlice const chunkSlice = builder.slice();
|
||||||
|
|
|
@ -1019,8 +1019,8 @@ std::shared_ptr<arangodb::LogicalCollection> TRI_vocbase_t::lookupCollectionByUu
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief looks up a data-source by identifier
|
/// @brief looks up a data-source by identifier
|
||||||
std::shared_ptr<arangodb::LogicalDataSource> TRI_vocbase_t::lookupDataSource(TRI_voc_cid_t id) const
|
std::shared_ptr<arangodb::LogicalDataSource> TRI_vocbase_t::lookupDataSource(
|
||||||
noexcept {
|
TRI_voc_cid_t id) const noexcept {
|
||||||
RECURSIVE_READ_LOCKER(_dataSourceLock, _dataSourceLockWriteOwner);
|
RECURSIVE_READ_LOCKER(_dataSourceLock, _dataSourceLockWriteOwner);
|
||||||
auto itr = _dataSourceById.find(id);
|
auto itr = _dataSourceById.find(id);
|
||||||
|
|
||||||
|
|
|
@ -1365,9 +1365,10 @@ actions.defineHttp({
|
||||||
}
|
}
|
||||||
|
|
||||||
// simon: RO is sufficient to rebalance shards for current db
|
// simon: RO is sufficient to rebalance shards for current db
|
||||||
if (req.database !== '_system'/* || !req.isAdminUser*/) {
|
if (!req.isAdminUser &&
|
||||||
|
users.permission(req.user, req.database) !== 'rw') {
|
||||||
actions.resultError(req, res, actions.HTTP_FORBIDDEN, 0,
|
actions.resultError(req, res, actions.HTTP_FORBIDDEN, 0,
|
||||||
'only allowed for admins on the _system database');
|
'only allowed for admins on the database');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -284,50 +284,12 @@ function cleanupOrphanedServices (knownServicePaths, knownBundlePaths) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function startup () {
|
function startup () {
|
||||||
if (isFoxxmaster()) {
|
|
||||||
const db = require('internal').db;
|
|
||||||
const dbName = db._name();
|
|
||||||
try {
|
|
||||||
db._useDatabase('_system');
|
|
||||||
const databases = db._databases();
|
|
||||||
for (const name of databases) {
|
|
||||||
try {
|
|
||||||
db._useDatabase(name);
|
|
||||||
upsertSystemServices();
|
|
||||||
} catch (e) {
|
|
||||||
console.warnStack(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
db._useDatabase(dbName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (global.ArangoServerState.role() === 'SINGLE') {
|
if (global.ArangoServerState.role() === 'SINGLE') {
|
||||||
commitLocalState(true);
|
commitLocalState(true);
|
||||||
}
|
}
|
||||||
selfHealAll();
|
selfHealAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
function upsertSystemServices () {
|
|
||||||
const serviceDefinitions = new Map();
|
|
||||||
for (const mount of SYSTEM_SERVICE_MOUNTS) {
|
|
||||||
try {
|
|
||||||
const serviceDefinition = utils.getServiceDefinition(mount) || {mount};
|
|
||||||
const service = FoxxService.create(serviceDefinition);
|
|
||||||
serviceDefinitions.set(mount, service.toJSON());
|
|
||||||
} catch (e) {
|
|
||||||
console.errorStack(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
db._query(aql`
|
|
||||||
FOR item IN ${Array.from(serviceDefinitions)}
|
|
||||||
UPSERT {mount: item[0]}
|
|
||||||
INSERT item[1]
|
|
||||||
REPLACE item[1]
|
|
||||||
IN ${utils.getStorage()}
|
|
||||||
`);
|
|
||||||
}
|
|
||||||
|
|
||||||
function commitLocalState (replace) {
|
function commitLocalState (replace) {
|
||||||
let modified = false;
|
let modified = false;
|
||||||
const rootPath = FoxxService.rootPath();
|
const rootPath = FoxxService.rootPath();
|
||||||
|
|
|
@ -50,6 +50,8 @@
|
||||||
#include "../Mocks/StorageEngineMock.h"
|
#include "../Mocks/StorageEngineMock.h"
|
||||||
#include "IResearch/common.h"
|
#include "IResearch/common.h"
|
||||||
|
|
||||||
|
#include "boost/optional.hpp"
|
||||||
|
|
||||||
using namespace arangodb;
|
using namespace arangodb;
|
||||||
using namespace arangodb::aql;
|
using namespace arangodb::aql;
|
||||||
using namespace arangodb::graph;
|
using namespace arangodb::graph;
|
||||||
|
@ -184,9 +186,18 @@ struct MockGraphDatabase {
|
||||||
EXPECT_TRUE(vertices->type());
|
EXPECT_TRUE(vertices->type());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct EdgeDef {
|
||||||
|
EdgeDef(size_t from, size_t to) : _from(from), _to(to){};
|
||||||
|
EdgeDef(size_t from, size_t to, double weight)
|
||||||
|
: _from(from), _to(to), _weight(weight){};
|
||||||
|
size_t _from;
|
||||||
|
size_t _to;
|
||||||
|
boost::optional<double> _weight;
|
||||||
|
};
|
||||||
|
|
||||||
// Create a collection with name <name> of edges given by <edges>
|
// Create a collection with name <name> of edges given by <edges>
|
||||||
void addEdgeCollection(std::string name, std::string vertexCollection,
|
void addEdgeCollection(std::string name, std::string vertexCollection,
|
||||||
std::vector<std::pair<size_t, size_t>> edgedef) {
|
std::vector<EdgeDef> edgedef) {
|
||||||
std::shared_ptr<arangodb::LogicalCollection> edges;
|
std::shared_ptr<arangodb::LogicalCollection> edges;
|
||||||
auto createJson = velocypack::Parser::fromJson("{ \"name\": \"" + name +
|
auto createJson = velocypack::Parser::fromJson("{ \"name\": \"" + name +
|
||||||
"\", \"type\": 3 }");
|
"\", \"type\": 3 }");
|
||||||
|
@ -205,10 +216,18 @@ struct MockGraphDatabase {
|
||||||
for (auto& p : edgedef) {
|
for (auto& p : edgedef) {
|
||||||
// std::cout << "edge: " << vertexCollection << " " << p.first << " -> "
|
// std::cout << "edge: " << vertexCollection << " " << p.first << " -> "
|
||||||
// << p.second << std::endl;
|
// << p.second << std::endl;
|
||||||
auto docJson = velocypack::Parser::fromJson(
|
// This is moderately horrible
|
||||||
"{ \"_from\": \"" + vertexCollection + "/" + std::to_string(p.first) +
|
auto docJson =
|
||||||
"\"" + ", \"_to\": \"" + vertexCollection + "/" +
|
p._weight.has_value()
|
||||||
std::to_string(p.second) + "\" }");
|
? velocypack::Parser::fromJson(
|
||||||
|
"{ \"_from\": \"" + vertexCollection + "/" +
|
||||||
|
std::to_string(p._from) + "\"" + ", \"_to\": \"" +
|
||||||
|
vertexCollection + "/" + std::to_string(p._to) +
|
||||||
|
"\", \"cost\": " + std::to_string(p._weight.value()) + "}")
|
||||||
|
: velocypack::Parser::fromJson(
|
||||||
|
"{ \"_from\": \"" + vertexCollection + "/" +
|
||||||
|
std::to_string(p._from) + "\"" + ", \"_to\": \"" +
|
||||||
|
vertexCollection + "/" + std::to_string(p._to) + "\" }");
|
||||||
docs.emplace_back(docJson);
|
docs.emplace_back(docJson);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -282,12 +301,13 @@ struct MockGraphDatabase {
|
||||||
spos.emplace_back(spo);
|
spos.emplace_back(spo);
|
||||||
return spo;
|
return spo;
|
||||||
}
|
}
|
||||||
};
|
}; // namespace graph
|
||||||
|
|
||||||
bool checkPath(ShortestPathOptions* spo, ShortestPathResult result, std::vector<std::string> vertices,
|
bool checkPath(ShortestPathOptions* spo, ShortestPathResult result,
|
||||||
|
std::vector<std::string> vertices,
|
||||||
std::vector<std::pair<std::string, std::string>> edges, std::string& msgs);
|
std::vector<std::pair<std::string, std::string>> edges, std::string& msgs);
|
||||||
|
|
||||||
}
|
} // namespace graph
|
||||||
}
|
} // namespace tests
|
||||||
}
|
} // namespace arangodb
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -212,6 +212,52 @@ TEST_F(KShortestPathsFinderTest, many_edges_between_two_nodes) {
|
||||||
ASSERT_TRUE(false == finder->getNextPathShortestPathResult(result));
|
ASSERT_TRUE(false == finder->getNextPathShortestPathResult(result));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class KShortestPathsFinderTestWeights : public ::testing::Test {
|
||||||
|
protected:
|
||||||
|
GraphTestSetup s;
|
||||||
|
MockGraphDatabase gdb;
|
||||||
|
|
||||||
|
arangodb::aql::Query* query;
|
||||||
|
arangodb::graph::ShortestPathOptions* spo;
|
||||||
|
|
||||||
|
KShortestPathsFinder* finder;
|
||||||
|
|
||||||
|
KShortestPathsFinderTestWeights() : gdb("testVocbase") {
|
||||||
|
gdb.addVertexCollection("v", 10);
|
||||||
|
gdb.addEdgeCollection("e", "v",
|
||||||
|
{{1, 2, 10},
|
||||||
|
{1, 3, 10},
|
||||||
|
{1, 10, 100},
|
||||||
|
{2, 4, 10},
|
||||||
|
{3, 4, 20},
|
||||||
|
{7, 3, 10},
|
||||||
|
{8, 3, 10},
|
||||||
|
{9, 3, 10}});
|
||||||
|
|
||||||
|
query = gdb.getQuery("RETURN 1");
|
||||||
|
spo = gdb.getShortestPathOptions(query);
|
||||||
|
spo->weightAttribute = "cost";
|
||||||
|
|
||||||
|
finder = new KShortestPathsFinder(*spo);
|
||||||
|
}
|
||||||
|
|
||||||
|
~KShortestPathsFinderTestWeights() { delete finder; }
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(KShortestPathsFinderTestWeights, diamond_path) {
|
||||||
|
auto start = velocypack::Parser::fromJson("\"v/1\"");
|
||||||
|
auto end = velocypack::Parser::fromJson("\"v/4\"");
|
||||||
|
ShortestPathResult result;
|
||||||
|
std::string msgs;
|
||||||
|
|
||||||
|
finder->startKShortestPathsTraversal(start->slice(), end->slice());
|
||||||
|
|
||||||
|
ASSERT_TRUE(finder->getNextPathShortestPathResult(result));
|
||||||
|
auto cpr = checkPath(spo, result, {"1", "2", "4"},
|
||||||
|
{{}, {"v/1", "v/2"}, {"v/2", "v/4"}}, msgs);
|
||||||
|
ASSERT_TRUE(cpr) << msgs;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace graph
|
} // namespace graph
|
||||||
} // namespace tests
|
} // namespace tests
|
||||||
} // namespace arangodb
|
} // namespace arangodb
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -46,6 +46,9 @@
|
||||||
#include "RestServer/DatabaseFeature.h"
|
#include "RestServer/DatabaseFeature.h"
|
||||||
#include "RestServer/QueryRegistryFeature.h"
|
#include "RestServer/QueryRegistryFeature.h"
|
||||||
#include "RestServer/SystemDatabaseFeature.h"
|
#include "RestServer/SystemDatabaseFeature.h"
|
||||||
|
#include "RestServer/TraverserEngineRegistryFeature.h"
|
||||||
|
#include "RestServer/AqlFeature.h"
|
||||||
|
#include "Aql/OptimizerRulesFeature.h"
|
||||||
#include "Sharding/ShardingFeature.h"
|
#include "Sharding/ShardingFeature.h"
|
||||||
#include "StorageEngine/EngineSelectorFeature.h"
|
#include "StorageEngine/EngineSelectorFeature.h"
|
||||||
#include "Transaction/Methods.h"
|
#include "Transaction/Methods.h"
|
||||||
|
@ -165,6 +168,9 @@ class IResearchDocumentTest : public ::testing::Test {
|
||||||
arangodb::LogLevel::ERR);
|
arangodb::LogLevel::ERR);
|
||||||
|
|
||||||
// setup required application features
|
// setup required application features
|
||||||
|
features.emplace_back(new arangodb::TraverserEngineRegistryFeature(server), false); // must be before AqlFeature
|
||||||
|
features.emplace_back(new arangodb::AqlFeature(server), true);
|
||||||
|
features.emplace_back(new arangodb::aql::OptimizerRulesFeature(server), true);
|
||||||
features.emplace_back(new arangodb::AuthenticationFeature(server), true);
|
features.emplace_back(new arangodb::AuthenticationFeature(server), true);
|
||||||
features.emplace_back(new arangodb::DatabaseFeature(server), false);
|
features.emplace_back(new arangodb::DatabaseFeature(server), false);
|
||||||
features.emplace_back(new arangodb::QueryRegistryFeature(server), false); // required for constructing TRI_vocbase_t
|
features.emplace_back(new arangodb::QueryRegistryFeature(server), false); // required for constructing TRI_vocbase_t
|
||||||
|
@ -722,7 +728,7 @@ TEST_F(IResearchDocumentTest, FieldIterator_traverse_complex_object_ordered_chec
|
||||||
auto const slice = json->slice();
|
auto const slice = json->slice();
|
||||||
|
|
||||||
arangodb::iresearch::IResearchLinkMeta linkMeta;
|
arangodb::iresearch::IResearchLinkMeta linkMeta;
|
||||||
linkMeta._analyzers.emplace_back(arangodb::iresearch::IResearchLinkMeta::Analyzer(
|
linkMeta._analyzers.emplace_back(arangodb::iresearch::FieldMeta::Analyzer(
|
||||||
analyzers->get(arangodb::StaticStrings::SystemDatabase +
|
analyzers->get(arangodb::StaticStrings::SystemDatabase +
|
||||||
"::iresearch-document-empty"),
|
"::iresearch-document-empty"),
|
||||||
"iresearch-document-empty")); // add analyzer
|
"iresearch-document-empty")); // add analyzer
|
||||||
|
@ -1549,12 +1555,12 @@ TEST_F(IResearchDocumentTest, FieldIterator_nullptr_analyzer) {
|
||||||
// last analyzer invalid
|
// last analyzer invalid
|
||||||
{
|
{
|
||||||
arangodb::iresearch::IResearchLinkMeta linkMeta;
|
arangodb::iresearch::IResearchLinkMeta linkMeta;
|
||||||
linkMeta._analyzers.emplace_back(arangodb::iresearch::IResearchLinkMeta::Analyzer(
|
linkMeta._analyzers.emplace_back(arangodb::iresearch::FieldMeta::Analyzer(
|
||||||
analyzers.get(arangodb::StaticStrings::SystemDatabase + "::empty"),
|
analyzers.get(arangodb::StaticStrings::SystemDatabase + "::empty"),
|
||||||
"empty")); // add analyzer
|
"empty")); // add analyzer
|
||||||
|
|
||||||
InvalidAnalyzer::returnNullFromMake = false;
|
InvalidAnalyzer::returnNullFromMake = false;
|
||||||
linkMeta._analyzers.emplace_back(arangodb::iresearch::IResearchLinkMeta::Analyzer(
|
linkMeta._analyzers.emplace_back(arangodb::iresearch::FieldMeta::Analyzer(
|
||||||
analyzers.get(arangodb::StaticStrings::SystemDatabase + "::invalid"),
|
analyzers.get(arangodb::StaticStrings::SystemDatabase + "::invalid"),
|
||||||
"invalid")); // add analyzer
|
"invalid")); // add analyzer
|
||||||
linkMeta._includeAllFields = true; // include all fields
|
linkMeta._includeAllFields = true; // include all fields
|
||||||
|
@ -1620,10 +1626,10 @@ TEST_F(IResearchDocumentTest, FieldIterator_nullptr_analyzer) {
|
||||||
linkMeta._analyzers.clear();
|
linkMeta._analyzers.clear();
|
||||||
|
|
||||||
InvalidAnalyzer::returnNullFromMake = false;
|
InvalidAnalyzer::returnNullFromMake = false;
|
||||||
linkMeta._analyzers.emplace_back(arangodb::iresearch::IResearchLinkMeta::Analyzer(
|
linkMeta._analyzers.emplace_back(arangodb::iresearch::FieldMeta::Analyzer(
|
||||||
analyzers.get(arangodb::StaticStrings::SystemDatabase + "::invalid"),
|
analyzers.get(arangodb::StaticStrings::SystemDatabase + "::invalid"),
|
||||||
"invalid")); // add analyzer
|
"invalid")); // add analyzer
|
||||||
linkMeta._analyzers.emplace_back(arangodb::iresearch::IResearchLinkMeta::Analyzer(
|
linkMeta._analyzers.emplace_back(arangodb::iresearch::FieldMeta::Analyzer(
|
||||||
analyzers.get(arangodb::StaticStrings::SystemDatabase + "::empty"),
|
analyzers.get(arangodb::StaticStrings::SystemDatabase + "::empty"),
|
||||||
"empty")); // add analyzer
|
"empty")); // add analyzer
|
||||||
linkMeta._includeAllFields = true; // include all fields
|
linkMeta._includeAllFields = true; // include all fields
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
#include "Aql/ExecutionPlan.h"
|
#include "Aql/ExecutionPlan.h"
|
||||||
#include "Aql/ExpressionContext.h"
|
#include "Aql/ExpressionContext.h"
|
||||||
#include "Aql/Query.h"
|
#include "Aql/Query.h"
|
||||||
|
#include "Aql/OptimizerRulesFeature.h"
|
||||||
#include "Cluster/ClusterFeature.h"
|
#include "Cluster/ClusterFeature.h"
|
||||||
#include "GeneralServer/AuthenticationFeature.h"
|
#include "GeneralServer/AuthenticationFeature.h"
|
||||||
#include "IResearch/AqlHelper.h"
|
#include "IResearch/AqlHelper.h"
|
||||||
|
@ -108,6 +109,7 @@ class IResearchFilterBooleanTest : public ::testing::Test {
|
||||||
features.emplace_back(new arangodb::V8DealerFeature(server),
|
features.emplace_back(new arangodb::V8DealerFeature(server),
|
||||||
false); // required for DatabaseFeature::createDatabase(...)
|
false); // required for DatabaseFeature::createDatabase(...)
|
||||||
features.emplace_back(new arangodb::ViewTypesFeature(server), false); // required for IResearchFeature
|
features.emplace_back(new arangodb::ViewTypesFeature(server), false); // required for IResearchFeature
|
||||||
|
features.emplace_back(new arangodb::aql::OptimizerRulesFeature(server), true);
|
||||||
features.emplace_back(new arangodb::AqlFeature(server), true);
|
features.emplace_back(new arangodb::AqlFeature(server), true);
|
||||||
features.emplace_back(functions = new arangodb::aql::AqlFunctionFeature(server),
|
features.emplace_back(functions = new arangodb::aql::AqlFunctionFeature(server),
|
||||||
true); // required for IResearchAnalyzerFeature
|
true); // required for IResearchAnalyzerFeature
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
#include "Aql/ExecutionPlan.h"
|
#include "Aql/ExecutionPlan.h"
|
||||||
#include "Aql/ExpressionContext.h"
|
#include "Aql/ExpressionContext.h"
|
||||||
#include "Aql/Query.h"
|
#include "Aql/Query.h"
|
||||||
|
#include "Aql/OptimizerRulesFeature.h"
|
||||||
#include "Cluster/ClusterFeature.h"
|
#include "Cluster/ClusterFeature.h"
|
||||||
#include "GeneralServer/AuthenticationFeature.h"
|
#include "GeneralServer/AuthenticationFeature.h"
|
||||||
#include "IResearch/AqlHelper.h"
|
#include "IResearch/AqlHelper.h"
|
||||||
|
@ -109,6 +110,7 @@ class IResearchFilterCompareTest : public ::testing::Test {
|
||||||
features.emplace_back(new arangodb::V8DealerFeature(server),
|
features.emplace_back(new arangodb::V8DealerFeature(server),
|
||||||
false); // required for DatabaseFeature::createDatabase(...)
|
false); // required for DatabaseFeature::createDatabase(...)
|
||||||
features.emplace_back(new arangodb::ViewTypesFeature(server), false); // required for IResearchFeature
|
features.emplace_back(new arangodb::ViewTypesFeature(server), false); // required for IResearchFeature
|
||||||
|
features.emplace_back(new arangodb::aql::OptimizerRulesFeature(server), true);
|
||||||
features.emplace_back(new arangodb::AqlFeature(server), true);
|
features.emplace_back(new arangodb::AqlFeature(server), true);
|
||||||
features.emplace_back(functions = new arangodb::aql::AqlFunctionFeature(server),
|
features.emplace_back(functions = new arangodb::aql::AqlFunctionFeature(server),
|
||||||
true); // required for IResearchAnalyzerFeature
|
true); // required for IResearchAnalyzerFeature
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
#include "Aql/ExecutionPlan.h"
|
#include "Aql/ExecutionPlan.h"
|
||||||
#include "Aql/ExpressionContext.h"
|
#include "Aql/ExpressionContext.h"
|
||||||
#include "Aql/Query.h"
|
#include "Aql/Query.h"
|
||||||
|
#include "Aql/OptimizerRulesFeature.h"
|
||||||
#include "Cluster/ClusterFeature.h"
|
#include "Cluster/ClusterFeature.h"
|
||||||
#include "GeneralServer/AuthenticationFeature.h"
|
#include "GeneralServer/AuthenticationFeature.h"
|
||||||
#include "IResearch/AqlHelper.h"
|
#include "IResearch/AqlHelper.h"
|
||||||
|
@ -111,6 +112,7 @@ class IResearchFilterFunctionTest : public ::testing::Test {
|
||||||
features.emplace_back(new arangodb::V8DealerFeature(server),
|
features.emplace_back(new arangodb::V8DealerFeature(server),
|
||||||
false); // required for DatabaseFeature::createDatabase(...)
|
false); // required for DatabaseFeature::createDatabase(...)
|
||||||
features.emplace_back(new arangodb::ViewTypesFeature(server), false); // required for IResearchFeature
|
features.emplace_back(new arangodb::ViewTypesFeature(server), false); // required for IResearchFeature
|
||||||
|
features.emplace_back(new arangodb::aql::OptimizerRulesFeature(server), true);
|
||||||
features.emplace_back(new arangodb::AqlFeature(server), true);
|
features.emplace_back(new arangodb::AqlFeature(server), true);
|
||||||
features.emplace_back(functions = new arangodb::aql::AqlFunctionFeature(server),
|
features.emplace_back(functions = new arangodb::aql::AqlFunctionFeature(server),
|
||||||
true); // required for IResearchAnalyzerFeature
|
true); // required for IResearchAnalyzerFeature
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
#include "Aql/ExecutionPlan.h"
|
#include "Aql/ExecutionPlan.h"
|
||||||
#include "Aql/ExpressionContext.h"
|
#include "Aql/ExpressionContext.h"
|
||||||
#include "Aql/Query.h"
|
#include "Aql/Query.h"
|
||||||
|
#include "Aql/OptimizerRulesFeature.h"
|
||||||
#include "Cluster/ClusterFeature.h"
|
#include "Cluster/ClusterFeature.h"
|
||||||
#include "GeneralServer/AuthenticationFeature.h"
|
#include "GeneralServer/AuthenticationFeature.h"
|
||||||
#include "IResearch/AqlHelper.h"
|
#include "IResearch/AqlHelper.h"
|
||||||
|
@ -109,6 +110,7 @@ class IResearchFilterInTest : public ::testing::Test {
|
||||||
false); // required for DatabaseFeature::createDatabase(...)
|
false); // required for DatabaseFeature::createDatabase(...)
|
||||||
features.emplace_back(new arangodb::ViewTypesFeature(server), false); // required for IResearchFeature
|
features.emplace_back(new arangodb::ViewTypesFeature(server), false); // required for IResearchFeature
|
||||||
features.emplace_back(new arangodb::AqlFeature(server), true);
|
features.emplace_back(new arangodb::AqlFeature(server), true);
|
||||||
|
features.emplace_back(new arangodb::aql::OptimizerRulesFeature(server), true);
|
||||||
features.emplace_back(functions = new arangodb::aql::AqlFunctionFeature(server),
|
features.emplace_back(functions = new arangodb::aql::AqlFunctionFeature(server),
|
||||||
true); // required for IResearchAnalyzerFeature
|
true); // required for IResearchAnalyzerFeature
|
||||||
features.emplace_back(new arangodb::iresearch::IResearchAnalyzerFeature(server), true);
|
features.emplace_back(new arangodb::iresearch::IResearchAnalyzerFeature(server), true);
|
||||||
|
|
|
@ -232,10 +232,39 @@ TEST_F(IResearchLinkHelperTestSingle, test_validate_cross_db_analyzer) {
|
||||||
TEST_F(IResearchLinkHelperTestSingle, test_normalize_single) {
|
TEST_F(IResearchLinkHelperTestSingle, test_normalize_single) {
|
||||||
auto* analyzers =
|
auto* analyzers =
|
||||||
arangodb::application_features::ApplicationServer::lookupFeature<arangodb::iresearch::IResearchAnalyzerFeature>();
|
arangodb::application_features::ApplicationServer::lookupFeature<arangodb::iresearch::IResearchAnalyzerFeature>();
|
||||||
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
|
|
||||||
TRI_vocbase_t& sysVocbase = server.getSystemDatabase();
|
TRI_vocbase_t& sysVocbase = server.getSystemDatabase();
|
||||||
|
|
||||||
// analyzer single-server
|
// analyzer single-server, for creation
|
||||||
|
{
|
||||||
|
auto json = arangodb::velocypack::Parser::fromJson(
|
||||||
|
"{ \
|
||||||
|
\"analyzerDefinitions\": [ { \"name\": \"testAnalyzer0\", \"type\": \"identity\" } ], \
|
||||||
|
\"analyzers\": [\"testAnalyzer0\" ] \
|
||||||
|
}");
|
||||||
|
arangodb::velocypack::Builder builder;
|
||||||
|
builder.openObject();
|
||||||
|
EXPECT_TRUE(arangodb::iresearch::IResearchLinkHelper::normalize(
|
||||||
|
builder, json->slice(), true, sysVocbase).ok());
|
||||||
|
builder.close();
|
||||||
|
EXPECT_EQ(nullptr, analyzers->get(arangodb::StaticStrings::SystemDatabase + "::testAnalyzer0"));
|
||||||
|
|
||||||
|
auto expected_json = arangodb::velocypack::Parser::fromJson(
|
||||||
|
"{ \
|
||||||
|
\"type\":\"arangosearch\", \
|
||||||
|
\"primarySort\":[], \
|
||||||
|
\"fields\":{}, \
|
||||||
|
\"includeAllFields\": false, \
|
||||||
|
\"trackListPositions\": false, \
|
||||||
|
\"storeValues\": \"none\", \
|
||||||
|
\"analyzerDefinitions\": [ \
|
||||||
|
{ \"name\": \"testAnalyzer0\", \"type\": \"identity\", \"properties\":{}, \"features\":[] } \
|
||||||
|
], \
|
||||||
|
\"analyzers\": [\"testAnalyzer0\" ] \
|
||||||
|
}");
|
||||||
|
EXPECT_EQUAL_SLICES(expected_json->slice(), builder.slice());
|
||||||
|
}
|
||||||
|
|
||||||
|
// analyzer single-server, user definition
|
||||||
{
|
{
|
||||||
auto json = arangodb::velocypack::Parser::fromJson(
|
auto json = arangodb::velocypack::Parser::fromJson(
|
||||||
"{ \
|
"{ \
|
||||||
|
@ -246,11 +275,50 @@ TEST_F(IResearchLinkHelperTestSingle, test_normalize_single) {
|
||||||
builder.openObject();
|
builder.openObject();
|
||||||
EXPECT_TRUE(arangodb::iresearch::IResearchLinkHelper::normalize(
|
EXPECT_TRUE(arangodb::iresearch::IResearchLinkHelper::normalize(
|
||||||
builder, json->slice(), false, sysVocbase).ok());
|
builder, json->slice(), false, sysVocbase).ok());
|
||||||
EXPECT_TRUE((true == !analyzers->get(arangodb::StaticStrings::SystemDatabase +
|
builder.close();
|
||||||
"::testAnalyzer1")));
|
EXPECT_EQ(nullptr, analyzers->get(arangodb::StaticStrings::SystemDatabase + "::testAnalyzer0"));
|
||||||
|
|
||||||
|
auto expected_json = arangodb::velocypack::Parser::fromJson(
|
||||||
|
"{ \
|
||||||
|
\"type\":\"arangosearch\", \
|
||||||
|
\"fields\":{}, \
|
||||||
|
\"includeAllFields\": false, \
|
||||||
|
\"trackListPositions\": false, \
|
||||||
|
\"storeValues\": \"none\", \
|
||||||
|
\"analyzers\": [\"testAnalyzer0\" ] \
|
||||||
|
}");
|
||||||
|
EXPECT_EQUAL_SLICES(expected_json->slice(), builder.slice());
|
||||||
}
|
}
|
||||||
|
|
||||||
// analyzer single-server (inRecovery) fail persist in recovery
|
// analyzer single-server, not for creation, missing "testAanalyzer0"
|
||||||
|
{
|
||||||
|
auto json = arangodb::velocypack::Parser::fromJson(
|
||||||
|
"{ \
|
||||||
|
\"analyzers\": [\"testAnalyzer0\" ] \
|
||||||
|
}");
|
||||||
|
arangodb::velocypack::Builder builder;
|
||||||
|
builder.openObject();
|
||||||
|
EXPECT_FALSE(arangodb::iresearch::IResearchLinkHelper::normalize(
|
||||||
|
builder, json->slice(), false, sysVocbase).ok());
|
||||||
|
builder.close();
|
||||||
|
EXPECT_EQ(nullptr, analyzers->get(arangodb::StaticStrings::SystemDatabase + "::testAnalyzer0"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// analyzer single-server, for creation, missing "testAanalyzer0"
|
||||||
|
{
|
||||||
|
auto json = arangodb::velocypack::Parser::fromJson(
|
||||||
|
"{ \
|
||||||
|
\"analyzers\": [\"testAnalyzer0\" ] \
|
||||||
|
}");
|
||||||
|
arangodb::velocypack::Builder builder;
|
||||||
|
builder.openObject();
|
||||||
|
EXPECT_FALSE(arangodb::iresearch::IResearchLinkHelper::normalize(
|
||||||
|
builder, json->slice(), false, sysVocbase).ok());
|
||||||
|
builder.close();
|
||||||
|
EXPECT_EQ(nullptr, analyzers->get(arangodb::StaticStrings::SystemDatabase + "::testAnalyzer0"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// analyzer single-server (inRecovery), for creation
|
||||||
{
|
{
|
||||||
auto json = arangodb::velocypack::Parser::fromJson(
|
auto json = arangodb::velocypack::Parser::fromJson(
|
||||||
"{ \
|
"{ \
|
||||||
|
@ -263,11 +331,55 @@ TEST_F(IResearchLinkHelperTestSingle, test_normalize_single) {
|
||||||
[&before]() -> void { StorageEngineMock::recoveryStateResult = before; });
|
[&before]() -> void { StorageEngineMock::recoveryStateResult = before; });
|
||||||
arangodb::velocypack::Builder builder;
|
arangodb::velocypack::Builder builder;
|
||||||
builder.openObject();
|
builder.openObject();
|
||||||
EXPECT_TRUE((false == arangodb::iresearch::IResearchLinkHelper::normalize(
|
EXPECT_TRUE(arangodb::iresearch::IResearchLinkHelper::normalize(
|
||||||
builder, json->slice(), false, sysVocbase)
|
builder, json->slice(), true, sysVocbase).ok());
|
||||||
.ok()));
|
builder.close();
|
||||||
EXPECT_TRUE((true == !analyzers->get(arangodb::StaticStrings::SystemDatabase +
|
EXPECT_EQ(nullptr, analyzers->get(arangodb::StaticStrings::SystemDatabase + "::testAnalyzer1"));
|
||||||
"::testAnalyzer2")));
|
|
||||||
|
auto expected_json = arangodb::velocypack::Parser::fromJson(
|
||||||
|
"{ \
|
||||||
|
\"type\":\"arangosearch\", \
|
||||||
|
\"primarySort\":[], \
|
||||||
|
\"fields\":{}, \
|
||||||
|
\"includeAllFields\": false, \
|
||||||
|
\"trackListPositions\": false, \
|
||||||
|
\"storeValues\": \"none\", \
|
||||||
|
\"analyzerDefinitions\": [ \
|
||||||
|
{ \"name\": \"testAnalyzer1\", \"type\": \"identity\", \"properties\":{}, \"features\":[] } \
|
||||||
|
], \
|
||||||
|
\"analyzers\": [\"testAnalyzer1\" ] \
|
||||||
|
}");
|
||||||
|
EXPECT_EQUAL_SLICES(expected_json->slice(), builder.slice());
|
||||||
|
}
|
||||||
|
|
||||||
|
// analyzer single-server (inRecovery), not for creation
|
||||||
|
{
|
||||||
|
auto json = arangodb::velocypack::Parser::fromJson(
|
||||||
|
"{ \
|
||||||
|
\"analyzerDefinitions\": [ { \"name\": \"testAnalyzer1\", \"type\": \"identity\" } ], \
|
||||||
|
\"analyzers\": [\"testAnalyzer1\" ] \
|
||||||
|
}");
|
||||||
|
auto before = StorageEngineMock::recoveryStateResult;
|
||||||
|
StorageEngineMock::recoveryStateResult = arangodb::RecoveryState::IN_PROGRESS;
|
||||||
|
auto restore = irs::make_finally(
|
||||||
|
[&before]() -> void { StorageEngineMock::recoveryStateResult = before; });
|
||||||
|
arangodb::velocypack::Builder builder;
|
||||||
|
builder.openObject();
|
||||||
|
EXPECT_TRUE(arangodb::iresearch::IResearchLinkHelper::normalize(
|
||||||
|
builder, json->slice(), false, sysVocbase).ok());
|
||||||
|
builder.close();
|
||||||
|
EXPECT_EQ(nullptr, analyzers->get(arangodb::StaticStrings::SystemDatabase + "::testAnalyzer1"));
|
||||||
|
|
||||||
|
auto expected_json = arangodb::velocypack::Parser::fromJson(
|
||||||
|
"{ \
|
||||||
|
\"type\":\"arangosearch\", \
|
||||||
|
\"fields\":{}, \
|
||||||
|
\"includeAllFields\": false, \
|
||||||
|
\"trackListPositions\": false, \
|
||||||
|
\"storeValues\": \"none\", \
|
||||||
|
\"analyzers\": [\"testAnalyzer1\" ] \
|
||||||
|
}");
|
||||||
|
EXPECT_EQUAL_SLICES(expected_json->slice(), builder.slice());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,7 @@
|
||||||
#include "ApplicationFeatures/GreetingsPhase.h"
|
#include "ApplicationFeatures/GreetingsPhase.h"
|
||||||
#include "ApplicationFeatures/V8Phase.h"
|
#include "ApplicationFeatures/V8Phase.h"
|
||||||
#include "Aql/AqlFunctionFeature.h"
|
#include "Aql/AqlFunctionFeature.h"
|
||||||
|
#include "Aql/OptimizerRulesFeature.h"
|
||||||
#include "Cluster/ClusterFeature.h"
|
#include "Cluster/ClusterFeature.h"
|
||||||
|
|
||||||
#if USE_ENTERPRISE
|
#if USE_ENTERPRISE
|
||||||
|
@ -48,6 +49,8 @@
|
||||||
#include "IResearch/IResearchFeature.h"
|
#include "IResearch/IResearchFeature.h"
|
||||||
#include "IResearch/IResearchLinkMeta.h"
|
#include "IResearch/IResearchLinkMeta.h"
|
||||||
#include "IResearch/VelocyPackHelper.h"
|
#include "IResearch/VelocyPackHelper.h"
|
||||||
|
#include "RestServer/AqlFeature.h"
|
||||||
|
#include "RestServer/TraverserEngineRegistryFeature.h"
|
||||||
#include "RestServer/DatabaseFeature.h"
|
#include "RestServer/DatabaseFeature.h"
|
||||||
#include "RestServer/QueryRegistryFeature.h"
|
#include "RestServer/QueryRegistryFeature.h"
|
||||||
#include "RestServer/SystemDatabaseFeature.h"
|
#include "RestServer/SystemDatabaseFeature.h"
|
||||||
|
@ -137,6 +140,9 @@ class IResearchLinkMetaTest : public ::testing::Test {
|
||||||
arangodb::LogLevel::ERR);
|
arangodb::LogLevel::ERR);
|
||||||
|
|
||||||
// setup required application features
|
// setup required application features
|
||||||
|
features.emplace_back(new arangodb::TraverserEngineRegistryFeature(server), false); // must be before AqlFeature
|
||||||
|
features.emplace_back(new arangodb::AqlFeature(server), true);
|
||||||
|
features.emplace_back(new arangodb::aql::OptimizerRulesFeature(server), true);
|
||||||
features.emplace_back(new arangodb::AuthenticationFeature(server), true);
|
features.emplace_back(new arangodb::AuthenticationFeature(server), true);
|
||||||
features.emplace_back(new arangodb::DatabaseFeature(server), false);
|
features.emplace_back(new arangodb::DatabaseFeature(server), false);
|
||||||
features.emplace_back(new arangodb::ShardingFeature(server), false);
|
features.emplace_back(new arangodb::ShardingFeature(server), false);
|
||||||
|
@ -237,16 +243,25 @@ class IResearchLinkMetaTest : public ::testing::Test {
|
||||||
TEST_F(IResearchLinkMetaTest, test_defaults) {
|
TEST_F(IResearchLinkMetaTest, test_defaults) {
|
||||||
arangodb::iresearch::IResearchLinkMeta meta;
|
arangodb::iresearch::IResearchLinkMeta meta;
|
||||||
|
|
||||||
|
{
|
||||||
|
ASSERT_EQ(1, meta._analyzerDefinitions.size());
|
||||||
|
EXPECT_TRUE("identity" == (*meta._analyzerDefinitions.begin())->name());
|
||||||
|
EXPECT_TRUE(irs::flags({irs::norm::type(), irs::frequency::type()}) ==
|
||||||
|
(*meta._analyzerDefinitions.begin())->features());
|
||||||
|
EXPECT_NE(nullptr, meta._analyzerDefinitions.begin()->get());
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_TRUE(meta._sort.empty());
|
||||||
EXPECT_TRUE(true == meta._fields.empty());
|
EXPECT_TRUE(true == meta._fields.empty());
|
||||||
EXPECT_TRUE(false == meta._includeAllFields);
|
EXPECT_TRUE(false == meta._includeAllFields);
|
||||||
EXPECT_TRUE(false == meta._trackListPositions);
|
EXPECT_TRUE(false == meta._trackListPositions);
|
||||||
EXPECT_TRUE((arangodb::iresearch::ValueStorage::NONE == meta._storeValues));
|
EXPECT_TRUE(arangodb::iresearch::ValueStorage::NONE == meta._storeValues);
|
||||||
EXPECT_TRUE(1U == meta._analyzers.size());
|
EXPECT_TRUE(1U == meta._analyzers.size());
|
||||||
EXPECT_TRUE((*(meta._analyzers.begin())));
|
EXPECT_TRUE(*(meta._analyzers.begin()));
|
||||||
EXPECT_TRUE(("identity" == meta._analyzers.begin()->_pool->name()));
|
EXPECT_TRUE("identity" == meta._analyzers.begin()->_pool->name());
|
||||||
EXPECT_TRUE(("identity" == meta._analyzers.begin()->_shortName));
|
EXPECT_TRUE("identity" == meta._analyzers.begin()->_shortName);
|
||||||
EXPECT_TRUE((irs::flags({irs::norm::type(), irs::frequency::type()}) ==
|
EXPECT_TRUE(irs::flags({irs::norm::type(), irs::frequency::type()}) ==
|
||||||
meta._analyzers.begin()->_pool->features()));
|
meta._analyzers.begin()->_pool->features());
|
||||||
EXPECT_TRUE(false == !meta._analyzers.begin()->_pool->get());
|
EXPECT_TRUE(false == !meta._analyzers.begin()->_pool->get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -260,14 +275,15 @@ TEST_F(IResearchLinkMetaTest, test_inheritDefaults) {
|
||||||
|
|
||||||
analyzers.start();
|
analyzers.start();
|
||||||
|
|
||||||
defaults._fields["abc"] = arangodb::iresearch::IResearchLinkMeta();
|
defaults._fields["abc"] = arangodb::iresearch::FieldMeta();
|
||||||
defaults._includeAllFields = true;
|
defaults._includeAllFields = true;
|
||||||
defaults._trackListPositions = true;
|
defaults._trackListPositions = true;
|
||||||
defaults._storeValues = arangodb::iresearch::ValueStorage::FULL;
|
defaults._storeValues = arangodb::iresearch::ValueStorage::VALUE;
|
||||||
defaults._analyzers.clear();
|
defaults._analyzers.clear();
|
||||||
defaults._analyzers.emplace_back(arangodb::iresearch::IResearchLinkMeta::Analyzer(
|
defaults._analyzers.emplace_back(arangodb::iresearch::IResearchLinkMeta::Analyzer(
|
||||||
analyzers.get("testVocbase::empty"), "empty"));
|
analyzers.get("testVocbase::empty"), "empty"));
|
||||||
defaults._fields["abc"]->_fields["xyz"] = arangodb::iresearch::IResearchLinkMeta();
|
defaults._fields["abc"]->_fields["xyz"] = arangodb::iresearch::IResearchLinkMeta();
|
||||||
|
defaults._sort.emplace_back(std::vector<arangodb::basics::AttributeName>{{"foo",false}}, true);
|
||||||
|
|
||||||
auto json = VPackParser::fromJson("{}");
|
auto json = VPackParser::fromJson("{}");
|
||||||
EXPECT_TRUE(true == meta.init(json->slice(), false, tmpString, nullptr, defaults));
|
EXPECT_TRUE(true == meta.init(json->slice(), false, tmpString, nullptr, defaults));
|
||||||
|
@ -301,7 +317,7 @@ TEST_F(IResearchLinkMetaTest, test_inheritDefaults) {
|
||||||
EXPECT_TRUE(true == expectedFields.empty());
|
EXPECT_TRUE(true == expectedFields.empty());
|
||||||
EXPECT_TRUE(true == meta._includeAllFields);
|
EXPECT_TRUE(true == meta._includeAllFields);
|
||||||
EXPECT_TRUE(true == meta._trackListPositions);
|
EXPECT_TRUE(true == meta._trackListPositions);
|
||||||
EXPECT_TRUE((arangodb::iresearch::ValueStorage::FULL == meta._storeValues));
|
EXPECT_TRUE((arangodb::iresearch::ValueStorage::VALUE == meta._storeValues));
|
||||||
|
|
||||||
EXPECT_TRUE(1U == meta._analyzers.size());
|
EXPECT_TRUE(1U == meta._analyzers.size());
|
||||||
EXPECT_TRUE((*(meta._analyzers.begin())));
|
EXPECT_TRUE((*(meta._analyzers.begin())));
|
||||||
|
@ -310,6 +326,8 @@ TEST_F(IResearchLinkMetaTest, test_inheritDefaults) {
|
||||||
EXPECT_TRUE((irs::flags({irs::frequency::type()}) ==
|
EXPECT_TRUE((irs::flags({irs::frequency::type()}) ==
|
||||||
meta._analyzers.begin()->_pool->features()));
|
meta._analyzers.begin()->_pool->features()));
|
||||||
EXPECT_TRUE(false == !meta._analyzers.begin()->_pool->get());
|
EXPECT_TRUE(false == !meta._analyzers.begin()->_pool->get());
|
||||||
|
|
||||||
|
EXPECT_EQ(0, meta._sort.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(IResearchLinkMetaTest, test_readDefaults) {
|
TEST_F(IResearchLinkMetaTest, test_readDefaults) {
|
||||||
|
@ -363,7 +381,7 @@ TEST_F(IResearchLinkMetaTest, test_readCustomizedValues) {
|
||||||
\"c\": { \
|
\"c\": { \
|
||||||
\"fields\": { \
|
\"fields\": { \
|
||||||
\"default\": { \"fields\": {}, \"includeAllFields\": false, \"trackListPositions\": false, \"storeValues\": \"none\", \"analyzers\": [ \"identity\" ] }, \
|
\"default\": { \"fields\": {}, \"includeAllFields\": false, \"trackListPositions\": false, \"storeValues\": \"none\", \"analyzers\": [ \"identity\" ] }, \
|
||||||
\"all\": { \"fields\": {\"d\": {}, \"e\": {}}, \"includeAllFields\": true, \"trackListPositions\": true, \"storeValues\": \"full\", \"analyzers\": [ \"empty\" ] }, \
|
\"all\": { \"fields\": {\"d\": {}, \"e\": {}}, \"includeAllFields\": true, \"trackListPositions\": true, \"storeValues\": \"value\", \"analyzers\": [ \"empty\" ] }, \
|
||||||
\"some\": { \"trackListPositions\": true, \"storeValues\": \"id\" }, \
|
\"some\": { \"trackListPositions\": true, \"storeValues\": \"id\" }, \
|
||||||
\"none\": {} \
|
\"none\": {} \
|
||||||
} \
|
} \
|
||||||
|
@ -371,7 +389,7 @@ TEST_F(IResearchLinkMetaTest, test_readCustomizedValues) {
|
||||||
}, \
|
}, \
|
||||||
\"includeAllFields\": true, \
|
\"includeAllFields\": true, \
|
||||||
\"trackListPositions\": true, \
|
\"trackListPositions\": true, \
|
||||||
\"storeValues\": \"full\", \
|
\"storeValues\": \"value\", \
|
||||||
\"analyzers\": [ \"empty\", \"identity\" ] \
|
\"analyzers\": [ \"empty\", \"identity\" ] \
|
||||||
}");
|
}");
|
||||||
|
|
||||||
|
@ -392,8 +410,8 @@ TEST_F(IResearchLinkMetaTest, test_readCustomizedValues) {
|
||||||
"testVocbase");
|
"testVocbase");
|
||||||
arangodb::iresearch::IResearchLinkMeta meta;
|
arangodb::iresearch::IResearchLinkMeta meta;
|
||||||
std::string tmpString;
|
std::string tmpString;
|
||||||
EXPECT_TRUE((true == meta.init(json->slice(), false, tmpString, &vocbase)));
|
EXPECT_TRUE(meta.init(json->slice(), false, tmpString, &vocbase));
|
||||||
EXPECT_TRUE((3U == meta._fields.size()));
|
EXPECT_TRUE(3U == meta._fields.size());
|
||||||
|
|
||||||
for (auto& field : meta._fields) {
|
for (auto& field : meta._fields) {
|
||||||
EXPECT_TRUE((1U == expectedFields.erase(field.key())));
|
EXPECT_TRUE((1U == expectedFields.erase(field.key())));
|
||||||
|
@ -421,7 +439,7 @@ TEST_F(IResearchLinkMetaTest, test_readCustomizedValues) {
|
||||||
EXPECT_TRUE((true == (actual._fields.find("e") != actual._fields.end())));
|
EXPECT_TRUE((true == (actual._fields.find("e") != actual._fields.end())));
|
||||||
EXPECT_TRUE((true == actual._includeAllFields));
|
EXPECT_TRUE((true == actual._includeAllFields));
|
||||||
EXPECT_TRUE((true == actual._trackListPositions));
|
EXPECT_TRUE((true == actual._trackListPositions));
|
||||||
EXPECT_TRUE((arangodb::iresearch::ValueStorage::FULL == actual._storeValues));
|
EXPECT_TRUE((arangodb::iresearch::ValueStorage::VALUE == actual._storeValues));
|
||||||
EXPECT_TRUE((1U == actual._analyzers.size()));
|
EXPECT_TRUE((1U == actual._analyzers.size()));
|
||||||
EXPECT_TRUE((*(actual._analyzers.begin())));
|
EXPECT_TRUE((*(actual._analyzers.begin())));
|
||||||
EXPECT_TRUE(("testVocbase::empty" == actual._analyzers.begin()->_pool->name()));
|
EXPECT_TRUE(("testVocbase::empty" == actual._analyzers.begin()->_pool->name()));
|
||||||
|
@ -452,7 +470,7 @@ TEST_F(IResearchLinkMetaTest, test_readCustomizedValues) {
|
||||||
EXPECT_TRUE((true == actual._fields.empty())); // not inherited
|
EXPECT_TRUE((true == actual._fields.empty())); // not inherited
|
||||||
EXPECT_TRUE((true == actual._includeAllFields)); // inherited
|
EXPECT_TRUE((true == actual._includeAllFields)); // inherited
|
||||||
EXPECT_TRUE((true == actual._trackListPositions)); // inherited
|
EXPECT_TRUE((true == actual._trackListPositions)); // inherited
|
||||||
EXPECT_TRUE((arangodb::iresearch::ValueStorage::FULL == actual._storeValues));
|
EXPECT_TRUE((arangodb::iresearch::ValueStorage::VALUE == actual._storeValues));
|
||||||
auto itr = actual._analyzers.begin();
|
auto itr = actual._analyzers.begin();
|
||||||
EXPECT_TRUE((*itr));
|
EXPECT_TRUE((*itr));
|
||||||
EXPECT_TRUE(("testVocbase::empty" == itr->_pool->name()));
|
EXPECT_TRUE(("testVocbase::empty" == itr->_pool->name()));
|
||||||
|
@ -474,7 +492,7 @@ TEST_F(IResearchLinkMetaTest, test_readCustomizedValues) {
|
||||||
EXPECT_TRUE((true == expectedFields.empty()));
|
EXPECT_TRUE((true == expectedFields.empty()));
|
||||||
EXPECT_TRUE((true == meta._includeAllFields));
|
EXPECT_TRUE((true == meta._includeAllFields));
|
||||||
EXPECT_TRUE((true == meta._trackListPositions));
|
EXPECT_TRUE((true == meta._trackListPositions));
|
||||||
EXPECT_TRUE((arangodb::iresearch::ValueStorage::FULL == meta._storeValues));
|
EXPECT_TRUE((arangodb::iresearch::ValueStorage::VALUE == meta._storeValues));
|
||||||
auto itr = meta._analyzers.begin();
|
auto itr = meta._analyzers.begin();
|
||||||
EXPECT_TRUE((*itr));
|
EXPECT_TRUE((*itr));
|
||||||
EXPECT_TRUE(("testVocbase::empty" == itr->_pool->name()));
|
EXPECT_TRUE(("testVocbase::empty" == itr->_pool->name()));
|
||||||
|
@ -644,13 +662,16 @@ TEST_F(IResearchLinkMetaTest, test_writeCustomizedValues) {
|
||||||
|
|
||||||
meta._includeAllFields = true;
|
meta._includeAllFields = true;
|
||||||
meta._trackListPositions = true;
|
meta._trackListPositions = true;
|
||||||
meta._storeValues = arangodb::iresearch::ValueStorage::FULL;
|
meta._storeValues = arangodb::iresearch::ValueStorage::VALUE;
|
||||||
|
meta._analyzerDefinitions.clear();
|
||||||
|
meta._analyzerDefinitions.emplace(analyzers.get("identity"));
|
||||||
|
meta._analyzerDefinitions.emplace(analyzers.get(arangodb::StaticStrings::SystemDatabase + "::empty"));
|
||||||
meta._analyzers.clear();
|
meta._analyzers.clear();
|
||||||
meta._analyzers.emplace_back(arangodb::iresearch::IResearchLinkMeta::Analyzer(
|
meta._analyzers.emplace_back(arangodb::iresearch::IResearchLinkMeta::Analyzer(
|
||||||
analyzers.get("identity"), "identity"));
|
analyzers.get("identity"), "identity"));
|
||||||
meta._analyzers.emplace_back(arangodb::iresearch::IResearchLinkMeta::Analyzer(
|
meta._analyzers.emplace_back(arangodb::iresearch::IResearchLinkMeta::Analyzer(
|
||||||
analyzers.get(arangodb::StaticStrings::SystemDatabase + "::empty"),
|
analyzers.get(arangodb::StaticStrings::SystemDatabase + "::empty"),
|
||||||
"enmpty"));
|
"empty"));
|
||||||
meta._fields["a"] = meta; // copy from meta
|
meta._fields["a"] = meta; // copy from meta
|
||||||
meta._fields["a"]->_fields.clear(); // do not inherit fields to match jSon inheritance
|
meta._fields["a"]->_fields.clear(); // do not inherit fields to match jSon inheritance
|
||||||
meta._fields["b"] = meta; // copy from meta
|
meta._fields["b"] = meta; // copy from meta
|
||||||
|
@ -779,7 +800,7 @@ TEST_F(IResearchLinkMetaTest, test_writeCustomizedValues) {
|
||||||
tmpSlice = slice.get("trackListPositions");
|
tmpSlice = slice.get("trackListPositions");
|
||||||
EXPECT_TRUE((true == tmpSlice.isBool() && true == tmpSlice.getBool()));
|
EXPECT_TRUE((true == tmpSlice.isBool() && true == tmpSlice.getBool()));
|
||||||
tmpSlice = slice.get("storeValues");
|
tmpSlice = slice.get("storeValues");
|
||||||
EXPECT_TRUE((true == tmpSlice.isString() && std::string("full") == tmpSlice.copyString()));
|
EXPECT_TRUE((true == tmpSlice.isString() && std::string("value") == tmpSlice.copyString()));
|
||||||
tmpSlice = slice.get("analyzers");
|
tmpSlice = slice.get("analyzers");
|
||||||
EXPECT_TRUE((true == tmpSlice.isArray() && 2 == tmpSlice.length()));
|
EXPECT_TRUE((true == tmpSlice.isArray() && 2 == tmpSlice.length()));
|
||||||
|
|
||||||
|
@ -894,7 +915,7 @@ TEST_F(IResearchLinkMetaTest, test_writeCustomizedValues) {
|
||||||
tmpSlice = slice.get("trackListPositions");
|
tmpSlice = slice.get("trackListPositions");
|
||||||
EXPECT_TRUE((true == tmpSlice.isBool() && true == tmpSlice.getBool()));
|
EXPECT_TRUE((true == tmpSlice.isBool() && true == tmpSlice.getBool()));
|
||||||
tmpSlice = slice.get("storeValues");
|
tmpSlice = slice.get("storeValues");
|
||||||
EXPECT_TRUE((true == tmpSlice.isString() && std::string("full") == tmpSlice.copyString()));
|
EXPECT_TRUE((true == tmpSlice.isString() && std::string("value") == tmpSlice.copyString()));
|
||||||
tmpSlice = slice.get("analyzers");
|
tmpSlice = slice.get("analyzers");
|
||||||
EXPECT_TRUE((true == tmpSlice.isArray() && 2 == tmpSlice.length()));
|
EXPECT_TRUE((true == tmpSlice.isArray() && 2 == tmpSlice.length()));
|
||||||
|
|
||||||
|
@ -1036,7 +1057,7 @@ TEST_F(IResearchLinkMetaTest, test_writeCustomizedValues) {
|
||||||
tmpSlice = slice.get("trackListPositions");
|
tmpSlice = slice.get("trackListPositions");
|
||||||
EXPECT_TRUE((true == tmpSlice.isBool() && true == tmpSlice.getBool()));
|
EXPECT_TRUE((true == tmpSlice.isBool() && true == tmpSlice.getBool()));
|
||||||
tmpSlice = slice.get("storeValues");
|
tmpSlice = slice.get("storeValues");
|
||||||
EXPECT_TRUE((true == tmpSlice.isString() && std::string("full") == tmpSlice.copyString()));
|
EXPECT_TRUE((true == tmpSlice.isString() && std::string("value") == tmpSlice.copyString()));
|
||||||
tmpSlice = slice.get("analyzers");
|
tmpSlice = slice.get("analyzers");
|
||||||
EXPECT_TRUE((true == tmpSlice.isArray() && 2 == tmpSlice.length()));
|
EXPECT_TRUE((true == tmpSlice.isArray() && 2 == tmpSlice.length()));
|
||||||
|
|
||||||
|
@ -1149,7 +1170,7 @@ TEST_F(IResearchLinkMetaTest, test_writeCustomizedValues) {
|
||||||
tmpSlice = slice.get("trackListPositions");
|
tmpSlice = slice.get("trackListPositions");
|
||||||
EXPECT_TRUE((true == tmpSlice.isBool() && true == tmpSlice.getBool()));
|
EXPECT_TRUE((true == tmpSlice.isBool() && true == tmpSlice.getBool()));
|
||||||
tmpSlice = slice.get("storeValues");
|
tmpSlice = slice.get("storeValues");
|
||||||
EXPECT_TRUE((true == tmpSlice.isString() && std::string("full") == tmpSlice.copyString()));
|
EXPECT_TRUE((true == tmpSlice.isString() && std::string("value") == tmpSlice.copyString()));
|
||||||
tmpSlice = slice.get("analyzers");
|
tmpSlice = slice.get("analyzers");
|
||||||
EXPECT_TRUE((true == tmpSlice.isArray() && 2 == tmpSlice.length()));
|
EXPECT_TRUE((true == tmpSlice.isArray() && 2 == tmpSlice.length()));
|
||||||
|
|
||||||
|
@ -1219,7 +1240,7 @@ TEST_F(IResearchLinkMetaTest, test_readMaskAll) {
|
||||||
\"fields\": { \"a\": {} }, \
|
\"fields\": { \"a\": {} }, \
|
||||||
\"includeAllFields\": true, \
|
\"includeAllFields\": true, \
|
||||||
\"trackListPositions\": true, \
|
\"trackListPositions\": true, \
|
||||||
\"storeValues\": \"full\", \
|
\"storeValues\": \"value\", \
|
||||||
\"analyzers\": [] \
|
\"analyzers\": [] \
|
||||||
}");
|
}");
|
||||||
EXPECT_TRUE(true == meta.init(json->slice(), false, tmpString, nullptr,
|
EXPECT_TRUE(true == meta.init(json->slice(), false, tmpString, nullptr,
|
||||||
|
@ -1380,6 +1401,31 @@ TEST_F(IResearchLinkMetaTest, test_readAnalyzerDefinitions) {
|
||||||
EXPECT_EQ(std::string("analyzerDefinitions[0].type"), errorField);
|
EXPECT_EQ(std::string("analyzerDefinitions[0].type"), errorField);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// missing analyzer definition single-server
|
||||||
|
{
|
||||||
|
auto json = VPackParser::fromJson(
|
||||||
|
"{ \
|
||||||
|
\"analyzers\": [ \"missing0\" ] \
|
||||||
|
}");
|
||||||
|
arangodb::iresearch::IResearchLinkMeta meta;
|
||||||
|
std::string errorField;
|
||||||
|
EXPECT_FALSE(meta.init(json->slice(), true, errorField, &vocbase));
|
||||||
|
EXPECT_EQ("analyzers.missing0", errorField);
|
||||||
|
}
|
||||||
|
|
||||||
|
// missing analyzer (full) single-server (ignore analyzer definition)
|
||||||
|
{
|
||||||
|
auto json = VPackParser::fromJson(
|
||||||
|
"{ \
|
||||||
|
\"analyzerDefinitions\": [ { \"name\": \"missing0\", \"type\": \"empty\", \"properties\": {\"args\":\"ru\"}, \"features\": [ \"frequency\" ] } ], \
|
||||||
|
\"analyzers\": [ \"missing0\" ] \
|
||||||
|
}");
|
||||||
|
arangodb::iresearch::IResearchLinkMeta meta;
|
||||||
|
std::string errorField;
|
||||||
|
EXPECT_FALSE(meta.init(json->slice(), false, errorField, &vocbase));
|
||||||
|
EXPECT_EQ("analyzers.missing0", errorField);
|
||||||
|
}
|
||||||
|
|
||||||
// missing analyzer (full) single-server
|
// missing analyzer (full) single-server
|
||||||
{
|
{
|
||||||
auto json = VPackParser::fromJson(
|
auto json = VPackParser::fromJson(
|
||||||
|
@ -1397,7 +1443,133 @@ TEST_F(IResearchLinkMetaTest, test_readAnalyzerDefinitions) {
|
||||||
meta._analyzers[0]._pool->properties());
|
meta._analyzers[0]._pool->properties());
|
||||||
EXPECT_EQ(1, meta._analyzers[0]._pool->features().size());
|
EXPECT_EQ(1, meta._analyzers[0]._pool->features().size());
|
||||||
EXPECT_TRUE(meta._analyzers[0]._pool->features().check(irs::frequency::type()));
|
EXPECT_TRUE(meta._analyzers[0]._pool->features().check(irs::frequency::type()));
|
||||||
EXPECT_EQ(std::string("missing0"), meta._analyzers[0]._shortName);
|
EXPECT_EQ("missing0", meta._analyzers[0]._shortName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// complex definition on single-server
|
||||||
|
{
|
||||||
|
auto json = VPackParser::fromJson(
|
||||||
|
"{ \
|
||||||
|
\"analyzerDefinitions\": [ \
|
||||||
|
{ \"name\": \"empty\", \"type\": \"empty\", \"properties\": {\"args\":\"ru\"}, \"features\": [ \"frequency\" ] }, \
|
||||||
|
{ \"name\": \"::empty\", \"type\": \"empty\", \"properties\": {\"args\":\"ru\"}, \"features\": [ \"frequency\" ] } \
|
||||||
|
], \
|
||||||
|
\"fields\" : { \"field\": { \"analyzers\": [ \"testVocbase::empty\", \"empty\", \"_system::empty\", \"::empty\" ] } } \
|
||||||
|
}");
|
||||||
|
arangodb::iresearch::IResearchLinkMeta meta;
|
||||||
|
std::string errorField;
|
||||||
|
EXPECT_TRUE(meta.init(json->slice(), true, errorField, &vocbase));
|
||||||
|
EXPECT_EQ(2, meta._analyzerDefinitions.size());
|
||||||
|
EXPECT_EQ(1, meta._analyzers.size());
|
||||||
|
{
|
||||||
|
auto& analyzer = meta._analyzers[0];
|
||||||
|
EXPECT_EQ(arangodb::iresearch::IResearchAnalyzerFeature::identity(), analyzer._pool);
|
||||||
|
}
|
||||||
|
ASSERT_EQ(1, meta._fields.size());
|
||||||
|
ASSERT_EQ(2, meta._fields.begin().value()->_analyzers.size());
|
||||||
|
{
|
||||||
|
auto& analyzer = meta._fields.begin().value()->_analyzers[0];
|
||||||
|
EXPECT_EQ("testVocbase::empty", analyzer._pool->name());
|
||||||
|
EXPECT_EQ("empty", analyzer._pool->type());
|
||||||
|
EXPECT_EQUAL_SLICES(VPackParser::fromJson("{\"args\" : \"ru\"}")->slice(),
|
||||||
|
analyzer._pool->properties());
|
||||||
|
EXPECT_EQ(1, analyzer._pool->features().size());
|
||||||
|
EXPECT_TRUE(analyzer._pool->features().check(irs::frequency::type()));
|
||||||
|
EXPECT_EQ("empty", analyzer._shortName);
|
||||||
|
EXPECT_EQ((*meta._analyzerDefinitions.find(analyzer._pool->name())), analyzer._pool);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto& analyzer = meta._fields.begin().value()->_analyzers[1];
|
||||||
|
EXPECT_EQ("_system::empty", analyzer._pool->name());
|
||||||
|
EXPECT_EQ("empty", analyzer._pool->type());
|
||||||
|
EXPECT_EQUAL_SLICES(VPackParser::fromJson("{\"args\" : \"ru\"}")->slice(),
|
||||||
|
analyzer._pool->properties());
|
||||||
|
EXPECT_EQ(1, analyzer._pool->features().size());
|
||||||
|
EXPECT_TRUE(analyzer._pool->features().check(irs::frequency::type()));
|
||||||
|
EXPECT_EQ("::empty", analyzer._shortName);
|
||||||
|
EXPECT_EQ((*meta._analyzerDefinitions.find(analyzer._pool->name())), analyzer._pool);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// complex definition on single-server
|
||||||
|
{
|
||||||
|
auto json = VPackParser::fromJson(
|
||||||
|
"{ \
|
||||||
|
\"analyzerDefinitions\": [ \
|
||||||
|
{ \"name\": \"::empty\", \"type\": \"empty\", \"properties\": {\"args\":\"ru\"}, \"features\": [ \"frequency\" ] } \
|
||||||
|
], \
|
||||||
|
\"fields\" : { \"field\": { \"analyzers\": [ \"testVocbase::empty\", \"empty\", \"_system::empty\", \"::empty\" ] } } \
|
||||||
|
}");
|
||||||
|
arangodb::iresearch::IResearchLinkMeta meta;
|
||||||
|
std::string errorField;
|
||||||
|
ASSERT_TRUE(meta.init(json->slice(), true, errorField, &vocbase));
|
||||||
|
EXPECT_EQ(2, meta._analyzerDefinitions.size());
|
||||||
|
EXPECT_EQ(1, meta._analyzers.size());
|
||||||
|
{
|
||||||
|
auto& analyzer = meta._analyzers[0];
|
||||||
|
EXPECT_EQ(arangodb::iresearch::IResearchAnalyzerFeature::identity(), analyzer._pool);
|
||||||
|
}
|
||||||
|
ASSERT_EQ(1, meta._fields.size());
|
||||||
|
ASSERT_EQ(2, meta._fields.begin().value()->_analyzers.size());
|
||||||
|
{
|
||||||
|
auto& analyzer = meta._fields.begin().value()->_analyzers[0];
|
||||||
|
EXPECT_EQ("testVocbase::empty", analyzer._pool->name());
|
||||||
|
EXPECT_EQ("empty", analyzer._pool->type());
|
||||||
|
// definition from cache since it's not presented "analyzerDefinitions"
|
||||||
|
EXPECT_EQUAL_SLICES(VPackParser::fromJson("{\"args\" : \"de\"}")->slice(),
|
||||||
|
analyzer._pool->properties());
|
||||||
|
EXPECT_EQ(1, analyzer._pool->features().size());
|
||||||
|
EXPECT_TRUE(analyzer._pool->features().check(irs::frequency::type()));
|
||||||
|
EXPECT_EQ("empty", analyzer._shortName);
|
||||||
|
EXPECT_EQ((*meta._analyzerDefinitions.find(analyzer._pool->name())), analyzer._pool);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto& analyzer = meta._fields.begin().value()->_analyzers[1];
|
||||||
|
EXPECT_EQ("_system::empty", analyzer._pool->name());
|
||||||
|
EXPECT_EQ("empty", analyzer._pool->type());
|
||||||
|
EXPECT_EQUAL_SLICES(VPackParser::fromJson("{\"args\" : \"ru\"}")->slice(),
|
||||||
|
analyzer._pool->properties());
|
||||||
|
EXPECT_EQ(1, analyzer._pool->features().size());
|
||||||
|
EXPECT_TRUE(analyzer._pool->features().check(irs::frequency::type()));
|
||||||
|
EXPECT_EQ("::empty", analyzer._shortName);
|
||||||
|
EXPECT_EQ((*meta._analyzerDefinitions.find(analyzer._pool->name())), analyzer._pool);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// missing analyzer definition coordinator
|
||||||
|
{
|
||||||
|
auto before = arangodb::ServerState::instance()->getRole();
|
||||||
|
arangodb::ServerState::instance()->setRole(arangodb::ServerState::ROLE_COORDINATOR);
|
||||||
|
auto restore = irs::make_finally([&before]() -> void {
|
||||||
|
arangodb::ServerState::instance()->setRole(before);
|
||||||
|
});
|
||||||
|
|
||||||
|
auto json = VPackParser::fromJson(
|
||||||
|
"{ \
|
||||||
|
\"analyzers\": [ \"missing1\" ] \
|
||||||
|
}");
|
||||||
|
arangodb::iresearch::IResearchLinkMeta meta;
|
||||||
|
std::string errorField;
|
||||||
|
EXPECT_FALSE(meta.init(json->slice(), true, errorField, &vocbase));
|
||||||
|
EXPECT_EQ("analyzers.missing1", errorField);
|
||||||
|
}
|
||||||
|
|
||||||
|
// missing analyzer definition coordinator
|
||||||
|
{
|
||||||
|
auto before = arangodb::ServerState::instance()->getRole();
|
||||||
|
arangodb::ServerState::instance()->setRole(arangodb::ServerState::ROLE_COORDINATOR);
|
||||||
|
auto restore = irs::make_finally([&before]() -> void {
|
||||||
|
arangodb::ServerState::instance()->setRole(before);
|
||||||
|
});
|
||||||
|
|
||||||
|
auto json = VPackParser::fromJson(
|
||||||
|
"{ \
|
||||||
|
\"fields\" : { \"field\": { \"analyzers\": [ \"missing1\" ] } } \
|
||||||
|
}");
|
||||||
|
arangodb::iresearch::IResearchLinkMeta meta;
|
||||||
|
std::string errorField;
|
||||||
|
EXPECT_FALSE(meta.init(json->slice(), true, errorField, &vocbase));
|
||||||
|
EXPECT_EQ("fields.field.analyzers.missing1", errorField);
|
||||||
}
|
}
|
||||||
|
|
||||||
// missing analyzer (full) coordinator
|
// missing analyzer (full) coordinator
|
||||||
|
@ -1415,8 +1587,138 @@ TEST_F(IResearchLinkMetaTest, test_readAnalyzerDefinitions) {
|
||||||
}");
|
}");
|
||||||
arangodb::iresearch::IResearchLinkMeta meta;
|
arangodb::iresearch::IResearchLinkMeta meta;
|
||||||
std::string errorField;
|
std::string errorField;
|
||||||
EXPECT_TRUE((false == meta.init(json->slice(), true, errorField, &vocbase)));
|
EXPECT_TRUE(meta.init(json->slice(), true, errorField, &vocbase));
|
||||||
EXPECT_EQ(std::string("analyzerDefinitions[0]"), errorField);
|
EXPECT_EQ(1, meta._analyzers.size());
|
||||||
|
EXPECT_EQ("testVocbase::missing1", meta._analyzers[0]._pool->name());
|
||||||
|
EXPECT_EQ("empty", meta._analyzers[0]._pool->type());
|
||||||
|
EXPECT_EQUAL_SLICES(VPackParser::fromJson("{\"args\" : \"ru\"}")->slice(),
|
||||||
|
meta._analyzers[0]._pool->properties());
|
||||||
|
EXPECT_EQ(1, meta._analyzers[0]._pool->features().size());
|
||||||
|
EXPECT_TRUE(meta._analyzers[0]._pool->features().check(irs::frequency::type()));
|
||||||
|
EXPECT_EQ("missing1", meta._analyzers[0]._shortName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// missing analyzer (full) coordinator (ignore analyzer definition)
|
||||||
|
{
|
||||||
|
auto before = arangodb::ServerState::instance()->getRole();
|
||||||
|
arangodb::ServerState::instance()->setRole(arangodb::ServerState::ROLE_COORDINATOR);
|
||||||
|
auto restore = irs::make_finally([&before]() -> void {
|
||||||
|
arangodb::ServerState::instance()->setRole(before);
|
||||||
|
});
|
||||||
|
|
||||||
|
auto json = VPackParser::fromJson(
|
||||||
|
"{ \
|
||||||
|
\"analyzerDefinitions\": [ { \"name\": \"missing1\", \"type\": \"empty\", \"properties\": {\"args\":\"ru\"}, \"features\": [ \"frequency\" ] } ], \
|
||||||
|
\"analyzers\": [ \"missing1\" ] \
|
||||||
|
}");
|
||||||
|
arangodb::iresearch::IResearchLinkMeta meta;
|
||||||
|
std::string errorField;
|
||||||
|
EXPECT_FALSE(meta.init(json->slice(), false, errorField, &vocbase));
|
||||||
|
EXPECT_EQ("analyzers.missing1", errorField);
|
||||||
|
}
|
||||||
|
|
||||||
|
// complex definition on coordinator
|
||||||
|
{
|
||||||
|
auto before = arangodb::ServerState::instance()->getRole();
|
||||||
|
arangodb::ServerState::instance()->setRole(arangodb::ServerState::ROLE_COORDINATOR);
|
||||||
|
auto restore = irs::make_finally([&before]() -> void {
|
||||||
|
arangodb::ServerState::instance()->setRole(before);
|
||||||
|
});
|
||||||
|
|
||||||
|
auto json = VPackParser::fromJson(
|
||||||
|
"{ \
|
||||||
|
\"analyzerDefinitions\": [ \
|
||||||
|
{ \"name\": \"empty\", \"type\": \"empty\", \"properties\": {\"args\":\"ru\"}, \"features\": [ \"frequency\" ] }, \
|
||||||
|
{ \"name\": \"::empty\", \"type\": \"empty\", \"properties\": {\"args\":\"ru\"}, \"features\": [ \"frequency\" ] } \
|
||||||
|
], \
|
||||||
|
\"fields\" : { \"field\": { \"analyzers\": [ \"testVocbase::empty\", \"empty\", \"_system::empty\", \"::empty\" ] } } \
|
||||||
|
}");
|
||||||
|
arangodb::iresearch::IResearchLinkMeta meta;
|
||||||
|
std::string errorField;
|
||||||
|
EXPECT_TRUE(meta.init(json->slice(), true, errorField, &vocbase));
|
||||||
|
EXPECT_EQ(2, meta._analyzerDefinitions.size());
|
||||||
|
EXPECT_EQ(1, meta._analyzers.size());
|
||||||
|
{
|
||||||
|
auto& analyzer = meta._analyzers[0];
|
||||||
|
EXPECT_EQ(arangodb::iresearch::IResearchAnalyzerFeature::identity(), analyzer._pool);
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_EQ(1, meta._fields.size());
|
||||||
|
ASSERT_EQ(2, meta._fields.begin().value()->_analyzers.size());
|
||||||
|
{
|
||||||
|
auto& analyzer = meta._fields.begin().value()->_analyzers[0];
|
||||||
|
EXPECT_EQ(std::string("testVocbase::empty"), analyzer._pool->name());
|
||||||
|
EXPECT_EQ(std::string("empty"), analyzer._pool->type());
|
||||||
|
EXPECT_EQUAL_SLICES(VPackParser::fromJson("{\"args\" : \"ru\"}")->slice(),
|
||||||
|
analyzer._pool->properties());
|
||||||
|
EXPECT_EQ(1, analyzer._pool->features().size());
|
||||||
|
EXPECT_TRUE(analyzer._pool->features().check(irs::frequency::type()));
|
||||||
|
EXPECT_EQ("empty", analyzer._shortName);
|
||||||
|
EXPECT_EQ((*meta._analyzerDefinitions.find(analyzer._pool->name())), analyzer._pool);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto& analyzer = meta._fields.begin().value()->_analyzers[1];
|
||||||
|
EXPECT_EQ(std::string("_system::empty"), analyzer._pool->name());
|
||||||
|
EXPECT_EQ(std::string("empty"), analyzer._pool->type());
|
||||||
|
EXPECT_EQUAL_SLICES(VPackParser::fromJson("{\"args\" : \"ru\"}")->slice(),
|
||||||
|
analyzer._pool->properties());
|
||||||
|
EXPECT_EQ(1, analyzer._pool->features().size());
|
||||||
|
EXPECT_TRUE(analyzer._pool->features().check(irs::frequency::type()));
|
||||||
|
EXPECT_EQ("::empty", analyzer._shortName);
|
||||||
|
EXPECT_EQ((*meta._analyzerDefinitions.find(analyzer._pool->name())), analyzer._pool);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// complex definition on coordinator
|
||||||
|
{
|
||||||
|
auto before = arangodb::ServerState::instance()->getRole();
|
||||||
|
arangodb::ServerState::instance()->setRole(arangodb::ServerState::ROLE_COORDINATOR);
|
||||||
|
auto restore = irs::make_finally([&before]() -> void {
|
||||||
|
arangodb::ServerState::instance()->setRole(before);
|
||||||
|
});
|
||||||
|
|
||||||
|
auto json = VPackParser::fromJson(
|
||||||
|
"{ \
|
||||||
|
\"analyzerDefinitions\": [ \
|
||||||
|
{ \"name\": \"::empty\", \"type\": \"empty\", \"properties\": {\"args\":\"ru\"}, \"features\": [ \"frequency\" ] } \
|
||||||
|
], \
|
||||||
|
\"fields\" : { \"field\": { \"analyzers\": [ \"testVocbase::empty\", \"empty\", \"_system::empty\", \"::empty\" ] } } \
|
||||||
|
}");
|
||||||
|
arangodb::iresearch::IResearchLinkMeta meta;
|
||||||
|
std::string errorField;
|
||||||
|
EXPECT_TRUE(meta.init(json->slice(), true, errorField, &vocbase));
|
||||||
|
EXPECT_EQ(2, meta._analyzerDefinitions.size());
|
||||||
|
EXPECT_EQ(1, meta._analyzers.size());
|
||||||
|
{
|
||||||
|
auto& analyzer = meta._analyzers[0];
|
||||||
|
EXPECT_EQ(arangodb::iresearch::IResearchAnalyzerFeature::identity(), analyzer._pool);
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_EQ(1, meta._fields.size());
|
||||||
|
ASSERT_EQ(2, meta._fields.begin().value()->_analyzers.size());
|
||||||
|
{
|
||||||
|
auto& analyzer = meta._fields.begin().value()->_analyzers[0];
|
||||||
|
EXPECT_EQ("testVocbase::empty", analyzer._pool->name());
|
||||||
|
EXPECT_EQ("empty", analyzer._pool->type());
|
||||||
|
// definition from cache since it's not presented "analyzerDefinitions"
|
||||||
|
EXPECT_EQUAL_SLICES(VPackParser::fromJson("{\"args\" : \"de\"}")->slice(),
|
||||||
|
analyzer._pool->properties());
|
||||||
|
EXPECT_EQ(1, analyzer._pool->features().size());
|
||||||
|
EXPECT_TRUE(analyzer._pool->features().check(irs::frequency::type()));
|
||||||
|
EXPECT_EQ("empty", analyzer._shortName);
|
||||||
|
EXPECT_EQ((*meta._analyzerDefinitions.find(analyzer._pool->name())), analyzer._pool);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto& analyzer = meta._fields.begin().value()->_analyzers[1];
|
||||||
|
EXPECT_EQ("_system::empty", analyzer._pool->name());
|
||||||
|
EXPECT_EQ("empty", analyzer._pool->type());
|
||||||
|
EXPECT_EQUAL_SLICES(VPackParser::fromJson("{\"args\" : \"ru\"}")->slice(),
|
||||||
|
analyzer._pool->properties());
|
||||||
|
EXPECT_EQ(1, analyzer._pool->features().size());
|
||||||
|
EXPECT_TRUE(analyzer._pool->features().check(irs::frequency::type()));
|
||||||
|
EXPECT_EQ("::empty", analyzer._shortName);
|
||||||
|
EXPECT_EQ((*meta._analyzerDefinitions.find(analyzer._pool->name())), analyzer._pool);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// missing analyzer (full) db-server
|
// missing analyzer (full) db-server
|
||||||
|
@ -1434,16 +1736,156 @@ TEST_F(IResearchLinkMetaTest, test_readAnalyzerDefinitions) {
|
||||||
}");
|
}");
|
||||||
arangodb::iresearch::IResearchLinkMeta meta;
|
arangodb::iresearch::IResearchLinkMeta meta;
|
||||||
std::string errorField;
|
std::string errorField;
|
||||||
EXPECT_TRUE((true == meta.init(json->slice(), true, errorField, &vocbase)));
|
EXPECT_TRUE(meta.init(json->slice(), true, errorField, &vocbase));
|
||||||
EXPECT_TRUE((1 == meta._analyzers.size()));
|
EXPECT_EQ(1, meta._analyzers.size());
|
||||||
EXPECT_TRUE(
|
EXPECT_EQ("testVocbase::missing2", meta._analyzers[0]._pool->name());
|
||||||
(std::string("testVocbase::missing2") == meta._analyzers[0]._pool->name()));
|
EXPECT_EQ("empty", meta._analyzers[0]._pool->type());
|
||||||
EXPECT_TRUE((std::string("empty") == meta._analyzers[0]._pool->type()));
|
|
||||||
EXPECT_EQUAL_SLICES(VPackParser::fromJson("{\"args\" : \"ru\"}")->slice(),
|
EXPECT_EQUAL_SLICES(VPackParser::fromJson("{\"args\" : \"ru\"}")->slice(),
|
||||||
meta._analyzers[0]._pool->properties());
|
meta._analyzers[0]._pool->properties());
|
||||||
EXPECT_TRUE((1 == meta._analyzers[0]._pool->features().size()));
|
EXPECT_EQ(1, meta._analyzers[0]._pool->features().size());
|
||||||
EXPECT_TRUE((true == meta._analyzers[0]._pool->features().check(irs::frequency::type())));
|
EXPECT_TRUE(meta._analyzers[0]._pool->features().check(irs::frequency::type()));
|
||||||
EXPECT_TRUE((std::string("missing2") == meta._analyzers[0]._shortName));
|
EXPECT_EQ("missing2", meta._analyzers[0]._shortName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// missing analyzer (full) db-server (ignore analyzer definition)
|
||||||
|
{
|
||||||
|
auto before = arangodb::ServerState::instance()->getRole();
|
||||||
|
arangodb::ServerState::instance()->setRole(arangodb::ServerState::ROLE_DBSERVER);
|
||||||
|
auto restore = irs::make_finally([&before]() -> void {
|
||||||
|
arangodb::ServerState::instance()->setRole(before);
|
||||||
|
});
|
||||||
|
|
||||||
|
auto json = VPackParser::fromJson(
|
||||||
|
"{ \
|
||||||
|
\"analyzerDefinitions\": [ { \"name\": \"missing2\", \"type\": \"empty\", \"properties\": { \"args\": \"ru\" }, \"features\": [ \"frequency\" ] } ], \
|
||||||
|
\"analyzers\": [ \"missing2\" ] \
|
||||||
|
}");
|
||||||
|
arangodb::iresearch::IResearchLinkMeta meta;
|
||||||
|
std::string errorField;
|
||||||
|
EXPECT_FALSE(meta.init(json->slice(), false, errorField, &vocbase));
|
||||||
|
EXPECT_EQ("analyzers.missing2", errorField);
|
||||||
|
}
|
||||||
|
|
||||||
|
// missing analyzer definition db-server
|
||||||
|
{
|
||||||
|
auto before = arangodb::ServerState::instance()->getRole();
|
||||||
|
arangodb::ServerState::instance()->setRole(arangodb::ServerState::ROLE_DBSERVER);
|
||||||
|
auto restore = irs::make_finally([&before]() -> void {
|
||||||
|
arangodb::ServerState::instance()->setRole(before);
|
||||||
|
});
|
||||||
|
|
||||||
|
auto json = VPackParser::fromJson(
|
||||||
|
"{ \
|
||||||
|
\"analyzers\": [ \"missing2\" ] \
|
||||||
|
}");
|
||||||
|
arangodb::iresearch::IResearchLinkMeta meta;
|
||||||
|
std::string errorField;
|
||||||
|
EXPECT_FALSE(meta.init(json->slice(), true, errorField, &vocbase));
|
||||||
|
EXPECT_EQ("analyzers.missing2", errorField);
|
||||||
|
}
|
||||||
|
|
||||||
|
// complex definition on db-server
|
||||||
|
{
|
||||||
|
auto before = arangodb::ServerState::instance()->getRole();
|
||||||
|
arangodb::ServerState::instance()->setRole(arangodb::ServerState::ROLE_DBSERVER);
|
||||||
|
auto restore = irs::make_finally([&before]() -> void {
|
||||||
|
arangodb::ServerState::instance()->setRole(before);
|
||||||
|
});
|
||||||
|
|
||||||
|
auto json = VPackParser::fromJson(
|
||||||
|
"{ \
|
||||||
|
\"analyzerDefinitions\": [ \
|
||||||
|
{ \"name\": \"empty\", \"type\": \"empty\", \"properties\": {\"args\":\"ru\"}, \"features\": [ \"frequency\" ] }, \
|
||||||
|
{ \"name\": \"::empty\", \"type\": \"empty\", \"properties\": {\"args\":\"ru\"}, \"features\": [ \"frequency\" ] } \
|
||||||
|
], \
|
||||||
|
\"fields\" : { \"field\": { \"analyzers\": [ \"testVocbase::empty\", \"empty\", \"_system::empty\", \"::empty\" ] } } \
|
||||||
|
}");
|
||||||
|
arangodb::iresearch::IResearchLinkMeta meta;
|
||||||
|
std::string errorField;
|
||||||
|
EXPECT_TRUE(meta.init(json->slice(), true, errorField, &vocbase));
|
||||||
|
EXPECT_EQ(2, meta._analyzerDefinitions.size());
|
||||||
|
EXPECT_EQ(1, meta._analyzers.size());
|
||||||
|
{
|
||||||
|
auto& analyzer = meta._analyzers[0];
|
||||||
|
EXPECT_EQ(arangodb::iresearch::IResearchAnalyzerFeature::identity(), analyzer._pool);
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_EQ(1, meta._fields.size());
|
||||||
|
ASSERT_EQ(2, meta._fields.begin().value()->_analyzers.size());
|
||||||
|
{
|
||||||
|
auto& analyzer = meta._fields.begin().value()->_analyzers[0];
|
||||||
|
EXPECT_EQ(std::string("testVocbase::empty"), analyzer._pool->name());
|
||||||
|
EXPECT_EQ(std::string("empty"), analyzer._pool->type());
|
||||||
|
EXPECT_EQUAL_SLICES(VPackParser::fromJson("{\"args\" : \"ru\"}")->slice(),
|
||||||
|
analyzer._pool->properties());
|
||||||
|
EXPECT_EQ(1, analyzer._pool->features().size());
|
||||||
|
EXPECT_TRUE(analyzer._pool->features().check(irs::frequency::type()));
|
||||||
|
EXPECT_EQ("empty", analyzer._shortName);
|
||||||
|
EXPECT_EQ((*meta._analyzerDefinitions.find(analyzer._pool->name())), analyzer._pool);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto& analyzer = meta._fields.begin().value()->_analyzers[1];
|
||||||
|
EXPECT_EQ(std::string("_system::empty"), analyzer._pool->name());
|
||||||
|
EXPECT_EQ(std::string("empty"), analyzer._pool->type());
|
||||||
|
EXPECT_EQUAL_SLICES(VPackParser::fromJson("{\"args\" : \"ru\"}")->slice(),
|
||||||
|
analyzer._pool->properties());
|
||||||
|
EXPECT_EQ(1, analyzer._pool->features().size());
|
||||||
|
EXPECT_TRUE(analyzer._pool->features().check(irs::frequency::type()));
|
||||||
|
EXPECT_EQ("::empty", analyzer._shortName);
|
||||||
|
EXPECT_EQ((*meta._analyzerDefinitions.find(analyzer._pool->name())), analyzer._pool);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// complex definition on db-server
|
||||||
|
{
|
||||||
|
auto before = arangodb::ServerState::instance()->getRole();
|
||||||
|
arangodb::ServerState::instance()->setRole(arangodb::ServerState::ROLE_DBSERVER);
|
||||||
|
auto restore = irs::make_finally([&before]() -> void {
|
||||||
|
arangodb::ServerState::instance()->setRole(before);
|
||||||
|
});
|
||||||
|
|
||||||
|
auto json = VPackParser::fromJson(
|
||||||
|
"{ \
|
||||||
|
\"analyzerDefinitions\": [ \
|
||||||
|
{ \"name\": \"::empty\", \"type\": \"empty\", \"properties\": {\"args\":\"ru\"}, \"features\": [ \"frequency\" ] } \
|
||||||
|
], \
|
||||||
|
\"fields\" : { \"field\": { \"analyzers\": [ \"testVocbase::empty\", \"empty\", \"_system::empty\", \"::empty\" ] } } \
|
||||||
|
}");
|
||||||
|
arangodb::iresearch::IResearchLinkMeta meta;
|
||||||
|
std::string errorField;
|
||||||
|
EXPECT_TRUE(meta.init(json->slice(), true, errorField, &vocbase));
|
||||||
|
EXPECT_EQ(2, meta._analyzerDefinitions.size());
|
||||||
|
EXPECT_EQ(1, meta._analyzers.size());
|
||||||
|
{
|
||||||
|
auto& analyzer = meta._analyzers[0];
|
||||||
|
EXPECT_EQ(arangodb::iresearch::IResearchAnalyzerFeature::identity(), analyzer._pool);
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_EQ(1, meta._fields.size());
|
||||||
|
ASSERT_EQ(2, meta._fields.begin().value()->_analyzers.size());
|
||||||
|
{
|
||||||
|
auto& analyzer = meta._fields.begin().value()->_analyzers[0];
|
||||||
|
EXPECT_EQ("testVocbase::empty", analyzer._pool->name());
|
||||||
|
EXPECT_EQ("empty", analyzer._pool->type());
|
||||||
|
// definition from cache since it's not presented "analyzerDefinitions"
|
||||||
|
EXPECT_EQUAL_SLICES(VPackParser::fromJson("{\"args\" : \"de\"}")->slice(),
|
||||||
|
analyzer._pool->properties());
|
||||||
|
EXPECT_EQ(1, analyzer._pool->features().size());
|
||||||
|
EXPECT_TRUE(analyzer._pool->features().check(irs::frequency::type()));
|
||||||
|
EXPECT_EQ("empty", analyzer._shortName);
|
||||||
|
EXPECT_EQ((*meta._analyzerDefinitions.find(analyzer._pool->name())), analyzer._pool);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto& analyzer = meta._fields.begin().value()->_analyzers[1];
|
||||||
|
EXPECT_EQ("_system::empty", analyzer._pool->name());
|
||||||
|
EXPECT_EQ("empty", analyzer._pool->type());
|
||||||
|
EXPECT_EQUAL_SLICES(VPackParser::fromJson("{\"args\" : \"ru\"}")->slice(),
|
||||||
|
analyzer._pool->properties());
|
||||||
|
EXPECT_EQ(1, analyzer._pool->features().size());
|
||||||
|
EXPECT_TRUE(analyzer._pool->features().check(irs::frequency::type()));
|
||||||
|
EXPECT_EQ("::empty", analyzer._shortName);
|
||||||
|
EXPECT_EQ((*meta._analyzerDefinitions.find(analyzer._pool->name())), analyzer._pool);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// missing analyzer (full) inRecovery
|
// missing analyzer (full) inRecovery
|
||||||
|
@ -1459,8 +1901,48 @@ TEST_F(IResearchLinkMetaTest, test_readAnalyzerDefinitions) {
|
||||||
[&before]() -> void { StorageEngineMock::recoveryStateResult = before; });
|
[&before]() -> void { StorageEngineMock::recoveryStateResult = before; });
|
||||||
arangodb::iresearch::IResearchLinkMeta meta;
|
arangodb::iresearch::IResearchLinkMeta meta;
|
||||||
std::string errorField;
|
std::string errorField;
|
||||||
EXPECT_TRUE((false == meta.init(json->slice(), true, errorField, &vocbase)));
|
EXPECT_TRUE(meta.init(json->slice(), true, errorField, &vocbase));
|
||||||
EXPECT_EQ(std::string("analyzers.missing3"), errorField); // not in the persisted collection
|
EXPECT_EQ(1, meta._analyzers.size());
|
||||||
|
EXPECT_EQ("testVocbase::missing3", meta._analyzers[0]._pool->name());
|
||||||
|
EXPECT_EQ("empty", meta._analyzers[0]._pool->type());
|
||||||
|
EXPECT_EQUAL_SLICES(VPackParser::fromJson("{\"args\" : \"ru\"}")->slice(),
|
||||||
|
meta._analyzers[0]._pool->properties());
|
||||||
|
EXPECT_EQ(1, meta._analyzers[0]._pool->features().size());
|
||||||
|
EXPECT_TRUE(meta._analyzers[0]._pool->features().check(irs::frequency::type()));
|
||||||
|
EXPECT_EQ("missing3", meta._analyzers[0]._shortName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// missing analyzer (full) inRecovery (ignore analyzer definition)
|
||||||
|
{
|
||||||
|
auto json = VPackParser::fromJson(
|
||||||
|
"{ \
|
||||||
|
\"analyzerDefinitions\": [ { \"name\": \"missing3\", \"type\": \"empty\", \"properties\": {\"args\":\"ru\"}, \"features\": [ \"frequency\" ] } ], \
|
||||||
|
\"analyzers\": [ \"missing3\" ] \
|
||||||
|
}");
|
||||||
|
auto before = StorageEngineMock::recoveryStateResult;
|
||||||
|
StorageEngineMock::recoveryStateResult = arangodb::RecoveryState::IN_PROGRESS;
|
||||||
|
auto restore = irs::make_finally(
|
||||||
|
[&before]() -> void { StorageEngineMock::recoveryStateResult = before; });
|
||||||
|
arangodb::iresearch::IResearchLinkMeta meta;
|
||||||
|
std::string errorField;
|
||||||
|
EXPECT_FALSE(meta.init(json->slice(), false, errorField, &vocbase));
|
||||||
|
EXPECT_EQ("analyzers.missing3", errorField);
|
||||||
|
}
|
||||||
|
|
||||||
|
// missing analyzer definition inRecovery
|
||||||
|
{
|
||||||
|
auto json = VPackParser::fromJson(
|
||||||
|
"{ \
|
||||||
|
\"analyzers\": [ \"missing3\" ] \
|
||||||
|
}");
|
||||||
|
auto before = StorageEngineMock::recoveryStateResult;
|
||||||
|
StorageEngineMock::recoveryStateResult = arangodb::RecoveryState::IN_PROGRESS;
|
||||||
|
auto restore = irs::make_finally(
|
||||||
|
[&before]() -> void { StorageEngineMock::recoveryStateResult = before; });
|
||||||
|
arangodb::iresearch::IResearchLinkMeta meta;
|
||||||
|
std::string errorField;
|
||||||
|
EXPECT_FALSE(meta.init(json->slice(), true, errorField, &vocbase));
|
||||||
|
EXPECT_EQ("analyzers.missing3", errorField);
|
||||||
}
|
}
|
||||||
|
|
||||||
// existing analyzer (name only)
|
// existing analyzer (name only)
|
||||||
|
@ -1507,6 +1989,108 @@ TEST_F(IResearchLinkMetaTest, test_readAnalyzerDefinitions) {
|
||||||
EXPECT_TRUE((std::string("empty") == meta._analyzers[0]._shortName));
|
EXPECT_TRUE((std::string("empty") == meta._analyzers[0]._shortName));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// complex definition inRecovery
|
||||||
|
{
|
||||||
|
auto before = StorageEngineMock::recoveryStateResult;
|
||||||
|
StorageEngineMock::recoveryStateResult = arangodb::RecoveryState::IN_PROGRESS;
|
||||||
|
auto restore = irs::make_finally(
|
||||||
|
[&before]() -> void { StorageEngineMock::recoveryStateResult = before; });
|
||||||
|
|
||||||
|
auto json = VPackParser::fromJson(
|
||||||
|
"{ \
|
||||||
|
\"analyzerDefinitions\": [ \
|
||||||
|
{ \"name\": \"empty\", \"type\": \"empty\", \"properties\": {\"args\":\"ru\"}, \"features\": [ \"frequency\" ] }, \
|
||||||
|
{ \"name\": \"::empty\", \"type\": \"empty\", \"properties\": {\"args\":\"ru\"}, \"features\": [ \"frequency\" ] } \
|
||||||
|
], \
|
||||||
|
\"fields\" : { \"field\": { \"analyzers\": [ \"testVocbase::empty\", \"empty\", \"_system::empty\", \"::empty\" ] } } \
|
||||||
|
}");
|
||||||
|
arangodb::iresearch::IResearchLinkMeta meta;
|
||||||
|
std::string errorField;
|
||||||
|
EXPECT_TRUE(meta.init(json->slice(), true, errorField, &vocbase));
|
||||||
|
EXPECT_EQ(2, meta._analyzerDefinitions.size());
|
||||||
|
EXPECT_EQ(1, meta._analyzers.size());
|
||||||
|
{
|
||||||
|
auto& analyzer = meta._analyzers[0];
|
||||||
|
EXPECT_EQ(arangodb::iresearch::IResearchAnalyzerFeature::identity(), analyzer._pool);
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_EQ(1, meta._fields.size());
|
||||||
|
ASSERT_EQ(2, meta._fields.begin().value()->_analyzers.size());
|
||||||
|
{
|
||||||
|
auto& analyzer = meta._fields.begin().value()->_analyzers[0];
|
||||||
|
EXPECT_EQ(std::string("testVocbase::empty"), analyzer._pool->name());
|
||||||
|
EXPECT_EQ(std::string("empty"), analyzer._pool->type());
|
||||||
|
EXPECT_EQUAL_SLICES(VPackParser::fromJson("{\"args\" : \"ru\"}")->slice(),
|
||||||
|
analyzer._pool->properties());
|
||||||
|
EXPECT_EQ(1, analyzer._pool->features().size());
|
||||||
|
EXPECT_TRUE(analyzer._pool->features().check(irs::frequency::type()));
|
||||||
|
EXPECT_EQ("empty", analyzer._shortName);
|
||||||
|
EXPECT_EQ((*meta._analyzerDefinitions.find(analyzer._pool->name())), analyzer._pool);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto& analyzer = meta._fields.begin().value()->_analyzers[1];
|
||||||
|
EXPECT_EQ(std::string("_system::empty"), analyzer._pool->name());
|
||||||
|
EXPECT_EQ(std::string("empty"), analyzer._pool->type());
|
||||||
|
EXPECT_EQUAL_SLICES(VPackParser::fromJson("{\"args\" : \"ru\"}")->slice(),
|
||||||
|
analyzer._pool->properties());
|
||||||
|
EXPECT_EQ(1, analyzer._pool->features().size());
|
||||||
|
EXPECT_TRUE(analyzer._pool->features().check(irs::frequency::type()));
|
||||||
|
EXPECT_EQ("::empty", analyzer._shortName);
|
||||||
|
EXPECT_EQ((*meta._analyzerDefinitions.find(analyzer._pool->name())), analyzer._pool);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// complex definition inRecovery
|
||||||
|
{
|
||||||
|
auto before = StorageEngineMock::recoveryStateResult;
|
||||||
|
StorageEngineMock::recoveryStateResult = arangodb::RecoveryState::IN_PROGRESS;
|
||||||
|
auto restore = irs::make_finally(
|
||||||
|
[&before]() -> void { StorageEngineMock::recoveryStateResult = before; });
|
||||||
|
|
||||||
|
auto json = VPackParser::fromJson(
|
||||||
|
"{ \
|
||||||
|
\"analyzerDefinitions\": [ \
|
||||||
|
{ \"name\": \"::empty\", \"type\": \"empty\", \"properties\": {\"args\":\"ru\"}, \"features\": [ \"frequency\" ] } \
|
||||||
|
], \
|
||||||
|
\"fields\" : { \"field\": { \"analyzers\": [ \"testVocbase::empty\", \"empty\", \"_system::empty\", \"::empty\" ] } } \
|
||||||
|
}");
|
||||||
|
arangodb::iresearch::IResearchLinkMeta meta;
|
||||||
|
std::string errorField;
|
||||||
|
EXPECT_TRUE(meta.init(json->slice(), true, errorField, &vocbase));
|
||||||
|
EXPECT_EQ(2, meta._analyzerDefinitions.size());
|
||||||
|
EXPECT_EQ(1, meta._analyzers.size());
|
||||||
|
{
|
||||||
|
auto& analyzer = meta._analyzers[0];
|
||||||
|
EXPECT_EQ(arangodb::iresearch::IResearchAnalyzerFeature::identity(), analyzer._pool);
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_EQ(1, meta._fields.size());
|
||||||
|
ASSERT_EQ(2, meta._fields.begin().value()->_analyzers.size());
|
||||||
|
{
|
||||||
|
auto& analyzer = meta._fields.begin().value()->_analyzers[0];
|
||||||
|
EXPECT_EQ("testVocbase::empty", analyzer._pool->name());
|
||||||
|
EXPECT_EQ("empty", analyzer._pool->type());
|
||||||
|
// definition from cache since it's not presented "analyzerDefinitions"
|
||||||
|
EXPECT_EQUAL_SLICES(VPackParser::fromJson("{\"args\" : \"de\"}")->slice(),
|
||||||
|
analyzer._pool->properties());
|
||||||
|
EXPECT_EQ(1, analyzer._pool->features().size());
|
||||||
|
EXPECT_TRUE(analyzer._pool->features().check(irs::frequency::type()));
|
||||||
|
EXPECT_EQ("empty", analyzer._shortName);
|
||||||
|
EXPECT_EQ((*meta._analyzerDefinitions.find(analyzer._pool->name())), analyzer._pool);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto& analyzer = meta._fields.begin().value()->_analyzers[1];
|
||||||
|
EXPECT_EQ("_system::empty", analyzer._pool->name());
|
||||||
|
EXPECT_EQ("empty", analyzer._pool->type());
|
||||||
|
EXPECT_EQUAL_SLICES(VPackParser::fromJson("{\"args\" : \"ru\"}")->slice(),
|
||||||
|
analyzer._pool->properties());
|
||||||
|
EXPECT_EQ(1, analyzer._pool->features().size());
|
||||||
|
EXPECT_TRUE(analyzer._pool->features().check(irs::frequency::type()));
|
||||||
|
EXPECT_EQ("::empty", analyzer._shortName);
|
||||||
|
EXPECT_EQ((*meta._analyzerDefinitions.find(analyzer._pool->name())), analyzer._pool);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// existing analyzer (full) analyzer creation not allowed (pass)
|
// existing analyzer (full) analyzer creation not allowed (pass)
|
||||||
{
|
{
|
||||||
auto json = VPackParser::fromJson(
|
auto json = VPackParser::fromJson(
|
||||||
|
@ -1575,16 +2159,16 @@ TEST_F(IResearchLinkMetaTest, test_readAnalyzerDefinitions) {
|
||||||
[&before]() -> void { StorageEngineMock::recoveryStateResult = before; });
|
[&before]() -> void { StorageEngineMock::recoveryStateResult = before; });
|
||||||
arangodb::iresearch::IResearchLinkMeta meta;
|
arangodb::iresearch::IResearchLinkMeta meta;
|
||||||
std::string errorField;
|
std::string errorField;
|
||||||
EXPECT_TRUE((true == meta.init(json->slice(), true, errorField, &vocbase)));
|
EXPECT_TRUE(meta.init(json->slice(), true, errorField, &vocbase));
|
||||||
EXPECT_TRUE((1 == meta._analyzers.size()));
|
EXPECT_EQ(1, meta._analyzers.size());
|
||||||
EXPECT_TRUE((std::string("testVocbase::empty") == meta._analyzers[0]._pool->name()));
|
EXPECT_EQ("testVocbase::empty", meta._analyzers[0]._pool->name());
|
||||||
EXPECT_TRUE((std::string("empty") == meta._analyzers[0]._pool->type()));
|
EXPECT_EQ("empty", meta._analyzers[0]._pool->type());
|
||||||
EXPECT_EQUAL_SLICES(
|
EXPECT_EQUAL_SLICES(
|
||||||
VPackParser::fromJson("{\"args\" : \"de\"}")->slice(),
|
VPackParser::fromJson("{\"args\" : \"de\"}")->slice(),
|
||||||
meta._analyzers[0]._pool->properties());
|
meta._analyzers[0]._pool->properties());
|
||||||
EXPECT_TRUE((1 == meta._analyzers[0]._pool->features().size()));
|
EXPECT_EQ(1, meta._analyzers[0]._pool->features().size());
|
||||||
EXPECT_TRUE((true == meta._analyzers[0]._pool->features().check(irs::frequency::type())));
|
EXPECT_TRUE(meta._analyzers[0]._pool->features().check(irs::frequency::type()));
|
||||||
EXPECT_TRUE((std::string("empty") == meta._analyzers[0]._shortName));
|
EXPECT_EQ("empty", meta._analyzers[0]._shortName);
|
||||||
}
|
}
|
||||||
|
|
||||||
// existing analyzer (definition mismatch)
|
// existing analyzer (definition mismatch)
|
||||||
|
@ -1596,8 +2180,38 @@ TEST_F(IResearchLinkMetaTest, test_readAnalyzerDefinitions) {
|
||||||
}");
|
}");
|
||||||
arangodb::iresearch::IResearchLinkMeta meta;
|
arangodb::iresearch::IResearchLinkMeta meta;
|
||||||
std::string errorField;
|
std::string errorField;
|
||||||
EXPECT_TRUE((false == meta.init(json->slice(), true, errorField, &vocbase)));
|
EXPECT_TRUE(meta.init(json->slice(), true, errorField, &vocbase));
|
||||||
EXPECT_EQ(std::string("analyzerDefinitions[0]"), errorField);
|
EXPECT_EQ(1, meta._analyzers.size());
|
||||||
|
EXPECT_EQ("testVocbase::empty", meta._analyzers[0]._pool->name());
|
||||||
|
EXPECT_EQ("empty", meta._analyzers[0]._pool->type());
|
||||||
|
EXPECT_EQUAL_SLICES(
|
||||||
|
VPackParser::fromJson("{\"args\" : \"ru\"}")->slice(),
|
||||||
|
meta._analyzers[0]._pool->properties());
|
||||||
|
EXPECT_EQ(1, meta._analyzers[0]._pool->features().size());
|
||||||
|
EXPECT_TRUE(meta._analyzers[0]._pool->features().check(irs::frequency::type()));
|
||||||
|
EXPECT_EQ("empty", meta._analyzers[0]._shortName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// existing analyzer (definition mismatch), don't read definitions
|
||||||
|
{
|
||||||
|
auto json = VPackParser::fromJson(
|
||||||
|
"{ \
|
||||||
|
\"analyzerDefinitions\": [ { \"name\": \"empty\", \"type\": \"empty\", \"properties\": {\"args\":\"ru\"}, \"features\": [ \"frequency\" ] } ], \
|
||||||
|
\"analyzers\": [ \"empty\" ] \
|
||||||
|
}");
|
||||||
|
arangodb::iresearch::IResearchLinkMeta meta;
|
||||||
|
std::string errorField;
|
||||||
|
EXPECT_TRUE(meta.init(json->slice(), false, errorField, &vocbase));
|
||||||
|
EXPECT_EQ(1, meta._analyzers.size());
|
||||||
|
EXPECT_EQ("testVocbase::empty", meta._analyzers[0]._pool->name());
|
||||||
|
EXPECT_EQ("empty", meta._analyzers[0]._pool->type());
|
||||||
|
// read definition from cache since we ignore "analyzerDefinitions"
|
||||||
|
EXPECT_EQUAL_SLICES(
|
||||||
|
VPackParser::fromJson("{\"args\" : \"de\"}")->slice(),
|
||||||
|
meta._analyzers[0]._pool->properties());
|
||||||
|
EXPECT_EQ(1, meta._analyzers[0]._pool->features().size());
|
||||||
|
EXPECT_TRUE(meta._analyzers[0]._pool->features().check(irs::frequency::type()));
|
||||||
|
EXPECT_EQ("empty", meta._analyzers[0]._shortName);
|
||||||
}
|
}
|
||||||
|
|
||||||
// existing analyzer (definition mismatch) inRecovery
|
// existing analyzer (definition mismatch) inRecovery
|
||||||
|
@ -1613,8 +2227,43 @@ TEST_F(IResearchLinkMetaTest, test_readAnalyzerDefinitions) {
|
||||||
[&before]() -> void { StorageEngineMock::recoveryStateResult = before; });
|
[&before]() -> void { StorageEngineMock::recoveryStateResult = before; });
|
||||||
arangodb::iresearch::IResearchLinkMeta meta;
|
arangodb::iresearch::IResearchLinkMeta meta;
|
||||||
std::string errorField;
|
std::string errorField;
|
||||||
EXPECT_TRUE((false == meta.init(json->slice(), true, errorField, &vocbase)));
|
EXPECT_TRUE(meta.init(json->slice(), true, errorField, &vocbase));
|
||||||
EXPECT_EQ(std::string("analyzerDefinitions[0]"), errorField);
|
EXPECT_EQ(1, meta._analyzers.size());
|
||||||
|
EXPECT_EQ("testVocbase::empty", meta._analyzers[0]._pool->name());
|
||||||
|
EXPECT_EQ("empty", meta._analyzers[0]._pool->type());
|
||||||
|
// read definition from cache since we ignore "analyzerDefinitions"
|
||||||
|
EXPECT_EQUAL_SLICES(
|
||||||
|
VPackParser::fromJson("{\"args\" : \"ru\"}")->slice(),
|
||||||
|
meta._analyzers[0]._pool->properties());
|
||||||
|
EXPECT_EQ(1, meta._analyzers[0]._pool->features().size());
|
||||||
|
EXPECT_TRUE(meta._analyzers[0]._pool->features().check(irs::frequency::type()));
|
||||||
|
EXPECT_EQ("empty", meta._analyzers[0]._shortName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// existing analyzer (definition mismatch) inRecovery, don't read definitions
|
||||||
|
{
|
||||||
|
auto json = VPackParser::fromJson(
|
||||||
|
"{ \
|
||||||
|
\"analyzerDefinitions\": [ { \"name\": \"empty\", \"type\": \"empty\", \"properties\": {\"args\":\"ru\"}, \"features\": [ \"frequency\" ] } ], \
|
||||||
|
\"analyzers\": [ \"empty\" ] \
|
||||||
|
}");
|
||||||
|
auto before = StorageEngineMock::recoveryStateResult;
|
||||||
|
StorageEngineMock::recoveryStateResult = arangodb::RecoveryState::IN_PROGRESS;
|
||||||
|
auto restore = irs::make_finally(
|
||||||
|
[&before]() -> void { StorageEngineMock::recoveryStateResult = before; });
|
||||||
|
arangodb::iresearch::IResearchLinkMeta meta;
|
||||||
|
std::string errorField;
|
||||||
|
EXPECT_TRUE(meta.init(json->slice(), false, errorField, &vocbase));
|
||||||
|
EXPECT_EQ(1, meta._analyzers.size());
|
||||||
|
EXPECT_EQ("testVocbase::empty", meta._analyzers[0]._pool->name());
|
||||||
|
EXPECT_EQ("empty", meta._analyzers[0]._pool->type());
|
||||||
|
// read definition from cache since we ignore "analyzerDefinitions"
|
||||||
|
EXPECT_EQUAL_SLICES(
|
||||||
|
VPackParser::fromJson("{\"args\" : \"de\"}")->slice(),
|
||||||
|
meta._analyzers[0]._pool->properties());
|
||||||
|
EXPECT_EQ(1, meta._analyzers[0]._pool->features().size());
|
||||||
|
EXPECT_TRUE(meta._analyzers[0]._pool->features().check(irs::frequency::type()));
|
||||||
|
EXPECT_EQ("empty", meta._analyzers[0]._shortName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1658,7 +2307,7 @@ TEST_F(IResearchLinkMetaTest, test_addNonUniqueAnalyzers) {
|
||||||
"{ \
|
"{ \
|
||||||
\"includeAllFields\": true, \
|
\"includeAllFields\": true, \
|
||||||
\"trackListPositions\": true, \
|
\"trackListPositions\": true, \
|
||||||
\"storeValues\": \"full\",\
|
\"storeValues\": \"value\",\
|
||||||
\"analyzers\": [ \"identity\",\"identity\"" // two built-in analyzers
|
\"analyzers\": [ \"identity\",\"identity\"" // two built-in analyzers
|
||||||
", \"" +
|
", \"" +
|
||||||
analyzerCustomInTestVocbase + "\"" + // local analyzer by full name
|
analyzerCustomInTestVocbase + "\"" + // local analyzer by full name
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
#include "ExpressionContextMock.h"
|
#include "ExpressionContextMock.h"
|
||||||
|
|
||||||
#include "analysis/token_attributes.hpp"
|
#include "analysis/token_attributes.hpp"
|
||||||
|
#include "analysis/analyzers.hpp"
|
||||||
#include "search/scorers.hpp"
|
#include "search/scorers.hpp"
|
||||||
#include "utils/locale_utils.hpp"
|
#include "utils/locale_utils.hpp"
|
||||||
#include "utils/log.hpp"
|
#include "utils/log.hpp"
|
||||||
|
@ -39,6 +40,7 @@
|
||||||
#include "Aql/AstNode.h"
|
#include "Aql/AstNode.h"
|
||||||
#include "Aql/Function.h"
|
#include "Aql/Function.h"
|
||||||
#include "Aql/SortCondition.h"
|
#include "Aql/SortCondition.h"
|
||||||
|
#include "Aql/OptimizerRulesFeature.h"
|
||||||
#include "Basics/ArangoGlobalContext.h"
|
#include "Basics/ArangoGlobalContext.h"
|
||||||
#include "Basics/error.h"
|
#include "Basics/error.h"
|
||||||
#include "Basics/files.h"
|
#include "Basics/files.h"
|
||||||
|
@ -130,6 +132,88 @@ struct DocIdScorer: public irs::sort {
|
||||||
|
|
||||||
REGISTER_SCORER_TEXT(DocIdScorer, DocIdScorer::make);
|
REGISTER_SCORER_TEXT(DocIdScorer, DocIdScorer::make);
|
||||||
|
|
||||||
|
struct TestAttribute : public irs::attribute {
|
||||||
|
DECLARE_ATTRIBUTE_TYPE();
|
||||||
|
};
|
||||||
|
|
||||||
|
DEFINE_ATTRIBUTE_TYPE(TestAttribute);
|
||||||
|
REGISTER_ATTRIBUTE(TestAttribute); // required to open reader on segments with analized fields
|
||||||
|
|
||||||
|
struct TestTermAttribute : public irs::term_attribute {
|
||||||
|
public:
|
||||||
|
void value(irs::bytes_ref const& value) { value_ = value; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class TestAnalyzer : public irs::analysis::analyzer {
|
||||||
|
public:
|
||||||
|
DECLARE_ANALYZER_TYPE();
|
||||||
|
|
||||||
|
TestAnalyzer() : irs::analysis::analyzer(TestAnalyzer::type()) {
|
||||||
|
_attrs.emplace(_term);
|
||||||
|
_attrs.emplace(_attr);
|
||||||
|
_attrs.emplace(_increment); // required by field_data::invert(...)
|
||||||
|
}
|
||||||
|
virtual irs::attribute_view const& attributes() const noexcept override {
|
||||||
|
return _attrs;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ptr make(irs::string_ref const& args) {
|
||||||
|
auto slice = arangodb::iresearch::slice(args);
|
||||||
|
if (slice.isNull()) throw std::exception();
|
||||||
|
if (slice.isNone()) return nullptr;
|
||||||
|
PTR_NAMED(TestAnalyzer, ptr);
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool normalize(irs::string_ref const& args, std::string& definition) {
|
||||||
|
// same validation as for make,
|
||||||
|
// as normalize usually called to sanitize data before make
|
||||||
|
auto slice = arangodb::iresearch::slice(args);
|
||||||
|
if (slice.isNull()) throw std::exception();
|
||||||
|
if (slice.isNone()) return false;
|
||||||
|
|
||||||
|
arangodb::velocypack::Builder builder;
|
||||||
|
if (slice.isString()) {
|
||||||
|
VPackObjectBuilder scope(&builder);
|
||||||
|
arangodb::iresearch::addStringRef(builder, "args",
|
||||||
|
arangodb::iresearch::getStringRef(slice));
|
||||||
|
} else if (slice.isObject() && slice.hasKey("args") && slice.get("args").isString()) {
|
||||||
|
VPackObjectBuilder scope(&builder);
|
||||||
|
arangodb::iresearch::addStringRef(builder, "args",
|
||||||
|
arangodb::iresearch::getStringRef(slice.get("args")));
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
definition = builder.buffer()->toString();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool next() override {
|
||||||
|
if (_data.empty()) return false;
|
||||||
|
|
||||||
|
_term.value(irs::bytes_ref(_data.c_str(), 1));
|
||||||
|
_data = irs::bytes_ref(_data.c_str() + 1, _data.size() - 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool reset(irs::string_ref const& data) override {
|
||||||
|
_data = irs::ref_cast<irs::byte_type>(data);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
irs::attribute_view _attrs;
|
||||||
|
irs::bytes_ref _data;
|
||||||
|
irs::increment _increment;
|
||||||
|
TestTermAttribute _term;
|
||||||
|
TestAttribute _attr;
|
||||||
|
};
|
||||||
|
|
||||||
|
DEFINE_ANALYZER_TYPE_NAMED(TestAnalyzer, "TestAnalyzer");
|
||||||
|
REGISTER_ANALYZER_VPACK(TestAnalyzer, TestAnalyzer::make, TestAnalyzer::normalize);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
@ -191,6 +275,7 @@ protected:
|
||||||
buildFeatureEntry(new arangodb::DatabaseFeature(server), false);
|
buildFeatureEntry(new arangodb::DatabaseFeature(server), false);
|
||||||
buildFeatureEntry(new arangodb::DatabasePathFeature(server), false);
|
buildFeatureEntry(new arangodb::DatabasePathFeature(server), false);
|
||||||
buildFeatureEntry(new arangodb::TraverserEngineRegistryFeature(server), false);
|
buildFeatureEntry(new arangodb::TraverserEngineRegistryFeature(server), false);
|
||||||
|
buildFeatureEntry(new arangodb::aql::OptimizerRulesFeature(server), true);
|
||||||
buildFeatureEntry(new arangodb::AqlFeature(server), true);
|
buildFeatureEntry(new arangodb::AqlFeature(server), true);
|
||||||
buildFeatureEntry(new arangodb::aql::AqlFunctionFeature(server), true); // required for IResearchAnalyzerFeature
|
buildFeatureEntry(new arangodb::aql::AqlFunctionFeature(server), true); // required for IResearchAnalyzerFeature
|
||||||
buildFeatureEntry(new arangodb::iresearch::IResearchAnalyzerFeature(server), true);
|
buildFeatureEntry(new arangodb::iresearch::IResearchAnalyzerFeature(server), true);
|
||||||
|
@ -6903,3 +6988,177 @@ TEST_F(IResearchViewTest, test_update_partial) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(IResearchViewTest, test_remove_referenced_analyzer) {
|
||||||
|
auto* databaseFeature = arangodb::application_features::ApplicationServer::getFeature<arangodb::DatabaseFeature>("Database");
|
||||||
|
ASSERT_NE(nullptr, databaseFeature);
|
||||||
|
|
||||||
|
TRI_vocbase_t* vocbase; // will be owned by DatabaseFeature
|
||||||
|
ASSERT_TRUE(databaseFeature->createDatabase(0, "testDatabase" TOSTRING(__LINE__), vocbase).ok());
|
||||||
|
ASSERT_NE(nullptr, vocbase);
|
||||||
|
|
||||||
|
// create _analyzers collection
|
||||||
|
{
|
||||||
|
auto createJson = arangodb::velocypack::Parser::fromJson(
|
||||||
|
"{ \"name\": \"" + arangodb::StaticStrings::AnalyzersCollection + "\", \"isSystem\":true }");
|
||||||
|
ASSERT_NE(nullptr, vocbase->createCollection(createJson->slice()));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* analyzers = arangodb::application_features::ApplicationServer::lookupFeature<
|
||||||
|
arangodb::iresearch::IResearchAnalyzerFeature>();
|
||||||
|
ASSERT_NE(nullptr, analyzers);
|
||||||
|
|
||||||
|
std::shared_ptr<arangodb::LogicalView> view;
|
||||||
|
std::shared_ptr<arangodb::LogicalCollection> collection;
|
||||||
|
|
||||||
|
// remove existing (used by link)
|
||||||
|
{
|
||||||
|
// add analyzer
|
||||||
|
{
|
||||||
|
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
|
||||||
|
ASSERT_TRUE(analyzers->emplace(result, vocbase->name() + "::test_analyzer3",
|
||||||
|
"TestAnalyzer", VPackParser::fromJson("\"abc\"")->slice()).ok());
|
||||||
|
ASSERT_NE(nullptr, analyzers->get(vocbase->name() + "::test_analyzer3"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// create collection
|
||||||
|
{
|
||||||
|
auto createJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testCollection1\" }");
|
||||||
|
collection = vocbase->createCollection(createJson->slice());
|
||||||
|
ASSERT_NE(nullptr, collection);
|
||||||
|
}
|
||||||
|
|
||||||
|
// create view
|
||||||
|
{
|
||||||
|
auto createJson = arangodb::velocypack::Parser::fromJson(
|
||||||
|
"{ \"name\": \"testView\", \"type\": \"arangosearch\" }");
|
||||||
|
view = vocbase->createView(createJson->slice());
|
||||||
|
ASSERT_NE(nullptr, view);
|
||||||
|
|
||||||
|
auto updateJson = arangodb::velocypack::Parser::fromJson(
|
||||||
|
"{ \"links\": { \"testCollection1\": { \"includeAllFields\": true, \"analyzers\":[\"test_analyzer3\"] }}}");
|
||||||
|
EXPECT_TRUE(view->properties(updateJson->slice(), true).ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_FALSE(analyzers->remove(vocbase->name() + "::test_analyzer3", false).ok()); // used by link
|
||||||
|
EXPECT_NE(nullptr, analyzers->get(vocbase->name() + "::test_analyzer3"));
|
||||||
|
EXPECT_TRUE(analyzers->remove(vocbase->name() + "::test_analyzer3", true).ok());
|
||||||
|
EXPECT_EQ(nullptr, analyzers->get(vocbase->name() + "::test_analyzer3"));
|
||||||
|
|
||||||
|
auto cleanup = arangodb::scopeGuard([vocbase, &view, &collection]() {
|
||||||
|
if (view) {
|
||||||
|
EXPECT_TRUE(vocbase->dropView(view->id(), false).ok());
|
||||||
|
view = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (collection) {
|
||||||
|
EXPECT_TRUE(vocbase->dropCollection(collection->id(), false, 1.0).ok());
|
||||||
|
collection = nullptr;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove existing (used by link)
|
||||||
|
{
|
||||||
|
// add analyzer
|
||||||
|
{
|
||||||
|
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
|
||||||
|
ASSERT_TRUE(analyzers->emplace(result, vocbase->name() + "::test_analyzer3",
|
||||||
|
"TestAnalyzer", VPackParser::fromJson("\"abc\"")->slice()).ok());
|
||||||
|
ASSERT_NE(nullptr, analyzers->get(vocbase->name() + "::test_analyzer3"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// create collection
|
||||||
|
{
|
||||||
|
auto createJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testCollection1\" }");
|
||||||
|
collection = vocbase->createCollection(createJson->slice());
|
||||||
|
ASSERT_NE(nullptr, collection);
|
||||||
|
}
|
||||||
|
|
||||||
|
// create view
|
||||||
|
{
|
||||||
|
auto createJson = arangodb::velocypack::Parser::fromJson(
|
||||||
|
"{ \"name\": \"testView\", \"type\": \"arangosearch\" }");
|
||||||
|
view = vocbase->createView(createJson->slice());
|
||||||
|
ASSERT_NE(nullptr, view);
|
||||||
|
|
||||||
|
auto updateJson = arangodb::velocypack::Parser::fromJson(
|
||||||
|
"{ \"analyzerDefinitions\" : { \
|
||||||
|
\"name\":\"test_analyzer3\", \"features\":[], \
|
||||||
|
\"type\":\"TestAnalyzer\", \"properties\": {\"args\":\"abc\"} \
|
||||||
|
}, \
|
||||||
|
\"links\": { \"testCollection1\": { \"includeAllFields\": true, \"analyzers\":[\"test_analyzer3\"] }} \
|
||||||
|
}");
|
||||||
|
EXPECT_TRUE(view->properties(updateJson->slice(), true).ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_FALSE(analyzers->remove(vocbase->name() + "::test_analyzer3", false).ok()); // used by link
|
||||||
|
EXPECT_NE(nullptr, analyzers->get(vocbase->name() + "::test_analyzer3"));
|
||||||
|
EXPECT_TRUE(analyzers->remove(vocbase->name() + "::test_analyzer3", true).ok());
|
||||||
|
EXPECT_EQ(nullptr, analyzers->get(vocbase->name() + "::test_analyzer3"));
|
||||||
|
|
||||||
|
auto cleanup = arangodb::scopeGuard([vocbase, &view, &collection]() {
|
||||||
|
if (view) {
|
||||||
|
EXPECT_TRUE(vocbase->dropView(view->id(), false).ok());
|
||||||
|
view = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (collection) {
|
||||||
|
EXPECT_TRUE(vocbase->dropCollection(collection->id(), false, 1.0).ok());
|
||||||
|
collection = nullptr;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove existing (properties don't match
|
||||||
|
{
|
||||||
|
// add analyzer
|
||||||
|
{
|
||||||
|
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
|
||||||
|
ASSERT_TRUE(analyzers->emplace(result, vocbase->name() + "::test_analyzer3",
|
||||||
|
"TestAnalyzer", VPackParser::fromJson("\"abcd\"")->slice()).ok());
|
||||||
|
ASSERT_NE(nullptr, analyzers->get(vocbase->name() + "::test_analyzer3"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// create collection
|
||||||
|
{
|
||||||
|
auto createJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testCollection1\" }");
|
||||||
|
collection = vocbase->createCollection(createJson->slice());
|
||||||
|
ASSERT_NE(nullptr, collection);
|
||||||
|
}
|
||||||
|
|
||||||
|
// create view
|
||||||
|
{
|
||||||
|
auto createJson = arangodb::velocypack::Parser::fromJson(
|
||||||
|
"{ \"name\": \"testView\", \"type\": \"arangosearch\" }");
|
||||||
|
view = vocbase->createView(createJson->slice());
|
||||||
|
ASSERT_NE(nullptr, view);
|
||||||
|
|
||||||
|
auto updateJson = arangodb::velocypack::Parser::fromJson(
|
||||||
|
"{ \"analyzerDefinitions\" : { \
|
||||||
|
\"name\":\"test_analyzer3\", \"features\":[], \
|
||||||
|
\"type\":\"TestAnalyzer\", \"properties\": \"abc\" \
|
||||||
|
}, \
|
||||||
|
\"links\": { \"testCollection1\": { \"includeAllFields\": true, \"analyzers\":[\"test_analyzer3\"] }} \
|
||||||
|
}");
|
||||||
|
EXPECT_TRUE(view->properties(updateJson->slice(), true).ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_FALSE(analyzers->remove(vocbase->name() + "::test_analyzer3", false).ok()); // used by link (we ignore analyzerDefinitions on single-server)
|
||||||
|
EXPECT_NE(nullptr, analyzers->get(vocbase->name() + "::test_analyzer3"));
|
||||||
|
EXPECT_TRUE(analyzers->remove(vocbase->name() + "::test_analyzer3", true).ok());
|
||||||
|
EXPECT_EQ(nullptr, analyzers->get(vocbase->name() + "::test_analyzer3"));
|
||||||
|
|
||||||
|
auto cleanup = arangodb::scopeGuard([vocbase, &view, &collection]() {
|
||||||
|
if (view) {
|
||||||
|
EXPECT_TRUE(vocbase->dropView(view->id(), false).ok());
|
||||||
|
view = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (collection) {
|
||||||
|
EXPECT_TRUE(vocbase->dropCollection(collection->id(), false, 1.0).ok());
|
||||||
|
collection = nullptr;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -469,7 +469,7 @@ std::string mangleString(std::string name, std::string suffix) {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string mangleStringIdentity(std::string name) {
|
std::string mangleStringIdentity(std::string name) {
|
||||||
arangodb::iresearch::kludge::mangleStringField(name, arangodb::iresearch::IResearchLinkMeta::Analyzer() // args
|
arangodb::iresearch::kludge::mangleStringField(name, arangodb::iresearch::FieldMeta::Analyzer() // args
|
||||||
);
|
);
|
||||||
|
|
||||||
return name;
|
return name;
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
|
|
||||||
#include "../IResearch/common.h"
|
#include "../IResearch/common.h"
|
||||||
#include "../Mocks/StorageEngineMock.h"
|
#include "../Mocks/StorageEngineMock.h"
|
||||||
|
#include "Aql/OptimizerRulesFeature.h"
|
||||||
#include "Aql/QueryRegistry.h"
|
#include "Aql/QueryRegistry.h"
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
@ -38,9 +39,11 @@
|
||||||
#include "IResearch/IResearchAnalyzerFeature.h"
|
#include "IResearch/IResearchAnalyzerFeature.h"
|
||||||
#include "IResearch/IResearchCommon.h"
|
#include "IResearch/IResearchCommon.h"
|
||||||
#include "IResearch/VelocyPackHelper.h"
|
#include "IResearch/VelocyPackHelper.h"
|
||||||
|
#include "RestServer/AqlFeature.h"
|
||||||
#include "RestServer/DatabaseFeature.h"
|
#include "RestServer/DatabaseFeature.h"
|
||||||
#include "RestServer/QueryRegistryFeature.h"
|
#include "RestServer/QueryRegistryFeature.h"
|
||||||
#include "RestServer/SystemDatabaseFeature.h"
|
#include "RestServer/SystemDatabaseFeature.h"
|
||||||
|
#include "RestServer/TraverserEngineRegistryFeature.h"
|
||||||
#include "StorageEngine/EngineSelectorFeature.h"
|
#include "StorageEngine/EngineSelectorFeature.h"
|
||||||
#include "VocBase/Methods/Collections.h"
|
#include "VocBase/Methods/Collections.h"
|
||||||
#include "Utils/ExecContext.h"
|
#include "Utils/ExecContext.h"
|
||||||
|
@ -203,12 +206,30 @@ TEST_F(V8AnalyzersTest, test_accessors) {
|
||||||
arangodb::application_features::ApplicationServer server(nullptr, nullptr);
|
arangodb::application_features::ApplicationServer server(nullptr, nullptr);
|
||||||
arangodb::iresearch::IResearchAnalyzerFeature* analyzers;
|
arangodb::iresearch::IResearchAnalyzerFeature* analyzers;
|
||||||
arangodb::DatabaseFeature* dbFeature;
|
arangodb::DatabaseFeature* dbFeature;
|
||||||
server.addFeature(new arangodb::QueryRegistryFeature(server)); // required for constructing TRI_vocbase_t
|
arangodb::QueryRegistryFeature* queryRegistry;
|
||||||
|
arangodb::AqlFeature* aqlFeature;
|
||||||
|
arangodb::aql::OptimizerRulesFeature* optimizer;
|
||||||
|
arangodb::TraverserEngineRegistryFeature* traverser;
|
||||||
|
server.addFeature(queryRegistry = new arangodb::QueryRegistryFeature(server)); // required for constructing TRI_vocbase_t
|
||||||
|
server.addFeature(optimizer = new arangodb::aql::OptimizerRulesFeature(server));
|
||||||
|
server.addFeature(traverser = new arangodb::TraverserEngineRegistryFeature(server) ); // must be before AqlFeature
|
||||||
|
server.addFeature(aqlFeature = new arangodb::AqlFeature(server));
|
||||||
server.addFeature(new arangodb::V8DealerFeature(server)); // required for DatabaseFeature::createDatabase(...)
|
server.addFeature(new arangodb::V8DealerFeature(server)); // required for DatabaseFeature::createDatabase(...)
|
||||||
server.addFeature(dbFeature = new arangodb::DatabaseFeature(server)); // required for IResearchAnalyzerFeature::emplace(...)
|
server.addFeature(dbFeature = new arangodb::DatabaseFeature(server)); // required for IResearchAnalyzerFeature::emplace(...)
|
||||||
server.addFeature(analyzers = new arangodb::iresearch::IResearchAnalyzerFeature(server)); // required for running upgrade task
|
server.addFeature(analyzers = new arangodb::iresearch::IResearchAnalyzerFeature(server)); // required for running upgrade task
|
||||||
|
|
||||||
auto cleanup = arangodb::scopeGuard([dbFeature](){ dbFeature->unprepare(); });
|
traverser->prepare();
|
||||||
|
aqlFeature->start();
|
||||||
|
optimizer->prepare();
|
||||||
|
queryRegistry->prepare();
|
||||||
|
|
||||||
|
auto cleanup = arangodb::scopeGuard([&](){
|
||||||
|
dbFeature->unprepare();
|
||||||
|
optimizer->unprepare();
|
||||||
|
aqlFeature->stop();
|
||||||
|
queryRegistry->unprepare();
|
||||||
|
traverser->unprepare();
|
||||||
|
});
|
||||||
|
|
||||||
// create system vocbase
|
// create system vocbase
|
||||||
{
|
{
|
||||||
|
@ -225,10 +246,8 @@ TEST_F(V8AnalyzersTest, test_accessors) {
|
||||||
}
|
}
|
||||||
|
|
||||||
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
|
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
|
||||||
ASSERT_TRUE((analyzers
|
ASSERT_TRUE(analyzers->emplace(result, arangodb::StaticStrings::SystemDatabase + "::testAnalyzer1",
|
||||||
->emplace(result, arangodb::StaticStrings::SystemDatabase + "::testAnalyzer1",
|
"identity", VPackSlice::noneSlice()).ok());
|
||||||
"identity", VPackSlice::noneSlice())
|
|
||||||
.ok()));
|
|
||||||
auto analyzer = analyzers->get(arangodb::StaticStrings::SystemDatabase +
|
auto analyzer = analyzers->get(arangodb::StaticStrings::SystemDatabase +
|
||||||
"::testAnalyzer1");
|
"::testAnalyzer1");
|
||||||
ASSERT_TRUE((false == !analyzer));
|
ASSERT_TRUE((false == !analyzer));
|
||||||
|
@ -242,8 +261,7 @@ TEST_F(V8AnalyzersTest, test_accessors) {
|
||||||
arangodb::ExecContextScope execContextScope(&execContext);
|
arangodb::ExecContextScope execContextScope(&execContext);
|
||||||
auto* authFeature = arangodb::AuthenticationFeature::instance();
|
auto* authFeature = arangodb::AuthenticationFeature::instance();
|
||||||
auto* userManager = authFeature->userManager();
|
auto* userManager = authFeature->userManager();
|
||||||
arangodb::aql::QueryRegistry queryRegistry(0); // required for UserManager::loadFromDB()
|
userManager->setQueryRegistry(arangodb::QueryRegistryFeature::registry());
|
||||||
userManager->setQueryRegistry(&queryRegistry);
|
|
||||||
auto resetUserManager = std::shared_ptr<arangodb::auth::UserManager>(
|
auto resetUserManager = std::shared_ptr<arangodb::auth::UserManager>(
|
||||||
userManager,
|
userManager,
|
||||||
[](arangodb::auth::UserManager* ptr) -> void { ptr->removeAllUsers(); });
|
[](arangodb::auth::UserManager* ptr) -> void { ptr->removeAllUsers(); });
|
||||||
|
@ -699,13 +717,31 @@ TEST_F(V8AnalyzersTest, test_create) {
|
||||||
arangodb::iresearch::IResearchAnalyzerFeature* analyzers;
|
arangodb::iresearch::IResearchAnalyzerFeature* analyzers;
|
||||||
arangodb::DatabaseFeature* dbFeature;
|
arangodb::DatabaseFeature* dbFeature;
|
||||||
arangodb::SystemDatabaseFeature* sysDatabase;
|
arangodb::SystemDatabaseFeature* sysDatabase;
|
||||||
server.addFeature(new arangodb::QueryRegistryFeature(server)); // required for constructing TRI_vocbase_t
|
arangodb::QueryRegistryFeature* queryRegistry;
|
||||||
|
arangodb::AqlFeature* aqlFeature;
|
||||||
|
arangodb::aql::OptimizerRulesFeature* optimizer;
|
||||||
|
arangodb::TraverserEngineRegistryFeature* traverser;
|
||||||
|
server.addFeature(queryRegistry = new arangodb::QueryRegistryFeature(server)); // required for constructing TRI_vocbase_t
|
||||||
|
server.addFeature(optimizer = new arangodb::aql::OptimizerRulesFeature(server));
|
||||||
|
server.addFeature(traverser = new arangodb::TraverserEngineRegistryFeature(server) ); // must be before AqlFeature
|
||||||
|
server.addFeature(aqlFeature = new arangodb::AqlFeature(server));
|
||||||
server.addFeature(new arangodb::V8DealerFeature(server)); // required for DatabaseFeature::createDatabase(...)
|
server.addFeature(new arangodb::V8DealerFeature(server)); // required for DatabaseFeature::createDatabase(...)
|
||||||
server.addFeature(dbFeature = new arangodb::DatabaseFeature(server)); // required for IResearchAnalyzerFeature::emplace(...)
|
server.addFeature(dbFeature = new arangodb::DatabaseFeature(server)); // required for IResearchAnalyzerFeature::emplace(...)
|
||||||
server.addFeature(analyzers = new arangodb::iresearch::IResearchAnalyzerFeature(server)); // required for running upgrade task
|
server.addFeature(analyzers = new arangodb::iresearch::IResearchAnalyzerFeature(server)); // required for running upgrade task
|
||||||
server.addFeature(sysDatabase = new arangodb::SystemDatabaseFeature(server)); // required for IResearchAnalyzerFeature::start()
|
server.addFeature(sysDatabase = new arangodb::SystemDatabaseFeature(server)); // required for IResearchAnalyzerFeature::start()
|
||||||
|
|
||||||
auto cleanup = arangodb::scopeGuard([dbFeature](){ dbFeature->unprepare(); });
|
traverser->prepare();
|
||||||
|
aqlFeature->start();
|
||||||
|
optimizer->prepare();
|
||||||
|
queryRegistry->prepare();
|
||||||
|
|
||||||
|
auto cleanup = arangodb::scopeGuard([&](){
|
||||||
|
dbFeature->unprepare();
|
||||||
|
optimizer->unprepare();
|
||||||
|
aqlFeature->stop();
|
||||||
|
queryRegistry->unprepare();
|
||||||
|
traverser->unprepare();
|
||||||
|
});
|
||||||
|
|
||||||
// create system vocbase
|
// create system vocbase
|
||||||
{
|
{
|
||||||
|
@ -746,8 +782,7 @@ TEST_F(V8AnalyzersTest, test_create) {
|
||||||
arangodb::ExecContextScope execContextScope(&execContext);
|
arangodb::ExecContextScope execContextScope(&execContext);
|
||||||
auto* authFeature = arangodb::AuthenticationFeature::instance();
|
auto* authFeature = arangodb::AuthenticationFeature::instance();
|
||||||
auto* userManager = authFeature->userManager();
|
auto* userManager = authFeature->userManager();
|
||||||
arangodb::aql::QueryRegistry queryRegistry(0); // required for UserManager::loadFromDB()
|
userManager->setQueryRegistry(arangodb::QueryRegistryFeature::registry());
|
||||||
userManager->setQueryRegistry(&queryRegistry);
|
|
||||||
auto resetUserManager = std::shared_ptr<arangodb::auth::UserManager>(
|
auto resetUserManager = std::shared_ptr<arangodb::auth::UserManager>(
|
||||||
userManager,
|
userManager,
|
||||||
[](arangodb::auth::UserManager* ptr) -> void { ptr->removeAllUsers(); });
|
[](arangodb::auth::UserManager* ptr) -> void { ptr->removeAllUsers(); });
|
||||||
|
@ -1404,13 +1439,32 @@ TEST_F(V8AnalyzersTest, test_get) {
|
||||||
arangodb::application_features::ApplicationServer server(nullptr, nullptr);
|
arangodb::application_features::ApplicationServer server(nullptr, nullptr);
|
||||||
arangodb::iresearch::IResearchAnalyzerFeature* analyzers;
|
arangodb::iresearch::IResearchAnalyzerFeature* analyzers;
|
||||||
arangodb::DatabaseFeature* dbFeature;
|
arangodb::DatabaseFeature* dbFeature;
|
||||||
|
arangodb::QueryRegistryFeature* queryRegistry;
|
||||||
|
arangodb::AqlFeature* aqlFeature;
|
||||||
|
arangodb::aql::OptimizerRulesFeature* optimizer;
|
||||||
|
arangodb::TraverserEngineRegistryFeature* traverser;
|
||||||
|
server.addFeature(queryRegistry = new arangodb::QueryRegistryFeature(server)); // required for constructing TRI_vocbase_t
|
||||||
|
server.addFeature(optimizer = new arangodb::aql::OptimizerRulesFeature(server));
|
||||||
|
server.addFeature(traverser = new arangodb::TraverserEngineRegistryFeature(server) ); // must be before AqlFeature
|
||||||
|
server.addFeature(aqlFeature = new arangodb::AqlFeature(server));
|
||||||
server.addFeature(new arangodb::QueryRegistryFeature(server)); // required for constructing TRI_vocbase_t
|
server.addFeature(new arangodb::QueryRegistryFeature(server)); // required for constructing TRI_vocbase_t
|
||||||
server.addFeature(new arangodb::V8DealerFeature(server)); // required for DatabaseFeature::createDatabase(...)
|
server.addFeature(new arangodb::V8DealerFeature(server)); // required for DatabaseFeature::createDatabase(...)
|
||||||
server.addFeature(dbFeature = new arangodb::DatabaseFeature(server)); // required for IResearchAnalyzerFeature::emplace(...)
|
server.addFeature(dbFeature = new arangodb::DatabaseFeature(server)); // required for IResearchAnalyzerFeature::emplace(...)
|
||||||
server.addFeature(analyzers = new arangodb::iresearch::IResearchAnalyzerFeature(server)); // required for running upgrade task
|
server.addFeature(analyzers = new arangodb::iresearch::IResearchAnalyzerFeature(server)); // required for running upgrade task
|
||||||
analyzers->prepare(); // add static analyzers
|
analyzers->prepare(); // add static analyzers
|
||||||
|
|
||||||
auto cleanup = arangodb::scopeGuard([dbFeature](){ dbFeature->unprepare(); });
|
traverser->prepare();
|
||||||
|
aqlFeature->start();
|
||||||
|
optimizer->prepare();
|
||||||
|
queryRegistry->prepare();
|
||||||
|
|
||||||
|
auto cleanup = arangodb::scopeGuard([&](){
|
||||||
|
dbFeature->unprepare();
|
||||||
|
optimizer->unprepare();
|
||||||
|
aqlFeature->stop();
|
||||||
|
queryRegistry->unprepare();
|
||||||
|
traverser->unprepare();
|
||||||
|
});
|
||||||
|
|
||||||
// create system vocbase
|
// create system vocbase
|
||||||
{
|
{
|
||||||
|
@ -1449,8 +1503,7 @@ TEST_F(V8AnalyzersTest, test_get) {
|
||||||
arangodb::ExecContextScope execContextScope(&execContext);
|
arangodb::ExecContextScope execContextScope(&execContext);
|
||||||
auto* authFeature = arangodb::AuthenticationFeature::instance();
|
auto* authFeature = arangodb::AuthenticationFeature::instance();
|
||||||
auto* userManager = authFeature->userManager();
|
auto* userManager = authFeature->userManager();
|
||||||
arangodb::aql::QueryRegistry queryRegistry(0); // required for UserManager::loadFromDB()
|
userManager->setQueryRegistry(arangodb::QueryRegistryFeature::registry());
|
||||||
userManager->setQueryRegistry(&queryRegistry);
|
|
||||||
auto resetUserManager = std::shared_ptr<arangodb::auth::UserManager>(
|
auto resetUserManager = std::shared_ptr<arangodb::auth::UserManager>(
|
||||||
userManager,
|
userManager,
|
||||||
[](arangodb::auth::UserManager* ptr) -> void { ptr->removeAllUsers(); });
|
[](arangodb::auth::UserManager* ptr) -> void { ptr->removeAllUsers(); });
|
||||||
|
@ -2068,12 +2121,32 @@ TEST_F(V8AnalyzersTest, test_list) {
|
||||||
arangodb::iresearch::IResearchAnalyzerFeature* analyzers;
|
arangodb::iresearch::IResearchAnalyzerFeature* analyzers;
|
||||||
arangodb::DatabaseFeature* dbFeature;
|
arangodb::DatabaseFeature* dbFeature;
|
||||||
arangodb::SystemDatabaseFeature* sysDatabase;
|
arangodb::SystemDatabaseFeature* sysDatabase;
|
||||||
server.addFeature(new arangodb::QueryRegistryFeature(server)); // required for constructing TRI_vocbase_t
|
arangodb::QueryRegistryFeature* queryRegistry;
|
||||||
|
arangodb::AqlFeature* aqlFeature;
|
||||||
|
arangodb::aql::OptimizerRulesFeature* optimizer;
|
||||||
|
arangodb::TraverserEngineRegistryFeature* traverser;
|
||||||
|
server.addFeature(queryRegistry = new arangodb::QueryRegistryFeature(server)); // required for constructing TRI_vocbase_t
|
||||||
|
server.addFeature(optimizer = new arangodb::aql::OptimizerRulesFeature(server));
|
||||||
|
server.addFeature(traverser = new arangodb::TraverserEngineRegistryFeature(server) ); // must be before AqlFeature
|
||||||
|
server.addFeature(aqlFeature = new arangodb::AqlFeature(server));
|
||||||
server.addFeature(new arangodb::V8DealerFeature(server)); // required for DatabaseFeature::createDatabase(...)
|
server.addFeature(new arangodb::V8DealerFeature(server)); // required for DatabaseFeature::createDatabase(...)
|
||||||
server.addFeature(dbFeature = new arangodb::DatabaseFeature(server)); // required for IResearchAnalyzerFeature::emplace(...)
|
server.addFeature(dbFeature = new arangodb::DatabaseFeature(server)); // required for IResearchAnalyzerFeature::emplace(...)
|
||||||
server.addFeature(sysDatabase = new arangodb::SystemDatabaseFeature(server)); // required for IResearchAnalyzerFeature::start()
|
server.addFeature(sysDatabase = new arangodb::SystemDatabaseFeature(server)); // required for IResearchAnalyzerFeature::start()
|
||||||
server.addFeature(analyzers = new arangodb::iresearch::IResearchAnalyzerFeature(server)); // required for running upgrade task
|
server.addFeature(analyzers = new arangodb::iresearch::IResearchAnalyzerFeature(server)); // required for running upgrade task
|
||||||
|
|
||||||
|
traverser->prepare();
|
||||||
|
aqlFeature->start();
|
||||||
|
optimizer->prepare();
|
||||||
|
queryRegistry->prepare();
|
||||||
|
|
||||||
|
auto cleanup = arangodb::scopeGuard([&](){
|
||||||
|
dbFeature->unprepare();
|
||||||
|
optimizer->unprepare();
|
||||||
|
aqlFeature->stop();
|
||||||
|
queryRegistry->unprepare();
|
||||||
|
traverser->unprepare();
|
||||||
|
});
|
||||||
|
|
||||||
// create system vocbase
|
// create system vocbase
|
||||||
{
|
{
|
||||||
auto const databases = arangodb::velocypack::Parser::fromJson(
|
auto const databases = arangodb::velocypack::Parser::fromJson(
|
||||||
|
@ -2094,8 +2167,6 @@ TEST_F(V8AnalyzersTest, test_list) {
|
||||||
arangodb::tests::AnalyzerCollectionName, false);
|
arangodb::tests::AnalyzerCollectionName, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto cleanup = arangodb::scopeGuard([dbFeature](){ dbFeature->unprepare(); });
|
|
||||||
|
|
||||||
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
|
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
|
||||||
ASSERT_TRUE((analyzers
|
ASSERT_TRUE((analyzers
|
||||||
->emplace(result, arangodb::StaticStrings::SystemDatabase + "::testAnalyzer1",
|
->emplace(result, arangodb::StaticStrings::SystemDatabase + "::testAnalyzer1",
|
||||||
|
@ -2115,8 +2186,7 @@ TEST_F(V8AnalyzersTest, test_list) {
|
||||||
arangodb::ExecContextScope execContextScope(&execContext);
|
arangodb::ExecContextScope execContextScope(&execContext);
|
||||||
auto* authFeature = arangodb::AuthenticationFeature::instance();
|
auto* authFeature = arangodb::AuthenticationFeature::instance();
|
||||||
auto* userManager = authFeature->userManager();
|
auto* userManager = authFeature->userManager();
|
||||||
arangodb::aql::QueryRegistry queryRegistry(0); // required for UserManager::loadFromDB()
|
userManager->setQueryRegistry(arangodb::QueryRegistryFeature::registry());
|
||||||
userManager->setQueryRegistry(&queryRegistry);
|
|
||||||
auto resetUserManager = std::shared_ptr<arangodb::auth::UserManager>(
|
auto resetUserManager = std::shared_ptr<arangodb::auth::UserManager>(
|
||||||
userManager,
|
userManager,
|
||||||
[](arangodb::auth::UserManager* ptr) -> void { ptr->removeAllUsers(); });
|
[](arangodb::auth::UserManager* ptr) -> void { ptr->removeAllUsers(); });
|
||||||
|
@ -2536,13 +2606,31 @@ TEST_F(V8AnalyzersTest, test_remove) {
|
||||||
arangodb::iresearch::IResearchAnalyzerFeature* analyzers;
|
arangodb::iresearch::IResearchAnalyzerFeature* analyzers;
|
||||||
arangodb::DatabaseFeature* dbFeature;
|
arangodb::DatabaseFeature* dbFeature;
|
||||||
arangodb::SystemDatabaseFeature* sysDbFeature(new arangodb::SystemDatabaseFeature(server));
|
arangodb::SystemDatabaseFeature* sysDbFeature(new arangodb::SystemDatabaseFeature(server));
|
||||||
server.addFeature(new arangodb::QueryRegistryFeature(server)); // required for constructing TRI_vocbase_t
|
arangodb::QueryRegistryFeature* queryRegistry;
|
||||||
|
arangodb::AqlFeature* aqlFeature;
|
||||||
|
arangodb::aql::OptimizerRulesFeature* optimizer;
|
||||||
|
arangodb::TraverserEngineRegistryFeature* traverser;
|
||||||
|
server.addFeature(queryRegistry = new arangodb::QueryRegistryFeature(server)); // required for constructing TRI_vocbase_t
|
||||||
|
server.addFeature(optimizer = new arangodb::aql::OptimizerRulesFeature(server));
|
||||||
|
server.addFeature(traverser = new arangodb::TraverserEngineRegistryFeature(server) ); // must be before AqlFeature
|
||||||
|
server.addFeature(aqlFeature = new arangodb::AqlFeature(server));
|
||||||
server.addFeature(new arangodb::V8DealerFeature(server)); // required for DatabaseFeature::createDatabase(...)
|
server.addFeature(new arangodb::V8DealerFeature(server)); // required for DatabaseFeature::createDatabase(...)
|
||||||
server.addFeature(dbFeature = new arangodb::DatabaseFeature(server)); // required for IResearchAnalyzerFeature::emplace(...)
|
server.addFeature(dbFeature = new arangodb::DatabaseFeature(server)); // required for IResearchAnalyzerFeature::emplace(...)
|
||||||
server.addFeature(analyzers = new arangodb::iresearch::IResearchAnalyzerFeature(server)); // required for running upgrade task
|
server.addFeature(analyzers = new arangodb::iresearch::IResearchAnalyzerFeature(server)); // required for running upgrade task
|
||||||
server.addFeature(sysDbFeature);
|
server.addFeature(sysDbFeature);
|
||||||
|
|
||||||
auto cleanup = arangodb::scopeGuard([dbFeature](){ dbFeature->unprepare(); });
|
traverser->prepare();
|
||||||
|
aqlFeature->start();
|
||||||
|
optimizer->prepare();
|
||||||
|
queryRegistry->prepare();
|
||||||
|
|
||||||
|
auto cleanup = arangodb::scopeGuard([&](){
|
||||||
|
dbFeature->unprepare();
|
||||||
|
optimizer->unprepare();
|
||||||
|
aqlFeature->stop();
|
||||||
|
queryRegistry->unprepare();
|
||||||
|
traverser->unprepare();
|
||||||
|
});
|
||||||
|
|
||||||
// create system vocbase
|
// create system vocbase
|
||||||
{
|
{
|
||||||
|
@ -2601,8 +2689,7 @@ TEST_F(V8AnalyzersTest, test_remove) {
|
||||||
arangodb::ExecContextScope execContextScope(&execContext);
|
arangodb::ExecContextScope execContextScope(&execContext);
|
||||||
auto* authFeature = arangodb::AuthenticationFeature::instance();
|
auto* authFeature = arangodb::AuthenticationFeature::instance();
|
||||||
auto* userManager = authFeature->userManager();
|
auto* userManager = authFeature->userManager();
|
||||||
arangodb::aql::QueryRegistry queryRegistry(0); // required for UserManager::loadFromDB()
|
userManager->setQueryRegistry(arangodb::QueryRegistryFeature::registry());
|
||||||
userManager->setQueryRegistry(&queryRegistry);
|
|
||||||
auto resetUserManager = std::shared_ptr<arangodb::auth::UserManager>(
|
auto resetUserManager = std::shared_ptr<arangodb::auth::UserManager>(
|
||||||
userManager,
|
userManager,
|
||||||
[](arangodb::auth::UserManager* ptr) -> void { ptr->removeAllUsers(); });
|
[](arangodb::auth::UserManager* ptr) -> void { ptr->removeAllUsers(); });
|
||||||
|
|
|
@ -316,7 +316,7 @@ function IResearchFeatureDDLTestSuite () {
|
||||||
|
|
||||||
var meta = { links: {
|
var meta = { links: {
|
||||||
"TestCollection0": { },
|
"TestCollection0": { },
|
||||||
"TestCollection1": { analyzers: [ "text_en"], includeAllFields: true, trackListPositions: true, storeValues: "full" },
|
"TestCollection1": { analyzers: [ "text_en"], includeAllFields: true, trackListPositions: true, storeValues: "value" },
|
||||||
"TestCollection2": { fields: {
|
"TestCollection2": { fields: {
|
||||||
"b": { fields: { "b1": {} } },
|
"b": { fields: { "b1": {} } },
|
||||||
"c": { includeAllFields: true },
|
"c": { includeAllFields: true },
|
||||||
|
@ -352,7 +352,7 @@ function IResearchFeatureDDLTestSuite () {
|
||||||
assertTrue(Boolean === properties.links.TestCollection1.trackListPositions.constructor);
|
assertTrue(Boolean === properties.links.TestCollection1.trackListPositions.constructor);
|
||||||
assertEqual(true, properties.links.TestCollection1.trackListPositions);
|
assertEqual(true, properties.links.TestCollection1.trackListPositions);
|
||||||
assertTrue(String === properties.links.TestCollection1.storeValues.constructor);
|
assertTrue(String === properties.links.TestCollection1.storeValues.constructor);
|
||||||
assertEqual("full", properties.links.TestCollection1.storeValues);
|
assertEqual("value", properties.links.TestCollection1.storeValues);
|
||||||
assertTrue(Array === properties.links.TestCollection1.analyzers.constructor);
|
assertTrue(Array === properties.links.TestCollection1.analyzers.constructor);
|
||||||
assertEqual(1, properties.links.TestCollection1.analyzers.length);
|
assertEqual(1, properties.links.TestCollection1.analyzers.length);
|
||||||
assertTrue(String === properties.links.TestCollection1.analyzers[0].constructor);
|
assertTrue(String === properties.links.TestCollection1.analyzers[0].constructor);
|
||||||
|
@ -405,7 +405,7 @@ function IResearchFeatureDDLTestSuite () {
|
||||||
assertTrue(Boolean === properties.links.TestCollection1.trackListPositions.constructor);
|
assertTrue(Boolean === properties.links.TestCollection1.trackListPositions.constructor);
|
||||||
assertEqual(true, properties.links.TestCollection1.trackListPositions);
|
assertEqual(true, properties.links.TestCollection1.trackListPositions);
|
||||||
assertTrue(String === properties.links.TestCollection1.storeValues.constructor);
|
assertTrue(String === properties.links.TestCollection1.storeValues.constructor);
|
||||||
assertEqual("full", properties.links.TestCollection1.storeValues);
|
assertEqual("value", properties.links.TestCollection1.storeValues);
|
||||||
assertTrue(Array === properties.links.TestCollection1.analyzers.constructor);
|
assertTrue(Array === properties.links.TestCollection1.analyzers.constructor);
|
||||||
assertEqual(1, properties.links.TestCollection1.analyzers.length);
|
assertEqual(1, properties.links.TestCollection1.analyzers.length);
|
||||||
assertTrue(String === properties.links.TestCollection1.analyzers[0].constructor);
|
assertTrue(String === properties.links.TestCollection1.analyzers[0].constructor);
|
||||||
|
@ -1114,7 +1114,47 @@ function IResearchFeatureDDLTestSuite () {
|
||||||
assertNull(analyzers.analyzer(analyzerName));
|
assertNull(analyzers.analyzer(analyzerName));
|
||||||
// this should be no name conflict
|
// this should be no name conflict
|
||||||
analyzers.save(analyzerName, "text", {"stopwords" : [], "locale":"en"});
|
analyzers.save(analyzerName, "text", {"stopwords" : [], "locale":"en"});
|
||||||
|
|
||||||
|
db._useDatabase("_system");
|
||||||
|
db._dropDatabase(dbName);
|
||||||
|
},
|
||||||
|
|
||||||
|
testIndexAnalyzerCollection : function() {
|
||||||
|
const dbName = "TestNameDroppedDB";
|
||||||
|
const analyzerName = "TestAnalyzer";
|
||||||
|
db._useDatabase("_system");
|
||||||
|
assertNotEqual(null, db._collection("_analyzers"));
|
||||||
|
try { db._dropDatabase(dbName); } catch (e) {}
|
||||||
|
try { analyzers.remove(analyzerName); } catch (e) {}
|
||||||
|
assertEqual(0, db._analyzers.count());
|
||||||
|
db._createDatabase(dbName);
|
||||||
|
db._useDatabase(dbName);
|
||||||
|
analyzers.save(analyzerName, "identity");
|
||||||
|
// recreating database
|
||||||
|
db._useDatabase("_system");
|
||||||
|
db._dropDatabase(dbName);
|
||||||
|
db._createDatabase(dbName);
|
||||||
|
db._useDatabase(dbName);
|
||||||
|
|
||||||
|
assertNull(analyzers.analyzer(analyzerName));
|
||||||
|
// this should be no name conflict
|
||||||
|
analyzers.save(analyzerName, "text", {"stopwords" : [], "locale":"en"});
|
||||||
|
assertEqual(1, db._analyzers.count());
|
||||||
|
|
||||||
|
var view = db._createView("analyzersView", "arangosearch", {
|
||||||
|
links: {
|
||||||
|
_analyzers : {
|
||||||
|
includeAllFields:true,
|
||||||
|
analyzers: [ analyzerName ]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var res = db._query("FOR d IN analyzersView OPTIONS {waitForSync:true} RETURN d").toArray();
|
||||||
|
assertEqual(1, db._analyzers.count());
|
||||||
|
assertEqual(1, res.length);
|
||||||
|
assertEqual(db._analyzers.toArray()[0], res[0]);
|
||||||
|
|
||||||
db._useDatabase("_system");
|
db._useDatabase("_system");
|
||||||
db._dropDatabase(dbName);
|
db._dropDatabase(dbName);
|
||||||
}
|
}
|
||||||
|
|
|
@ -316,7 +316,7 @@ function IResearchFeatureDDLTestSuite () {
|
||||||
|
|
||||||
var meta = { links: {
|
var meta = { links: {
|
||||||
"TestCollection0": { },
|
"TestCollection0": { },
|
||||||
"TestCollection1": { analyzers: [ "text_en"], includeAllFields: true, trackListPositions: true, storeValues: "full" },
|
"TestCollection1": { analyzers: [ "text_en"], includeAllFields: true, trackListPositions: true, storeValues: "value" },
|
||||||
"TestCollection2": { fields: {
|
"TestCollection2": { fields: {
|
||||||
"b": { fields: { "b1": {} } },
|
"b": { fields: { "b1": {} } },
|
||||||
"c": { includeAllFields: true },
|
"c": { includeAllFields: true },
|
||||||
|
@ -352,7 +352,7 @@ function IResearchFeatureDDLTestSuite () {
|
||||||
assertTrue(Boolean === properties.links.TestCollection1.trackListPositions.constructor);
|
assertTrue(Boolean === properties.links.TestCollection1.trackListPositions.constructor);
|
||||||
assertEqual(true, properties.links.TestCollection1.trackListPositions);
|
assertEqual(true, properties.links.TestCollection1.trackListPositions);
|
||||||
assertTrue(String === properties.links.TestCollection1.storeValues.constructor);
|
assertTrue(String === properties.links.TestCollection1.storeValues.constructor);
|
||||||
assertEqual("full", properties.links.TestCollection1.storeValues);
|
assertEqual("value", properties.links.TestCollection1.storeValues);
|
||||||
assertTrue(Array === properties.links.TestCollection1.analyzers.constructor);
|
assertTrue(Array === properties.links.TestCollection1.analyzers.constructor);
|
||||||
assertEqual(1, properties.links.TestCollection1.analyzers.length);
|
assertEqual(1, properties.links.TestCollection1.analyzers.length);
|
||||||
assertTrue(String === properties.links.TestCollection1.analyzers[0].constructor);
|
assertTrue(String === properties.links.TestCollection1.analyzers[0].constructor);
|
||||||
|
@ -405,7 +405,7 @@ function IResearchFeatureDDLTestSuite () {
|
||||||
assertTrue(Boolean === properties.links.TestCollection1.trackListPositions.constructor);
|
assertTrue(Boolean === properties.links.TestCollection1.trackListPositions.constructor);
|
||||||
assertEqual(true, properties.links.TestCollection1.trackListPositions);
|
assertEqual(true, properties.links.TestCollection1.trackListPositions);
|
||||||
assertTrue(String === properties.links.TestCollection1.storeValues.constructor);
|
assertTrue(String === properties.links.TestCollection1.storeValues.constructor);
|
||||||
assertEqual("full", properties.links.TestCollection1.storeValues);
|
assertEqual("value", properties.links.TestCollection1.storeValues);
|
||||||
assertTrue(Array === properties.links.TestCollection1.analyzers.constructor);
|
assertTrue(Array === properties.links.TestCollection1.analyzers.constructor);
|
||||||
assertEqual(1, properties.links.TestCollection1.analyzers.length);
|
assertEqual(1, properties.links.TestCollection1.analyzers.length);
|
||||||
assertTrue(String === properties.links.TestCollection1.analyzers[0].constructor);
|
assertTrue(String === properties.links.TestCollection1.analyzers[0].constructor);
|
||||||
|
@ -1114,7 +1114,50 @@ function IResearchFeatureDDLTestSuite () {
|
||||||
assertNull(analyzers.analyzer(analyzerName));
|
assertNull(analyzers.analyzer(analyzerName));
|
||||||
// this should be no name conflict
|
// this should be no name conflict
|
||||||
analyzers.save(analyzerName, "text", {"stopwords" : [], "locale":"en"});
|
analyzers.save(analyzerName, "text", {"stopwords" : [], "locale":"en"});
|
||||||
|
|
||||||
|
db._useDatabase("_system");
|
||||||
|
db._dropDatabase(dbName);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief test link on analyzers collection
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
testIndexAnalyzerCollection : function() {
|
||||||
|
const dbName = "TestNameDroppedDB";
|
||||||
|
const analyzerName = "TestAnalyzer";
|
||||||
|
db._useDatabase("_system");
|
||||||
|
assertNotEqual(null, db._collection("_analyzers"));
|
||||||
|
try { db._dropDatabase(dbName); } catch (e) {}
|
||||||
|
try { analyzers.remove(analyzerName); } catch (e) {}
|
||||||
|
assertEqual(0, db._analyzers.count());
|
||||||
|
db._createDatabase(dbName);
|
||||||
|
db._useDatabase(dbName);
|
||||||
|
analyzers.save(analyzerName, "identity");
|
||||||
|
// recreating database
|
||||||
|
db._useDatabase("_system");
|
||||||
|
db._dropDatabase(dbName);
|
||||||
|
db._createDatabase(dbName);
|
||||||
|
db._useDatabase(dbName);
|
||||||
|
|
||||||
|
assertNull(analyzers.analyzer(analyzerName));
|
||||||
|
// this should be no name conflict
|
||||||
|
analyzers.save(analyzerName, "text", {"stopwords" : [], "locale":"en"});
|
||||||
|
assertEqual(1, db._analyzers.count());
|
||||||
|
|
||||||
|
var view = db._createView("analyzersView", "arangosearch", {
|
||||||
|
links: {
|
||||||
|
_analyzers : {
|
||||||
|
includeAllFields:true,
|
||||||
|
analyzers: [ analyzerName ]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var res = db._query("FOR d IN analyzersView OPTIONS {waitForSync:true} RETURN d").toArray();
|
||||||
|
assertEqual(1, db._analyzers.count());
|
||||||
|
assertEqual(1, res.length);
|
||||||
|
assertEqual(db._analyzers.toArray()[0], res[0]);
|
||||||
|
|
||||||
db._useDatabase("_system");
|
db._useDatabase("_system");
|
||||||
db._dropDatabase(dbName);
|
db._dropDatabase(dbName);
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,38 +56,53 @@ function iResearchFeatureAqlTestSuite () {
|
||||||
testAnalyzersInvalidPropertiesDiscarded : function() {
|
testAnalyzersInvalidPropertiesDiscarded : function() {
|
||||||
{
|
{
|
||||||
try {analyzers.remove("normPropAnalyzer"); } catch (e) {}
|
try {analyzers.remove("normPropAnalyzer"); } catch (e) {}
|
||||||
|
assertEqual(0, db._analyzers.count());
|
||||||
let analyzer = analyzers.save("normPropAnalyzer", "norm", { "locale":"en", "invalid_param":true});
|
let analyzer = analyzers.save("normPropAnalyzer", "norm", { "locale":"en", "invalid_param":true});
|
||||||
|
assertEqual(1, db._analyzers.count());
|
||||||
assertTrue(null != analyzer);
|
assertTrue(null != analyzer);
|
||||||
assertTrue(null == analyzer.properties.invalid_param);
|
assertTrue(null == analyzer.properties.invalid_param);
|
||||||
analyzers.remove("normPropAnalyzer", true);
|
analyzers.remove("normPropAnalyzer", true);
|
||||||
|
assertEqual(0, db._analyzers.count());
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
try {analyzers.remove("textPropAnalyzer"); } catch (e) {}
|
try {analyzers.remove("textPropAnalyzer"); } catch (e) {}
|
||||||
|
assertEqual(0, db._analyzers.count());
|
||||||
let analyzer = analyzers.save("textPropAnalyzer", "text", {"stopwords" : [], "locale":"en", "invalid_param":true});
|
let analyzer = analyzers.save("textPropAnalyzer", "text", {"stopwords" : [], "locale":"en", "invalid_param":true});
|
||||||
|
assertEqual(1, db._analyzers.count());
|
||||||
assertTrue(null != analyzer);
|
assertTrue(null != analyzer);
|
||||||
assertTrue(null == analyzer.properties.invalid_param);
|
assertTrue(null == analyzer.properties.invalid_param);
|
||||||
analyzers.remove("textPropAnalyzer", true);
|
analyzers.remove("textPropAnalyzer", true);
|
||||||
|
assertEqual(0, db._analyzers.count());
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
try {analyzers.remove("delimiterPropAnalyzer"); } catch (e) {}
|
try {analyzers.remove("delimiterPropAnalyzer"); } catch (e) {}
|
||||||
|
assertEqual(0, db._analyzers.count());
|
||||||
let analyzer = analyzers.save("delimiterPropAnalyzer", "delimiter", { "delimiter":"|", "invalid_param":true});
|
let analyzer = analyzers.save("delimiterPropAnalyzer", "delimiter", { "delimiter":"|", "invalid_param":true});
|
||||||
|
assertEqual(1, db._analyzers.count());
|
||||||
assertTrue(null != analyzer);
|
assertTrue(null != analyzer);
|
||||||
assertTrue(null == analyzer.properties.invalid_param);
|
assertTrue(null == analyzer.properties.invalid_param);
|
||||||
analyzers.remove("delimiterPropAnalyzer", true);
|
analyzers.remove("delimiterPropAnalyzer", true);
|
||||||
|
assertEqual(0, db._analyzers.count());
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
try {analyzers.remove("stemPropAnalyzer"); } catch (e) {}
|
try {analyzers.remove("stemPropAnalyzer"); } catch (e) {}
|
||||||
|
assertEqual(0, db._analyzers.count());
|
||||||
let analyzer = analyzers.save("stemPropAnalyzer", "stem", { "locale":"en", "invalid_param":true});
|
let analyzer = analyzers.save("stemPropAnalyzer", "stem", { "locale":"en", "invalid_param":true});
|
||||||
|
assertEqual(1, db._analyzers.count());
|
||||||
assertTrue(null != analyzer);
|
assertTrue(null != analyzer);
|
||||||
assertTrue(null == analyzer.properties.invalid_param);
|
assertTrue(null == analyzer.properties.invalid_param);
|
||||||
analyzers.remove("stemPropAnalyzer", true);
|
analyzers.remove("stemPropAnalyzer", true);
|
||||||
|
assertEqual(0, db._analyzers.count());
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
try {analyzers.remove("ngramPropAnalyzer"); } catch (e) {}
|
try {analyzers.remove("ngramPropAnalyzer"); } catch (e) {}
|
||||||
|
assertEqual(0, db._analyzers.count());
|
||||||
let analyzer = analyzers.save("ngramPropAnalyzer", "ngram", { "min":1, "max":5, "preserveOriginal":true, "invalid_param":true});
|
let analyzer = analyzers.save("ngramPropAnalyzer", "ngram", { "min":1, "max":5, "preserveOriginal":true, "invalid_param":true});
|
||||||
|
assertEqual(1, db._analyzers.count());
|
||||||
assertTrue(null != analyzer);
|
assertTrue(null != analyzer);
|
||||||
assertTrue(null == analyzer.properties.invalid_param);
|
assertTrue(null == analyzer.properties.invalid_param);
|
||||||
analyzers.remove("ngramPropAnalyzer", true);
|
analyzers.remove("ngramPropAnalyzer", true);
|
||||||
|
assertEqual(0, db._analyzers.count());
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
testAnalyzerRemovalWithDatabaseName_InSystem: function() {
|
testAnalyzerRemovalWithDatabaseName_InSystem: function() {
|
||||||
|
@ -203,6 +218,8 @@ function iResearchFeatureAqlTestSuite () {
|
||||||
let oldListInCollection = db._analyzers.toArray();
|
let oldListInCollection = db._analyzers.toArray();
|
||||||
assertTrue(Array === oldList.constructor);
|
assertTrue(Array === oldList.constructor);
|
||||||
|
|
||||||
|
assertEqual(0, db._analyzers.count());
|
||||||
|
|
||||||
// creation
|
// creation
|
||||||
analyzers.save("testAnalyzer", "stem", { "locale":"en"}, [ "frequency" ]);
|
analyzers.save("testAnalyzer", "stem", { "locale":"en"}, [ "frequency" ]);
|
||||||
|
|
||||||
|
@ -216,12 +233,27 @@ function iResearchFeatureAqlTestSuite () {
|
||||||
assertTrue(Array === analyzer.features().constructor);
|
assertTrue(Array === analyzer.features().constructor);
|
||||||
assertEqual(1, analyzer.features().length);
|
assertEqual(1, analyzer.features().length);
|
||||||
assertEqual([ "frequency" ], analyzer.features());
|
assertEqual([ "frequency" ], analyzer.features());
|
||||||
|
|
||||||
|
// check persisted analyzer
|
||||||
|
assertEqual(1, db._analyzers.count());
|
||||||
|
let savedAnalyzer = db._analyzers.toArray()[0];
|
||||||
|
assertTrue(null !== savedAnalyzer);
|
||||||
|
assertEqual(7, Object.keys(savedAnalyzer).length);
|
||||||
|
assertEqual("_analyzers/testAnalyzer", savedAnalyzer._id);
|
||||||
|
assertEqual("testAnalyzer", savedAnalyzer._key);
|
||||||
|
assertEqual("testAnalyzer", savedAnalyzer.name);
|
||||||
|
assertEqual("stem", savedAnalyzer.type);
|
||||||
|
assertEqual(analyzer.properties(), savedAnalyzer.properties);
|
||||||
|
assertEqual(analyzer.features(), savedAnalyzer.features);
|
||||||
|
|
||||||
analyzer = undefined; // release reference
|
analyzer = undefined; // release reference
|
||||||
|
|
||||||
// check the analyzers collection in database
|
// check the analyzers collection in database
|
||||||
assertEqual(oldListInCollection.length + 1, db._analyzers.toArray().length);
|
assertEqual(oldListInCollection.length + 1, db._analyzers.toArray().length);
|
||||||
let dbAnalyzer = db._query("FOR d in _analyzers FILTER d.name=='testAnalyzer' RETURN d").toArray();
|
let dbAnalyzer = db._query("FOR d in _analyzers FILTER d.name=='testAnalyzer' RETURN d").toArray();
|
||||||
assertEqual(1, dbAnalyzer.length);
|
assertEqual(1, dbAnalyzer.length);
|
||||||
|
assertEqual("_analyzers/testAnalyzer", dbAnalyzer[0]._id);
|
||||||
|
assertEqual("testAnalyzer", dbAnalyzer[0]._key);
|
||||||
assertEqual("testAnalyzer", dbAnalyzer[0].name);
|
assertEqual("testAnalyzer", dbAnalyzer[0].name);
|
||||||
assertEqual("stem", dbAnalyzer[0].type);
|
assertEqual("stem", dbAnalyzer[0].type);
|
||||||
assertEqual(1, Object.keys(dbAnalyzer[0].properties).length);
|
assertEqual(1, Object.keys(dbAnalyzer[0].properties).length);
|
||||||
|
|
|
@ -1127,6 +1127,39 @@ function aqlUpsertOptionsSuite() {
|
||||||
validateDocsAreUpdated(docs, invalid, true);
|
validateDocsAreUpdated(docs, invalid, true);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
testUpsertEmptyObject: function () {
|
||||||
|
let NEW, OLD, res;
|
||||||
|
col.insert({ "value1": "find me" });
|
||||||
|
|
||||||
|
// before "find me" was found because the empty object
|
||||||
|
// was ignored now we will insert instead
|
||||||
|
let q1 = `UPSERT { "value1" : "find me", "value2" : {} }
|
||||||
|
INSERT { "value1" : "find me", "value2" : {} }
|
||||||
|
UPDATE { "value1" : "not gonna happen" }
|
||||||
|
IN ${collectionName}
|
||||||
|
RETURN [$OLD, $NEW]`;
|
||||||
|
|
||||||
|
res = db._query(q1).toArray();
|
||||||
|
OLD = res[0][0];
|
||||||
|
NEW = res[0][1];
|
||||||
|
assertEqual(NEW.value1, "find me");
|
||||||
|
assertEqual(NEW.value2, {});
|
||||||
|
|
||||||
|
|
||||||
|
// now we update the exact doucment
|
||||||
|
let q2 = `UPSERT { "value1" : "find me", "value2" : {} }
|
||||||
|
INSERT { "value1" : "not gonna happen" }
|
||||||
|
UPDATE { "value1" : "update" }
|
||||||
|
IN ${collectionName}
|
||||||
|
RETURN [$OLD, $NEW]`;
|
||||||
|
|
||||||
|
res = db._query(q2).toArray();
|
||||||
|
OLD = res[0][0];
|
||||||
|
NEW = res[0][1];
|
||||||
|
assertEqual(NEW.value1, "update");
|
||||||
|
assertEqual(NEW.value2, {});
|
||||||
|
},
|
||||||
|
|
||||||
/* We cannot yet solve this. If you need to ensure _rev value checks put them in the UPDATE {} clause
|
/* We cannot yet solve this. If you need to ensure _rev value checks put them in the UPDATE {} clause
|
||||||
testUpsertSingleWithInvalidRevInMatch : function () {
|
testUpsertSingleWithInvalidRevInMatch : function () {
|
||||||
const invalid = genInvalidValue();
|
const invalid = genInvalidValue();
|
||||||
|
|
|
@ -732,6 +732,16 @@ function dumpTestEnterpriseSuite () {
|
||||||
assertEqual(6, p.numberOfShards);
|
assertEqual(6, p.numberOfShards);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief test link on analyzers collection
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
testIndexAnalyzerCollection : function() {
|
||||||
|
var res = db._query("FOR d IN analyzersView OPTIONS {waitForSync:true} RETURN d").toArray();
|
||||||
|
assertEqual(1, db._analyzers.count());
|
||||||
|
assertEqual(1, res.length);
|
||||||
|
assertEqual(db._analyzers.toArray()[0], res[0]);
|
||||||
|
},
|
||||||
|
|
||||||
testViewOnSmartEdgeCollection : function() {
|
testViewOnSmartEdgeCollection : function() {
|
||||||
try {
|
try {
|
||||||
db._createView("check", "arangosearch", {});
|
db._createView("check", "arangosearch", {});
|
||||||
|
|
|
@ -488,6 +488,7 @@ function dumpTestSuite () {
|
||||||
/// @brief test custom analyzers restoring
|
/// @brief test custom analyzers restoring
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
testAnalyzers: function() {
|
testAnalyzers: function() {
|
||||||
|
assertNotEqual(null, db._collection("_analyzers"));
|
||||||
assertEqual(1, db._analyzers.count()); // only 1 stored custom analyzer
|
assertEqual(1, db._analyzers.count()); // only 1 stored custom analyzer
|
||||||
|
|
||||||
let analyzer = analyzers.analyzer("custom");
|
let analyzer = analyzers.analyzer("custom");
|
||||||
|
@ -497,8 +498,17 @@ function dumpTestSuite () {
|
||||||
assertEqual(" ", analyzer.properties().delimiter);
|
assertEqual(" ", analyzer.properties().delimiter);
|
||||||
assertEqual(1, analyzer.features().length);
|
assertEqual(1, analyzer.features().length);
|
||||||
assertEqual("frequency", analyzer.features()[0]);
|
assertEqual("frequency", analyzer.features()[0]);
|
||||||
}
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief test link on analyzers collection
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
testIndexAnalyzerCollection : function() {
|
||||||
|
var res = db._query("FOR d IN analyzersView OPTIONS {waitForSync:true} RETURN d").toArray();
|
||||||
|
assertEqual(1, db._analyzers.count());
|
||||||
|
assertEqual(1, res.length);
|
||||||
|
assertEqual(db._analyzers.toArray()[0], res[0]);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -453,6 +453,7 @@ function dumpTestSuite () {
|
||||||
/// @brief test custom analyzers restoring
|
/// @brief test custom analyzers restoring
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
testAnalyzers: function() {
|
testAnalyzers: function() {
|
||||||
|
assertNotEqual(null, db._collection("_analyzers"));
|
||||||
assertEqual(1, db._analyzers.count()); // only 1 stored custom analyzer
|
assertEqual(1, db._analyzers.count()); // only 1 stored custom analyzer
|
||||||
|
|
||||||
let analyzer = analyzers.analyzer("custom");
|
let analyzer = analyzers.analyzer("custom");
|
||||||
|
@ -462,8 +463,17 @@ function dumpTestSuite () {
|
||||||
assertEqual(" ", analyzer.properties().delimiter);
|
assertEqual(" ", analyzer.properties().delimiter);
|
||||||
assertEqual(1, analyzer.features().length);
|
assertEqual(1, analyzer.features().length);
|
||||||
assertEqual("frequency", analyzer.features()[0]);
|
assertEqual("frequency", analyzer.features()[0]);
|
||||||
}
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief test link on analyzers collection
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
testIndexAnalyzerCollection : function() {
|
||||||
|
var res = db._query("FOR d IN analyzersView OPTIONS {waitForSync:true} RETURN d").toArray();
|
||||||
|
assertEqual(1, db._analyzers.count());
|
||||||
|
assertEqual(1, res.length);
|
||||||
|
assertEqual(db._analyzers.toArray()[0], res[0]);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -287,6 +287,18 @@ function setupSatelliteCollections() {
|
||||||
c.save({ value: -1, text: "the red foxx jumps over the pond" });
|
c.save({ value: -1, text: "the red foxx jumps over the pond" });
|
||||||
} catch (err) { }
|
} catch (err) { }
|
||||||
|
|
||||||
|
// setup a view on _analyzers collection
|
||||||
|
try {
|
||||||
|
let view = db._createView("analyzersView", "arangosearch", {
|
||||||
|
links: {
|
||||||
|
_analyzers : {
|
||||||
|
includeAllFields:true,
|
||||||
|
analyzers: [ analyzer.name ]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (err) { }
|
||||||
|
|
||||||
setupSmartGraph();
|
setupSmartGraph();
|
||||||
setupSmartArangoSearch();
|
setupSmartArangoSearch();
|
||||||
setupSatelliteCollections();
|
setupSatelliteCollections();
|
||||||
|
|
|
@ -259,6 +259,18 @@
|
||||||
c.save({ value: -1, text: "the red foxx jumps over the pond" });
|
c.save({ value: -1, text: "the red foxx jumps over the pond" });
|
||||||
} catch (err) { }
|
} catch (err) { }
|
||||||
|
|
||||||
|
// setup a view on _analyzers collection
|
||||||
|
try {
|
||||||
|
let view = db._createView("analyzersView", "arangosearch", {
|
||||||
|
links: {
|
||||||
|
_analyzers : {
|
||||||
|
includeAllFields: true,
|
||||||
|
analyzers: [ analyzer.name ]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (err) { }
|
||||||
|
|
||||||
// Install Foxx
|
// Install Foxx
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const SERVICE_PATH = fs.makeAbsolute(fs.join(
|
const SERVICE_PATH = fs.makeAbsolute(fs.join(
|
||||||
|
|
|
@ -930,6 +930,51 @@ function BaseTestConfig () {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
testSyncViewOnAnalyzersCollection: function () {
|
||||||
|
connectToMaster();
|
||||||
|
|
||||||
|
// create custom analyzer
|
||||||
|
assertNotEqual(null, db._collection("_analyzers"));
|
||||||
|
assertEqual(0, db._analyzers.count());
|
||||||
|
let analyzer = analyzers.save('custom', 'delimiter', { delimiter: ' ' }, ['frequency']);
|
||||||
|
assertEqual(1, db._analyzers.count());
|
||||||
|
|
||||||
|
// create view & collection on master
|
||||||
|
db._flushCache();
|
||||||
|
db._createView('analyzersView', 'arangosearch', {
|
||||||
|
consolidationIntervalMsec:0,
|
||||||
|
links: { _analyzers: { analyzers: [ analyzer.name ], includeAllFields:true } }
|
||||||
|
});
|
||||||
|
|
||||||
|
db._flushCache();
|
||||||
|
connectToSlave();
|
||||||
|
internal.wait(0.1, false);
|
||||||
|
// sync on slave
|
||||||
|
replication.sync({ endpoint: masterEndpoint });
|
||||||
|
|
||||||
|
db._flushCache();
|
||||||
|
|
||||||
|
assertEqual(1, db._analyzers.count());
|
||||||
|
var res = db._query("FOR d IN analyzersView OPTIONS {waitForSync:true} RETURN d").toArray();
|
||||||
|
assertEqual(1, db._analyzers.count());
|
||||||
|
assertEqual(1, res.length);
|
||||||
|
assertEqual(db._analyzers.toArray()[0], res[0]);
|
||||||
|
|
||||||
|
{
|
||||||
|
// check state is the same
|
||||||
|
let view = db._view('analyzersView');
|
||||||
|
assertTrue(view !== null);
|
||||||
|
let props = view.properties();
|
||||||
|
assertTrue(props.hasOwnProperty('links'));
|
||||||
|
assertEqual(0, props.consolidationIntervalMsec);
|
||||||
|
assertEqual(Object.keys(props.links).length, 1);
|
||||||
|
assertTrue(props.links.hasOwnProperty('_analyzers'));
|
||||||
|
assertTrue(props.links['_analyzers'].includeAllFields);
|
||||||
|
assertTrue(props.links['_analyzers'].analyzers.length, 1);
|
||||||
|
assertTrue(props.links['_analyzers'].analyzers[0], 'custom');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// //////////////////////////////////////////////////////////////////////////////
|
// //////////////////////////////////////////////////////////////////////////////
|
||||||
// / @brief test with edges
|
// / @brief test with edges
|
||||||
// //////////////////////////////////////////////////////////////////////////////
|
// //////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
Loading…
Reference in New Issue