Better error messages for nested field type mismatch.

This commit is contained in:
Kishore Nallan 2022-09-11 18:53:14 +05:30
parent f9f7afff52
commit aeb4424662
3 changed files with 86 additions and 18 deletions

View File

@ -398,9 +398,9 @@ struct field {
static bool flatten_obj(nlohmann::json& doc, nlohmann::json& value, bool has_array, bool has_obj_array,
const std::string& flat_name, std::unordered_map<std::string, field>& flattened_fields);
static bool flatten_field(nlohmann::json& doc, nlohmann::json& obj, const field& the_field,
std::vector<std::string>& path_parts, size_t path_index, bool has_array,
bool has_obj_array, std::unordered_map<std::string, field>& flattened_fields);
static Option<bool> flatten_field(nlohmann::json& doc, nlohmann::json& obj, const field& the_field,
std::vector<std::string>& path_parts, size_t path_index, bool has_array,
bool has_obj_array, std::unordered_map<std::string, field>& flattened_fields);
static Option<bool> flatten_doc(nlohmann::json& document, const std::vector<field>& nested_fields,
std::vector<field>& flattened_fields);

View File

@ -612,14 +612,14 @@ bool field::flatten_obj(nlohmann::json& doc, nlohmann::json& value, bool has_arr
return true;
}
bool field::flatten_field(nlohmann::json& doc, nlohmann::json& obj, const field& the_field,
Option<bool> field::flatten_field(nlohmann::json& doc, nlohmann::json& obj, const field& the_field,
std::vector<std::string>& path_parts, size_t path_index,
bool has_array, bool has_obj_array, std::unordered_map<std::string, field>& flattened_fields) {
if(path_index == path_parts.size()) {
// end of path: check if obj matches expected type
std::string detected_type;
if(!field::get_type(obj, detected_type)) {
return false;
return Option<bool>(400, "Field `" + the_field.name + "` has an incorrect type.");
}
if(std::isalnum(detected_type.back()) && has_array) {
@ -641,7 +641,7 @@ bool field::flatten_field(nlohmann::json& doc, nlohmann::json& obj, const field&
flatten_obj(doc, obj, has_array, has_obj_array, the_field.name, flattened_fields);
} else {
if(doc.count(the_field.name) != 0 && flattened_fields.find(the_field.name) == flattened_fields.end()) {
return true;
return Option<bool>(true);
}
if(has_array) {
@ -656,9 +656,14 @@ bool field::flatten_field(nlohmann::json& doc, nlohmann::json& obj, const field&
flattened_fields[the_field.name] = flattened_field;
}
return true;
return Option<bool>(true);
} else {
return false;
if(has_obj_array && !the_field.is_array()) {
return Option<bool>(400, "Field `" + the_field.name + "` has an incorrect type. "
"Hint: field inside an array of objects must be an array type as well.");
}
return Option<bool>(400, "Field `" + the_field.name + "` has an incorrect type.");
}
}
@ -667,18 +672,25 @@ bool field::flatten_field(nlohmann::json& doc, nlohmann::json& obj, const field&
if(it != obj.end()) {
if(it.value().is_array()) {
if(it.value().empty()) {
return Option<bool>(404, "Field `" + the_field.name + "` not found.");
}
has_array = true;
bool resolved = false;
for(auto& ele: it.value()) {
has_obj_array = has_obj_array || ele.is_object();
resolved |= flatten_field(doc, ele, the_field, path_parts, path_index + 1, has_array, has_obj_array, flattened_fields);
Option<bool> op = flatten_field(doc, ele, the_field, path_parts, path_index + 1, has_array,
has_obj_array, flattened_fields);
if(!op.ok()) {
return op;
}
}
return resolved;
return Option<bool>(true);
} else {
return flatten_field(doc, it.value(), the_field, path_parts, path_index + 1, has_array, has_obj_array, flattened_fields);
}
} {
return false;
return Option<bool>(404, "Field `" + the_field.name + "` not found.");
}
}
@ -697,9 +709,15 @@ Option<bool> field::flatten_doc(nlohmann::json& document,
continue;
}
bool resolved = flatten_field(document, document, nested_field, field_parts, 0, false, false, flattened_fields_map);
if(!resolved && !nested_field.optional) {
return Option<bool>(400, "Field `" + nested_field.name + "` was not found or has an incorrect type.");
auto op = flatten_field(document, document, nested_field, field_parts, 0, false, false, flattened_fields_map);
if(op.ok()) {
continue;
}
if(op.code() == 404 && nested_field.optional) {
continue;
} else {
return op;
}
}

View File

@ -279,7 +279,7 @@ TEST_F(CollectionNestedFieldsTest, FlattenJSONObjectHandleErrors) {
nlohmann::json doc = nlohmann::json::parse(json_str);
auto flatten_op = field::flatten_doc(doc, nested_fields, flattened_fields);
ASSERT_FALSE(flatten_op.ok());
ASSERT_EQ("Field `locations` was not found or has an incorrect type.", flatten_op.error());
ASSERT_EQ("Field `locations` not found.", flatten_op.error());
nested_fields = {
field("company", field_types::INT32, false)
@ -288,7 +288,7 @@ TEST_F(CollectionNestedFieldsTest, FlattenJSONObjectHandleErrors) {
flattened_fields.clear();
flatten_op = field::flatten_doc(doc, nested_fields, flattened_fields);
ASSERT_FALSE(flatten_op.ok());
ASSERT_EQ("Field `company` was not found or has an incorrect type.", flatten_op.error());
ASSERT_EQ("Field `company` has an incorrect type.", flatten_op.error());
}
TEST_F(CollectionNestedFieldsTest, SearchOnFieldsOnWildcardSchema) {
@ -937,7 +937,7 @@ TEST_F(CollectionNestedFieldsTest, FieldsWithExplicitSchema) {
add_op = coll3->add(doc2.dump(), CREATE);
ASSERT_FALSE(add_op.ok());
ASSERT_EQ("Field `details` was not found or has an incorrect type.", add_op.error());
ASSERT_EQ("Field `details` not found.", add_op.error());
// check fields and their properties
auto coll_fields = coll1->get_fields();
@ -1452,3 +1452,53 @@ TEST_F(CollectionNestedFieldsTest, UpdateOfNestFields) {
results = coll1->search("*", {}, "company.num_employees: 2000", {}, {}, {0}, 10, 1, FREQUENCY, {false}).get();
ASSERT_EQ(1, results["found"].get<size_t>());
}
TEST_F(CollectionNestedFieldsTest, NestedSchemaWithSingularType) {
nlohmann::json schema = R"({
"name": "coll1",
"enable_nested_fields": true,
"fields": [
{"name": "studies.year", "type": "int32", "optional": false}
]
})"_json;
auto op = collectionManager.create_collection(schema);
ASSERT_TRUE(op.ok());
Collection* coll1 = op.get();
auto doc1 = R"({
"id": "0",
"studies": [{"name": "College 1", "year": 1997}]
})"_json;
auto add_op = coll1->add(doc1.dump(), CREATE);
ASSERT_FALSE(add_op.ok());
ASSERT_EQ("Field `studies.year` has an incorrect type. "
"Hint: field inside an array of objects must be an array type as well.", add_op.error());
// even when field is optional, there should be an error
schema = R"({
"name": "coll2",
"enable_nested_fields": true,
"fields": [
{"name": "studies.year", "type": "int32", "optional": true}
]
})"_json;
op = collectionManager.create_collection(schema);
ASSERT_TRUE(op.ok());
Collection* coll2 = op.get();
add_op = coll2->add(doc1.dump(), CREATE);
ASSERT_FALSE(add_op.ok());
ASSERT_EQ("Field `studies.year` has an incorrect type. "
"Hint: field inside an array of objects must be an array type as well.", add_op.error());
// allow optional field to be missing when value is singular
doc1 = R"({
"id": "0",
"studies": {"name": "College 1"}
})"_json;
add_op = coll2->add(doc1.dump(), CREATE);
ASSERT_TRUE(add_op.ok());
}