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:
Kishore Nallan 2024-03-30 16:44:57 +05:30
commit e7f9bd30bd
13 changed files with 305 additions and 119 deletions

View File

@ -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;

View File

@ -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";

View File

@ -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,

View File

@ -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);
}
}
}

View File

@ -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();

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);
}

View File

@ -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");
}

View File

@ -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"]);
}

View File

@ -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)))";