mirror of
https://github.com/typesense/typesense.git
synced 2025-05-20 21:52:23 +08:00
Merge branch 'v0.26-facets' into v27
# Conflicts: # include/collection.h # include/index.h # src/collection.cpp # src/collection_manager.cpp # src/index.cpp # test/collection_synonyms_test.cpp
This commit is contained in:
commit
e7f9bd30bd
@ -587,7 +587,8 @@ public:
|
||||
bool enable_typos_for_numerical_tokens = true,
|
||||
bool enable_synonyms = true,
|
||||
bool synonym_prefix = false,
|
||||
uint32_t synonym_num_typos = 0) const;
|
||||
uint32_t synonym_num_typos = 0,
|
||||
bool enable_lazy_filter = true) const;
|
||||
|
||||
Option<bool> get_filter_ids(const std::string & filter_query, filter_result_t& filter_result) const;
|
||||
|
||||
|
@ -531,6 +531,7 @@ namespace sort_field_const {
|
||||
}
|
||||
|
||||
namespace ref_include {
|
||||
static const std::string strategy_key = "strategy";
|
||||
static const std::string merge_string = "merge";
|
||||
static const std::string nest_string = "nest";
|
||||
static const std::string nest_array_string = "nest_array";
|
||||
|
@ -174,6 +174,8 @@ struct search_args {
|
||||
size_t facet_sample_threshold;
|
||||
drop_tokens_param_t drop_tokens_mode;
|
||||
|
||||
bool enable_lazy_filter;
|
||||
|
||||
search_args(std::vector<query_tokens_t> field_query_tokens, std::vector<search_field_t> search_fields,
|
||||
const text_match_type_t match_type,
|
||||
filter_node_t* filter_tree_root, std::vector<facet>& facets,
|
||||
@ -189,7 +191,8 @@ struct search_args {
|
||||
size_t min_len_1typo, size_t min_len_2typo, size_t max_candidates, const std::vector<enable_t>& infixes,
|
||||
const size_t max_extra_prefix, const size_t max_extra_suffix, const size_t facet_query_num_typos,
|
||||
const bool filter_curated_hits, const enable_t split_join_tokens, vector_query_t& vector_query,
|
||||
size_t facet_sample_percent, size_t facet_sample_threshold, drop_tokens_param_t drop_tokens_mode) :
|
||||
size_t facet_sample_percent, size_t facet_sample_threshold, drop_tokens_param_t drop_tokens_mode,
|
||||
bool enable_lazy_filter) :
|
||||
field_query_tokens(field_query_tokens),
|
||||
search_fields(search_fields), match_type(match_type), filter_tree_root(filter_tree_root), facets(facets),
|
||||
included_ids(included_ids), excluded_ids(excluded_ids), sort_fields_std(sort_fields_std),
|
||||
@ -208,7 +211,7 @@ struct search_args {
|
||||
facet_query_num_typos(facet_query_num_typos), filter_curated_hits(filter_curated_hits),
|
||||
split_join_tokens(split_join_tokens), vector_query(vector_query),
|
||||
facet_sample_percent(facet_sample_percent), facet_sample_threshold(facet_sample_threshold),
|
||||
drop_tokens_mode(drop_tokens_mode) {
|
||||
drop_tokens_mode(drop_tokens_mode), enable_lazy_filter(enable_lazy_filter) {
|
||||
|
||||
const size_t topster_size = std::max((size_t)1, max_hits); // needs to be atleast 1 since scoring is mandatory
|
||||
topster = new Topster(topster_size, group_limit);
|
||||
@ -692,7 +695,9 @@ public:
|
||||
facet_index_type_t facet_index_type = DETECT,
|
||||
bool enable_typos_for_numerical_tokens = true,
|
||||
bool enable_synonyms = true,
|
||||
bool synonym_prefix = false, uint32_t synonym_num_typos = 0
|
||||
bool synonym_prefix = false,
|
||||
uint32_t synonym_num_typos = 0,
|
||||
bool enable_lazy_filter = true
|
||||
) const;
|
||||
|
||||
void remove_field(uint32_t seq_id, const nlohmann::json& document, const std::string& field_name,
|
||||
|
@ -468,6 +468,11 @@ nlohmann::json Collection::get_summary_json() const {
|
||||
field_json[fields::infix] = coll_field.infix;
|
||||
field_json[fields::locale] = coll_field.locale;
|
||||
field_json[fields::stem] = coll_field.stem;
|
||||
|
||||
if(coll_field.range_index) {
|
||||
field_json[fields::range_index] = coll_field.range_index;
|
||||
}
|
||||
|
||||
// no need to sned hnsw_params for text fields
|
||||
if(coll_field.num_dim > 0) {
|
||||
field_json[fields::hnsw_params] = coll_field.hnsw_params;
|
||||
@ -780,7 +785,11 @@ void Collection::batch_index(std::vector<index_record>& index_records, std::vect
|
||||
} else {
|
||||
// remove flattened field values before storing on disk
|
||||
remove_flat_fields(index_record.doc);
|
||||
|
||||
for(auto& field: fields) {
|
||||
if(!field.store) {
|
||||
index_record.doc.erase(field.name);
|
||||
}
|
||||
}
|
||||
const std::string& seq_id_str = std::to_string(index_record.seq_id);
|
||||
const std::string& serialized_json = index_record.doc.dump(-1, ' ', false,
|
||||
nlohmann::detail::error_handler_t::ignore);
|
||||
@ -1753,8 +1762,8 @@ Option<nlohmann::json> Collection::search(std::string raw_query,
|
||||
bool enable_typos_for_numerical_tokens,
|
||||
bool enable_synonyms,
|
||||
bool synonym_prefix,
|
||||
uint32_t synonyms_num_typos) const {
|
||||
|
||||
uint32_t synonyms_num_typos,
|
||||
bool enable_lazy_filter) const {
|
||||
std::shared_lock lock(mutex);
|
||||
|
||||
// setup thread local vars
|
||||
@ -2344,7 +2353,8 @@ Option<nlohmann::json> Collection::search(std::string raw_query,
|
||||
min_len_1typo, min_len_2typo, max_candidates, infixes,
|
||||
max_extra_prefix, max_extra_suffix, facet_query_num_typos,
|
||||
filter_curated_hits, split_join_tokens, vector_query,
|
||||
facet_sample_percent, facet_sample_threshold, drop_tokens_param);
|
||||
facet_sample_percent, facet_sample_threshold, drop_tokens_param,
|
||||
enable_lazy_filter);
|
||||
|
||||
std::unique_ptr<search_args> search_params_guard(search_params);
|
||||
|
||||
@ -6062,6 +6072,9 @@ Index* Collection::init_index() {
|
||||
}
|
||||
|
||||
reference_fields.emplace(field.name, reference_pair(ref_coll_name, ref_field_name));
|
||||
if (field.nested) {
|
||||
object_reference_helper_fields.insert(field.name + fields::REFERENCE_HELPER_FIELD_SUFFIX);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1116,10 +1116,37 @@ Option<bool> parse_nested_exclude(const std::string& exclude_field_exp,
|
||||
return Option<bool>(true);
|
||||
}
|
||||
|
||||
Option<bool> parse_ref_include_parameters(const std::string& include_field_exp, const std::string& parameters,
|
||||
ref_include::strategy_enum& strategy_enum) {
|
||||
std::vector<std::string> parameters_map;
|
||||
StringUtils::split(parameters, parameters_map, ",");
|
||||
for (const auto &item: parameters_map) {
|
||||
std::vector<std::string> parameter_pair;
|
||||
StringUtils::split(item, parameter_pair, ":");
|
||||
if (parameter_pair.size() != 2) {
|
||||
continue;
|
||||
}
|
||||
auto const& key = StringUtils::trim(parameter_pair[0]);
|
||||
if (key == ref_include::strategy_key) {
|
||||
auto const& include_strategy = StringUtils::trim(parameter_pair[1]);
|
||||
|
||||
auto string_to_enum_op = ref_include::string_to_enum(include_strategy);
|
||||
if (!string_to_enum_op.ok()) {
|
||||
return Option<bool>(400, "Error parsing `" + include_field_exp + "`: " + string_to_enum_op.error());
|
||||
}
|
||||
strategy_enum = string_to_enum_op.get();
|
||||
} else {
|
||||
return Option<bool>(400, "Unknown reference `include_fields` parameter: `" + key + "`.");
|
||||
}
|
||||
}
|
||||
|
||||
return Option<bool>(true);
|
||||
}
|
||||
|
||||
Option<bool> parse_nested_include(const std::string& include_field_exp,
|
||||
CollectionManager::ref_include_collection_names_t* const ref_include_coll_names,
|
||||
std::vector<ref_include_exclude_fields>& ref_include_exclude_fields_vec) {
|
||||
// Format: $ref_collection_name(field_1, field_2, $nested_ref_coll(nested_field_1: nested_include_strategy) as nested_ref_alias: include_strategy) as ref_alias
|
||||
// Format: $ref_collection_name(field_1, field_2, $nested_ref_coll(nested_field_1, strategy: nested_include_strategy) as nested_ref_alias, strategy: include_strategy) as ref_alias
|
||||
size_t index = 0;
|
||||
while (index < include_field_exp.size()) {
|
||||
auto parenthesis_index = include_field_exp.find('(');
|
||||
@ -1135,7 +1162,7 @@ Option<bool> parse_nested_include(const std::string& include_field_exp,
|
||||
std::vector<ref_include_exclude_fields> nested_ref_include_exclude_fields_vec;
|
||||
if (nested_include_pos < closing_parenthesis_pos) {
|
||||
// Nested reference include.
|
||||
// "... $product_variants(title, $inventory(qty:merge) as inventory :nest) as variants ..."
|
||||
// "... $product_variants(title, $inventory(qty, strategy:merge) as inventory, strategy :nest) as variants ..."
|
||||
do {
|
||||
ref_fields += include_field_exp.substr(index, nested_include_pos - index);
|
||||
StringUtils::trim(ref_fields);
|
||||
@ -1162,28 +1189,31 @@ Option<bool> parse_nested_include(const std::string& include_field_exp,
|
||||
} while(index < include_field_exp.size() && nested_include_pos < closing_parenthesis_pos);
|
||||
}
|
||||
|
||||
// ... $inventory(qty:merge) as inventory ...
|
||||
auto include_strategy = ref_include::nest_string;
|
||||
auto strategy_enum = ref_include::nest;
|
||||
if (colon_pos < closing_parenthesis_pos) { // Merge strategy is specified.
|
||||
include_strategy = include_field_exp.substr(colon_pos + 1, closing_parenthesis_pos - colon_pos - 1);
|
||||
StringUtils::trim(include_strategy);
|
||||
|
||||
auto string_to_enum_op = ref_include::string_to_enum(include_strategy);
|
||||
if (!string_to_enum_op.ok()) {
|
||||
return Option<bool>(400, "Error parsing `" + include_field_exp + "`: " + string_to_enum_op.error());
|
||||
}
|
||||
strategy_enum = string_to_enum_op.get();
|
||||
|
||||
if (index < colon_pos) {
|
||||
ref_fields += include_field_exp.substr(index, colon_pos - index);
|
||||
}
|
||||
} else if (index < closing_parenthesis_pos) {
|
||||
if (index < closing_parenthesis_pos) {
|
||||
ref_fields += include_field_exp.substr(index, closing_parenthesis_pos - index);
|
||||
}
|
||||
index = closing_parenthesis_pos;
|
||||
|
||||
// ... $inventory(qty, strategy:merge) as inventory
|
||||
auto strategy_enum = ref_include::nest;
|
||||
if (colon_pos < closing_parenthesis_pos) {
|
||||
auto const& parameters_start = ref_fields.rfind(',', colon_pos);
|
||||
std::string parameters;
|
||||
if (parameters_start == std::string::npos) {
|
||||
parameters = ref_fields;
|
||||
ref_fields.clear();
|
||||
} else {
|
||||
parameters = ref_fields.substr(parameters_start + 1);
|
||||
ref_fields = ref_fields.substr(0, parameters_start);
|
||||
}
|
||||
|
||||
auto parse_params_op = parse_ref_include_parameters(include_field_exp, parameters, strategy_enum);
|
||||
if (!parse_params_op.ok()) {
|
||||
return parse_params_op;
|
||||
}
|
||||
}
|
||||
StringUtils::trim(ref_fields);
|
||||
|
||||
index = closing_parenthesis_pos;
|
||||
auto as_pos = include_field_exp.find(" as ", index);
|
||||
comma_pos = include_field_exp.find(',', index);
|
||||
if (as_pos != std::string::npos && as_pos < comma_pos) {
|
||||
@ -1253,20 +1283,23 @@ Option<bool> CollectionManager::_initialize_ref_include_exclude_fields_vec(const
|
||||
auto ref_collection_name = ref_include.substr(1, parenthesis_index - 1);
|
||||
auto ref_fields = ref_include.substr(parenthesis_index + 1, ref_include.size() - parenthesis_index - 2);
|
||||
|
||||
auto include_strategy = ref_include::nest_string;
|
||||
auto strategy_enum = ref_include::nest;
|
||||
auto colon_pos = ref_fields.find(':');
|
||||
if (colon_pos != std::string::npos) {
|
||||
include_strategy = ref_fields.substr(colon_pos + 1, ref_fields.size() - colon_pos - 1);
|
||||
StringUtils::trim(include_strategy);
|
||||
|
||||
auto string_to_enum_op = ref_include::string_to_enum(include_strategy);
|
||||
if (!string_to_enum_op.ok()) {
|
||||
return Option<bool>(400, "Error parsing `" + include_field_exp + "`: " + string_to_enum_op.error());
|
||||
auto const& parameters_start = ref_fields.rfind(',', colon_pos);
|
||||
std::string parameters;
|
||||
if (parameters_start == std::string::npos) {
|
||||
parameters = ref_fields;
|
||||
ref_fields.clear();
|
||||
} else {
|
||||
parameters = ref_fields.substr(parameters_start + 1);
|
||||
ref_fields = ref_fields.substr(0, parameters_start);
|
||||
}
|
||||
strategy_enum = string_to_enum_op.get();
|
||||
|
||||
ref_fields = ref_fields.substr(0, colon_pos);
|
||||
auto parse_params_op = parse_ref_include_parameters(include_field_exp, parameters, strategy_enum);
|
||||
if (!parse_params_op.ok()) {
|
||||
return parse_params_op;
|
||||
}
|
||||
}
|
||||
|
||||
// For an alias `foo`,
|
||||
@ -1446,6 +1479,7 @@ Option<bool> CollectionManager::do_search(std::map<std::string, std::string>& re
|
||||
const char *VOICE_QUERY = "voice_query";
|
||||
|
||||
const char *ENABLE_TYPOS_FOR_NUMERICAL_TOKENS = "enable_typos_for_numerical_tokens";
|
||||
const char *ENABLE_LAZY_FILTER = "enable_lazy_filter";
|
||||
|
||||
const char *SYNONYM_PREFIX = "synonym_prefix";
|
||||
const char *SYNONYM_NUM_TYPOS = "synonym_num_typos";
|
||||
@ -1573,6 +1607,7 @@ Option<bool> CollectionManager::do_search(std::map<std::string, std::string>& re
|
||||
bool enable_highlight_v1 = true;
|
||||
text_match_type_t match_type = max_score;
|
||||
bool enable_typos_for_numerical_tokens = true;
|
||||
bool enable_lazy_filter = true;
|
||||
|
||||
size_t remote_embedding_timeout_ms = 5000;
|
||||
size_t remote_embedding_num_tries = 2;
|
||||
@ -1648,6 +1683,7 @@ Option<bool> CollectionManager::do_search(std::map<std::string, std::string>& re
|
||||
{ENABLE_TYPOS_FOR_NUMERICAL_TOKENS, &enable_typos_for_numerical_tokens},
|
||||
{ENABLE_SYNONYMS, &enable_synonyms},
|
||||
{SYNONYM_PREFIX, &synonym_prefix},
|
||||
{ENABLE_LAZY_FILTER, &enable_lazy_filter},
|
||||
};
|
||||
|
||||
std::unordered_map<std::string, std::vector<std::string>*> str_list_values = {
|
||||
@ -1865,7 +1901,8 @@ Option<bool> CollectionManager::do_search(std::map<std::string, std::string>& re
|
||||
enable_typos_for_numerical_tokens,
|
||||
enable_synonyms,
|
||||
synonym_prefix,
|
||||
synonym_num_typos);
|
||||
synonym_num_typos,
|
||||
enable_lazy_filter);
|
||||
|
||||
uint64_t timeMillis = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::high_resolution_clock::now() - begin).count();
|
||||
|
@ -2256,7 +2256,9 @@ Option<bool> Index::run_search(search_args* search_params, const std::string& co
|
||||
enable_typos_for_numerical_tokens,
|
||||
enable_synonyms,
|
||||
synonym_prefix,
|
||||
synonym_num_typos);
|
||||
synonym_num_typos,
|
||||
search_params->enable_lazy_filter
|
||||
);
|
||||
}
|
||||
|
||||
void Index::collate_included_ids(const std::vector<token_t>& q_included_tokens,
|
||||
@ -2745,8 +2747,8 @@ Option<bool> Index::search(std::vector<query_tokens_t>& field_query_tokens, cons
|
||||
facet_index_type_t facet_index_type,
|
||||
bool enable_typos_for_numerical_tokens,
|
||||
bool enable_synonyms, bool synonym_prefix,
|
||||
uint32_t synonym_num_typos) const {
|
||||
|
||||
uint32_t synonym_num_typos,
|
||||
bool enable_lazy_filter) const {
|
||||
std::shared_lock lock(mutex);
|
||||
|
||||
auto filter_result_iterator = new filter_result_iterator_t(collection_name, this, filter_tree_root,
|
||||
@ -2769,7 +2771,7 @@ Option<bool> Index::search(std::vector<query_tokens_t>& field_query_tokens, cons
|
||||
}
|
||||
#else
|
||||
|
||||
if (filter_result_iterator->approx_filter_ids_length < 25'000) {
|
||||
if (!enable_lazy_filter || filter_result_iterator->approx_filter_ids_length < 25'000) {
|
||||
filter_result_iterator->compute_iterators();
|
||||
}
|
||||
#endif
|
||||
@ -3095,7 +3097,8 @@ Option<bool> Index::search(std::vector<query_tokens_t>& field_query_tokens, cons
|
||||
synonym_prefix, synonym_num_typos);
|
||||
}
|
||||
|
||||
if (search_schema.find(the_fields[0].name) != search_schema.end() && search_schema.at(the_fields[0].name).stem) {
|
||||
const bool& do_stemming = (search_schema.find(the_fields[0].name) != search_schema.end() && search_schema.at(the_fields[0].name).stem);
|
||||
if (do_stemming) {
|
||||
auto stemmer = search_schema.at(the_fields[0].name).get_stemmer();
|
||||
for(auto& q_include_token: q_include_tokens) {
|
||||
q_include_token = stemmer->stem(q_include_token);
|
||||
@ -3114,7 +3117,12 @@ Option<bool> Index::search(std::vector<query_tokens_t>& field_query_tokens, cons
|
||||
std::vector<token_t> q_pos_syn;
|
||||
for(size_t j=0; j < q_syn_vec.size(); j++) {
|
||||
bool is_prefix = (j == q_syn_vec.size()-1);
|
||||
q_pos_syn.emplace_back(j, q_syn_vec[j], is_prefix, q_syn_vec[j].size(), 0);
|
||||
std::string token_val = q_syn_vec[j];
|
||||
if (do_stemming) {
|
||||
auto stemmer = search_schema.at(the_fields[0].name).get_stemmer();
|
||||
token_val = stemmer->stem(q_syn_vec[j]);
|
||||
}
|
||||
q_pos_syn.emplace_back(j, token_val, is_prefix, token_val.size(), 0);
|
||||
}
|
||||
|
||||
q_pos_synonyms.push_back(q_pos_syn);
|
||||
|
@ -519,17 +519,14 @@ Option<bool> StringUtils::split_reference_include_exclude_fields(const std::stri
|
||||
}
|
||||
|
||||
// In case of nested reference include, we might end up with one of the following scenarios:
|
||||
// $ref_include( $nested_ref_include(foo :merge)as nest ) as ref
|
||||
// ...^
|
||||
// $ref_include( $nested_ref_include(foo :merge)as nest, bar ) as ref
|
||||
// ...^
|
||||
// $ref_include( $nested_ref_include(foo :merge)as nest :merge ) as ref
|
||||
// ...^
|
||||
// $ref_include( $nested_ref_include(foo, strategy:merge)as nest ) as ref
|
||||
// ...^
|
||||
// $ref_include( $nested_ref_include(foo, strategy:merge)as nest, bar ) as ref
|
||||
// ...^
|
||||
auto closing_parenthesis_pos = include_exclude_fields.find(')', index);
|
||||
auto comma_pos = include_exclude_fields.find(',', index);
|
||||
auto colon_pos = include_exclude_fields.find(':', index);
|
||||
auto alias_start_pos = include_exclude_fields.find(" as ", index);
|
||||
auto alias_end_pos = std::min(std::min(closing_parenthesis_pos, comma_pos), colon_pos);
|
||||
auto alias_end_pos = std::min(closing_parenthesis_pos, comma_pos);
|
||||
std::string alias;
|
||||
if (alias_start_pos != std::string::npos && alias_start_pos < alias_end_pos) {
|
||||
alias = include_exclude_fields.substr(alias_start_pos, alias_end_pos - alias_start_pos);
|
||||
|
@ -1336,7 +1336,7 @@ TEST_F(CollectionJoinTest, FilterByReference_SingleMatch) {
|
||||
{"q", "Dan"},
|
||||
{"query_by", "customer_name"},
|
||||
{"filter_by", "$Products(rating:>3)"},
|
||||
{"include_fields", "$Products(*:merge)"},
|
||||
{"include_fields", "$Products(*, strategy:merge)"},
|
||||
};
|
||||
|
||||
search_op_bool = collectionManager.do_search(req_params, embedded_params, json_res, now_ts);
|
||||
@ -1352,7 +1352,7 @@ TEST_F(CollectionJoinTest, FilterByReference_SingleMatch) {
|
||||
{"q", "Dan"},
|
||||
{"query_by", "customer_name"},
|
||||
{"filter_by", "$Products(id:*) && product_price:>100"},
|
||||
{"include_fields", "$Products(*:merge)"},
|
||||
{"include_fields", "$Products(*, strategy:merge)"},
|
||||
};
|
||||
|
||||
search_op_bool = collectionManager.do_search(req_params, embedded_params, json_res, now_ts);
|
||||
@ -2104,7 +2104,7 @@ TEST_F(CollectionJoinTest, FilterByNestedReferences) {
|
||||
{"collection", "Coll_A"},
|
||||
{"q", "*"},
|
||||
{"filter_by", "$Coll_B($Coll_C(id: != 0))"},
|
||||
{"include_fields", "title, $Coll_B(title, $Coll_C(title):nest_array)"}
|
||||
{"include_fields", "title, $Coll_B(title, $Coll_C(title), strategy:nest_array)"}
|
||||
};
|
||||
search_op = collectionManager.do_search(req_params, embedded_params, json_res, now_ts);
|
||||
ASSERT_TRUE(search_op.ok());
|
||||
@ -2208,7 +2208,7 @@ TEST_F(CollectionJoinTest, FilterByNestedReferences) {
|
||||
{"collection", "Coll_B"},
|
||||
{"q", "*"},
|
||||
{"filter_by", "$Coll_C($Coll_D(id: *))"},
|
||||
{"include_fields", "title, $Coll_C(title, $Coll_D(title:nest_array):nest_array)"}
|
||||
{"include_fields", "title, $Coll_C(title, $Coll_D(title, strategy:nest_array), strategy:nest_array)"}
|
||||
};
|
||||
search_op = collectionManager.do_search(req_params, embedded_params, json_res, now_ts);
|
||||
ASSERT_TRUE(search_op.ok());
|
||||
@ -2252,7 +2252,7 @@ TEST_F(CollectionJoinTest, FilterByNestedReferences) {
|
||||
{"collection", "Coll_D"},
|
||||
{"q", "*"},
|
||||
{"filter_by", "$Coll_C($Coll_B(id: [0, 1]))"},
|
||||
{"include_fields", "title, $Coll_C(title, $Coll_B(title:nest_array):nest_array)"}
|
||||
{"include_fields", "title, $Coll_C(title, $Coll_B(title, strategy:nest_array), strategy:nest_array)"}
|
||||
};
|
||||
search_op = collectionManager.do_search(req_params, embedded_params, json_res, now_ts);
|
||||
ASSERT_TRUE(search_op.ok());
|
||||
@ -2677,7 +2677,7 @@ TEST_F(CollectionJoinTest, IncludeExcludeFieldsByReference) {
|
||||
{"q", "*"},
|
||||
{"query_by", "product_name"},
|
||||
{"filter_by", "$Customers(customer_id:=customer_a && product_price:<100)"},
|
||||
{"include_fields", "*, $Customers(*:nest_array) as Customers"}
|
||||
{"include_fields", "*, $Customers(*, strategy:nest_array) as Customers"}
|
||||
};
|
||||
search_op = collectionManager.do_search(req_params, embedded_params, json_res, now_ts);
|
||||
ASSERT_TRUE(search_op.ok());
|
||||
@ -2706,7 +2706,7 @@ TEST_F(CollectionJoinTest, IncludeExcludeFieldsByReference) {
|
||||
{"q", "*"},
|
||||
{"query_by", "product_name"},
|
||||
{"filter_by", "$Customers(customer_id:=customer_a && product_price:<100)"},
|
||||
{"include_fields", "*, $Customers(*:merge) as Customers"}
|
||||
{"include_fields", "*, $Customers(*, strategy:merge) as Customers"}
|
||||
};
|
||||
search_op = collectionManager.do_search(req_params, embedded_params, json_res, now_ts);
|
||||
ASSERT_TRUE(search_op.ok());
|
||||
@ -2731,7 +2731,7 @@ TEST_F(CollectionJoinTest, IncludeExcludeFieldsByReference) {
|
||||
{"q", "*"},
|
||||
{"query_by", "product_name"},
|
||||
{"filter_by", "$Customers(customer_id:=customer_a && product_price:<100)"},
|
||||
{"include_fields", "$Customers(bar:merge)"}
|
||||
{"include_fields", "$Customers(bar, strategy:merge)"}
|
||||
};
|
||||
search_op = collectionManager.do_search(req_params, embedded_params, json_res, now_ts);
|
||||
ASSERT_TRUE(search_op.ok());
|
||||
@ -2753,7 +2753,7 @@ TEST_F(CollectionJoinTest, IncludeExcludeFieldsByReference) {
|
||||
{"q", "*"},
|
||||
{"query_by", "product_name"},
|
||||
{"filter_by", "$Customers(customer_id:=customer_a && product_price:<100)"},
|
||||
{"include_fields", "$Customers(product_price:merge)"}
|
||||
{"include_fields", "$Customers(product_price, strategy:merge)"}
|
||||
};
|
||||
search_op = collectionManager.do_search(req_params, embedded_params, json_res, now_ts);
|
||||
ASSERT_TRUE(search_op.ok());
|
||||
@ -2770,7 +2770,7 @@ TEST_F(CollectionJoinTest, IncludeExcludeFieldsByReference) {
|
||||
{"q", "*"},
|
||||
{"query_by", "product_name"},
|
||||
{"filter_by", "$Customers(customer_id:=customer_a && product_price:<100)"},
|
||||
{"include_fields", "$Customers(product_price, customer_id:merge)"}
|
||||
{"include_fields", "$Customers(product_price, customer_id, strategy:merge)"}
|
||||
};
|
||||
search_op = collectionManager.do_search(req_params, embedded_params, json_res, now_ts);
|
||||
ASSERT_TRUE(search_op.ok());
|
||||
@ -2789,7 +2789,7 @@ TEST_F(CollectionJoinTest, IncludeExcludeFieldsByReference) {
|
||||
{"q", "*"},
|
||||
{"query_by", "product_name"},
|
||||
{"filter_by", "$Customers(customer_id:=customer_a && product_price:<100)"},
|
||||
{"include_fields", "*, $Customers(product_price, customer_id:merge)"}
|
||||
{"include_fields", "*, $Customers(product_price, customer_id, strategy:merge)"}
|
||||
};
|
||||
search_op = collectionManager.do_search(req_params, embedded_params, json_res, now_ts);
|
||||
ASSERT_TRUE(search_op.ok());
|
||||
@ -2805,7 +2805,7 @@ TEST_F(CollectionJoinTest, IncludeExcludeFieldsByReference) {
|
||||
{"q", "*"},
|
||||
{"query_by", "product_name"},
|
||||
{"filter_by", "$Customers(customer_id:=customer_a && product_price:<100)"},
|
||||
{"include_fields", "$Customers(product*:merge)"}
|
||||
{"include_fields", "$Customers(product*, strategy:merge)"}
|
||||
};
|
||||
search_op = collectionManager.do_search(req_params, embedded_params, json_res, now_ts);
|
||||
ASSERT_TRUE(search_op.ok());
|
||||
@ -2822,7 +2822,7 @@ TEST_F(CollectionJoinTest, IncludeExcludeFieldsByReference) {
|
||||
{"q", "s"},
|
||||
{"query_by", "product_name"},
|
||||
{"filter_by", "$Customers(customer_id:=customer_a && product_price:<100)"},
|
||||
{"include_fields", "$Customers(product*:merge)"},
|
||||
{"include_fields", "$Customers(product*, strategy:merge)"},
|
||||
{"exclude_fields", "$Customers(product_id_sequence_id)"}
|
||||
};
|
||||
search_op = collectionManager.do_search(req_params, embedded_params, json_res, now_ts);
|
||||
@ -2883,7 +2883,7 @@ TEST_F(CollectionJoinTest, IncludeExcludeFieldsByReference) {
|
||||
{"q", "*"},
|
||||
{"query_by", "product_name"},
|
||||
{"filter_by", "product_name:soap && $Customers(product_price:>100)"},
|
||||
{"include_fields", "product_name, $Customers(product_price:merge)"},
|
||||
{"include_fields", "product_name, $Customers(product_price, strategy:merge)"},
|
||||
{"exclude_fields", ""}
|
||||
};
|
||||
search_op = collectionManager.do_search(req_params, embedded_params, json_res, now_ts);
|
||||
@ -2904,7 +2904,7 @@ TEST_F(CollectionJoinTest, IncludeExcludeFieldsByReference) {
|
||||
{"q", "soap"},
|
||||
{"query_by", "product_name"},
|
||||
{"filter_by", "$Customers(product_price: >0)"},
|
||||
{"include_fields", "product_name, $Customers(customer_name, product_price:merge)"},
|
||||
{"include_fields", "product_name, $Customers(customer_name, product_price, strategy:merge)"},
|
||||
{"exclude_fields", ""}
|
||||
};
|
||||
search_op = collectionManager.do_search(req_params, embedded_params, json_res, now_ts);
|
||||
@ -2929,7 +2929,7 @@ TEST_F(CollectionJoinTest, IncludeExcludeFieldsByReference) {
|
||||
{"q", "natural products"},
|
||||
{"query_by", "embedding"},
|
||||
{"filter_by", "$Customers(customer_id:=customer_a && product_price:<100)"},
|
||||
{"include_fields", "product_name, $Customers(product_price:merge)"},
|
||||
{"include_fields", "product_name, $Customers(product_price, strategy:merge)"},
|
||||
{"exclude_fields", ""}
|
||||
};
|
||||
search_op = collectionManager.do_search(req_params, embedded_params, json_res, now_ts);
|
||||
@ -2959,7 +2959,7 @@ TEST_F(CollectionJoinTest, IncludeExcludeFieldsByReference) {
|
||||
{"q", "*"},
|
||||
{"vector_query", "embedding:(" + vec_string + ", flat_search_cutoff: 0)"},
|
||||
{"filter_by", "$Customers(customer_id:=customer_a && product_price:<100)"},
|
||||
{"include_fields", "product_name, $Customers(product_price : merge)"},
|
||||
{"include_fields", "product_name, $Customers(product_price, strategy : merge)"},
|
||||
{"exclude_fields", ""}
|
||||
};
|
||||
search_op = collectionManager.do_search(req_params, embedded_params, json_res, now_ts);
|
||||
@ -2979,7 +2979,7 @@ TEST_F(CollectionJoinTest, IncludeExcludeFieldsByReference) {
|
||||
{"q", "soap"},
|
||||
{"query_by", "product_name, embedding"},
|
||||
{"filter_by", "$Customers(customer_id:=customer_a && product_price:<100)"},
|
||||
{"include_fields", "product_name, $Customers(product_price: merge)"},
|
||||
{"include_fields", "product_name, $Customers(product_price, strategy: merge)"},
|
||||
{"exclude_fields", ""}
|
||||
};
|
||||
search_op = collectionManager.do_search(req_params, embedded_params, json_res, now_ts);
|
||||
@ -3001,7 +3001,7 @@ TEST_F(CollectionJoinTest, IncludeExcludeFieldsByReference) {
|
||||
{"q", "natural products"},
|
||||
{"query_by", "product_name, embedding"},
|
||||
{"filter_by", "$Customers(customer_id:=customer_a && product_price:<100)"},
|
||||
{"include_fields", "product_name, $Customers(product_price :merge)"},
|
||||
{"include_fields", "product_name, $Customers(product_price , strategy:merge)"},
|
||||
{"exclude_fields", ""}
|
||||
};
|
||||
search_op = collectionManager.do_search(req_params, embedded_params, json_res, now_ts);
|
||||
@ -3024,7 +3024,7 @@ TEST_F(CollectionJoinTest, IncludeExcludeFieldsByReference) {
|
||||
{"query_by", "product_name"},
|
||||
{"infix", "always"},
|
||||
{"filter_by", "$Customers(customer_id:=customer_a && product_price:<100)"},
|
||||
{"include_fields", "product_name, $Customers(product_price:merge)"},
|
||||
{"include_fields", "product_name, $Customers(product_price, strategy:merge)"},
|
||||
{"exclude_fields", ""}
|
||||
};
|
||||
search_op = collectionManager.do_search(req_params, embedded_params, json_res, now_ts);
|
||||
@ -3044,7 +3044,7 @@ TEST_F(CollectionJoinTest, IncludeExcludeFieldsByReference) {
|
||||
{"q", "Dan"},
|
||||
{"query_by", "customer_name"},
|
||||
{"filter_by", "$Products(rating:>3)"},
|
||||
{"include_fields", "$Products(product_name:merge), product_price"}
|
||||
{"include_fields", "$Products(product_name, strategy:merge), product_price"}
|
||||
};
|
||||
|
||||
search_op = collectionManager.do_search(req_params, embedded_params, json_res, now_ts);
|
||||
@ -3065,7 +3065,7 @@ TEST_F(CollectionJoinTest, IncludeExcludeFieldsByReference) {
|
||||
{"q", "Joe"},
|
||||
{"query_by", "customer_name"},
|
||||
{"filter_by", "product_price:<100"},
|
||||
{"include_fields", "$Products(product_name: merge), product_price"}
|
||||
{"include_fields", "$Products(product_name, strategy: merge), product_price"}
|
||||
};
|
||||
search_op = collectionManager.do_search(req_params, embedded_params, json_res, now_ts);
|
||||
ASSERT_TRUE(search_op.ok());
|
||||
@ -3085,7 +3085,7 @@ TEST_F(CollectionJoinTest, IncludeExcludeFieldsByReference) {
|
||||
{"q", "soap"},
|
||||
{"query_by", "product_name"},
|
||||
{"filter_by", "$Customers(id:*)"},
|
||||
{"include_fields", "id, $Customers(id :merge)"}
|
||||
{"include_fields", "id, $Customers(id , strategy:merge)"}
|
||||
};
|
||||
search_op = collectionManager.do_search(req_params, embedded_params, json_res, now_ts);
|
||||
ASSERT_FALSE(search_op.ok());
|
||||
@ -3097,7 +3097,7 @@ TEST_F(CollectionJoinTest, IncludeExcludeFieldsByReference) {
|
||||
{"q", "soap"},
|
||||
{"query_by", "product_name"},
|
||||
{"filter_by", "$Customers(id:*)"},
|
||||
{"include_fields", "id, $Customers(id :nest) as id"}
|
||||
{"include_fields", "id, $Customers(id , strategy:nest) as id"}
|
||||
};
|
||||
search_op = collectionManager.do_search(req_params, embedded_params, json_res, now_ts);
|
||||
ASSERT_FALSE(search_op.ok());
|
||||
@ -3110,7 +3110,7 @@ TEST_F(CollectionJoinTest, IncludeExcludeFieldsByReference) {
|
||||
{"query_by", "customer_name"},
|
||||
{"filter_by", "product_price:<100"},
|
||||
// With merge, alias is prepended
|
||||
{"include_fields", "$Products(product_name:merge) as prod, product_price"}
|
||||
{"include_fields", "$Products(product_name, strategy:merge) as prod, product_price"}
|
||||
};
|
||||
search_op = collectionManager.do_search(req_params, embedded_params, json_res, now_ts);
|
||||
ASSERT_TRUE(search_op.ok());
|
||||
@ -3130,7 +3130,7 @@ TEST_F(CollectionJoinTest, IncludeExcludeFieldsByReference) {
|
||||
{"query_by", "customer_name"},
|
||||
{"filter_by", "product_price:<100"},
|
||||
// With nest, alias becomes the key
|
||||
{"include_fields", "$Products(product_name:nest) as prod, product_price"}
|
||||
{"include_fields", "$Products(product_name, strategy:nest) as prod, product_price"}
|
||||
};
|
||||
search_op = collectionManager.do_search(req_params, embedded_params, json_res, now_ts);
|
||||
ASSERT_TRUE(search_op.ok());
|
||||
@ -3151,7 +3151,7 @@ TEST_F(CollectionJoinTest, IncludeExcludeFieldsByReference) {
|
||||
{"query_by", "product_name"},
|
||||
{"filter_by", "$Customers(id:*)"},
|
||||
// With nest, alias becomes the key
|
||||
{"include_fields", "$Customers(customer_name, product_price :nest) as CustomerPrices, product_name"}
|
||||
{"include_fields", "$Customers(customer_name, product_price , strategy:nest) as CustomerPrices, product_name"}
|
||||
};
|
||||
search_op = collectionManager.do_search(req_params, embedded_params, json_res, now_ts);
|
||||
ASSERT_TRUE(search_op.ok());
|
||||
@ -3374,7 +3374,7 @@ TEST_F(CollectionJoinTest, IncludeExcludeFieldsByReference) {
|
||||
{"q", "R"},
|
||||
{"query_by", "user_name"},
|
||||
{"filter_by", "$Participants(org_id:=org_a) && $Links(repo_id:=repo_b)"},
|
||||
{"include_fields", "user_id, user_name, $Repos(repo_content:merge), $Organizations(name:merge) as org"},
|
||||
{"include_fields", "user_id, user_name, $Repos(repo_content, strategy:merge), $Organizations(name, strategy:merge) as org"},
|
||||
{"exclude_fields", "$Participants(*), $Links(*), "}
|
||||
};
|
||||
search_op = collectionManager.do_search(req_params, embedded_params, json_res, now_ts);
|
||||
@ -3448,7 +3448,7 @@ TEST_F(CollectionJoinTest, FilterByReferenceArrayField) {
|
||||
std::map<std::string, std::string> req_params = {
|
||||
{"collection", "songs"},
|
||||
{"q", "*"},
|
||||
{"include_fields", "$genres(name:merge) as genre"},
|
||||
{"include_fields", "$genres(name, strategy:merge) as genre"},
|
||||
{"exclude_fields", "genres_sequence_id"},
|
||||
};
|
||||
nlohmann::json embedded_params;
|
||||
@ -3479,7 +3479,7 @@ TEST_F(CollectionJoinTest, FilterByReferenceArrayField) {
|
||||
{"collection", "genres"},
|
||||
{"q", "*"},
|
||||
{"filter_by", "$songs(id: *)"},
|
||||
{"include_fields", "$songs(title:merge) as song"},
|
||||
{"include_fields", "$songs(title, strategy:merge) as song"},
|
||||
};
|
||||
search_op_bool = collectionManager.do_search(req_params, embedded_params, json_res, now_ts);
|
||||
ASSERT_TRUE(search_op_bool.ok());
|
||||
@ -3790,7 +3790,7 @@ TEST_F(CollectionJoinTest, FilterByObjectReferenceField) {
|
||||
req_params = {
|
||||
{"collection", "Foods"},
|
||||
{"q", "*"},
|
||||
{"include_fields", "$Portions(*:merge)"}
|
||||
{"include_fields", "$Portions(*, strategy:merge)"}
|
||||
};
|
||||
search_op_bool = collectionManager.do_search(req_params, embedded_params, json_res, now_ts);
|
||||
ASSERT_TRUE(search_op_bool.ok());
|
||||
@ -3821,6 +3821,64 @@ TEST_F(CollectionJoinTest, FilterByObjectReferenceField) {
|
||||
ASSERT_EQ(1 , res_obj["hits"][0]["document"]["portions"][2].at("count"));
|
||||
|
||||
|
||||
ASSERT_EQ("Bread", res_obj["hits"][1]["document"]["name"]);
|
||||
ASSERT_EQ(1, res_obj["hits"][1]["document"].count("portions"));
|
||||
ASSERT_EQ(1, res_obj["hits"][1]["document"]["portions"].size());
|
||||
|
||||
ASSERT_EQ(5, res_obj["hits"][1]["document"]["portions"][0].size());
|
||||
ASSERT_EQ("portion_a", res_obj["hits"][1]["document"]["portions"][0].at("portion_id"));
|
||||
ASSERT_EQ(500 , res_obj["hits"][1]["document"]["portions"][0].at("quantity"));
|
||||
ASSERT_EQ("g", res_obj["hits"][1]["document"]["portions"][0].at("unit"));
|
||||
ASSERT_EQ(10 , res_obj["hits"][1]["document"]["portions"][0].at("count"));
|
||||
|
||||
// recreate collection manager to ensure that it initializes `object_reference_helper_fields` correctly.
|
||||
collectionManager.dispose();
|
||||
delete store;
|
||||
|
||||
store = new Store(state_dir_path);
|
||||
collectionManager.init(store, 1.0, "auth_key", quit);
|
||||
auto load_op = collectionManager.load(8, 1000);
|
||||
|
||||
if(!load_op.ok()) {
|
||||
LOG(ERROR) << load_op.error();
|
||||
}
|
||||
ASSERT_TRUE(load_op.ok());
|
||||
|
||||
req_params = {
|
||||
{"collection", "Foods"},
|
||||
{"q", "*"},
|
||||
{"include_fields", "$Portions(*, strategy:merge)"}
|
||||
};
|
||||
search_op_bool = collectionManager.do_search(req_params, embedded_params, json_res, now_ts);
|
||||
LOG(INFO) << search_op_bool.error();
|
||||
ASSERT_TRUE(search_op_bool.ok());
|
||||
|
||||
res_obj = nlohmann::json::parse(json_res);
|
||||
ASSERT_EQ(2, res_obj["found"].get<size_t>());
|
||||
ASSERT_EQ(2, res_obj["hits"].size());
|
||||
ASSERT_EQ(3, res_obj["hits"][0]["document"].size());
|
||||
ASSERT_EQ(1, res_obj["hits"][0]["document"].count("name"));
|
||||
|
||||
ASSERT_EQ("Milk", res_obj["hits"][0]["document"]["name"]);
|
||||
ASSERT_EQ(1, res_obj["hits"][0]["document"].count("portions"));
|
||||
ASSERT_EQ(3, res_obj["hits"][0]["document"]["portions"].size());
|
||||
|
||||
ASSERT_EQ(5, res_obj["hits"][0]["document"]["portions"][0].size());
|
||||
ASSERT_EQ("portion_b", res_obj["hits"][0]["document"]["portions"][0].at("portion_id"));
|
||||
ASSERT_EQ(1 , res_obj["hits"][0]["document"]["portions"][0].at("quantity"));
|
||||
ASSERT_EQ("lt", res_obj["hits"][0]["document"]["portions"][0].at("unit"));
|
||||
ASSERT_EQ(3 , res_obj["hits"][0]["document"]["portions"][0].at("count"));
|
||||
|
||||
ASSERT_EQ(1, res_obj["hits"][0]["document"]["portions"][1].size());
|
||||
ASSERT_EQ(3 , res_obj["hits"][0]["document"]["portions"][1].at("count"));
|
||||
|
||||
ASSERT_EQ(5, res_obj["hits"][0]["document"]["portions"][2].size());
|
||||
ASSERT_EQ("portion_c", res_obj["hits"][0]["document"]["portions"][2].at("portion_id"));
|
||||
ASSERT_EQ(500 , res_obj["hits"][0]["document"]["portions"][2].at("quantity"));
|
||||
ASSERT_EQ("ml", res_obj["hits"][0]["document"]["portions"][2].at("unit"));
|
||||
ASSERT_EQ(1 , res_obj["hits"][0]["document"]["portions"][2].at("count"));
|
||||
|
||||
|
||||
ASSERT_EQ("Bread", res_obj["hits"][1]["document"]["name"]);
|
||||
ASSERT_EQ(1, res_obj["hits"][1]["document"].count("portions"));
|
||||
ASSERT_EQ(1, res_obj["hits"][1]["document"]["portions"].size());
|
||||
@ -4163,7 +4221,7 @@ TEST_F(CollectionJoinTest, SortByReference) {
|
||||
{"query_by", "product_name"},
|
||||
{"filter_by", "$Customers(customer_id:=customer_a)"},
|
||||
{"sort_by", "$Customers(product_price:asc)"},
|
||||
{"include_fields", "product_id, $Customers(product_price:merge)"},
|
||||
{"include_fields", "product_id, $Customers(product_price, strategy:merge)"},
|
||||
};
|
||||
search_op = collectionManager.do_search(req_params, embedded_params, json_res, now_ts);
|
||||
ASSERT_TRUE(search_op.ok());
|
||||
@ -4182,7 +4240,7 @@ TEST_F(CollectionJoinTest, SortByReference) {
|
||||
{"query_by", "product_name"},
|
||||
{"filter_by", "$Customers(customer_id:=customer_a)"},
|
||||
{"sort_by", "$Customers(product_price:desc)"},
|
||||
{"include_fields", "product_id, $Customers(product_price:merge)"},
|
||||
{"include_fields", "product_id, $Customers(product_price, strategy:merge)"},
|
||||
};
|
||||
search_op = collectionManager.do_search(req_params, embedded_params, json_res, now_ts);
|
||||
ASSERT_TRUE(search_op.ok());
|
||||
@ -4202,7 +4260,7 @@ TEST_F(CollectionJoinTest, SortByReference) {
|
||||
{"query_by", "product_name"},
|
||||
{"filter_by", "$Customers(customer_id:=customer_a)"},
|
||||
{"sort_by", "$Customers(product_id:asc)"},
|
||||
{"include_fields", "product_id, $Customers(product_price:merge)"},
|
||||
{"include_fields", "product_id, $Customers(product_price, strategy:merge)"},
|
||||
};
|
||||
search_op = collectionManager.do_search(req_params, embedded_params, json_res, now_ts);
|
||||
ASSERT_TRUE(search_op.ok());
|
||||
@ -4222,7 +4280,7 @@ TEST_F(CollectionJoinTest, SortByReference) {
|
||||
{"query_by", "product_name"},
|
||||
{"filter_by", "$Customers(customer_id:=customer_a)"},
|
||||
{"sort_by", "$Customers(_eval(product_available:true):asc)"},
|
||||
{"include_fields", "product_id, $Customers(product_price:merge)"},
|
||||
{"include_fields", "product_id, $Customers(product_price, strategy:merge)"},
|
||||
};
|
||||
search_op = collectionManager.do_search(req_params, embedded_params, json_res, now_ts);
|
||||
ASSERT_TRUE(search_op.ok());
|
||||
@ -4241,7 +4299,7 @@ TEST_F(CollectionJoinTest, SortByReference) {
|
||||
{"query_by", "product_name"},
|
||||
{"filter_by", "$Customers(customer_id:=customer_a)"},
|
||||
{"sort_by", "$Customers(_eval(product_available:true):desc)"},
|
||||
{"include_fields", "product_id, $Customers(product_price:merge)"},
|
||||
{"include_fields", "product_id, $Customers(product_price, strategy:merge)"},
|
||||
};
|
||||
search_op = collectionManager.do_search(req_params, embedded_params, json_res, now_ts);
|
||||
ASSERT_TRUE(search_op.ok());
|
||||
@ -4261,7 +4319,7 @@ TEST_F(CollectionJoinTest, SortByReference) {
|
||||
{"query_by", "product_name"},
|
||||
{"filter_by", "$Customers(customer_id:=customer_a)"},
|
||||
{"sort_by", "$Customers(product_price:desc)"},
|
||||
{"include_fields", "product_id, $Customers(product_price:merge)"},
|
||||
{"include_fields", "product_id, $Customers(product_price, strategy:merge)"},
|
||||
};
|
||||
search_op = collectionManager.do_search(req_params, embedded_params, json_res, now_ts);
|
||||
ASSERT_TRUE(search_op.ok());
|
||||
@ -4280,7 +4338,7 @@ TEST_F(CollectionJoinTest, SortByReference) {
|
||||
{"q", R"("our")"},
|
||||
{"query_by", "product_description"},
|
||||
{"filter_by", "$Customers(customer_id:=customer_a)"},
|
||||
{"include_fields", "product_id, $Customers(product_price:merge)"},
|
||||
{"include_fields", "product_id, $Customers(product_price, strategy:merge)"},
|
||||
{"sort_by", "$Customers(product_price:desc)"},
|
||||
};
|
||||
search_op = collectionManager.do_search(req_params, embedded_params, json_res, now_ts);
|
||||
@ -4300,7 +4358,7 @@ TEST_F(CollectionJoinTest, SortByReference) {
|
||||
{"q", "natural products"},
|
||||
{"query_by", "embedding"},
|
||||
{"filter_by", "$Customers(customer_id:=customer_a)"},
|
||||
{"include_fields", "product_id, $Customers(product_price:merge)"},
|
||||
{"include_fields", "product_id, $Customers(product_price, strategy:merge)"},
|
||||
{"sort_by", "$Customers(product_price:desc)"},
|
||||
};
|
||||
search_op = collectionManager.do_search(req_params, embedded_params, json_res, now_ts);
|
||||
@ -4334,7 +4392,7 @@ TEST_F(CollectionJoinTest, SortByReference) {
|
||||
{"q", "*"},
|
||||
{"vector_query", "embedding:(" + vec_string + ", flat_search_cutoff: 0)"},
|
||||
{"filter_by", "$Customers(customer_id:=customer_a)"},
|
||||
{"include_fields", "product_id, $Customers(product_price:merge)"},
|
||||
{"include_fields", "product_id, $Customers(product_price, strategy:merge)"},
|
||||
{"sort_by", "$Customers(product_price:desc)"},
|
||||
};
|
||||
search_op = collectionManager.do_search(req_params, embedded_params, json_res, now_ts);
|
||||
@ -4358,7 +4416,7 @@ TEST_F(CollectionJoinTest, SortByReference) {
|
||||
{"q", "soap"},
|
||||
{"query_by", "product_name, embedding"},
|
||||
{"filter_by", "$Customers(customer_id:=customer_a)"},
|
||||
{"include_fields", "product_id, $Customers(product_price:merge)"},
|
||||
{"include_fields", "product_id, $Customers(product_price, strategy:merge)"},
|
||||
{"sort_by", "$Customers(product_price:desc)"},
|
||||
};
|
||||
search_op = collectionManager.do_search(req_params, embedded_params, json_res, now_ts);
|
||||
@ -4384,7 +4442,7 @@ TEST_F(CollectionJoinTest, SortByReference) {
|
||||
{"q", "natural products"},
|
||||
{"query_by", "product_name, embedding"},
|
||||
{"filter_by", "$Customers(customer_id:=customer_a)"},
|
||||
{"include_fields", "product_id, $Customers(product_price:merge)"},
|
||||
{"include_fields", "product_id, $Customers(product_price, strategy:merge)"},
|
||||
{"sort_by", "$Customers(product_price:desc)"},
|
||||
};
|
||||
search_op = collectionManager.do_search(req_params, embedded_params, json_res, now_ts);
|
||||
@ -4409,7 +4467,7 @@ TEST_F(CollectionJoinTest, SortByReference) {
|
||||
{"query_by", "product_name"},
|
||||
{"infix", "always"},
|
||||
{"filter_by", "$Customers(customer_id:=customer_a)"},
|
||||
{"include_fields", "product_id, $Customers(product_price:merge)"},
|
||||
{"include_fields", "product_id, $Customers(product_price, strategy:merge)"},
|
||||
{"sort_by", "$Customers(product_price:desc)"},
|
||||
};
|
||||
search_op = collectionManager.do_search(req_params, embedded_params, json_res, now_ts);
|
||||
@ -4428,7 +4486,7 @@ TEST_F(CollectionJoinTest, SortByReference) {
|
||||
{"collection", "Customers"},
|
||||
{"q", "*"},
|
||||
{"filter_by", "customer_name:= [Joe, Dan] && product_price:<100"},
|
||||
{"include_fields", "$Products(product_name:merge), product_price"},
|
||||
{"include_fields", "$Products(product_name, strategy:merge), product_price"},
|
||||
{"sort_by", "$Products(product_name:desc)"},
|
||||
};
|
||||
search_op = collectionManager.do_search(req_params, embedded_params, json_res, now_ts);
|
||||
@ -4447,7 +4505,7 @@ TEST_F(CollectionJoinTest, SortByReference) {
|
||||
{"collection", "Customers"},
|
||||
{"q", "*"},
|
||||
{"filter_by", "customer_name:= [Joe, Dan] && product_price:<100"},
|
||||
{"include_fields", "$Products(product_name:merge), product_price"},
|
||||
{"include_fields", "$Products(product_name, strategy:merge), product_price"},
|
||||
{"sort_by", "$Products(product_name:asc)"},
|
||||
};
|
||||
search_op = collectionManager.do_search(req_params, embedded_params, json_res, now_ts);
|
||||
@ -4465,7 +4523,7 @@ TEST_F(CollectionJoinTest, SortByReference) {
|
||||
req_params = {
|
||||
{"collection", "Customers"},
|
||||
{"q", "*"},
|
||||
{"include_fields", "$Products(product_name:merge), customer_name, id"},
|
||||
{"include_fields", "$Products(product_name, strategy:merge), customer_name, id"},
|
||||
{"sort_by", "$Products(product_name:asc), customer_name:desc"},
|
||||
};
|
||||
search_op = collectionManager.do_search(req_params, embedded_params, json_res, now_ts);
|
||||
@ -4638,7 +4696,7 @@ TEST_F(CollectionJoinTest, SortByReference) {
|
||||
{"collection", "Users"},
|
||||
{"q", "*"},
|
||||
{"filter_by", "$Links(repo_id:=[repo_a, repo_d])"},
|
||||
{"include_fields", "user_id, user_name, $Repos(repo_content, repo_stars:merge), "},
|
||||
{"include_fields", "user_id, user_name, $Repos(repo_content, repo_stars, strategy:merge), "},
|
||||
{"exclude_fields", "$Links(*), "},
|
||||
{"sort_by", "$Repos(repo_stars: asc)"}
|
||||
};
|
||||
@ -4668,7 +4726,7 @@ TEST_F(CollectionJoinTest, SortByReference) {
|
||||
{"collection", "Users"},
|
||||
{"q", "*"},
|
||||
{"filter_by", "$Links(repo_id:=[repo_a, repo_d])"},
|
||||
{"include_fields", "user_id, user_name, $Repos(repo_content, repo_stars:merge), "},
|
||||
{"include_fields", "user_id, user_name, $Repos(repo_content, repo_stars, strategy:merge), "},
|
||||
{"exclude_fields", "$Links(*), "},
|
||||
{"sort_by", "$Repos(repo_stars: desc), user_name:desc"}
|
||||
};
|
||||
@ -5031,7 +5089,7 @@ TEST_F(CollectionJoinTest, FilterByReferenceAlias) {
|
||||
{"collection", "Customers"},
|
||||
{"q", "*"},
|
||||
{"filter_by", "customer_name:= [Joe, Dan] && product_price:<100"},
|
||||
{"include_fields", "$Products_alias(product_name:merge), product_price"},
|
||||
{"include_fields", "$Products_alias(product_name, strategy:merge), product_price"},
|
||||
{"sort_by", "$Products_alias(product_name:desc)"},
|
||||
};
|
||||
search_op = collectionManager.do_search(req_params, embedded_params, json_res, now_ts);
|
||||
|
@ -1541,16 +1541,23 @@ TEST_F(CollectionManagerTest, InitializeRefIncludeExcludeFields) {
|
||||
exclude_fields_vec.clear();
|
||||
|
||||
filter_query = "";
|
||||
include_fields_vec = {"$Customers(product_price: foo) as customers"};
|
||||
include_fields_vec = {"$Customers(product_price, strategy: foo) as customers"};
|
||||
initialize_op = CollectionManager::_initialize_ref_include_exclude_fields_vec(filter_query, include_fields_vec,
|
||||
exclude_fields_vec,
|
||||
ref_include_exclude_fields_vec);
|
||||
ASSERT_FALSE(initialize_op.ok());
|
||||
ASSERT_EQ("Error parsing `$Customers(product_price: foo) as customers`: Unknown include strategy `foo`. "
|
||||
ASSERT_EQ("Error parsing `$Customers(product_price, strategy: foo) as customers`: Unknown include strategy `foo`. "
|
||||
"Valid options are `merge`, `nest`, `nest_array`.", initialize_op.error());
|
||||
|
||||
include_fields_vec = {"$Customers(product_price, foo: bar) as customers"};
|
||||
initialize_op = CollectionManager::_initialize_ref_include_exclude_fields_vec(filter_query, include_fields_vec,
|
||||
exclude_fields_vec,
|
||||
ref_include_exclude_fields_vec);
|
||||
ASSERT_FALSE(initialize_op.ok());
|
||||
ASSERT_EQ("Unknown reference `include_fields` parameter: `foo`.", initialize_op.error());
|
||||
|
||||
filter_query = "$Customers(customer_id:=customer_a && (product_price:>100 && product_price:<200))";
|
||||
include_fields_vec = {"$Customers(product_price: merge) as customers"};
|
||||
include_fields_vec = {"$Customers(product_price, strategy: merge) as customers"};
|
||||
initialize_op = CollectionManager::_initialize_ref_include_exclude_fields_vec(filter_query, include_fields_vec,
|
||||
exclude_fields_vec,
|
||||
ref_include_exclude_fields_vec);
|
||||
@ -1564,7 +1571,7 @@ TEST_F(CollectionManagerTest, InitializeRefIncludeExcludeFields) {
|
||||
ref_include_exclude_fields_vec.clear();
|
||||
|
||||
filter_query = "$Customers(customer_id:=customer_a && (product_price:>100 && product_price:<200))";
|
||||
include_fields_vec = {"$Customers(product_price: nest_array) as customers"};
|
||||
include_fields_vec = {"$Customers(product_price, strategy: nest_array) as customers"};
|
||||
initialize_op = CollectionManager::_initialize_ref_include_exclude_fields_vec(filter_query, include_fields_vec,
|
||||
exclude_fields_vec,
|
||||
ref_include_exclude_fields_vec);
|
||||
@ -1603,14 +1610,14 @@ TEST_F(CollectionManagerTest, InitializeRefIncludeExcludeFields) {
|
||||
ref_include_exclude_fields_vec.clear();
|
||||
|
||||
filter_query = "$product_variants( $inventory($retailers(location:(33.865,-118.375,100 km))))";
|
||||
include_fields_vec = {"$product_variants(title, $inventory(qty:merge) as inventory: nest) as variants"};
|
||||
include_fields_vec = {"$product_variants(title, $inventory(qty, strategy:merge) as inventory, strategy: nest) as variants"};
|
||||
initialize_op = CollectionManager::_initialize_ref_include_exclude_fields_vec(filter_query, include_fields_vec,
|
||||
exclude_fields_vec,
|
||||
ref_include_exclude_fields_vec);
|
||||
ASSERT_TRUE(initialize_op.ok());
|
||||
ASSERT_EQ(1, ref_include_exclude_fields_vec.size());
|
||||
ASSERT_EQ("product_variants", ref_include_exclude_fields_vec[0].collection_name);
|
||||
ASSERT_EQ("title,", ref_include_exclude_fields_vec[0].include_fields);
|
||||
ASSERT_EQ("title", ref_include_exclude_fields_vec[0].include_fields);
|
||||
ASSERT_EQ("variants", ref_include_exclude_fields_vec[0].alias);
|
||||
ASSERT_EQ(ref_include::nest, ref_include_exclude_fields_vec[0].strategy);
|
||||
|
||||
@ -1628,15 +1635,15 @@ TEST_F(CollectionManagerTest, InitializeRefIncludeExcludeFields) {
|
||||
ref_include_exclude_fields_vec.clear();
|
||||
|
||||
filter_query = "$product_variants( $inventory(id:*) && $retailers(location:(33.865,-118.375,100 km)))";
|
||||
include_fields_vec = {"$product_variants(title, $inventory(qty:merge) as inventory,"
|
||||
" $retailers(title): merge) as variants"};
|
||||
include_fields_vec = {"$product_variants(title, $inventory(qty, strategy:merge) as inventory,"
|
||||
" $retailers(title), strategy: merge) as variants"};
|
||||
initialize_op = CollectionManager::_initialize_ref_include_exclude_fields_vec(filter_query, include_fields_vec,
|
||||
exclude_fields_vec,
|
||||
ref_include_exclude_fields_vec);
|
||||
ASSERT_TRUE(initialize_op.ok());
|
||||
ASSERT_EQ(1, ref_include_exclude_fields_vec.size());
|
||||
ASSERT_EQ("product_variants", ref_include_exclude_fields_vec[0].collection_name);
|
||||
ASSERT_EQ("title,", ref_include_exclude_fields_vec[0].include_fields);
|
||||
ASSERT_EQ("title", ref_include_exclude_fields_vec[0].include_fields);
|
||||
ASSERT_EQ("variants.", ref_include_exclude_fields_vec[0].alias);
|
||||
ASSERT_EQ(ref_include::merge, ref_include_exclude_fields_vec[0].strategy);
|
||||
|
||||
@ -1653,8 +1660,8 @@ TEST_F(CollectionManagerTest, InitializeRefIncludeExcludeFields) {
|
||||
ref_include_exclude_fields_vec.clear();
|
||||
|
||||
filter_query = "$product_variants( $inventory(id:*) && $retailers(location:(33.865,-118.375,100 km)))";
|
||||
include_fields_vec = {"$product_variants(title, $inventory(qty:merge) as inventory, description,"
|
||||
" $retailers(title), foo: merge) as variants"};
|
||||
include_fields_vec = {"$product_variants(title, $inventory(qty, strategy:merge) as inventory, description,"
|
||||
" $retailers(title), foo, strategy: merge) as variants"};
|
||||
initialize_op = CollectionManager::_initialize_ref_include_exclude_fields_vec(filter_query, include_fields_vec,
|
||||
exclude_fields_vec,
|
||||
ref_include_exclude_fields_vec);
|
||||
@ -1722,7 +1729,7 @@ TEST_F(CollectionManagerTest, InitializeRefIncludeExcludeFields) {
|
||||
ref_include_exclude_fields_vec.clear();
|
||||
|
||||
filter_query = "$product_variants( $inventory($retailers(location:(33.865,-118.375,100 km))))";
|
||||
include_fields_vec = {"$product_variants(title, $inventory(qty:merge) as inventory: nest) as variants"};
|
||||
include_fields_vec = {"$product_variants(title, $inventory(qty, strategy:merge) as inventory, strategy: nest) as variants"};
|
||||
exclude_fields_vec = {"$product_variants(title, $inventory(qty, $retailers(title)))"};
|
||||
initialize_op = CollectionManager::_initialize_ref_include_exclude_fields_vec(filter_query, include_fields_vec,
|
||||
exclude_fields_vec,
|
||||
@ -1730,7 +1737,7 @@ TEST_F(CollectionManagerTest, InitializeRefIncludeExcludeFields) {
|
||||
ASSERT_TRUE(initialize_op.ok());
|
||||
ASSERT_EQ(1, ref_include_exclude_fields_vec.size());
|
||||
ASSERT_EQ("product_variants", ref_include_exclude_fields_vec[0].collection_name);
|
||||
ASSERT_EQ("title,", ref_include_exclude_fields_vec[0].include_fields);
|
||||
ASSERT_EQ("title", ref_include_exclude_fields_vec[0].include_fields);
|
||||
ASSERT_EQ("title,", ref_include_exclude_fields_vec[0].exclude_fields);
|
||||
ASSERT_EQ("variants", ref_include_exclude_fields_vec[0].alias);
|
||||
ASSERT_EQ(ref_include::nest, ref_include_exclude_fields_vec[0].strategy);
|
||||
|
@ -2985,4 +2985,31 @@ TEST_F(CollectionSpecificMoreTest, TestStemmingWithSynonym) {
|
||||
auto res = coll_stem->search("making", {"word"}, "", {}, {}, {0}, 10, 1, FREQUENCY, {true}, 0).get();
|
||||
ASSERT_EQ(1, res["hits"].size());
|
||||
ASSERT_EQ("foobar", res["hits"][0]["document"]["word"].get<std::string>());
|
||||
}
|
||||
|
||||
TEST_F(CollectionSpecificMoreTest, TestFieldStore) {
|
||||
nlohmann::json schema = R"({
|
||||
"name": "words",
|
||||
"fields": [
|
||||
{"name": "word_to_store", "type": "string", "store": true },
|
||||
{"name": "word_not_to_store", "type": "string", "store": false }
|
||||
]
|
||||
})"_json;
|
||||
|
||||
auto coll_store_res = collectionManager.create_collection(schema);
|
||||
ASSERT_TRUE(coll_store_res.ok());
|
||||
|
||||
auto coll_store = coll_store_res.get();
|
||||
|
||||
nlohmann::json doc;
|
||||
doc["word_to_store"] = "store";
|
||||
doc["word_not_to_store"] = "not store";
|
||||
ASSERT_TRUE(coll_store->add(doc.dump()).ok());
|
||||
|
||||
auto res = coll_store->search("*", {}, {}, {}, {}, {0}, 10, 1, FREQUENCY, {false}, 1);
|
||||
ASSERT_TRUE(res.ok());
|
||||
|
||||
ASSERT_EQ(1, res.get()["hits"].size());
|
||||
ASSERT_EQ("store", res.get()["hits"][0]["document"]["word_to_store"].get<std::string>());
|
||||
ASSERT_TRUE(res.get()["hits"][0]["document"].count("word_not_to_store") == 0);
|
||||
}
|
@ -1414,3 +1414,31 @@ TEST_F(CollectionSynonymsTest, SynonymsPagination) {
|
||||
ASSERT_FALSE(synonym_op.ok());
|
||||
ASSERT_EQ("Invalid offset param.", synonym_op.error());
|
||||
}
|
||||
|
||||
TEST_F(CollectionSynonymsTest, SynonymWithStemming) {
|
||||
nlohmann::json schema = R"({
|
||||
"name": "coll1",
|
||||
"fields": [
|
||||
{"name": "name", "type": "string", "stem": true}
|
||||
]
|
||||
})"_json;
|
||||
|
||||
auto coll1 = collectionManager.create_collection(schema).get();
|
||||
std::vector<std::string> records = {"k8s", "kubernetes"};
|
||||
|
||||
for(size_t i = 0; i < records.size(); i++) {
|
||||
nlohmann::json doc;
|
||||
doc["id"] = std::to_string(i);
|
||||
doc["name"] = records[i];
|
||||
ASSERT_TRUE(coll1->add(doc.dump()).ok());
|
||||
}
|
||||
|
||||
coll1->add_synonym(R"({"id": "syn-1", "synonyms": ["k8s", "kubernetes"]})"_json);
|
||||
|
||||
auto res = coll1->search("k8s", {"name"}, "", {}, {}, {2}, 10, 1, FREQUENCY, {true}, 0).get();
|
||||
|
||||
ASSERT_EQ(2, res["hits"].size());
|
||||
ASSERT_EQ(2, res["found"].get<uint32_t>());
|
||||
|
||||
collectionManager.drop_collection("coll1");
|
||||
}
|
||||
|
@ -947,4 +947,8 @@ TEST_F(NumericRangeTrieTest, Integration) {
|
||||
|
||||
results = coll_array_fields->search("Jeremy", query_fields, "rating: [7.812 .. 9.999, 1.05 .. 1.09]", facets, sort_fields, {0}, 10, 1, FREQUENCY, {false}).get();
|
||||
ASSERT_EQ(3, results["hits"].size());
|
||||
|
||||
auto coll_json = coll_array_fields->get_summary_json();
|
||||
ASSERT_TRUE(coll_json["fields"][2]["range_index"]);
|
||||
ASSERT_TRUE(coll_json["fields"][4]["range_index"]);
|
||||
}
|
||||
|
@ -450,18 +450,18 @@ TEST(StringUtilsTest, SplitIncludeExcludeFields) {
|
||||
}
|
||||
|
||||
TEST(StringUtilsTest, SplitReferenceIncludeExcludeFields) {
|
||||
std::string include_fields = "$retailer(id,title: merge) as retailer_info:merge) as variants, foo", token;
|
||||
std::string include_fields = "$retailer(id,title,strategy:merge) as retailer_info, strategy:merge) as variants, foo", token;
|
||||
size_t index = 0;
|
||||
auto tokenize_op = StringUtils::split_reference_include_exclude_fields(include_fields, index, token);
|
||||
ASSERT_TRUE(tokenize_op.ok());
|
||||
ASSERT_EQ("$retailer(id,title: merge) as retailer_info", token);
|
||||
ASSERT_EQ(":merge) as variants, foo", include_fields.substr(index));
|
||||
ASSERT_EQ("$retailer(id,title,strategy:merge) as retailer_info", token);
|
||||
ASSERT_EQ(", strategy:merge) as variants, foo", include_fields.substr(index));
|
||||
|
||||
include_fields = "$inventory(qty,sku,$retailer(id,title: merge) as retailer_info) as inventory) as variants, foo";
|
||||
include_fields = "$inventory(qty,sku,$retailer(id,title, strategy : merge) as retailer_info) as inventory) as variants, foo";
|
||||
index = 0;
|
||||
tokenize_op = StringUtils::split_reference_include_exclude_fields(include_fields, index, token);
|
||||
ASSERT_TRUE(tokenize_op.ok());
|
||||
ASSERT_EQ("$inventory(qty,sku,$retailer(id,title: merge) as retailer_info) as inventory", token);
|
||||
ASSERT_EQ("$inventory(qty,sku,$retailer(id,title, strategy : merge) as retailer_info) as inventory", token);
|
||||
ASSERT_EQ(") as variants, foo", include_fields.substr(index));
|
||||
|
||||
std::string exclude_fields = "$Collection(title), $product_variants(id,$inventory(qty,sku,$retailer(id,title)))";
|
||||
|
Loading…
x
Reference in New Issue
Block a user