Fix: improve optimization; add tests

This commit is contained in:
Pieter Vander Vennet 2023-11-30 00:30:46 +01:00
parent 78238dccc7
commit 595503cfc3
3 changed files with 47 additions and 8 deletions

View file

@ -233,6 +233,15 @@ export class And extends TagsFilter {
return And.construct(newAnds)
}
/**
* const raw = {"and": [{"or":["leisure=playground","playground!=forest"]},{"or":["leisure=playground","playground!=forest"]}]}
* const parsed = TagUtils.Tag(raw)
* parsed.optimize().asJson() // => {"or":["leisure=playground","playground!=forest"]}
*
* const raw = {"and": [{"and":["advertising=screen"]}, {"and":["advertising~*"]}]}]
* const parsed = TagUtils.Tag(raw)
* parsed.optimize().asJson() // => "advertising=screen"
*/
optimize(): TagsFilter | boolean {
if (this.and.length === 0) {
return true
@ -294,9 +303,17 @@ export class And extends TagsFilter {
optimized.splice(i, 1)
i--
}
} else if (v !== opt.value) {
// detected an internal conflict
return false
} else {
if (!v.match(opt.value)) {
// We _know_ that for the key of the RegexTag `opt`, the value will be `v`.
// As such, if `opt.value` cannot match `v`, we detected an internal conflict and can fail
return false
} else {
// Another tag already provided a _stricter_ value then this regex, so we can remove this one!
optimized.splice(i, 1)
i--
}
}
}
}
@ -374,10 +391,13 @@ export class And extends TagsFilter {
const elements = containedOr.or.filter(
(candidate) => !commonValues.some((cv) => cv.shadows(candidate))
)
newOrs.push(Or.construct(elements))
if (elements.length > 0) {
newOrs.push(Or.construct(elements))
}
}
if (newOrs.length > 0) {
commonValues.push(And.construct(newOrs))
}
commonValues.push(And.construct(newOrs))
const result = new Or(commonValues).optimize()
if (result === false) {
return false

View file

@ -162,6 +162,12 @@ export class Or extends TagsFilter {
return Or.construct(newOrs)
}
/**
* const raw = {"or": [{"and":["leisure=playground","playground!=forest"]},{"and":["leisure=playground","playground!=forest"]}]}
* const parsed = TagUtils.Tag(raw)
* parsed.optimize().asJson() // => {"and":["leisure=playground","playground!=forest"]}
*
*/
optimize(): TagsFilter | boolean {
if (this.or.length === 0) {
return false
@ -241,16 +247,21 @@ export class Or extends TagsFilter {
const elements = containedAnd.and.filter(
(candidate) => !commonValues.some((cv) => cv.shadows(candidate))
)
if (elements.length == 0) {
continue
}
newAnds.push(And.construct(elements))
}
if (newAnds.length > 0) {
commonValues.push(Or.construct(newAnds))
}
commonValues.push(Or.construct(newAnds))
const result = new And(commonValues).optimize()
if (result === true) {
return true
} else if (result === false) {
// neutral element: skip
} else {
} else if (commonValues.length > 0) {
newOrs.push(And.construct(commonValues))
}
}

View file

@ -325,6 +325,14 @@ export class TagUtils {
return tags
}
static optimzeJson(json: TagConfigJson): TagConfigJson | boolean {
const optimized = TagUtils.Tag(json).optimize()
if (optimized === true || optimized === false) {
return optimized
}
return optimized.asJson()
}
/**
* Given multiple tagsfilters which can be used as answer, will take the tags with the same keys together as set.
*