////////////////////////////////////////////////////////////////////////////// /// DISCLAIMER /// /// Copyright 2017 EMC Corporation /// /// Licensed under the Apache License, Version 2.0 (the "License"); /// you may not use this file except in compliance with the License. /// You may obtain a copy of the License at /// /// http://www.apache.org/licenses/LICENSE-2.0 /// /// Unless required by applicable law or agreed to in writing, software /// distributed under the License is distributed on an "AS IS" BASIS, /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. /// See the License for the specific language governing permissions and /// limitations under the License. /// /// Copyright holder is EMC Corporation /// /// @author Andrey Abramov /// @author Vasiliy Nabatchikov //////////////////////////////////////////////////////////////////////////////// #include "gtest/gtest.h" #include "analysis/analyzers.hpp" #include "analysis/token_attributes.hpp" #include "utils/locale_utils.hpp" #include "IResearch/common.h" #include "Mocks/LogLevels.h" #include "Mocks/Servers.h" #include "Mocks/StorageEngineMock.h" #include "ApplicationFeatures/CommunicationFeaturePhase.h" #include "ApplicationFeatures/GreetingsFeaturePhase.h" #include "Aql/AqlFunctionFeature.h" #include "Aql/OptimizerRulesFeature.h" #include "Cluster/ClusterFeature.h" #include "FeaturePhases/BasicFeaturePhaseServer.h" #include "FeaturePhases/ClusterFeaturePhase.h" #include "FeaturePhases/DatabaseFeaturePhase.h" #include "FeaturePhases/V8FeaturePhase.h" #include "GeneralServer/AuthenticationFeature.h" #include "IResearch/IResearchCommon.h" #include "IResearch/IResearchFeature.h" #include "IResearch/IResearchLinkMeta.h" #include "IResearch/VelocyPackHelper.h" #include "RestServer/AqlFeature.h" #include "RestServer/TraverserEngineRegistryFeature.h" #include "RestServer/DatabaseFeature.h" #include "RestServer/QueryRegistryFeature.h" #include "RestServer/SystemDatabaseFeature.h" #include "Sharding/ShardingFeature.h" #include "StorageEngine/EngineSelectorFeature.h" #include "V8Server/V8DealerFeature.h" #include "VocBase/Methods/Collections.h" #include "velocypack/Builder.h" #include "velocypack/Iterator.h" #include "velocypack/Parser.h" #if USE_ENTERPRISE #include "Enterprise/Ldap/LdapFeature.h" #endif namespace { struct TestAttributeZ : public irs::attribute { DECLARE_ATTRIBUTE_TYPE(); }; DEFINE_ATTRIBUTE_TYPE(TestAttributeZ); REGISTER_ATTRIBUTE(TestAttributeZ); class EmptyAnalyzer : public irs::analysis::analyzer { public: DECLARE_ANALYZER_TYPE(); static ptr make(irs::string_ref const&) { PTR_NAMED(EmptyAnalyzer, ptr); return ptr; } static bool normalize(irs::string_ref const& args, std::string& out) { auto slice = arangodb::iresearch::slice(args); 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; } out = builder.buffer()->toString(); return true; } EmptyAnalyzer() : irs::analysis::analyzer(EmptyAnalyzer::type()) { _attrs.emplace(_attr); } virtual irs::attribute_view const& attributes() const NOEXCEPT override { return _attrs; } virtual bool next() override { return false; } virtual bool reset(irs::string_ref const& data) override { return true; } private: irs::attribute_view _attrs; TestAttributeZ _attr; }; DEFINE_ANALYZER_TYPE_NAMED(EmptyAnalyzer, "empty"); REGISTER_ANALYZER_VPACK(EmptyAnalyzer, EmptyAnalyzer::make, EmptyAnalyzer::normalize); static const VPackBuilder systemDatabaseBuilder = dbArgsBuilder(); static const VPackSlice systemDatabaseArgs = systemDatabaseBuilder.slice(); } // namespace // ----------------------------------------------------------------------------- // --SECTION-- setup / tear-down // ----------------------------------------------------------------------------- class IResearchLinkMetaTest : public ::testing::Test, public arangodb::tests::LogSuppressor, public arangodb::tests::LogSuppressor { protected: arangodb::tests::mocks::MockAqlServer server; IResearchLinkMetaTest() { arangodb::tests::init(); auto& dbFeature = server.getFeature(); auto sysvocbase = dbFeature.useDatabase(arangodb::StaticStrings::SystemDatabase); arangodb::methods::Collections::createSystem( *sysvocbase, arangodb::tests::AnalyzerCollectionName, false); TRI_vocbase_t* vocbase; dbFeature.createDatabase(testDBInfo(server.server()), vocbase); // required for IResearchAnalyzerFeature::emplace(...) arangodb::methods::Collections::createSystem( *vocbase, arangodb::tests::AnalyzerCollectionName, false); auto& analyzers = server.getFeature(); arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result; analyzers.emplace(result, "testVocbase::empty", "empty", VPackParser::fromJson("{ \"args\": \"de\" }")->slice(), irs::flags{irs::frequency::type()}); // cache the 'empty' analyzer for 'testVocbase' } }; // ----------------------------------------------------------------------------- // --SECTION-- test suite // ----------------------------------------------------------------------------- TEST_F(IResearchLinkMetaTest, test_defaults) { 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(false == meta._includeAllFields); EXPECT_TRUE(false == meta._trackListPositions); EXPECT_TRUE(arangodb::iresearch::ValueStorage::NONE == meta._storeValues); EXPECT_TRUE(1U == meta._analyzers.size()); EXPECT_TRUE(*(meta._analyzers.begin())); EXPECT_TRUE("identity" == meta._analyzers.begin()->_pool->name()); EXPECT_TRUE("identity" == meta._analyzers.begin()->_shortName); EXPECT_TRUE(irs::flags({irs::norm::type(), irs::frequency::type()}) == meta._analyzers.begin()->_pool->features()); EXPECT_FALSE(!meta._analyzers.begin()->_pool->get()); } TEST_F(IResearchLinkMetaTest, test_inheritDefaults) { arangodb::iresearch::IResearchAnalyzerFeature analyzers(server.server()); arangodb::iresearch::IResearchLinkMeta defaults; arangodb::iresearch::IResearchLinkMeta meta; std::unordered_set expectedFields = {"abc"}; std::unordered_set expectedOverrides = {"xyz"}; std::string tmpString; analyzers.start(); defaults._fields["abc"] = arangodb::iresearch::FieldMeta(); defaults._includeAllFields = true; defaults._trackListPositions = true; defaults._storeValues = arangodb::iresearch::ValueStorage::VALUE; defaults._analyzers.clear(); defaults._analyzers.emplace_back(arangodb::iresearch::IResearchLinkMeta::Analyzer( analyzers.get("testVocbase::empty"), "empty")); defaults._fields["abc"]->_fields["xyz"] = arangodb::iresearch::IResearchLinkMeta(); defaults._sort.emplace_back(std::vector{{"foo",false}}, true); auto json = VPackParser::fromJson("{}"); EXPECT_TRUE(meta.init(json->slice(), false, tmpString, nullptr, defaults)); EXPECT_EQ(1U, meta._fields.size()); for (auto& field : meta._fields) { EXPECT_EQ(1U, expectedFields.erase(field.key())); EXPECT_EQ(1U, field.value()->_fields.size()); for (auto& fieldOverride : field.value()->_fields) { auto& actual = *(fieldOverride.value()); EXPECT_EQ(1U, expectedOverrides.erase(fieldOverride.key())); if ("xyz" == fieldOverride.key()) { EXPECT_TRUE(actual._fields.empty()); EXPECT_FALSE(actual._includeAllFields); EXPECT_FALSE(actual._trackListPositions); EXPECT_EQ(arangodb::iresearch::ValueStorage::NONE, actual._storeValues); EXPECT_EQ(1U, actual._analyzers.size()); EXPECT_TRUE(*(actual._analyzers.begin())); EXPECT_EQ("identity", actual._analyzers.begin()->_pool->name()); EXPECT_EQ("identity", actual._analyzers.begin()->_shortName); EXPECT_TRUE((irs::flags({irs::norm::type(), irs::frequency::type()}) == actual._analyzers.begin()->_pool->features())); EXPECT_FALSE(!actual._analyzers.begin()->_pool->get()); } } } EXPECT_TRUE(expectedOverrides.empty()); EXPECT_TRUE(expectedFields.empty()); EXPECT_TRUE(meta._includeAllFields); EXPECT_TRUE(meta._trackListPositions); EXPECT_TRUE((arangodb::iresearch::ValueStorage::VALUE == meta._storeValues)); EXPECT_EQ(1U, meta._analyzers.size()); EXPECT_TRUE(*(meta._analyzers.begin())); EXPECT_EQ("testVocbase::empty", meta._analyzers.begin()->_pool->name()); EXPECT_EQ("empty", meta._analyzers.begin()->_shortName); EXPECT_TRUE((irs::flags({irs::frequency::type()}) == meta._analyzers.begin()->_pool->features())); EXPECT_FALSE(!meta._analyzers.begin()->_pool->get()); EXPECT_EQ(0, meta._sort.size()); } TEST_F(IResearchLinkMetaTest, test_readDefaults) { auto json = VPackParser::fromJson("{}"); // without active vobcase { arangodb::iresearch::IResearchLinkMeta meta; std::string tmpString; EXPECT_TRUE(meta.init(json->slice(), false, tmpString)); EXPECT_TRUE(meta._fields.empty()); EXPECT_FALSE(meta._includeAllFields); EXPECT_FALSE(meta._trackListPositions); EXPECT_EQ(arangodb::iresearch::ValueStorage::NONE, meta._storeValues); EXPECT_EQ(1U, meta._analyzers.size()); EXPECT_TRUE(*(meta._analyzers.begin())); EXPECT_EQ("identity", meta._analyzers.begin()->_pool->name()); EXPECT_EQ("identity", meta._analyzers.begin()->_shortName); EXPECT_TRUE((irs::flags({irs::norm::type(), irs::frequency::type()}) == meta._analyzers.begin()->_pool->features())); EXPECT_FALSE(!meta._analyzers.begin()->_pool->get()); } // with active vocbase { TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, testDBInfo(server.server())); arangodb::iresearch::IResearchLinkMeta meta; std::string tmpString; EXPECT_TRUE(meta.init(json->slice(), false, tmpString, &vocbase)); EXPECT_TRUE(meta._fields.empty()); EXPECT_FALSE(meta._includeAllFields); EXPECT_FALSE(meta._trackListPositions); EXPECT_EQ(arangodb::iresearch::ValueStorage::NONE, meta._storeValues); EXPECT_EQ(1U, meta._analyzers.size()); EXPECT_TRUE(*(meta._analyzers.begin())); EXPECT_EQ("identity", meta._analyzers.begin()->_pool->name()); EXPECT_EQ("identity", meta._analyzers.begin()->_shortName); EXPECT_TRUE((irs::flags({irs::norm::type(), irs::frequency::type()}) == meta._analyzers.begin()->_pool->features())); EXPECT_FALSE(!meta._analyzers.begin()->_pool->get()); } } TEST_F(IResearchLinkMetaTest, test_readCustomizedValues) { auto json = VPackParser::fromJson( "{ \ \"fields\": { \ \"a\": {}, \ \"b\": {}, \ \"c\": { \ \"fields\": { \ \"default\": { \"fields\": {}, \"includeAllFields\": false, \"trackListPositions\": false, \"storeValues\": \"none\", \"analyzers\": [ \"identity\" ] }, \ \"all\": { \"fields\": {\"d\": {}, \"e\": {}}, \"includeAllFields\": true, \"trackListPositions\": true, \"storeValues\": \"value\", \"analyzers\": [ \"empty\" ] }, \ \"some\": { \"trackListPositions\": true, \"storeValues\": \"id\" }, \ \"none\": {} \ } \ } \ }, \ \"includeAllFields\": true, \ \"trackListPositions\": true, \ \"storeValues\": \"value\", \ \"analyzers\": [ \"empty\", \"identity\" ] \ }"); // without active vocbase { arangodb::iresearch::IResearchLinkMeta meta; std::string tmpString; EXPECT_FALSE(meta.init(json->slice(), false, tmpString)); } // with active vocbase { std::unordered_set expectedFields = {"a", "b", "c"}; std::unordered_set expectedOverrides = {"default", "all", "some", "none"}; std::unordered_set expectedAnalyzers = {"empty", "identity"}; TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, testDBInfo(server.server())); arangodb::iresearch::IResearchLinkMeta meta; std::string tmpString; EXPECT_TRUE(meta.init(json->slice(), false, tmpString, &vocbase)); EXPECT_EQ(3U, meta._fields.size()); for (auto& field : meta._fields) { EXPECT_EQ(1U, expectedFields.erase(field.key())); for (auto& fieldOverride : field.value()->_fields) { auto& actual = *(fieldOverride.value()); EXPECT_EQ(1U, expectedOverrides.erase(fieldOverride.key())); if ("default" == fieldOverride.key()) { EXPECT_TRUE(actual._fields.empty()); EXPECT_FALSE(actual._includeAllFields); EXPECT_FALSE(actual._trackListPositions); EXPECT_EQ(arangodb::iresearch::ValueStorage::NONE, actual._storeValues); EXPECT_EQ(1U, actual._analyzers.size()); EXPECT_TRUE(*(actual._analyzers.begin())); EXPECT_EQ("identity", actual._analyzers.begin()->_pool->name()); EXPECT_EQ("identity", actual._analyzers.begin()->_shortName); EXPECT_TRUE((irs::flags({irs::norm::type(), irs::frequency::type()}) == actual._analyzers.begin()->_pool->features())); EXPECT_FALSE(!actual._analyzers.begin()->_pool->get()); } else if ("all" == fieldOverride.key()) { EXPECT_EQ(2U, actual._fields.size()); EXPECT_TRUE((actual._fields.find("d") != actual._fields.end())); EXPECT_TRUE((actual._fields.find("e") != actual._fields.end())); EXPECT_TRUE(actual._includeAllFields); EXPECT_TRUE(actual._trackListPositions); EXPECT_EQ(arangodb::iresearch::ValueStorage::VALUE, actual._storeValues); EXPECT_EQ(1U, actual._analyzers.size()); EXPECT_TRUE(*(actual._analyzers.begin())); EXPECT_EQ("testVocbase::empty", actual._analyzers.begin()->_pool->name()); EXPECT_EQ("empty", actual._analyzers.begin()->_shortName); EXPECT_TRUE((irs::flags({irs::frequency::type()}) == actual._analyzers.begin()->_pool->features())); EXPECT_FALSE(!actual._analyzers.begin()->_pool->get()); } else if ("some" == fieldOverride.key()) { EXPECT_TRUE(actual._fields.empty()); // not inherited EXPECT_TRUE(actual._includeAllFields); // inherited EXPECT_TRUE(actual._trackListPositions); EXPECT_EQ(arangodb::iresearch::ValueStorage::ID, actual._storeValues); EXPECT_EQ(2U, actual._analyzers.size()); auto itr = actual._analyzers.begin(); EXPECT_TRUE(*itr); EXPECT_EQ("testVocbase::empty", itr->_pool->name()); EXPECT_EQ("empty", itr->_shortName); EXPECT_EQ(irs::flags({irs::frequency::type()}), itr->_pool->features()); EXPECT_FALSE(!itr->_pool->get()); ++itr; EXPECT_TRUE(*itr); EXPECT_EQ("identity", itr->_pool->name()); EXPECT_EQ("identity", itr->_shortName); EXPECT_TRUE((irs::flags({irs::norm::type(), irs::frequency::type()}) == itr->_pool->features())); EXPECT_FALSE(!itr->_pool->get()); } else if ("none" == fieldOverride.key()) { EXPECT_TRUE(actual._fields.empty()); // not inherited EXPECT_TRUE(actual._includeAllFields); // inherited EXPECT_TRUE(actual._trackListPositions); // inherited EXPECT_EQ(arangodb::iresearch::ValueStorage::VALUE, actual._storeValues); auto itr = actual._analyzers.begin(); EXPECT_TRUE(*itr); EXPECT_EQ("testVocbase::empty", itr->_pool->name()); EXPECT_EQ("empty", itr->_shortName); EXPECT_EQ(irs::flags({irs::frequency::type()}), itr->_pool->features()); EXPECT_FALSE(!itr->_pool->get()); ++itr; EXPECT_TRUE(*itr); EXPECT_EQ("identity", itr->_pool->name()); EXPECT_EQ("identity", itr->_shortName); EXPECT_TRUE((irs::flags({irs::norm::type(), irs::frequency::type()}) == itr->_pool->features())); EXPECT_FALSE(!itr->_pool->get()); } } } EXPECT_TRUE(expectedOverrides.empty()); EXPECT_TRUE(expectedFields.empty()); EXPECT_TRUE(meta._includeAllFields); EXPECT_TRUE(meta._trackListPositions); EXPECT_EQ(arangodb::iresearch::ValueStorage::VALUE, meta._storeValues); auto itr = meta._analyzers.begin(); EXPECT_TRUE(*itr); EXPECT_EQ("testVocbase::empty", itr->_pool->name()); EXPECT_EQ("empty", itr->_shortName); EXPECT_EQ(irs::flags({irs::frequency::type()}), itr->_pool->features()); EXPECT_FALSE(!itr->_pool->get()); ++itr; EXPECT_TRUE(*itr); EXPECT_EQ("identity", itr->_pool->name()); EXPECT_EQ("identity", itr->_shortName); EXPECT_TRUE((irs::flags({irs::norm::type(), irs::frequency::type()}) == itr->_pool->features())); EXPECT_FALSE(!itr->_pool->get()); } } TEST_F(IResearchLinkMetaTest, test_writeDefaults) { // without active vobcase (not fullAnalyzerDefinition) { arangodb::iresearch::IResearchLinkMeta meta; arangodb::velocypack::Builder builder; arangodb::velocypack::Slice tmpSlice; builder.openObject(); EXPECT_TRUE(meta.json(builder, false)); builder.close(); auto slice = builder.slice(); EXPECT_EQ(5U, slice.length()); tmpSlice = slice.get("fields"); EXPECT_TRUE(tmpSlice.isObject() && 0 == tmpSlice.length()); tmpSlice = slice.get("includeAllFields"); EXPECT_TRUE(tmpSlice.isBool() && false == tmpSlice.getBool()); tmpSlice = slice.get("trackListPositions"); EXPECT_TRUE(tmpSlice.isBool() && false == tmpSlice.getBool()); tmpSlice = slice.get("storeValues"); EXPECT_TRUE(tmpSlice.isString() && std::string("none") == tmpSlice.copyString()); tmpSlice = slice.get("analyzers"); EXPECT_TRUE((true == tmpSlice.isArray() && 1 == tmpSlice.length() && tmpSlice.at(0).isString() && std::string("identity") == tmpSlice.at(0).copyString())); } // without active vobcase (with fullAnalyzerDefinition) { arangodb::iresearch::IResearchLinkMeta meta; arangodb::velocypack::Builder builder; arangodb::velocypack::Slice tmpSlice; builder.openObject(); EXPECT_TRUE(meta.json(builder, true)); builder.close(); auto slice = builder.slice(); EXPECT_EQ(7U, slice.length()); tmpSlice = slice.get("fields"); EXPECT_TRUE(tmpSlice.isObject() && 0 == tmpSlice.length()); tmpSlice = slice.get("includeAllFields"); EXPECT_TRUE(tmpSlice.isBool() && false == tmpSlice.getBool()); tmpSlice = slice.get("trackListPositions"); EXPECT_TRUE(tmpSlice.isBool() && false == tmpSlice.getBool()); tmpSlice = slice.get("storeValues"); EXPECT_TRUE(tmpSlice.isString() && std::string("none") == tmpSlice.copyString()); tmpSlice = slice.get("analyzers"); EXPECT_TRUE((true == tmpSlice.isArray() && 1 == tmpSlice.length() && tmpSlice.at(0).isString() && std::string("identity") == tmpSlice.at(0).copyString())); tmpSlice = slice.get("analyzerDefinitions"); EXPECT_TRUE((true == tmpSlice.isArray() && 1 == tmpSlice.length() && tmpSlice.at(0).isObject() && tmpSlice.at(0).get("name").isString() && std::string("identity") == tmpSlice.at(0).get("name").copyString() && tmpSlice.at(0).get("type").isString() && std::string("identity") == tmpSlice.at(0).get("type").copyString() && tmpSlice.at(0).get("properties").isObject() && tmpSlice.at(0).get("features").isArray() && 2 == tmpSlice.at(0).get("features").length() // frequency+norm )); EXPECT_EQUAL_SLICES(tmpSlice.at(0).get("properties"), VPackSlice::emptyObjectSlice()); tmpSlice = slice.get("primarySort"); EXPECT_TRUE(tmpSlice.isArray() && 0 == tmpSlice.length()); } // with active vocbase (not fullAnalyzerDefinition) { TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, testDBInfo(server.server())); arangodb::iresearch::IResearchLinkMeta meta; arangodb::velocypack::Builder builder; arangodb::velocypack::Slice tmpSlice; builder.openObject(); EXPECT_TRUE(meta.json(builder, false, nullptr, &vocbase)); builder.close(); auto slice = builder.slice(); EXPECT_EQ(5U, slice.length()); tmpSlice = slice.get("fields"); EXPECT_TRUE(tmpSlice.isObject() && 0 == tmpSlice.length()); tmpSlice = slice.get("includeAllFields"); EXPECT_TRUE(tmpSlice.isBool() && false == tmpSlice.getBool()); tmpSlice = slice.get("trackListPositions"); EXPECT_TRUE(tmpSlice.isBool() && false == tmpSlice.getBool()); tmpSlice = slice.get("storeValues"); EXPECT_TRUE(tmpSlice.isString() && std::string("none") == tmpSlice.copyString()); tmpSlice = slice.get("analyzers"); EXPECT_TRUE((true == tmpSlice.isArray() && 1 == tmpSlice.length() && tmpSlice.at(0).isString() && std::string("identity") == tmpSlice.at(0).copyString())); } // with active vocbase (with fullAnalyzerDefinition) { TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, testDBInfo(server.server())); arangodb::iresearch::IResearchLinkMeta meta; arangodb::velocypack::Builder builder; arangodb::velocypack::Slice tmpSlice; builder.openObject(); EXPECT_TRUE(meta.json(builder, true, nullptr, &vocbase)); builder.close(); auto slice = builder.slice(); EXPECT_EQ(7U, slice.length()); tmpSlice = slice.get("fields"); EXPECT_TRUE(tmpSlice.isObject() && 0 == tmpSlice.length()); tmpSlice = slice.get("includeAllFields"); EXPECT_TRUE(tmpSlice.isBool() && false == tmpSlice.getBool()); tmpSlice = slice.get("trackListPositions"); EXPECT_TRUE(tmpSlice.isBool() && false == tmpSlice.getBool()); tmpSlice = slice.get("storeValues"); EXPECT_TRUE(tmpSlice.isString() && std::string("none") == tmpSlice.copyString()); tmpSlice = slice.get("analyzers"); EXPECT_TRUE((true == tmpSlice.isArray() && 1 == tmpSlice.length() && tmpSlice.at(0).isString() && std::string("identity") == tmpSlice.at(0).copyString())); tmpSlice = slice.get("analyzerDefinitions"); EXPECT_TRUE((true == tmpSlice.isArray() && 1 == tmpSlice.length() && tmpSlice.at(0).isObject() && tmpSlice.at(0).get("name").isString() && std::string("identity") == tmpSlice.at(0).get("name").copyString() && tmpSlice.at(0).get("type").isString() && std::string("identity") == tmpSlice.at(0).get("type").copyString() && tmpSlice.at(0).get("properties").isObject() && tmpSlice.at(0).get("features").isArray() && 2 == tmpSlice.at(0).get("features").length() // frequency+norm )); EXPECT_EQUAL_SLICES(tmpSlice.at(0).get("properties"), VPackSlice::emptyObjectSlice()); tmpSlice = slice.get("primarySort"); EXPECT_TRUE(tmpSlice.isArray() && 0 == tmpSlice.length()); } } TEST_F(IResearchLinkMetaTest, test_writeCustomizedValues) { arangodb::iresearch::IResearchAnalyzerFeature analyzers(server.server()); analyzers.prepare(); // add static analyzers arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult emplaceResult; arangodb::iresearch::IResearchLinkMeta meta; analyzers.emplace(emplaceResult, arangodb::StaticStrings::SystemDatabase + "::empty", "empty", VPackParser::fromJson("{ \"args\": \"en\" }")->slice(), {irs::frequency::type()}); meta._includeAllFields = true; meta._trackListPositions = true; 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.emplace_back(arangodb::iresearch::IResearchLinkMeta::Analyzer( analyzers.get("identity"), "identity")); meta._analyzers.emplace_back(arangodb::iresearch::IResearchLinkMeta::Analyzer( analyzers.get(arangodb::StaticStrings::SystemDatabase + "::empty"), "empty")); meta._fields["a"] = meta; // copy from meta meta._fields["a"]->_fields.clear(); // do not inherit fields to match jSon inheritance meta._fields["b"] = meta; // copy from meta meta._fields["b"]->_fields.clear(); // do not inherit fields to match jSon inheritance meta._fields["c"] = meta; // copy from meta meta._fields["c"]->_fields.clear(); // do not inherit fields to match jSon inheritance meta._fields["c"]->_fields["default"]; // default values meta._fields["c"]->_fields["all"]; // will override values below meta._fields["c"]->_fields["some"] = meta._fields["c"]; // initialize with parent, override below meta._fields["c"]->_fields["none"] = meta._fields["c"]; // initialize with parent meta._sort.emplace_back({arangodb::basics::AttributeName("_key", false)}, true); meta._sort.emplace_back({arangodb::basics::AttributeName("_id", false)}, false); auto& overrideAll = *(meta._fields["c"]->_fields["all"]); auto& overrideSome = *(meta._fields["c"]->_fields["some"]); auto& overrideNone = *(meta._fields["c"]->_fields["none"]); overrideAll._fields.clear(); // do not inherit fields to match jSon inheritance overrideAll._fields["x"] = arangodb::iresearch::IResearchLinkMeta(); overrideAll._fields["y"] = arangodb::iresearch::IResearchLinkMeta(); overrideAll._includeAllFields = false; overrideAll._trackListPositions = false; overrideAll._storeValues = arangodb::iresearch::ValueStorage::NONE; overrideAll._analyzers.clear(); overrideAll._analyzers.emplace_back(arangodb::iresearch::IResearchLinkMeta::Analyzer( analyzers.get(arangodb::StaticStrings::SystemDatabase + "::empty"), "empty")); overrideSome._fields.clear(); // do not inherit fields to match jSon inheritance overrideSome._trackListPositions = false; overrideSome._storeValues = arangodb::iresearch::ValueStorage::ID; overrideNone._fields.clear(); // do not inherit fields to match jSon inheritance // without active vobcase (not fullAnalyzerDefinition) { std::unordered_set expectedFields = {"a", "b", "c"}; std::unordered_set expectedOverrides = {"default", "all", "some", "none"}; std::unordered_set expectedAnalyzers = { arangodb::StaticStrings::SystemDatabase + "::empty", "identity"}; arangodb::velocypack::Builder builder; arangodb::velocypack::Slice tmpSlice; builder.openObject(); EXPECT_TRUE(meta.json(builder, false)); builder.close(); auto slice = builder.slice(); EXPECT_EQ(5U, slice.length()); tmpSlice = slice.get("fields"); EXPECT_TRUE(tmpSlice.isObject() && 3 == tmpSlice.length()); for (arangodb::velocypack::ObjectIterator itr(tmpSlice); itr.valid(); ++itr) { auto key = itr.key(); auto value = itr.value(); EXPECT_TRUE(key.isString() && 1 == expectedFields.erase(key.copyString())); EXPECT_TRUE(value.isObject()); if (!value.hasKey("fields")) { continue; } tmpSlice = value.get("fields"); for (arangodb::velocypack::ObjectIterator overrideItr(tmpSlice); overrideItr.valid(); ++overrideItr) { auto fieldOverride = overrideItr.key(); auto sliceOverride = overrideItr.value(); EXPECT_TRUE(fieldOverride.isString() && sliceOverride.isObject()); EXPECT_EQ(1U, expectedOverrides.erase(fieldOverride.copyString())); if ("default" == fieldOverride.copyString()) { EXPECT_EQ(4U, sliceOverride.length()); tmpSlice = sliceOverride.get("includeAllFields"); EXPECT_TRUE((false == tmpSlice.getBool())); tmpSlice = sliceOverride.get("trackListPositions"); EXPECT_TRUE((false == tmpSlice.getBool())); tmpSlice = sliceOverride.get("storeValues"); EXPECT_TRUE((true == tmpSlice.isString() && std::string("none") == tmpSlice.copyString())); tmpSlice = sliceOverride.get("analyzers"); EXPECT_TRUE((true == tmpSlice.isArray() && 1 == tmpSlice.length() && tmpSlice.at(0).isString() && std::string("identity") == tmpSlice.at(0).copyString())); } else if ("all" == fieldOverride.copyString()) { std::unordered_set expectedFields = {"x", "y"}; EXPECT_EQ(5U, sliceOverride.length()); tmpSlice = sliceOverride.get("fields"); EXPECT_TRUE(tmpSlice.isObject() && 2 == tmpSlice.length()); for (arangodb::velocypack::ObjectIterator overrideFieldItr(tmpSlice); overrideFieldItr.valid(); ++overrideFieldItr) { EXPECT_TRUE((true == overrideFieldItr.key().isString() && 1 == expectedFields.erase(overrideFieldItr.key().copyString()))); } EXPECT_TRUE(expectedFields.empty()); tmpSlice = sliceOverride.get("includeAllFields"); EXPECT_TRUE(tmpSlice.isBool() && false == tmpSlice.getBool()); tmpSlice = sliceOverride.get("trackListPositions"); EXPECT_TRUE(tmpSlice.isBool() && false == tmpSlice.getBool()); tmpSlice = sliceOverride.get("storeValues"); EXPECT_TRUE((true == tmpSlice.isString() && std::string("none") == tmpSlice.copyString())); tmpSlice = sliceOverride.get("analyzers"); EXPECT_TRUE((true == tmpSlice.isArray() && 1 == tmpSlice.length() && tmpSlice.at(0).isString() && std::string(arangodb::StaticStrings::SystemDatabase + "::empty") == tmpSlice.at(0).copyString())); } else if ("some" == fieldOverride.copyString()) { EXPECT_EQ(2U, sliceOverride.length()); tmpSlice = sliceOverride.get("trackListPositions"); EXPECT_TRUE(tmpSlice.isBool() && false == tmpSlice.getBool()); tmpSlice = sliceOverride.get("storeValues"); EXPECT_TRUE((true == tmpSlice.isString() && std::string("id") == tmpSlice.copyString())); } else if ("none" == fieldOverride.copyString()) { EXPECT_EQ(0U, sliceOverride.length()); } } } EXPECT_TRUE(expectedOverrides.empty()); EXPECT_TRUE(expectedFields.empty()); tmpSlice = slice.get("includeAllFields"); EXPECT_TRUE(tmpSlice.isBool() && true == tmpSlice.getBool()); tmpSlice = slice.get("trackListPositions"); EXPECT_TRUE(tmpSlice.isBool() && true == tmpSlice.getBool()); tmpSlice = slice.get("storeValues"); EXPECT_TRUE(tmpSlice.isString() && std::string("value") == tmpSlice.copyString()); tmpSlice = slice.get("analyzers"); EXPECT_TRUE(tmpSlice.isArray() && 2 == tmpSlice.length()); for (arangodb::velocypack::ArrayIterator analyzersItr(tmpSlice); analyzersItr.valid(); ++analyzersItr) { auto key = *analyzersItr; EXPECT_TRUE(key.isString() && 1 == expectedAnalyzers.erase(key.copyString())); } EXPECT_TRUE(expectedAnalyzers.empty()); } // without active vobcase (with fullAnalyzerDefinition) { std::unordered_set expectedFields = {"a", "b", "c"}; std::unordered_set expectedOverrides = {"default", "all", "some", "none"}; std::unordered_set expectedAnalyzers = { arangodb::StaticStrings::SystemDatabase + "::empty", "identity"}; std::set> expectedAnalyzerDefinitions = { { arangodb::StaticStrings::SystemDatabase + "::empty", VPackParser::fromJson("{\"args\":\"en\"}")->slice().toString() }, {"identity", VPackSlice::emptyObjectSlice().toString()}, }; arangodb::velocypack::Builder builder; arangodb::velocypack::Slice tmpSlice; builder.openObject(); EXPECT_TRUE(meta.json(builder, true)); builder.close(); auto slice = builder.slice(); EXPECT_EQ(7U, slice.length()); tmpSlice = slice.get("fields"); EXPECT_TRUE(tmpSlice.isObject() && 3 == tmpSlice.length()); for (arangodb::velocypack::ObjectIterator itr(tmpSlice); itr.valid(); ++itr) { auto key = itr.key(); auto value = itr.value(); EXPECT_TRUE(key.isString() && 1 == expectedFields.erase(key.copyString())); EXPECT_TRUE(value.isObject()); if (!value.hasKey("fields")) { continue; } tmpSlice = value.get("fields"); for (arangodb::velocypack::ObjectIterator overrideItr(tmpSlice); overrideItr.valid(); ++overrideItr) { auto fieldOverride = overrideItr.key(); auto sliceOverride = overrideItr.value(); EXPECT_TRUE(fieldOverride.isString() && sliceOverride.isObject()); EXPECT_EQ(1U, expectedOverrides.erase(fieldOverride.copyString())); if ("default" == fieldOverride.copyString()) { EXPECT_EQ(4U, sliceOverride.length()); tmpSlice = sliceOverride.get("includeAllFields"); EXPECT_TRUE((false == tmpSlice.getBool())); tmpSlice = sliceOverride.get("trackListPositions"); EXPECT_TRUE((false == tmpSlice.getBool())); tmpSlice = sliceOverride.get("storeValues"); EXPECT_TRUE((true == tmpSlice.isString() && std::string("none") == tmpSlice.copyString())); tmpSlice = sliceOverride.get("analyzers"); EXPECT_TRUE((true == tmpSlice.isArray() && 1 == tmpSlice.length() && tmpSlice.at(0).isString() && std::string("identity") == tmpSlice.at(0).copyString())); } else if ("all" == fieldOverride.copyString()) { std::unordered_set expectedFields = {"x", "y"}; EXPECT_EQ(5U, sliceOverride.length()); tmpSlice = sliceOverride.get("fields"); EXPECT_TRUE(tmpSlice.isObject() && 2 == tmpSlice.length()); for (arangodb::velocypack::ObjectIterator overrideFieldItr(tmpSlice); overrideFieldItr.valid(); ++overrideFieldItr) { EXPECT_TRUE((true == overrideFieldItr.key().isString() && 1 == expectedFields.erase(overrideFieldItr.key().copyString()))); } EXPECT_TRUE(expectedFields.empty()); tmpSlice = sliceOverride.get("includeAllFields"); EXPECT_TRUE(tmpSlice.isBool() && false == tmpSlice.getBool()); tmpSlice = sliceOverride.get("trackListPositions"); EXPECT_TRUE(tmpSlice.isBool() && false == tmpSlice.getBool()); tmpSlice = sliceOverride.get("storeValues"); EXPECT_TRUE((true == tmpSlice.isString() && std::string("none") == tmpSlice.copyString())); tmpSlice = sliceOverride.get("analyzers"); EXPECT_TRUE((true == tmpSlice.isArray() && 1 == tmpSlice.length() && tmpSlice.at(0).isString() && arangodb::StaticStrings::SystemDatabase + "::empty" == tmpSlice.at(0).copyString())); } else if ("some" == fieldOverride.copyString()) { EXPECT_EQ(2U, sliceOverride.length()); tmpSlice = sliceOverride.get("trackListPositions"); EXPECT_TRUE(tmpSlice.isBool() && false == tmpSlice.getBool()); tmpSlice = sliceOverride.get("storeValues"); EXPECT_TRUE((true == tmpSlice.isString() && std::string("id") == tmpSlice.copyString())); } else if ("none" == fieldOverride.copyString()) { EXPECT_EQ(0U, sliceOverride.length()); } } } EXPECT_TRUE(expectedOverrides.empty()); EXPECT_TRUE(expectedFields.empty()); tmpSlice = slice.get("includeAllFields"); EXPECT_TRUE(tmpSlice.isBool() && true == tmpSlice.getBool()); tmpSlice = slice.get("trackListPositions"); EXPECT_TRUE(tmpSlice.isBool() && true == tmpSlice.getBool()); tmpSlice = slice.get("storeValues"); EXPECT_TRUE(tmpSlice.isString() && std::string("value") == tmpSlice.copyString()); tmpSlice = slice.get("analyzers"); EXPECT_TRUE(tmpSlice.isArray() && 2 == tmpSlice.length()); for (arangodb::velocypack::ArrayIterator analyzersItr(tmpSlice); analyzersItr.valid(); ++analyzersItr) { auto key = *analyzersItr; EXPECT_TRUE(key.isString() && 1 == expectedAnalyzers.erase(key.copyString())); } EXPECT_TRUE(expectedAnalyzers.empty()); tmpSlice = slice.get("analyzerDefinitions"); EXPECT_TRUE(tmpSlice.isArray() && 2 == tmpSlice.length()); for (arangodb::velocypack::ArrayIterator analyzersItr(tmpSlice); analyzersItr.valid(); ++analyzersItr) { auto value = *analyzersItr; EXPECT_TRUE( (true == value.isObject() && value.hasKey("name") && value.get("name").isString() && value.hasKey("type") && value.get("type").isString() && value.hasKey("properties") && (value.get("properties").isObject() || value.get("properties").isNull()) && value.hasKey("features") && value.get("features").isArray() && (1 == value.get("features").length() || 2 == value.get("features").length()) // empty/identity 1/2 && 1 == expectedAnalyzerDefinitions.erase( std::make_pair(value.get("name").copyString(), value.get("properties").isNull() ? "" : value.get("properties").toString())))); } EXPECT_TRUE(expectedAnalyzerDefinitions.empty()); std::string errorField; tmpSlice = slice.get("primarySort"); EXPECT_TRUE(tmpSlice.isArray()); arangodb::iresearch::IResearchViewSort sort; EXPECT_TRUE(sort.fromVelocyPack(tmpSlice, errorField)); EXPECT_EQ(2, sort.size()); EXPECT_TRUE(sort.direction(0)); EXPECT_TRUE((std::vector{{"_key", false}} == sort.field(0))); EXPECT_FALSE(sort.direction(1)); EXPECT_TRUE((std::vector{{"_id", false}} == sort.field(1))); } // with active vocbase (not fullAnalyzerDefinition) { std::unordered_set expectedFields = {"a", "b", "c"}; std::unordered_set expectedOverrides = {"default", "all", "some", "none"}; std::unordered_set expectedAnalyzers = {"::empty", "identity"}; TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, testDBInfo(server.server())); arangodb::velocypack::Builder builder; arangodb::velocypack::Slice tmpSlice; builder.openObject(); EXPECT_TRUE(meta.json(builder, false, nullptr, &vocbase)); builder.close(); auto slice = builder.slice(); EXPECT_EQ(5U, slice.length()); tmpSlice = slice.get("fields"); EXPECT_TRUE(tmpSlice.isObject() && 3 == tmpSlice.length()); for (arangodb::velocypack::ObjectIterator itr(tmpSlice); itr.valid(); ++itr) { auto key = itr.key(); auto value = itr.value(); EXPECT_TRUE(key.isString() && 1 == expectedFields.erase(key.copyString())); EXPECT_TRUE(value.isObject()); if (!value.hasKey("fields")) { continue; } tmpSlice = value.get("fields"); for (arangodb::velocypack::ObjectIterator overrideItr(tmpSlice); overrideItr.valid(); ++overrideItr) { auto fieldOverride = overrideItr.key(); auto sliceOverride = overrideItr.value(); EXPECT_TRUE(fieldOverride.isString() && sliceOverride.isObject()); EXPECT_EQ(1U, expectedOverrides.erase(fieldOverride.copyString())); if ("default" == fieldOverride.copyString()) { EXPECT_EQ(4U, sliceOverride.length()); tmpSlice = sliceOverride.get("includeAllFields"); EXPECT_TRUE((false == tmpSlice.getBool())); tmpSlice = sliceOverride.get("trackListPositions"); EXPECT_TRUE((false == tmpSlice.getBool())); tmpSlice = sliceOverride.get("storeValues"); EXPECT_TRUE((true == tmpSlice.isString() && std::string("none") == tmpSlice.copyString())); tmpSlice = sliceOverride.get("analyzers"); EXPECT_TRUE((true == tmpSlice.isArray() && 1 == tmpSlice.length() && tmpSlice.at(0).isString() && std::string("identity") == tmpSlice.at(0).copyString())); } else if ("all" == fieldOverride.copyString()) { std::unordered_set expectedFields = {"x", "y"}; EXPECT_EQ(5U, sliceOverride.length()); tmpSlice = sliceOverride.get("fields"); EXPECT_TRUE(tmpSlice.isObject() && 2 == tmpSlice.length()); for (arangodb::velocypack::ObjectIterator overrideFieldItr(tmpSlice); overrideFieldItr.valid(); ++overrideFieldItr) { EXPECT_TRUE((true == overrideFieldItr.key().isString() && 1 == expectedFields.erase(overrideFieldItr.key().copyString()))); } EXPECT_TRUE(expectedFields.empty()); tmpSlice = sliceOverride.get("includeAllFields"); EXPECT_TRUE(tmpSlice.isBool() && false == tmpSlice.getBool()); tmpSlice = sliceOverride.get("trackListPositions"); EXPECT_TRUE(tmpSlice.isBool() && false == tmpSlice.getBool()); tmpSlice = sliceOverride.get("storeValues"); EXPECT_TRUE((true == tmpSlice.isString() && std::string("none") == tmpSlice.copyString())); tmpSlice = sliceOverride.get("analyzers"); EXPECT_TRUE((true == tmpSlice.isArray() && 1 == tmpSlice.length() && tmpSlice.at(0).isString() && std::string("::empty") == tmpSlice.at(0).copyString())); } else if ("some" == fieldOverride.copyString()) { EXPECT_EQ(2U, sliceOverride.length()); tmpSlice = sliceOverride.get("trackListPositions"); EXPECT_TRUE(tmpSlice.isBool() && false == tmpSlice.getBool()); tmpSlice = sliceOverride.get("storeValues"); EXPECT_TRUE((true == tmpSlice.isString() && std::string("id") == tmpSlice.copyString())); } else if ("none" == fieldOverride.copyString()) { EXPECT_EQ(0U, sliceOverride.length()); } } } EXPECT_TRUE(expectedOverrides.empty()); EXPECT_TRUE(expectedFields.empty()); tmpSlice = slice.get("includeAllFields"); EXPECT_TRUE(tmpSlice.isBool() && true == tmpSlice.getBool()); tmpSlice = slice.get("trackListPositions"); EXPECT_TRUE(tmpSlice.isBool() && true == tmpSlice.getBool()); tmpSlice = slice.get("storeValues"); EXPECT_TRUE(tmpSlice.isString() && std::string("value") == tmpSlice.copyString()); tmpSlice = slice.get("analyzers"); EXPECT_TRUE(tmpSlice.isArray() && 2 == tmpSlice.length()); for (arangodb::velocypack::ArrayIterator analyzersItr(tmpSlice); analyzersItr.valid(); ++analyzersItr) { auto key = *analyzersItr; EXPECT_TRUE(key.isString() && 1 == expectedAnalyzers.erase(key.copyString())); } EXPECT_TRUE(expectedAnalyzers.empty()); } // with active vocbase (with fullAnalyzerDefinition) { std::unordered_set expectedFields = {"a", "b", "c"}; std::unordered_set expectedOverrides = {"default", "all", "some", "none"}; std::unordered_set expectedAnalyzers = { "::empty", "identity"}; std::set> expectedAnalyzerDefinitions = { {"::empty", VPackParser::fromJson("{\"args\":\"en\"}")->slice().toString()}, {"identity", VPackSlice::emptyObjectSlice().toString()}, }; TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, testDBInfo(server.server())); arangodb::velocypack::Builder builder; arangodb::velocypack::Slice tmpSlice; builder.openObject(); EXPECT_TRUE(meta.json(builder, true, nullptr, &vocbase)); builder.close(); auto slice = builder.slice(); EXPECT_EQ(7U, slice.length()); tmpSlice = slice.get("fields"); EXPECT_TRUE(tmpSlice.isObject() && 3 == tmpSlice.length()); for (arangodb::velocypack::ObjectIterator itr(tmpSlice); itr.valid(); ++itr) { auto key = itr.key(); auto value = itr.value(); EXPECT_TRUE(key.isString() && 1 == expectedFields.erase(key.copyString())); EXPECT_TRUE(value.isObject()); if (!value.hasKey("fields")) { continue; } tmpSlice = value.get("fields"); for (arangodb::velocypack::ObjectIterator overrideItr(tmpSlice); overrideItr.valid(); ++overrideItr) { auto fieldOverride = overrideItr.key(); auto sliceOverride = overrideItr.value(); EXPECT_TRUE(fieldOverride.isString() && sliceOverride.isObject()); EXPECT_EQ(1U, expectedOverrides.erase(fieldOverride.copyString())); if ("default" == fieldOverride.copyString()) { EXPECT_EQ(4U, sliceOverride.length()); tmpSlice = sliceOverride.get("includeAllFields"); EXPECT_TRUE((false == tmpSlice.getBool())); tmpSlice = sliceOverride.get("trackListPositions"); EXPECT_TRUE((false == tmpSlice.getBool())); tmpSlice = sliceOverride.get("storeValues"); EXPECT_TRUE((true == tmpSlice.isString() && std::string("none") == tmpSlice.copyString())); tmpSlice = sliceOverride.get("analyzers"); EXPECT_TRUE((true == tmpSlice.isArray() && 1 == tmpSlice.length() && tmpSlice.at(0).isString() && std::string("identity") == tmpSlice.at(0).copyString())); } else if ("all" == fieldOverride.copyString()) { std::unordered_set expectedFields = {"x", "y"}; EXPECT_EQ(5U, sliceOverride.length()); tmpSlice = sliceOverride.get("fields"); EXPECT_TRUE(tmpSlice.isObject() && 2 == tmpSlice.length()); for (arangodb::velocypack::ObjectIterator overrideFieldItr(tmpSlice); overrideFieldItr.valid(); ++overrideFieldItr) { EXPECT_TRUE((true == overrideFieldItr.key().isString() && 1 == expectedFields.erase(overrideFieldItr.key().copyString()))); } EXPECT_TRUE(expectedFields.empty()); tmpSlice = sliceOverride.get("includeAllFields"); EXPECT_TRUE(tmpSlice.isBool() && false == tmpSlice.getBool()); tmpSlice = sliceOverride.get("trackListPositions"); EXPECT_TRUE(tmpSlice.isBool() && false == tmpSlice.getBool()); tmpSlice = sliceOverride.get("storeValues"); EXPECT_TRUE((true == tmpSlice.isString() && std::string("none") == tmpSlice.copyString())); tmpSlice = sliceOverride.get("analyzers"); EXPECT_TRUE((true == tmpSlice.isArray() && 1 == tmpSlice.length() && tmpSlice.at(0).isString() && "::empty" == tmpSlice.at(0).copyString())); } else if ("some" == fieldOverride.copyString()) { EXPECT_EQ(2U, sliceOverride.length()); tmpSlice = sliceOverride.get("trackListPositions"); EXPECT_TRUE(tmpSlice.isBool() && false == tmpSlice.getBool()); tmpSlice = sliceOverride.get("storeValues"); EXPECT_TRUE((true == tmpSlice.isString() && std::string("id") == tmpSlice.copyString())); } else if ("none" == fieldOverride.copyString()) { EXPECT_EQ(0U, sliceOverride.length()); } } } EXPECT_TRUE(expectedOverrides.empty()); EXPECT_TRUE(expectedFields.empty()); tmpSlice = slice.get("includeAllFields"); EXPECT_TRUE(tmpSlice.isBool() && true == tmpSlice.getBool()); tmpSlice = slice.get("trackListPositions"); EXPECT_TRUE(tmpSlice.isBool() && true == tmpSlice.getBool()); tmpSlice = slice.get("storeValues"); EXPECT_TRUE(tmpSlice.isString() && std::string("value") == tmpSlice.copyString()); tmpSlice = slice.get("analyzers"); EXPECT_TRUE(tmpSlice.isArray() && 2 == tmpSlice.length()); for (arangodb::velocypack::ArrayIterator analyzersItr(tmpSlice); analyzersItr.valid(); ++analyzersItr) { auto key = *analyzersItr; EXPECT_TRUE(key.isString() && 1 == expectedAnalyzers.erase(key.copyString())); } EXPECT_TRUE(expectedAnalyzers.empty()); tmpSlice = slice.get("analyzerDefinitions"); EXPECT_TRUE(tmpSlice.isArray() && 2 == tmpSlice.length()); for (arangodb::velocypack::ArrayIterator analyzersItr(tmpSlice); analyzersItr.valid(); ++analyzersItr) { auto value = *analyzersItr; EXPECT_TRUE( (true == value.isObject() && value.hasKey("name") && value.get("name").isString() && value.hasKey("type") && value.get("type").isString() && value.hasKey("properties") && (value.get("properties").isObject() || value.get("properties").isNull()) && value.hasKey("features") && value.get("features").isArray() && (1 == value.get("features").length() || 2 == value.get("features").length()) // empty/identity 1/2 && 1 == expectedAnalyzerDefinitions.erase( std::make_pair(value.get("name").copyString(), value.get("properties").isNull() ? "" : value.get("properties").toString())))); } EXPECT_TRUE(expectedAnalyzerDefinitions.empty()); std::string errorField; tmpSlice = slice.get("primarySort"); EXPECT_TRUE(tmpSlice.isArray()); EXPECT_EQ(2, tmpSlice.length()); { auto valueSlice = tmpSlice.at(0); EXPECT_TRUE(valueSlice.isObject()); EXPECT_EQ(2, valueSlice.length()); EXPECT_TRUE(valueSlice.get("field").isString()); EXPECT_EQ("_key", valueSlice.get("field").copyString()); EXPECT_TRUE(valueSlice.get("asc").isBool()); EXPECT_TRUE(valueSlice.get("asc").getBool()); } { auto valueSlice = tmpSlice.at(1); EXPECT_TRUE(valueSlice.isObject()); EXPECT_EQ(2, valueSlice.length()); EXPECT_TRUE(valueSlice.get("field").isString()); EXPECT_EQ("_id", valueSlice.get("field").copyString()); EXPECT_TRUE(valueSlice.get("asc").isBool()); EXPECT_FALSE(valueSlice.get("asc").getBool()); } } } TEST_F(IResearchLinkMetaTest, test_readMaskAll) { arangodb::iresearch::IResearchLinkMeta meta; arangodb::iresearch::IResearchLinkMeta::Mask mask; std::string tmpString; auto json = VPackParser::fromJson( "{ \ \"fields\": { \"a\": {} }, \ \"includeAllFields\": true, \ \"trackListPositions\": true, \ \"storeValues\": \"value\", \ \"analyzers\": [] \ }"); EXPECT_TRUE(true == meta.init(json->slice(), false, tmpString, nullptr, arangodb::iresearch::IResearchLinkMeta::DEFAULT(), &mask)); EXPECT_TRUE(mask._fields); EXPECT_TRUE(mask._includeAllFields); EXPECT_TRUE(mask._trackListPositions); EXPECT_TRUE(mask._storeValues); EXPECT_TRUE(mask._analyzers); } TEST_F(IResearchLinkMetaTest, test_readMaskNone) { arangodb::iresearch::IResearchLinkMeta meta; arangodb::iresearch::IResearchLinkMeta::Mask mask; std::string tmpString; auto json = VPackParser::fromJson("{}"); EXPECT_TRUE(true == meta.init(json->slice(), false, tmpString, nullptr, arangodb::iresearch::IResearchLinkMeta::DEFAULT(), &mask)); EXPECT_FALSE(mask._fields); EXPECT_FALSE(mask._includeAllFields); EXPECT_FALSE(mask._trackListPositions); EXPECT_FALSE(mask._storeValues); EXPECT_FALSE(mask._analyzers); } TEST_F(IResearchLinkMetaTest, test_writeMaskAll) { // not fullAnalyzerDefinition { arangodb::iresearch::IResearchLinkMeta meta; arangodb::iresearch::IResearchLinkMeta::Mask mask(true); arangodb::velocypack::Builder builder; builder.openObject(); EXPECT_TRUE(meta.json(builder, false, nullptr, nullptr, &mask)); builder.close(); auto slice = builder.slice(); EXPECT_EQ(5U, slice.length()); EXPECT_TRUE(slice.hasKey("fields")); EXPECT_TRUE(slice.hasKey("includeAllFields")); EXPECT_TRUE(slice.hasKey("trackListPositions")); EXPECT_TRUE(slice.hasKey("storeValues")); EXPECT_TRUE(slice.hasKey("analyzers")); } // with fullAnalyzerDefinition { arangodb::iresearch::IResearchLinkMeta meta; arangodb::iresearch::IResearchLinkMeta::Mask mask(true); arangodb::velocypack::Builder builder; builder.openObject(); EXPECT_TRUE(meta.json(builder, true, nullptr, nullptr, &mask)); builder.close(); auto slice = builder.slice(); EXPECT_EQ(7U, slice.length()); EXPECT_TRUE(slice.hasKey("fields")); EXPECT_TRUE(slice.hasKey("includeAllFields")); EXPECT_TRUE(slice.hasKey("trackListPositions")); EXPECT_TRUE(slice.hasKey("storeValues")); EXPECT_TRUE(slice.hasKey("analyzers")); EXPECT_TRUE(slice.hasKey("analyzerDefinitions")); EXPECT_TRUE(slice.hasKey("primarySort")); } } TEST_F(IResearchLinkMetaTest, test_writeMaskNone) { // not fullAnalyzerDefinition { arangodb::iresearch::IResearchLinkMeta meta; arangodb::iresearch::IResearchLinkMeta::Mask mask(false); arangodb::velocypack::Builder builder; builder.openObject(); EXPECT_TRUE(meta.json(builder, false, nullptr, nullptr, &mask)); builder.close(); auto slice = builder.slice(); EXPECT_EQ(0U, slice.length()); } // with fullAnalyzerDefinition { arangodb::iresearch::IResearchLinkMeta meta; arangodb::iresearch::IResearchLinkMeta::Mask mask(false); arangodb::velocypack::Builder builder; builder.openObject(); EXPECT_TRUE(meta.json(builder, true, nullptr, nullptr, &mask)); builder.close(); auto slice = builder.slice(); EXPECT_EQ(0U, slice.length()); } } TEST_F(IResearchLinkMetaTest, test_readAnalyzerDefinitions) { TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, testDBInfo(server.server())); // missing analyzer (name only) { auto json = VPackParser::fromJson( "{ \ \"analyzers\": [ \"empty1\" ] \ }"); arangodb::iresearch::IResearchLinkMeta meta; std::string errorField; EXPECT_FALSE(meta.init(json->slice(), true, errorField, &vocbase)); EXPECT_EQ(std::string("analyzers.empty1"), errorField); } // missing analyzer (name only) inRecovery { auto json = VPackParser::fromJson( "{ \ \"analyzers\": [ \"empty1\" ] \ }"); 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(std::string("analyzers.empty1"), errorField); } // missing analyzer (full) no name (fail) required { auto json = VPackParser::fromJson( "{ \ \"analyzerDefinitions\": [ { \"type\": \"empty\", \"properties\": \"ru\", \"features\": [ \"frequency\" ] } ], \ \"analyzers\": [ \"empty\" ] \ }"); arangodb::iresearch::IResearchLinkMeta meta; std::string errorField; EXPECT_FALSE(meta.init(json->slice(), true, errorField, &vocbase)); EXPECT_EQ(std::string("analyzerDefinitions[0].name"), errorField); } // missing analyzer (full) no type (fail) required { auto json = VPackParser::fromJson( "{ \ \"analyzerDefinitions\": [ { \"name\": \"missing0\", \"properties\": \"ru\", \"features\": [ \"frequency\" ] } ], \ \"analyzers\": [ \"missing0\" ] \ }"); arangodb::iresearch::IResearchLinkMeta meta; std::string errorField; EXPECT_FALSE(meta.init(json->slice(), true, errorField, &vocbase)); 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 { auto json = VPackParser::fromJson( "{ \ \"analyzerDefinitions\": [ { \"name\": \"missing0\", \"type\": \"empty\", \"properties\": {\"args\":\"ru\"}, \"features\": [ \"frequency\" ] } ], \ \"analyzers\": [ \"missing0\" ] \ }"); arangodb::iresearch::IResearchLinkMeta meta; std::string errorField; EXPECT_TRUE(meta.init(json->slice(), true, errorField, &vocbase)); EXPECT_EQ(1, meta._analyzers.size()); EXPECT_EQ(std::string("testVocbase::missing0"), meta._analyzers[0]._pool->name()); EXPECT_EQ(std::string("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("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 { 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_TRUE(meta.init(json->slice(), true, errorField, &vocbase)); 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 { 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_TRUE(meta.init(json->slice(), true, errorField, &vocbase)); EXPECT_EQ(1, meta._analyzers.size()); EXPECT_EQ("testVocbase::missing2", meta._analyzers[0]._pool->name()); EXPECT_EQ("empty", meta._analyzers[0]._pool->type()); EXPECT_EQ(std::string("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("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 { 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_TRUE(meta.init(json->slice(), true, errorField, &vocbase)); 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) { auto json = VPackParser::fromJson( "{ \ \"analyzers\": [ \"empty\" ] \ }"); arangodb::iresearch::IResearchLinkMeta meta; std::string errorField; EXPECT_TRUE(meta.init(json->slice(), true, errorField, &vocbase)); EXPECT_EQ(1, meta._analyzers.size()); EXPECT_EQ(std::string("testVocbase::empty"), meta._analyzers[0]._pool->name()); EXPECT_EQ(std::string("empty"), meta._analyzers[0]._pool->type()); 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(std::string("empty"), meta._analyzers[0]._shortName); } // existing analyzer (name only) inRecovery { auto json = VPackParser::fromJson( "{ \ \"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(), true, errorField, &vocbase)); EXPECT_EQ(1, meta._analyzers.size()); EXPECT_EQ(std::string("testVocbase::empty"), meta._analyzers[0]._pool->name()); EXPECT_EQ(std::string("empty"), meta._analyzers[0]._pool->type()); 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(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) { auto json = VPackParser::fromJson( "{ \ \"analyzerDefinitions\": [ { \"name\": \"empty\", \"type\": \"empty\", \"properties\": {\"args\":\"de\"}, \"features\": [ \"frequency\" ] } ], \ \"analyzers\": [ \"empty\" ] \ }"); arangodb::iresearch::IResearchLinkMeta meta; std::string errorField; EXPECT_TRUE(meta.init(json->slice(), true, errorField, &vocbase)); EXPECT_EQ(1, meta._analyzers.size()); EXPECT_EQ(std::string("testVocbase::empty"), meta._analyzers[0]._pool->name()); EXPECT_EQ(std::string("empty"), meta._analyzers[0]._pool->type()); 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(std::string("empty"), meta._analyzers[0]._shortName); } // existing analyzer (full) analyzer definition not allowed { auto json = VPackParser::fromJson( "{ \ \"analyzers\": [ { \"name\": \"empty\", \"type\": \"empty\", \"properties\": {\"args\":\"de\"}, \"features\": [ \"frequency\" ] } ], \ \"analyzers\": [ \"empty\" ] \ }"); arangodb::iresearch::IResearchLinkMeta meta; std::string errorField; EXPECT_FALSE(meta.init(json->slice(), false, errorField, &vocbase)); EXPECT_EQ(std::string("analyzers[0]"), errorField); } // existing analyzer (full) { auto json = VPackParser::fromJson( "{ \ \"analyzerDefinitions\": [ { \"name\": \"empty\", \"type\": \"empty\", \"properties\": {\"args\":\"de\"}, \"features\": [ \"frequency\" ] } ], \ \"analyzers\": [ \"empty\" ] \ }"); arangodb::iresearch::IResearchLinkMeta meta; std::string errorField; EXPECT_TRUE(meta.init(json->slice(), true, errorField, &vocbase)); EXPECT_EQ(1, meta._analyzers.size()); EXPECT_EQ(std::string("testVocbase::empty"), meta._analyzers[0]._pool->name()); EXPECT_EQ(std::string("empty"), meta._analyzers[0]._pool->type()); 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(std::string("empty"), meta._analyzers[0]._shortName); } // existing analyzer (full) inRecovery { auto json = VPackParser::fromJson( "{ \ \"analyzerDefinitions\": [ { \"name\": \"empty\", \"type\": \"empty\", \"properties\": {\"args\":\"de\"}, \"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(), true, 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()); 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) { 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(), true, 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()); 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 { 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(), true, 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\" : \"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); } } // https://github.com/arangodb/backlog/issues/581 // (ArangoSearch view doesn't validate uniqueness of analyzers) TEST_F(IResearchLinkMetaTest, test_addNonUniqueAnalyzers) { TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, testDBInfo(server.server())); auto& analyzers = server.getFeature(); const std::string analyzerCustomName = "test_addNonUniqueAnalyzersMyIdentity"; const std::string analyzerCustomInSystem = arangodb::StaticStrings::SystemDatabase + "::" + analyzerCustomName; const std::string analyzerCustomInTestVocbase = vocbase.name() + "::" + analyzerCustomName; // this is for test cleanup auto testCleanup = irs::make_finally([&analyzerCustomInSystem, &analyzers, &analyzerCustomInTestVocbase]() -> void { analyzers.remove(analyzerCustomInSystem); analyzers.remove(analyzerCustomInTestVocbase); }); { arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult emplaceResult; // we need custom analyzer in _SYSTEM database and other will be in testVocbase with same name (it is ok to add both!). analyzers.emplace(emplaceResult, analyzerCustomInSystem, "identity", VPackParser::fromJson("{ \"args\": \"en\" }")->slice(), {irs::frequency::type()}); } { arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult emplaceResult; analyzers.emplace(emplaceResult, analyzerCustomInTestVocbase, "identity", VPackParser::fromJson("{ \"args\": \"en\" }")->slice(), {irs::frequency::type()}); } { const std::string testJson = std::string( "{ \ \"includeAllFields\": true, \ \"trackListPositions\": true, \ \"storeValues\": \"value\",\ \"analyzers\": [ \"identity\",\"identity\"" // two built-in analyzers ", \"" + analyzerCustomInTestVocbase + "\"" + // local analyzer by full name ", \"" + analyzerCustomName + "\"" + // local analyzer by short name ", \"::" + analyzerCustomName + "\"" + // _system analyzer by short name ", \"" + analyzerCustomInSystem + "\"" + // _system analyzer by full name " ] \ }"); arangodb::iresearch::IResearchLinkMeta meta; std::unordered_set expectedAnalyzers; expectedAnalyzers.insert(analyzers.get("identity")->name()); expectedAnalyzers.insert(analyzers.get(analyzerCustomInTestVocbase)->name()); expectedAnalyzers.insert(analyzers.get(analyzerCustomInSystem)->name()); arangodb::iresearch::IResearchLinkMeta::Mask mask(false); auto json = VPackParser::fromJson(testJson); std::string errorField; EXPECT_TRUE(true == meta.init(json->slice(), false, errorField, &vocbase, arangodb::iresearch::IResearchLinkMeta::DEFAULT(), &mask)); EXPECT_TRUE(mask._analyzers); EXPECT_TRUE(errorField.empty()); EXPECT_EQ(expectedAnalyzers.size(), meta._analyzers.size()); for (decltype(meta._analyzers)::const_iterator analyzersItr = meta._analyzers.begin(); analyzersItr != meta._analyzers.end(); ++analyzersItr) { EXPECT_EQ(1, expectedAnalyzers.erase(analyzersItr->_pool->name())); } EXPECT_TRUE(expectedAnalyzers.empty()); } }