contains with maxContains and minContains not behaving as expected
jamesbmorris92 opened this issue · 11 comments
What version of Ajv are you using? Does the issue happen if you use the latest version?
"version": "8.12.0",
Ajv options object
import AJV from "ajv/dist/2019";
const ajv = new AJV({
$data: true,
useDefaults: true,
allErrors: true,
verbose: true,
strict: false
})
JSON Schema
"testFields": {
"type": "array",
"minItems": 1,
"contains": {
"properties": {
"lastUpdatedAtTime": {
"const": true
}
}
},
"minContains": 1,
"maxContains": 1,
"items": {
"type": "object",
"properties": {
"name": {
"description": "",
"type": "string"
},
"lastUpdatedAtTime": {
"description": "Whether the field is a 'Date-time modified' field.",
"type": "boolean",
"default": false
}
},
"additionalProperties": false
},
"uniqueItemProperties": [
"name"
]
}
Sample data
[
{
"lastUpdatedAtTime": true,
"name": "test1"
},
{
"lastUpdatedAtTime": false,
"name": "test2"
},
{
"lastUpdatedAtTime": false,
"name": "test3"
},
{
"lastUpdatedAtTime": true,
"name": "test4"
}
]
Your code
const validate = ajv.compile(jsonSchema)
validate(data);
Validation result, data AFTER validation, error messages
[
{
"instancePath": "/solutionFactoryConfiguration/viObjects/entities/1/testFields/1/lastUpdatedAtTime",
"schemaPath": "#/properties/solutionFactoryConfiguration/properties/viObjects/properties/entities/items/properties/testFields/contains/properties/lastUpdatedAtTime/const",
"keyword": "const",
"params": {
"allowedValue": true
},
"message": "must be equal to constant",
"schema": true,
"parentSchema": {
"const": true
},
"data": false
},
{
"instancePath": "/solutionFactoryConfiguration/viObjects/entities/1/testFields/2/lastUpdatedAtTime",
"schemaPath": "#/properties/solutionFactoryConfiguration/properties/viObjects/properties/entities/items/properties/testFields/contains/properties/lastUpdatedAtTime/const",
"keyword": "const",
"params": {
"allowedValue": true
},
"message": "must be equal to constant",
"schema": true,
"parentSchema": {
"const": true
},
"data": false
},
{
"instancePath": "/solutionFactoryConfiguration/viObjects/entities/1/testFields",
"schemaPath": "#/properties/solutionFactoryConfiguration/properties/viObjects/properties/entities/items/properties/testFields/contains",
"keyword": "contains",
"params": {
"minContains": 1,
"maxContains": 1
},
"message": "must contain at least 1 and no more than 1 valid item(s)",
"schema": {
"properties": {
"lastUpdatedAtTime": {
"const": true
}
}
},
"parentSchema": {
"type": "array",
"minItems": 1,
"contains": {
"properties": {
"lastUpdatedAtTime": {
"const": true
}
}
},
"minContains": 1,
"maxContains": 1,
"items": {
"type": "object",
"properties": {
"name": {
"description": "",
"type": "string"
},
"lastUpdatedAtTime": {
"description": "Whether the field is a 'Date-time modified' field.",
"type": "boolean",
"default": false
}
},
"additionalProperties": false
},
"uniqueItemProperties": [
"name"
]
},
"data": [
{
"name": "test1",
"lastUpdatedAtTime": true
},
{
"name": "test2",
"lastUpdatedAtTime": false
},
{
"name": "test3",
"lastUpdatedAtTime": false
},
{
"name": "test4",
"lastUpdatedAtTime": true
}
]
}
]
What results did you expect?
I got three errors where I expected just one. I expected the third error as a result of using contains with maxContains and minContains because I had more than one row of test data where lastUpdatedAtTime: true. However I didn't expect the first two errors that say the lastUpdatedAtTime must be equal to constant and the only permitted value is true.
Are you going to resolve the issue?
I would like to but i'm not sure how. Apologies if i've made any mistakes or missed anything above this is my first issue submission.
Hi @jamesbmorris92, just to help me investigate, could you setup an example of this in runkit?
Hi @jasoniangreen here's an example: https://runkit.com/jamesbmorris92/6682b96af2a88c000808ae0e
Hi @jasoniangreen here's an example: https://runkit.com/jamesbmorris92/6682b96af2a88c000808ae0e
Thanks @jamesbmorris92, I will look into this, but in the meantime, I have a question. It's quite a complex / deeply nested example, do you think the example can be simplified further while still exhibiting the problem?
@jasoniangreen my apologies, i've made the example a bit more simple with less nesting:
https://runkit.com/jamesbmorris92/6682b96af2a88c000808ae0e
hi @jasoniangreen were you able to recreate the issue?
I can see what you report, I've just got to dig more into the docs to understand what should be expected. There are a lot of cases in AJV and JSON Schema where you will legitimately get more errors than you expect, I'm just trying to understand if this is one of those situations.
Ah @jamesbmorris92 if you remove the allErrors: true
option, it get's the expected result of just the contains error. Does this solve your issue? All errors true means that AJV will continue to check your data after encountering the first error, which seems to be the case here.
@jasoniangreen i had to trim down my schema and data quite a lot for this example and it's plausible that there would be lots of other errors in the data and I want to report those other errors to users.
The first two errors, the ones where the message is "must be equal to constant" don't seem right to me? the way I understood the contains in combination with the minContains was that atleast one (minItems) and at most one (maxItems) of the items in the array needs to satisfy the contains. In my example this is true, the first object in the array with "lastUpdatedAtTime": true satisfies the condition, all the rest of the objects in the array do not? Apologies if i'm misunderstanding?
So I think this is just a product of how contains
in AJV works through an array looking for items to match contains
schema. So with allErrors true it is going to report on all errors that it finds along the way and in the case of contains
finding the errors against that subschema is how it finds matches for contains
... I feel like I've written contains
too much.
With larger and more complex schemas it's inevitable that allErrors
is going to get more and more noisy.
So unless @epoberezkin disagrees I think this should be closed as this is not a behaviour we will consider changing.
@jasoniangreen thanks for all your help digging into this for me. Does that mean that what I want where it validates for only a single instance of lastUpdatedDttm: true is not possible? is there any other functionality to achieve this?