mirror of
https://github.com/typesense/typesense.git
synced 2025-05-22 06:40:30 +08:00
Allow override to be active within specific time window.
This commit is contained in:
parent
6f38ee4270
commit
743abd461c
@ -112,6 +112,10 @@ struct override_t {
|
||||
std::string sort_by;
|
||||
std::string replace_query;
|
||||
|
||||
// epoch seconds
|
||||
int64_t window_start_ts = -1;
|
||||
int64_t window_end_ts = -1;
|
||||
|
||||
override_t() = default;
|
||||
|
||||
static Option<bool> parse(const nlohmann::json& override_json, const std::string& id, override_t& override) {
|
||||
@ -265,6 +269,14 @@ struct override_t {
|
||||
override.stop_processing = override_json["stop_processing"].get<bool>();
|
||||
}
|
||||
|
||||
if(override_json.count("window_start_ts") != 0) {
|
||||
override.window_start_ts = override_json["window_start_ts"].get<int64_t>();
|
||||
}
|
||||
|
||||
if(override_json.count("window_end_ts") != 0) {
|
||||
override.window_end_ts = override_json["window_end_ts"].get<int64_t>();
|
||||
}
|
||||
|
||||
// we have to also detect if it is a dynamic query rule
|
||||
size_t i = 0;
|
||||
while(i < override.rule.query.size()) {
|
||||
@ -321,6 +333,14 @@ struct override_t {
|
||||
override["replace_query"] = replace_query;
|
||||
}
|
||||
|
||||
if(window_start_ts != -1) {
|
||||
override["window_start_ts"] = window_start_ts;
|
||||
}
|
||||
|
||||
if(window_end_ts != -1) {
|
||||
override["window_end_ts"] = window_end_ts;
|
||||
}
|
||||
|
||||
override["remove_matched_tokens"] = remove_matched_tokens;
|
||||
override["filter_curated_hits"] = filter_curated_hits;
|
||||
override["stop_processing"] = stop_processing;
|
||||
|
@ -451,6 +451,15 @@ void Collection::curate_results(string& actual_query, bool enable_overrides, boo
|
||||
for(const auto& override_kv: overrides) {
|
||||
const auto& override = override_kv.second;
|
||||
|
||||
auto now_epoch = int64_t(std::time(0));
|
||||
if(override.window_start_ts != -1 && now_epoch < override.window_start_ts) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(override.window_end_ts != -1 && now_epoch > override.window_end_ts) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// ID-based overrides are applied first as they take precedence over filter-based overrides
|
||||
if(!override.filter_by.empty()) {
|
||||
filter_overrides.push_back(&override);
|
||||
|
@ -271,7 +271,7 @@ TEST_F(CollectionOverrideTest, OverrideJSONValidation) {
|
||||
|
||||
parse_op = override_t::parse(include_json2, "", override2);
|
||||
ASSERT_FALSE(parse_op.ok());
|
||||
ASSERT_STREQ("Must contain one of: `includes`, `excludes`, `filter_by`, `sort_by`, `remove_matched_tokens`.",
|
||||
ASSERT_STREQ("Must contain one of: `includes`, `excludes`, `filter_by`, `sort_by`, `remove_matched_tokens`, `replace_query`.",
|
||||
parse_op.error().c_str());
|
||||
|
||||
include_json2["includes"] = nlohmann::json::array();
|
||||
@ -841,6 +841,78 @@ TEST_F(CollectionOverrideTest, ReplaceQuery) {
|
||||
ASSERT_EQ("Only one of `replace_query` or `remove_matched_tokens` can be specified.", op.error());
|
||||
}
|
||||
|
||||
TEST_F(CollectionOverrideTest, WindowForRule) {
|
||||
Collection *coll1;
|
||||
|
||||
std::vector<field> fields = {field("name", field_types::STRING, false),
|
||||
field("points", field_types::INT32, false)};
|
||||
|
||||
coll1 = collectionManager.get_collection("coll1").get();
|
||||
if(coll1 == nullptr) {
|
||||
coll1 = collectionManager.create_collection("coll1", 1, fields, "points").get();
|
||||
}
|
||||
|
||||
nlohmann::json doc1;
|
||||
doc1["id"] = "0";
|
||||
doc1["name"] = "Amazing Shoes";
|
||||
doc1["points"] = 30;
|
||||
ASSERT_TRUE(coll1->add(doc1.dump()).ok());
|
||||
|
||||
std::vector<sort_by> sort_fields = { sort_by("_text_match", "DESC"), sort_by("points", "DESC") };
|
||||
|
||||
nlohmann::json override_json = R"({
|
||||
"id": "rule-1",
|
||||
"rule": {
|
||||
"query": "boots",
|
||||
"match": "exact"
|
||||
},
|
||||
"replace_query": "shoes"
|
||||
})"_json;
|
||||
|
||||
override_t override_rule;
|
||||
auto op = override_t::parse(override_json, "rule-1", override_rule);
|
||||
ASSERT_TRUE(op.ok());
|
||||
coll1->add_override(override_rule);
|
||||
|
||||
auto results = coll1->search("boots", {"name"}, "",
|
||||
{}, sort_fields, {2}, 10, 1, FREQUENCY, {true}, 0).get();
|
||||
|
||||
ASSERT_EQ(1, results["hits"].size());
|
||||
ASSERT_EQ("0", results["hits"][0]["document"]["id"].get<std::string>());
|
||||
|
||||
// rule must not match when window_start is set into the future
|
||||
override_json["window_start_ts"] = 35677971263; // year 3100, here we come! ;)
|
||||
op = override_t::parse(override_json, "rule-1", override_rule);
|
||||
ASSERT_TRUE(op.ok());
|
||||
coll1->add_override(override_rule);
|
||||
|
||||
results = coll1->search("boots", {"name"}, "",
|
||||
{}, sort_fields, {2}, 10, 1, FREQUENCY, {true}, 0).get();
|
||||
ASSERT_EQ(0, results["hits"].size());
|
||||
|
||||
// rule must not match when window_end is set into the past
|
||||
override_json["window_start_ts"] = -1;
|
||||
override_json["window_end_ts"] = 965388863;
|
||||
op = override_t::parse(override_json, "rule-1", override_rule);
|
||||
ASSERT_TRUE(op.ok());
|
||||
coll1->add_override(override_rule);
|
||||
|
||||
results = coll1->search("boots", {"name"}, "",
|
||||
{}, sort_fields, {2}, 10, 1, FREQUENCY, {true}, 0).get();
|
||||
ASSERT_EQ(0, results["hits"].size());
|
||||
|
||||
// resetting both should bring the override back in action
|
||||
override_json["window_start_ts"] = 965388863;
|
||||
override_json["window_end_ts"] = 35677971263;
|
||||
op = override_t::parse(override_json, "rule-1", override_rule);
|
||||
ASSERT_TRUE(op.ok());
|
||||
coll1->add_override(override_rule);
|
||||
|
||||
results = coll1->search("boots", {"name"}, "",
|
||||
{}, sort_fields, {2}, 10, 1, FREQUENCY, {true}, 0).get();
|
||||
ASSERT_EQ(1, results["hits"].size());
|
||||
}
|
||||
|
||||
TEST_F(CollectionOverrideTest, PinnedAndHiddenHits) {
|
||||
auto pinned_hits = "13:1,4:2";
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user