From 3ab8517a8caf2fd84d37918105fcddc91a5ebc31 Mon Sep 17 00:00:00 2001 From: Krunal Gandhi Date: Tue, 28 May 2024 03:02:53 +0000 Subject: [PATCH] facet parent for array fields (#1756) * return object itself as parent for array fields * make variables const reference --- include/collection.h | 3 +- src/collection.cpp | 14 ++++- test/collection_faceting_test.cpp | 56 ++++++++++++++++++++ test/collection_optimized_faceting_test.cpp | 57 +++++++++++++++++++++ 4 files changed, 127 insertions(+), 3 deletions(-) diff --git a/include/collection.h b/include/collection.h index 0d3a8633..834e7b3c 100644 --- a/include/collection.h +++ b/include/collection.h @@ -479,7 +479,8 @@ public: bool facet_value_to_string(const facet &a_facet, const facet_count_t &facet_count, nlohmann::json &document, std::string &value) const; - nlohmann::json get_facet_parent(const std::string& facet_field_name, const nlohmann::json& document) const; + nlohmann::json get_facet_parent(const std::string& facet_field_name, const nlohmann::json& document, + const std::string& val, bool is_array) const; static void populate_result_kvs(Topster *topster, std::vector> &result_kvs, const spp::sparse_hash_map& groups_processed, diff --git a/src/collection.cpp b/src/collection.cpp index 2408dee8..eae5ab50 100644 --- a/src/collection.cpp +++ b/src/collection.cpp @@ -3053,7 +3053,7 @@ Option Collection::search(std::string raw_query, nlohmann::json parent; if(the_field.nested && should_return_parent) { - parent = get_facet_parent(the_field.name, document); + parent = get_facet_parent(the_field.name, document, value, the_field.is_array()); } const auto& highlighted_text = highlight.snippets.empty() ? value : highlight.snippets[0]; @@ -3806,7 +3806,8 @@ bool Collection::facet_value_to_string(const facet &a_facet, const facet_count_t return true; } -nlohmann::json Collection::get_facet_parent(const std::string& facet_field_name, const nlohmann::json& document) const { +nlohmann::json Collection::get_facet_parent(const std::string& facet_field_name, const nlohmann::json& document, + const std::string& val, bool is_array) const { std::vector tokens; StringUtils::split(facet_field_name, tokens, "."); std::vector level_docs; @@ -3832,6 +3833,15 @@ nlohmann::json Collection::get_facet_parent(const std::string& facet_field_name, if(!parent_found) { doc = level_docs[0]; //return the top most root + + if(is_array) { + const auto& field = tokens[tokens.size() - 1]; + for(const auto& obj : doc) { + if(obj[field] == val) { + return obj; + } + } + } } return doc; } diff --git a/test/collection_faceting_test.cpp b/test/collection_faceting_test.cpp index c7effede..bee3158f 100644 --- a/test/collection_faceting_test.cpp +++ b/test/collection_faceting_test.cpp @@ -2402,6 +2402,62 @@ TEST_F(CollectionFacetingTest, FacetingReturnParentObject) { ASSERT_EQ("blue", results["facet_counts"][0]["counts"][1]["value"]); } +TEST_F(CollectionFacetingTest, FacetingReturnParentArrayFields) { + nlohmann::json schema = R"({ + "name": "coll1", + "enable_nested_fields": true, + "fields": [ + {"name": "tags.id", "type": "string[]", "facet": true } + ] + })"_json; + + auto op = collectionManager.create_collection(schema); + ASSERT_TRUE(op.ok()); + Collection* coll1 = op.get(); + + nlohmann::json doc1 = R"({ + "tags": [ + { + "id": "tag-1", + "name": "name for tag-1" + }, + { + "id": "tag-2", + "name": "name for tag-2" + } + ] + })"_json; + + auto add_op = coll1->add(doc1.dump(), CREATE); + ASSERT_TRUE(add_op.ok()); + + auto search_op = coll1->search("*", {}, "", {"tags.id"}, + {}, {2}, 10, 1, FREQUENCY, {true}, + 1, spp::sparse_hash_set(), + spp::sparse_hash_set(), 10, "", + 30, 4, "", + Index::TYPO_TOKENS_THRESHOLD, "", "", {}, + 3, "", "", {}, + UINT32_MAX, true, false, true, + "", false, 6000 * 1000, 4, 7, + fallback, 4, {off}, INT16_MAX, INT16_MAX, + 2, 2, false, "", + true, 0, max_score, 100, + 0, 0, "exhaustive", 30000, + 2, "", {"tags.id"}); + + if(!search_op.ok()) { + LOG(ERROR) << search_op.error(); + FAIL(); + } + auto results = search_op.get(); + ASSERT_EQ(1, results["facet_counts"].size()); + ASSERT_EQ(2, results["facet_counts"][0]["counts"].size()); + ASSERT_EQ("{\"id\":\"tag-2\",\"name\":\"name for tag-2\"}", results["facet_counts"][0]["counts"][0]["parent"].dump()); + ASSERT_EQ("tag-2", results["facet_counts"][0]["counts"][0]["value"]); + ASSERT_EQ("{\"id\":\"tag-1\",\"name\":\"name for tag-1\"}", results["facet_counts"][0]["counts"][1]["parent"].dump()); + ASSERT_EQ("tag-1", results["facet_counts"][0]["counts"][1]["value"]); +} TEST_F(CollectionFacetingTest, FacetSortByAlpha) { nlohmann::json schema = R"({ diff --git a/test/collection_optimized_faceting_test.cpp b/test/collection_optimized_faceting_test.cpp index 237b1c40..c5643e67 100644 --- a/test/collection_optimized_faceting_test.cpp +++ b/test/collection_optimized_faceting_test.cpp @@ -1992,6 +1992,63 @@ TEST_F(CollectionOptimizedFacetingTest, FacetingReturnParentObject) { ASSERT_EQ("blue", results["facet_counts"][0]["counts"][1]["value"]); } +TEST_F(CollectionOptimizedFacetingTest, FacetingReturnParentArrayFields) { + nlohmann::json schema = R"({ + "name": "coll1", + "enable_nested_fields": true, + "fields": [ + {"name": "tags.id", "type": "string[]", "facet": true } + ] + })"_json; + + auto op = collectionManager.create_collection(schema); + ASSERT_TRUE(op.ok()); + Collection* coll1 = op.get(); + + nlohmann::json doc1 = R"({ + "tags": [ + { + "id": "tag-1", + "name": "name for tag-1" + }, + { + "id": "tag-2", + "name": "name for tag-2" + } + ] + })"_json; + + auto add_op = coll1->add(doc1.dump(), CREATE); + ASSERT_TRUE(add_op.ok()); + + auto search_op = coll1->search("*", {}, "", {"tags.id"}, + {}, {2}, 10, 1, FREQUENCY, {true}, + 1, spp::sparse_hash_set(), + spp::sparse_hash_set(), 10, "", + 30, 4, "", + Index::TYPO_TOKENS_THRESHOLD, "", "", {}, + 3, "", "", {}, + UINT32_MAX, true, false, true, + "", false, 6000 * 1000, 4, 7, + fallback, 4, {off}, INT16_MAX, INT16_MAX, + 2, 2, false, "", + true, 0, max_score, 100, + 0, 0, "exhaustive", 30000, + 2, "", {"tags.id"}); + + if(!search_op.ok()) { + LOG(ERROR) << search_op.error(); + FAIL(); + } + auto results = search_op.get(); + ASSERT_EQ(1, results["facet_counts"].size()); + ASSERT_EQ(2, results["facet_counts"][0]["counts"].size()); + ASSERT_EQ("{\"id\":\"tag-2\",\"name\":\"name for tag-2\"}", results["facet_counts"][0]["counts"][0]["parent"].dump()); + ASSERT_EQ("tag-2", results["facet_counts"][0]["counts"][0]["value"]); + ASSERT_EQ("{\"id\":\"tag-1\",\"name\":\"name for tag-1\"}", results["facet_counts"][0]["counts"][1]["parent"].dump()); + ASSERT_EQ("tag-1", results["facet_counts"][0]["counts"][1]["value"]); +} + TEST_F(CollectionOptimizedFacetingTest, FacetSortByAlpha) { nlohmann::json schema = R"({ "name": "coll1",