Merge branch 'v0.26-facets' into v0.26-facets

This commit is contained in:
Kishore Nallan 2023-09-07 20:43:33 +05:30 committed by GitHub
commit 3b8b80e23e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 212 additions and 50 deletions

View File

@ -5289,6 +5289,7 @@ bool Collection::get_enable_nested_fields() {
Option<bool> Collection::parse_facet(const std::string& facet_field, std::vector<facet>& facets) const {
const std::regex base_pattern(".+\\(.*\\)");
const std::regex range_pattern("[[a-zA-Z]+:\\[([0-9]+)\\,\\s*([0-9]+)\\]");
const std::string _alpha = "_alpha";
if ((facet_field.find(":") != std::string::npos)
&& (facet_field.find("sort") == std::string::npos)) { //range based facet
@ -5457,9 +5458,21 @@ Option<bool> Collection::parse_facet(const std::string& facet_field, std::vector
return Option<bool>(404, error);
}
if (facet_field.find("sort") != std::string::npos) { //sort params are supplied with facet
pos = facet_field.find("sort_field");
if(pos == std::string::npos) { //alpha sort
if (facet_field.find("sort_by") != std::string::npos) { //sort params are supplied with facet
std::vector<std::string> tokens;
StringUtils::split(facet_field, tokens, ":");
if(tokens.size() != 3) {
std::string error = "Invalid sort format.";
return Option<bool>(400, error);
}
//remove possible whitespaces
for(auto i=0; i < 3; ++i) {
StringUtils::trim(tokens[i]);
}
if(tokens[1] == _alpha) {
const field &a_field = search_schema.at(facet_field_copy);
if (!a_field.is_string()) {
std::string error = "Facet field should be string type to apply alpha sort.";
@ -5467,9 +5480,7 @@ Option<bool> Collection::parse_facet(const std::string& facet_field, std::vector
}
sort_alpha = true;
} else { //sort_field based sort
auto sort_field_fixed_len = strlen("sort_field:");
auto sort_field_len = facet_field.size() - pos - sort_field_fixed_len - 1;
sort_field = facet_field.substr(pos + sort_field_fixed_len, sort_field_len);
sort_field = tokens[1];
if (search_schema.count(sort_field) == 0 || !search_schema.at(sort_field).facet) {
std::string error = "Could not find a facet field named `" + sort_field + "` in the schema.";
@ -5483,11 +5494,17 @@ Option<bool> Collection::parse_facet(const std::string& facet_field, std::vector
}
}
if (facet_field.find("asc") != std::string::npos) {
if (tokens[2].find("asc") != std::string::npos) {
order = "asc";
} else if (facet_field.find("desc") != std::string::npos) {
} else if (tokens[2].find("desc") != std::string::npos) {
order = "desc";
} else {
std::string error = "Invalid sort param.";
return Option<bool>(400, error);
}
} else if (facet_field != facet_field_copy) {
std::string error = "Invalid sort format.";
return Option<bool>(400, error);
}
facets.emplace_back(facet(facet_field_copy, {}, false, sort_alpha,

View File

@ -2023,7 +2023,7 @@ TEST_F(CollectionFacetingTest, FacetSortByAlpha) {
ASSERT_TRUE(add_op.ok());
//sort facets by phone in asc order
auto search_op = coll1->search("*", {}, "", {"phone(sort:asc)"},
auto search_op = coll1->search("*", {}, "", {"phone(sort_by:_alpha:asc)"},
{}, {2});
if (!search_op.ok()) {
@ -2043,7 +2043,7 @@ TEST_F(CollectionFacetingTest, FacetSortByAlpha) {
ASSERT_EQ("Z6 Lite", results["facet_counts"][0]["counts"][6]["value"]);
//sort facets by brand in desc order
search_op = coll1->search("*", {}, "", {"brand(sort:desc)"},
search_op = coll1->search("*", {}, "", {"brand(sort_by:_alpha:desc)"},
{}, {2});
if (!search_op.ok()) {
@ -2063,7 +2063,8 @@ TEST_F(CollectionFacetingTest, FacetSortByAlpha) {
ASSERT_EQ("Iqoo", results["facet_counts"][0]["counts"][6]["value"]);
//sort facets by brand in desc order and phone by asc order
search_op = coll1->search("*", {}, "", {"brand(sort:desc)", "phone(sort:asc)"},
search_op = coll1->search("*", {}, "", {"brand(sort_by:_alpha:desc)",
"phone(sort_by:_alpha:asc)"},
{}, {2});
if (!search_op.ok()) {
@ -2091,13 +2092,6 @@ TEST_F(CollectionFacetingTest, FacetSortByAlpha) {
ASSERT_EQ("S22 Ultra", results["facet_counts"][1]["counts"][4]["value"]);
ASSERT_EQ("T2", results["facet_counts"][1]["counts"][5]["value"]);
ASSERT_EQ("Z6 Lite", results["facet_counts"][1]["counts"][6]["value"]);
//try sort on non string field
search_op = coll1->search("*", {}, "", {"rating(sort:desc)"},
{}, {2});
ASSERT_EQ(400, search_op.code());
ASSERT_EQ("Facet field should be string type to apply alpha sort.", search_op.error());
}
TEST_F(CollectionFacetingTest, FacetSortByOtherField) {
@ -2170,7 +2164,7 @@ TEST_F(CollectionFacetingTest, FacetSortByOtherField) {
//search by calories in asc order
auto search_op = coll1->search("*", {},"",
{"receipe.name(sort:asc, sort_field:receipe.calories)"},
{"receipe.name(sort_by:receipe.calories:asc)"},
{}, {2});
if(!search_op.ok()) {
@ -2189,7 +2183,7 @@ TEST_F(CollectionFacetingTest, FacetSortByOtherField) {
//search by calories in desc order
search_op = coll1->search("*", {},"",
{"receipe.name(sort:desc, sort_field:receipe.calories)"},
{"receipe.name(sort_by:receipe.calories:desc)"},
{}, {2});
if(!search_op.ok()) {
@ -2205,13 +2199,6 @@ TEST_F(CollectionFacetingTest, FacetSortByOtherField) {
ASSERT_EQ("butter chicken", results["facet_counts"][0]["counts"][2]["value"]);
ASSERT_EQ("noodles", results["facet_counts"][0]["counts"][3]["value"]);
ASSERT_EQ("schezwan rice", results["facet_counts"][0]["counts"][4]["value"]);
//try sort by stirng field
search_op = coll1->search("*", {}, "", {"receipe.name(sort:desc, sort_field:receipe.origin)"},
{}, {2});
ASSERT_EQ(400, search_op.code());
ASSERT_EQ("Sort field should be non string type to apply sort.", search_op.error());
}
TEST_F(CollectionFacetingTest, FacetSortByOtherFloatField) {
@ -2284,7 +2271,7 @@ TEST_F(CollectionFacetingTest, FacetSortByOtherFloatField) {
//search by calories in asc order
auto search_op = coll1->search("*", {},"",
{"investment.name(sort:asc, sort_field:investment.interest_rate)"},
{"investment.name(sort_by:investment.interest_rate:asc)"},
{}, {2});
if(!search_op.ok()) {
@ -2303,7 +2290,7 @@ TEST_F(CollectionFacetingTest, FacetSortByOtherFloatField) {
//search by calories in desc order
search_op = coll1->search("*", {},"",
{"investment.name(sort:desc, sort_field:investment.interest_rate)"},
{"investment.name(sort_by:investment.interest_rate:desc)"},
{}, {2});
if(!search_op.ok()) {
@ -2319,4 +2306,89 @@ TEST_F(CollectionFacetingTest, FacetSortByOtherFloatField) {
ASSERT_EQ("Bonds", results["facet_counts"][0]["counts"][2]["value"]);
ASSERT_EQ("Term Deposits", results["facet_counts"][0]["counts"][3]["value"]);
ASSERT_EQ("Gold", results["facet_counts"][0]["counts"][4]["value"]);
}
TEST_F(CollectionFacetingTest, FacetSortValidation) {
nlohmann::json schema = R"({
"name": "coll1",
"fields": [
{"name": "phone", "type": "string", "optional": false, "facet": true },
{"name": "brand", "type": "string", "optional": false, "facet": true },
{"name": "rating", "type": "float", "optional": false, "facet": true }
]
})"_json;
auto op = collectionManager.create_collection(schema);
ASSERT_TRUE(op.ok());
Collection *coll1 = op.get();
nlohmann::json doc;
doc["phone"] = "Oneplus 11R";
doc["brand"] = "Oneplus";
doc["rating"] = 4.6;
auto add_op = coll1->add(doc.dump(), CREATE);
ASSERT_TRUE(add_op.ok());
doc["phone"] = "Fusion Plus";
doc["brand"] = "Moto";
doc["rating"] = 4.2;
add_op = coll1->add(doc.dump(), CREATE);
ASSERT_TRUE(add_op.ok());
doc["phone"] = "S22 Ultra";
doc["brand"] = "Samsung";
doc["rating"] = 4.1;
add_op = coll1->add(doc.dump(), CREATE);
ASSERT_TRUE(add_op.ok());
//try sort on non string field
auto search_op = coll1->search("*", {}, "", {"rating(sort_by:_alpha:desc)"},
{}, {2});
ASSERT_EQ(400, search_op.code());
ASSERT_EQ("Facet field should be string type to apply alpha sort.", search_op.error());
//try sort by string field
search_op = coll1->search("*", {}, "", {"phone(sort_by:brand:desc)"},
{}, {2});
ASSERT_EQ(400, search_op.code());
ASSERT_EQ("Sort field should be non string type to apply sort.", search_op.error());
//incorrect syntax
search_op = coll1->search("*", {}, "", {"phone(sort_by:desc)"},
{}, {2});
ASSERT_EQ(400, search_op.code());
ASSERT_EQ("Invalid sort format.", search_op.error());
search_op = coll1->search("*", {}, "", {"phone(sort:_alpha:desc)"},
{}, {2});
ASSERT_EQ(400, search_op.code());
ASSERT_EQ("Invalid sort format.", search_op.error());
//invalid param
search_op = coll1->search("*", {}, "", {"phone(sort_by:_alpha:foo)"},
{}, {2});
ASSERT_EQ(400, search_op.code());
ASSERT_EQ("Invalid sort param.", search_op.error());
//whitespace is allowed
search_op = coll1->search("*", {}, "", {"phone( sort_by: _alpha : asc)"},
{}, {2});
if (!search_op.ok()) {
LOG(ERROR) << search_op.error();
FAIL();
}
auto results = search_op.get();
ASSERT_EQ(1, results["facet_counts"].size());
ASSERT_EQ(3, results["facet_counts"][0]["counts"].size());
ASSERT_EQ("Fusion Plus", results["facet_counts"][0]["counts"][0]["value"]);
ASSERT_EQ("Oneplus 11R", results["facet_counts"][0]["counts"][1]["value"]);
ASSERT_EQ("S22 Ultra", results["facet_counts"][0]["counts"][2]["value"]);
}

View File

@ -1501,7 +1501,7 @@ TEST_F(CollectionOptimizedFacetingTest, FacetSortByAlpha) {
ASSERT_TRUE(add_op.ok());
//sort facets by phone in asc order
auto search_op = coll1->search("*", {}, "", {"phone(sort:asc)"},
auto search_op = coll1->search("*", {}, "", {"phone(sort_by:_alpha:asc)"},
{}, {2});
if (!search_op.ok()) {
@ -1521,7 +1521,7 @@ TEST_F(CollectionOptimizedFacetingTest, FacetSortByAlpha) {
ASSERT_EQ("Z6 Lite", results["facet_counts"][0]["counts"][6]["value"]);
//sort facets by brand in desc order
search_op = coll1->search("*", {}, "", {"brand(sort:desc)"},
search_op = coll1->search("*", {}, "", {"brand(sort_by:_alpha:desc)"},
{}, {2});
if (!search_op.ok()) {
@ -1541,7 +1541,8 @@ TEST_F(CollectionOptimizedFacetingTest, FacetSortByAlpha) {
ASSERT_EQ("Iqoo", results["facet_counts"][0]["counts"][6]["value"]);
//sort facets by brand in desc order and phone by asc order
search_op = coll1->search("*", {}, "", {"brand(sort:desc)", "phone(sort:asc)"},
search_op = coll1->search("*", {}, "", {"brand(sort_by:_alpha:desc)",
"phone(sort_by:_alpha:asc)"},
{}, {2});
if (!search_op.ok()) {
@ -1569,13 +1570,6 @@ TEST_F(CollectionOptimizedFacetingTest, FacetSortByAlpha) {
ASSERT_EQ("S22 Ultra", results["facet_counts"][1]["counts"][4]["value"]);
ASSERT_EQ("T2", results["facet_counts"][1]["counts"][5]["value"]);
ASSERT_EQ("Z6 Lite", results["facet_counts"][1]["counts"][6]["value"]);
//try sort on non string field
search_op = coll1->search("*", {}, "", {"rating(sort:desc)"},
{}, {2});
ASSERT_EQ(400, search_op.code());
ASSERT_EQ("Facet field should be string type to apply alpha sort.", search_op.error());
}
TEST_F(CollectionOptimizedFacetingTest, FacetSortByOtherField) {
@ -1648,7 +1642,7 @@ TEST_F(CollectionOptimizedFacetingTest, FacetSortByOtherField) {
//search by calories in asc order
auto search_op = coll1->search("*", {},"",
{"receipe.name(sort:asc, sort_field:receipe.calories)"},
{"receipe.name(sort_by:receipe.calories:asc)"},
{}, {2});
if(!search_op.ok()) {
@ -1667,7 +1661,7 @@ TEST_F(CollectionOptimizedFacetingTest, FacetSortByOtherField) {
//search by calories in desc order
search_op = coll1->search("*", {},"",
{"receipe.name(sort:desc, sort_field:receipe.calories)"},
{"receipe.name(sort_by:receipe.calories:desc)"},
{}, {2});
if(!search_op.ok()) {
@ -1683,13 +1677,6 @@ TEST_F(CollectionOptimizedFacetingTest, FacetSortByOtherField) {
ASSERT_EQ("butter chicken", results["facet_counts"][0]["counts"][2]["value"]);
ASSERT_EQ("noodles", results["facet_counts"][0]["counts"][3]["value"]);
ASSERT_EQ("schezwan rice", results["facet_counts"][0]["counts"][4]["value"]);
//try sort by stirng field
search_op = coll1->search("*", {}, "", {"receipe.name(sort:desc, sort_field:receipe.origin)"},
{}, {2});
ASSERT_EQ(400, search_op.code());
ASSERT_EQ("Sort field should be non string type to apply sort.", search_op.error());
}
TEST_F(CollectionOptimizedFacetingTest, FacetSortByOtherFloatField) {
@ -1762,7 +1749,7 @@ TEST_F(CollectionOptimizedFacetingTest, FacetSortByOtherFloatField) {
//search by calories in asc order
auto search_op = coll1->search("*", {},"",
{"investment.name(sort:asc, sort_field:investment.interest_rate)"},
{"investment.name(sort_by:investment.interest_rate:asc)"},
{}, {2});
if(!search_op.ok()) {
@ -1781,7 +1768,7 @@ TEST_F(CollectionOptimizedFacetingTest, FacetSortByOtherFloatField) {
//search by calories in desc order
search_op = coll1->search("*", {},"",
{"investment.name(sort:desc, sort_field:investment.interest_rate)"},
{"investment.name(sort_by:investment.interest_rate:desc)"},
{}, {2});
if(!search_op.ok()) {
@ -1797,4 +1784,90 @@ TEST_F(CollectionOptimizedFacetingTest, FacetSortByOtherFloatField) {
ASSERT_EQ("Bonds", results["facet_counts"][0]["counts"][2]["value"]);
ASSERT_EQ("Term Deposits", results["facet_counts"][0]["counts"][3]["value"]);
ASSERT_EQ("Gold", results["facet_counts"][0]["counts"][4]["value"]);
}
TEST_F(CollectionOptimizedFacetingTest, FacetSortValidation) {
nlohmann::json schema = R"({
"name": "coll1",
"fields": [
{"name": "phone", "type": "string", "optional": false, "facet": true },
{"name": "brand", "type": "string", "optional": false, "facet": true },
{"name": "rating", "type": "float", "optional": false, "facet": true }
]
})"_json;
auto op = collectionManager.create_collection(schema);
ASSERT_TRUE(op.ok());
Collection *coll1 = op.get();
nlohmann::json doc;
doc["phone"] = "Oneplus 11R";
doc["brand"] = "Oneplus";
doc["rating"] = 4.6;
auto add_op = coll1->add(doc.dump(), CREATE);
ASSERT_TRUE(add_op.ok());
doc["phone"] = "Fusion Plus";
doc["brand"] = "Moto";
doc["rating"] = 4.2;
add_op = coll1->add(doc.dump(), CREATE);
ASSERT_TRUE(add_op.ok());
doc["phone"] = "S22 Ultra";
doc["brand"] = "Samsung";
doc["rating"] = 4.1;
add_op = coll1->add(doc.dump(), CREATE);
ASSERT_TRUE(add_op.ok());
//try sort on non string field
auto search_op = coll1->search("*", {}, "", {"rating(sort_by:_alpha:desc)"},
{}, {2});
ASSERT_EQ(400, search_op.code());
ASSERT_EQ("Facet field should be string type to apply alpha sort.", search_op.error());
//try sort by string field
search_op = coll1->search("*", {}, "", {"phone(sort_by:brand:desc)"},
{}, {2});
ASSERT_EQ(400, search_op.code());
ASSERT_EQ("Sort field should be non string type to apply sort.", search_op.error());
//incorrect syntax
search_op = coll1->search("*", {}, "", {"phone(sort_by:desc)"},
{}, {2});
ASSERT_EQ(400, search_op.code());
ASSERT_EQ("Invalid sort format.", search_op.error());
search_op = coll1->search("*", {}, "", {"phone(sort:_alpha:desc)"},
{}, {2});
ASSERT_EQ(400, search_op.code());
ASSERT_EQ("Invalid sort format.", search_op.error());
//invalid param
search_op = coll1->search("*", {}, "", {"phone(sort_by:_alpha:foo)"},
{}, {2});
ASSERT_EQ(400, search_op.code());
ASSERT_EQ("Invalid sort param.", search_op.error());
//whitespace is allowed
search_op = coll1->search("*", {}, "", {"phone( sort_by: _alpha : asc)"},
{}, {2});
if (!search_op.ok()) {
LOG(ERROR) << search_op.error();
FAIL();
}
auto results = search_op.get();
ASSERT_EQ(1, results["facet_counts"].size());
ASSERT_EQ(3, results["facet_counts"][0]["counts"].size());
ASSERT_EQ("Fusion Plus", results["facet_counts"][0]["counts"][0]["value"]);
ASSERT_EQ("Oneplus 11R", results["facet_counts"][0]["counts"][1]["value"]);
ASSERT_EQ("S22 Ultra", results["facet_counts"][0]["counts"][2]["value"]);
}