Allow override rule to have only tags w/o query or filter by.

This commit is contained in:
Kishore Nallan 2023-12-12 13:42:19 +05:30
parent 16f7813e02
commit 9802ac1d5e
5 changed files with 107 additions and 17 deletions

View File

@ -187,7 +187,7 @@ private:
std::set<uint32_t>& excluded_set,
string& actual_query, const string& filter_query,
bool already_segmented,
const std::set<std::string>& tags,
const bool tags_matched,
const std::map<size_t, std::vector<std::string>>& pinned_hits,
const std::vector<std::string>& hidden_hits,
std::vector<std::pair<uint32_t, uint32_t>>& included_ids,

View File

@ -814,7 +814,7 @@ bool Collection::does_override_match(const override_t& override, std::string& qu
std::set<uint32_t>& excluded_set,
string& actual_query, const string& filter_query,
bool already_segmented,
const std::set<std::string>& tags,
const bool tags_matched,
const std::map<size_t, std::vector<std::string>>& pinned_hits,
const std::vector<std::string>& hidden_hits,
std::vector<std::pair<uint32_t, uint32_t>>& included_ids,
@ -838,19 +838,23 @@ bool Collection::does_override_match(const override_t& override, std::string& qu
filter_overrides.push_back(&override);
}
bool filter_by_match = (override.rule.query.empty() && override.rule.match.empty() &&
!override.rule.filter_by.empty() && override.rule.filter_by == filter_query);
if(tags_matched && override.rule.query.empty() && override.rule.filter_by.empty()) {
// allowed
} else {
bool filter_by_match = (override.rule.query.empty() && override.rule.match.empty() &&
!override.rule.filter_by.empty() && override.rule.filter_by == filter_query);
bool query_match = (override.rule.match == override_t::MATCH_EXACT && override.rule.normalized_query == query) ||
(override.rule.match == override_t::MATCH_CONTAINS &&
StringUtils::contains_word(query, override.rule.normalized_query));
bool query_match = (override.rule.match == override_t::MATCH_EXACT && override.rule.normalized_query == query) ||
(override.rule.match == override_t::MATCH_CONTAINS &&
StringUtils::contains_word(query, override.rule.normalized_query));
if(!filter_by_match && !query_match) {
return false;
}
if(!filter_by_match && !query_match) {
return false;
}
if(!override.rule.filter_by.empty() && override.rule.filter_by != filter_query) {
return false;
if(!override.rule.filter_by.empty() && override.rule.filter_by != filter_query) {
return false;
}
}
// have to ensure that dropped hits take precedence over added hits
@ -953,7 +957,7 @@ void Collection::curate_results(string& actual_query, const string& filter_query
if(override.rule.tags == tags) {
bool match_found = does_override_match(override, query, excluded_set, actual_query,
filter_query, already_segmented, tags,
filter_query, already_segmented, true,
pinned_hits, hidden_hits, included_ids,
excluded_ids, filter_overrides, filter_curated_hits,
curated_sort_by, override_metadata);
@ -1000,7 +1004,7 @@ void Collection::curate_results(string& actual_query, const string& filter_query
}
bool match_found = does_override_match(override, query, excluded_set, actual_query,
filter_query, already_segmented, tags,
filter_query, already_segmented, true,
pinned_hits, hidden_hits, included_ids,
excluded_ids, filter_overrides, filter_curated_hits,
curated_sort_by, override_metadata);
@ -1018,7 +1022,7 @@ void Collection::curate_results(string& actual_query, const string& filter_query
for(const auto& override_kv: overrides) {
const auto& override = override_kv.second;
bool match_found = does_override_match(override, query, excluded_set, actual_query, filter_query,
already_segmented, tags, pinned_hits, hidden_hits, included_ids,
already_segmented, false, pinned_hits, hidden_hits, included_ids,
excluded_ids, filter_overrides, filter_curated_hits,
curated_sort_by, override_metadata);
if(match_found && override.stop_processing) {

View File

@ -2010,8 +2010,11 @@ bool Index::static_filter_query_eval(const override_t* override,
std::vector<std::string>& tokens,
filter_node_t*& filter_tree_root) const {
std::string query = StringUtils::join(tokens, " ");
bool tag_matched = (!override->rule.tags.empty() && override->rule.filter_by.empty() &&
override->rule.query.empty());
if ((override->rule.match == override_t::MATCH_EXACT && override->rule.normalized_query == query) ||
if (tag_matched ||
(override->rule.match == override_t::MATCH_EXACT && override->rule.normalized_query == query) ||
(override->rule.match == override_t::MATCH_CONTAINS &&
StringUtils::contains_word(query, override->rule.normalized_query))) {
filter_node_t* new_filter_tree_root = nullptr;

View File

@ -15,7 +15,7 @@ Option<bool> override_t::parse(const nlohmann::json& override_json, const std::s
return Option<bool>(400, "Missing `rule` definition.");
}
if (override_json["rule"].count("filter_by") == 0 &&
if (override_json["rule"].count("filter_by") == 0 && override_json["rule"].count("tags") == 0 &&
(override_json["rule"].count("query") == 0 || override_json["rule"].count("match") == 0)) {
return Option<bool>(400, "The `rule` definition must contain a `query` and `match`.");
}

View File

@ -3627,6 +3627,89 @@ TEST_F(CollectionOverrideTest, OverrideWithTagsWithoutStopProcessing) {
collectionManager.drop_collection("coll1");
}
TEST_F(CollectionOverrideTest, TagsOnlyRule) {
Collection* coll1;
std::vector<field> fields = {field("name", field_types::STRING, false),
field("category", field_types::STRING_ARRAY, true),};
coll1 = collectionManager.get_collection("coll1").get();
if (coll1 == nullptr) {
coll1 = collectionManager.create_collection("coll1", 1, fields, "").get();
}
nlohmann::json doc1;
doc1["id"] = "0";
doc1["name"] = "queryA";
doc1["category"] = {"kids"};
nlohmann::json doc2;
doc2["id"] = "1";
doc2["name"] = "queryA";
doc2["category"] = {"kitchen"};
ASSERT_TRUE(coll1->add(doc1.dump()).ok());
ASSERT_TRUE(coll1->add(doc2.dump()).ok());
std::vector<sort_by> sort_fields = {sort_by("_text_match", "DESC")};
override_t override1;
auto override_json1 = R"({
"id": "ov-1",
"rule": {
"tags": ["listing"]
},
"filter_by": "category: kids"
})"_json;
auto op = override_t::parse(override_json1, "ov-1", override1);
ASSERT_TRUE(op.ok());
coll1->add_override(override1);
auto results = coll1->search("queryA", {"name"}, "",
{}, sort_fields, {2}, 10, 1, FREQUENCY,
{false}, Index::DROP_TOKENS_THRESHOLD,
spp::sparse_hash_set<std::string>(),
spp::sparse_hash_set<std::string>(), 10, "", 30, 4, "title", 20, {}, {}, {}, 0,
"<mark>", "</mark>", {}, 1000, true, false, true, "", false, 10000,
4, 7, fallback, 4, {off}, 100, 100, 2, 2, false, "", true, 0, max_score, 100, 0,
0, HASH, 30000, 2, "", {}, {}, "right_to_left",
true, true, false, -1, "", "listing").get();
ASSERT_EQ(1, results["hits"].size());
ASSERT_EQ("0", results["hits"][0]["document"]["id"].get<std::string>());
// with include rule
override_t override2;
auto override_json2 = R"({
"id": "ov-2",
"rule": {
"tags": ["listing2"]
},
"includes": [
{"id": "1", "position": 1}
]
})"_json;
op = override_t::parse(override_json2, "ov-2", override2);
ASSERT_TRUE(op.ok());
coll1->add_override(override2);
results = coll1->search("foobar", {"name"}, "",
{}, sort_fields, {2}, 10, 1, FREQUENCY,
{false}, Index::DROP_TOKENS_THRESHOLD,
spp::sparse_hash_set<std::string>(),
spp::sparse_hash_set<std::string>(), 10, "", 30, 4, "title", 20, {}, {}, {}, 0,
"<mark>", "</mark>", {}, 1000, true, false, true, "", false, 10000,
4, 7, fallback, 4, {off}, 100, 100, 2, 2, false, "", true, 0, max_score, 100, 0,
0, HASH, 30000, 2, "", {}, {}, "right_to_left",
true, true, false, -1, "", "listing2").get();
ASSERT_EQ(1, results["hits"].size());
ASSERT_EQ("1", results["hits"][0]["document"]["id"].get<std::string>());
collectionManager.drop_collection("coll1");
}
TEST_F(CollectionOverrideTest, MetadataValidation) {
Collection* coll1;