diff --git a/src/field.cpp b/src/field.cpp index 2ee6bc37..4e8ebf82 100644 --- a/src/field.cpp +++ b/src/field.cpp @@ -347,9 +347,8 @@ bool field::flatten_obj(nlohmann::json& doc, nlohmann::json& value, bool has_arr while(it != value.end()) { const std::string& child_field_name = flat_name + "." + it.key(); if(it.value().is_null()) { - if(has_array) { - doc[child_field_name].push_back(nullptr); - } else { + if(!has_array) { + // we don't want to push null values into an array because that's not valid doc[child_field_name] = nullptr; } diff --git a/src/index.cpp b/src/index.cpp index da28bdc3..8d607ec4 100644 --- a/src/index.cpp +++ b/src/index.cpp @@ -6314,13 +6314,13 @@ void Index::remove_field(uint32_t seq_id, const nlohmann::json& document, const if (posting_t::num_ids(leaf->values) == 0) { void* values = art_delete(search_index.at(field_name), key, key_len); posting_t::destroy_list(values); - } - } - if(search_field.infix) { - auto strhash = StringUtils::hash_wy(key, token.size()); - const auto& infix_sets = infix_index.at(search_field.name); - infix_sets[strhash % 4]->erase(token); + if(search_field.infix) { + auto strhash = StringUtils::hash_wy(key, token.size()); + const auto& infix_sets = infix_index.at(search_field.name); + infix_sets[strhash % 4]->erase(token); + } + } } } } else if(search_field.is_int32()) { diff --git a/test/collection_infix_search_test.cpp b/test/collection_infix_search_test.cpp index f1b11067..bdf67e27 100644 --- a/test/collection_infix_search_test.cpp +++ b/test/collection_infix_search_test.cpp @@ -462,7 +462,7 @@ TEST_F(CollectionInfixSearchTest, InfixDeleteAndUpdate) { collectionManager.drop_collection("coll1"); } -TEST_F(CollectionInfixSearchTest, MultiFielInfixSearch) { +TEST_F(CollectionInfixSearchTest, MultiFieldInfixSearch) { std::vector fields = {field("title", field_types::STRING, false, false, true, "", -1, 1), field("mpn", field_types::STRING, false, false, true, "", -1, 1), field("points", field_types::INT32, false),}; @@ -494,3 +494,53 @@ TEST_F(CollectionInfixSearchTest, MultiFielInfixSearch) { collectionManager.drop_collection("coll1"); } + +TEST_F(CollectionInfixSearchTest, DeleteDocWithInfixIndex) { + std::vector fields = {field("title", field_types::STRING, false, false, true, "", -1, 1), + field("mpn", field_types::STRING, false, false, true, "", -1, 1), + field("points", field_types::INT32, false),}; + + Collection* coll1 = collectionManager.create_collection("coll1", 1, fields, "points").get(); + + nlohmann::json doc; + doc["id"] = "0"; + doc["title"] = "Running Shoe"; + doc["mpn"] = "HYDGHSGAH"; + doc["points"] = 100; + ASSERT_TRUE(coll1->add(doc.dump()).ok()); + + doc["id"] = "1"; + doc["title"] = "Running Band"; + doc["mpn"] = "GHX100037IN"; + doc["points"] = 100; + ASSERT_TRUE(coll1->add(doc.dump()).ok()); + + auto results = coll1->search("nni", + {"title"}, "", {}, {}, {0}, 3, 1, FREQUENCY, {true}, 5, + spp::sparse_hash_set(), + spp::sparse_hash_set(), 10, "", 30, 4, "title", 20, {}, {}, {}, 0, + "", "", {}, 1000, true, false, true, "", false, 6000 * 1000, 4, 7, fallback, + 4, {always}).get(); + + ASSERT_EQ(2, results["found"].get()); + ASSERT_EQ(2, results["hits"].size()); + + // drop one document + + coll1->remove("0"); + + // search again + + results = coll1->search("nni", + {"title"}, "", {}, {}, {0}, 3, 1, FREQUENCY, {true}, 5, + spp::sparse_hash_set(), + spp::sparse_hash_set(), 10, "", 30, 4, "title", 20, {}, {}, {}, 0, + "", "", {}, 1000, true, false, true, "", false, 6000 * 1000, 4, 7, fallback, + 4, {always}).get(); + + ASSERT_EQ(1, results["found"].get()); + ASSERT_EQ(1, results["hits"].size()); + ASSERT_STREQ("1", results["hits"][0]["document"]["id"].get().c_str()); + + collectionManager.drop_collection("coll1"); +} \ No newline at end of file diff --git a/test/collection_nested_fields_test.cpp b/test/collection_nested_fields_test.cpp index f92f7e0b..0b882311 100644 --- a/test/collection_nested_fields_test.cpp +++ b/test/collection_nested_fields_test.cpp @@ -1487,6 +1487,109 @@ TEST_F(CollectionNestedFieldsTest, ExplicitSchemaForNestedArrayTypeValidation) { "Hint: field inside an array of objects must be an array type as well.", add_op.error()); } +TEST_F(CollectionNestedFieldsTest, OptionalNestedOptionalOjectArrStringField) { + nlohmann::json schema = R"({ + "name": "coll1", + "enable_nested_fields": true, + "fields": [ + {"facet":true,"name":"data","optional":false,"type":"object"}, + {"facet":false,"name":"data.locations.stateShort","optional":true,"type":"string[]"} + ] + })"_json; + + auto op = collectionManager.create_collection(schema); + ASSERT_TRUE(op.ok()); + Collection* coll1 = op.get(); + + auto doc1 = R"({ + "data": { + "locations": [ + { + "stateShort": null + } + ] + } + })"_json; + + auto add_op = coll1->add(doc1.dump(), CREATE); + ASSERT_TRUE(add_op.ok()); + + doc1 = R"({ + "data": { + "locations": [ + { + "stateShort": null + }, + { + "stateShort": "NY" + } + ] + } + })"_json; + + coll1->add(doc1.dump(), CREATE); + + auto results = coll1->search("ny", {"data.locations.stateShort"}, + "", {}, {}, {0}, 10, 1, + token_ordering::FREQUENCY, {true}, 10, spp::sparse_hash_set(), + spp::sparse_hash_set(), 10, "", 30, 4).get(); + + ASSERT_EQ(1, results["found"].get()); +} + +TEST_F(CollectionNestedFieldsTest, OptionalNestedNonOptionalOjectArrStringField) { + nlohmann::json schema = R"({ + "name": "coll1", + "enable_nested_fields": true, + "fields": [ + {"facet":true,"name":"data","type":"object"}, + {"facet":false,"name":"data.locations.stateShort","type":"string[]"} + ] + })"_json; + + auto op = collectionManager.create_collection(schema); + ASSERT_TRUE(op.ok()); + Collection* coll1 = op.get(); + + auto doc1 = R"({ + "data": { + "locations": [ + { + "stateShort": null + } + ] + } + })"_json; + + auto add_op = coll1->add(doc1.dump(), CREATE); + ASSERT_FALSE(add_op.ok()); + ASSERT_EQ("Field `data.locations.stateShort` has been declared in the schema, but is not found in the document.", + add_op.error()); + + doc1 = R"({ + "data": { + "locations": [ + { + "stateShort": null + }, + { + "stateShort": "NY" + } + ] + } + })"_json; + + coll1->add(doc1.dump(), CREATE); + + auto results = coll1->search("ny", {"data.locations.stateShort"}, + "", {}, {}, {0}, 10, 1, + token_ordering::FREQUENCY, {true}, 10, spp::sparse_hash_set(), + spp::sparse_hash_set(), 10, "", 30, 4).get(); + + ASSERT_EQ(1, results["found"].get()); +} + + TEST_F(CollectionNestedFieldsTest, UnindexedNestedFieldShouldNotClutterSchema) { nlohmann::json schema = R"({ "name": "coll1",