Improve tag optimazations, fixes rendering of climbing map
This commit is contained in:
parent
01ba686270
commit
01567a4b80
16 changed files with 875 additions and 303 deletions
347
test/Logic/Tags/OptimizeTags.spec.ts
Normal file
347
test/Logic/Tags/OptimizeTags.spec.ts
Normal file
|
@ -0,0 +1,347 @@
|
|||
import {describe} from 'mocha'
|
||||
import {expect} from 'chai'
|
||||
import {TagsFilter} from "../../../Logic/Tags/TagsFilter";
|
||||
import {And} from "../../../Logic/Tags/And";
|
||||
import {Tag} from "../../../Logic/Tags/Tag";
|
||||
import {TagUtils} from "../../../Logic/Tags/TagUtils";
|
||||
import {Or} from "../../../Logic/Tags/Or";
|
||||
import {RegexTag} from "../../../Logic/Tags/RegexTag";
|
||||
|
||||
describe("Tag optimalization", () => {
|
||||
|
||||
describe("And", () => {
|
||||
it("with condition and nested and should be flattened", () => {
|
||||
const t = new And(
|
||||
[
|
||||
new And([
|
||||
new Tag("x", "y")
|
||||
]),
|
||||
new Tag("a", "b")
|
||||
]
|
||||
)
|
||||
const opt = <TagsFilter>t.optimize()
|
||||
expect(TagUtils.toString(opt)).eq(`a=b&x=y`)
|
||||
})
|
||||
|
||||
it("should be 'true' if no conditions are given", () => {
|
||||
const t = new And(
|
||||
[]
|
||||
)
|
||||
const opt = t.optimize()
|
||||
expect(opt).eq(true)
|
||||
})
|
||||
|
||||
it("with nested ors and common property should be extracted", () => {
|
||||
|
||||
// foo&bar & (x=y | a=b) & (x=y | c=d) & foo=bar is equivalent too foo=bar & ((x=y) | (a=b & c=d))
|
||||
const t = new And([
|
||||
new Tag("foo", "bar"),
|
||||
new Or([
|
||||
new Tag("x", "y"),
|
||||
new Tag("a", "b")
|
||||
]),
|
||||
new Or([
|
||||
new Tag("x", "y"),
|
||||
new Tag("c", "d")
|
||||
])
|
||||
])
|
||||
const opt = <TagsFilter>t.optimize()
|
||||
expect(TagUtils.toString(opt)).eq("foo=bar& (x=y| (a=b&c=d) )")
|
||||
})
|
||||
|
||||
it("with nested ors and common regextag should be extracted", () => {
|
||||
|
||||
// foo&bar & (x=y | a=b) & (x=y | c=d) & foo=bar is equivalent too foo=bar & ((x=y) | (a=b & c=d))
|
||||
const t = new And([
|
||||
new Tag("foo", "bar"),
|
||||
new Or([
|
||||
new RegexTag("x", "y"),
|
||||
new RegexTag("a", "b")
|
||||
]),
|
||||
new Or([
|
||||
new RegexTag("x", "y"),
|
||||
new RegexTag("c", "d")
|
||||
])
|
||||
])
|
||||
const opt = <TagsFilter>t.optimize()
|
||||
expect(TagUtils.toString(opt)).eq("foo=bar& ( (a=b&c=d) |x=y)")
|
||||
})
|
||||
|
||||
it("with nested ors and inverted regextags should _not_ be extracted", () => {
|
||||
|
||||
// foo&bar & (x=y | a=b) & (x=y | c=d) & foo=bar is equivalent too foo=bar & ((x=y) | (a=b & c=d))
|
||||
const t = new And([
|
||||
new Tag("foo", "bar"),
|
||||
new Or([
|
||||
new RegexTag("x", "y"),
|
||||
new RegexTag("a", "b")
|
||||
]),
|
||||
new Or([
|
||||
new RegexTag("x", "y", true),
|
||||
new RegexTag("c", "d")
|
||||
])
|
||||
])
|
||||
const opt = <TagsFilter>t.optimize()
|
||||
expect(TagUtils.toString(opt)).eq("foo=bar& (a=b|x=y) & (c=d|x!=y)")
|
||||
})
|
||||
|
||||
it("should move regextag to the end", () => {
|
||||
const t = new And([
|
||||
new RegexTag("x", "y"),
|
||||
new Tag("a", "b")
|
||||
])
|
||||
const opt = <TagsFilter>t.optimize()
|
||||
expect(TagUtils.toString(opt)).eq("a=b&x=y")
|
||||
|
||||
})
|
||||
|
||||
it("should sort tags by their popularity (least popular first)", () => {
|
||||
const t = new And([
|
||||
new Tag("bicycle", "yes"),
|
||||
new Tag("amenity", "binoculars")
|
||||
])
|
||||
const opt = <TagsFilter>t.optimize()
|
||||
expect(TagUtils.toString(opt)).eq("amenity=binoculars&bicycle=yes")
|
||||
|
||||
})
|
||||
|
||||
it("should optimize nested ORs", () => {
|
||||
const filter = TagUtils.Tag({
|
||||
or: [
|
||||
"X=Y", "FOO=BAR",
|
||||
{
|
||||
"and": [
|
||||
{
|
||||
"or": ["X=Y", "FOO=BAR"]
|
||||
},
|
||||
"bicycle=yes"
|
||||
]
|
||||
}
|
||||
]
|
||||
})
|
||||
// (X=Y | FOO=BAR | (bicycle=yes & (X=Y | FOO=BAR)) )
|
||||
// This is equivalent to (X=Y | FOO=BAR)
|
||||
const opt = filter.optimize()
|
||||
console.log(opt)
|
||||
})
|
||||
|
||||
it("should optimize an advanced, real world case", () => {
|
||||
const filter = TagUtils.Tag({
|
||||
or: [
|
||||
{
|
||||
"and": [
|
||||
{
|
||||
"or": ["amenity=charging_station", "disused:amenity=charging_station", "planned:amenity=charging_station", "construction:amenity=charging_station"]
|
||||
},
|
||||
"bicycle=yes"
|
||||
]
|
||||
},
|
||||
{
|
||||
"and": [
|
||||
{
|
||||
"or": ["amenity=charging_station", "disused:amenity=charging_station", "planned:amenity=charging_station", "construction:amenity=charging_station"]
|
||||
},
|
||||
]
|
||||
},
|
||||
"amenity=toilets",
|
||||
"amenity=bench",
|
||||
"leisure=picnic_table",
|
||||
{
|
||||
"and": [
|
||||
"tower:type=observation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"and": [
|
||||
"amenity=bicycle_repair_station"
|
||||
]
|
||||
},
|
||||
{
|
||||
"and": [
|
||||
{
|
||||
"or": [
|
||||
"amenity=bicycle_rental",
|
||||
"bicycle_rental~*",
|
||||
"service:bicycle:rental=yes",
|
||||
"rental~.*bicycle.*"
|
||||
]
|
||||
},
|
||||
"bicycle_rental!=docking_station"
|
||||
]
|
||||
},
|
||||
{
|
||||
"and": [
|
||||
"leisure=playground",
|
||||
"playground!=forest"
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
const opt = <TagsFilter>filter.optimize()
|
||||
const expected = ["amenity=charging_station",
|
||||
"amenity=toilets",
|
||||
"amenity=bench",
|
||||
"amenity=bicycle_repair_station",
|
||||
"construction:amenity=charging_station",
|
||||
"disused:amenity=charging_station",
|
||||
"leisure=picnic_table",
|
||||
"planned:amenity=charging_station",
|
||||
"tower:type=observation",
|
||||
"(amenity=bicycle_rental|service:bicycle:rental=yes|bicycle_rental~^..*$|rental~^.*bicycle.*$) &bicycle_rental!=docking_station",
|
||||
"leisure=playground&playground!=forest"]
|
||||
|
||||
expect((<Or>opt).or.map(f => TagUtils.toString(f))).deep.eq(
|
||||
expected
|
||||
)
|
||||
})
|
||||
|
||||
it("should detect conflicting tags", () => {
|
||||
const q = new And([new Tag("key", "value"), new RegexTag("key", "value", true)])
|
||||
expect(q.optimize()).eq(false)
|
||||
})
|
||||
|
||||
it("should detect conflicting tags with a regex", () => {
|
||||
const q = new And([new Tag("key", "value"), new RegexTag("key", /value/, true)])
|
||||
expect(q.optimize()).eq(false)
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe("Or", () => {
|
||||
|
||||
|
||||
it("with nested And which has a common property should be dropped", () => {
|
||||
|
||||
const t = new Or([
|
||||
new Tag("foo", "bar"),
|
||||
new And([
|
||||
new Tag("foo", "bar"),
|
||||
new Tag("x", "y"),
|
||||
])
|
||||
])
|
||||
const opt = <TagsFilter>t.optimize()
|
||||
expect(TagUtils.toString(opt)).eq("foo=bar")
|
||||
|
||||
})
|
||||
|
||||
it("should flatten nested ors", () => {
|
||||
const t = new Or([
|
||||
new Or([
|
||||
new Tag("x", "y")
|
||||
])
|
||||
]).optimize()
|
||||
expect(t).deep.eq(new Tag("x", "y"))
|
||||
})
|
||||
|
||||
it("should flatten nested ors", () => {
|
||||
const t = new Or([
|
||||
new Tag("a", "b"),
|
||||
new Or([
|
||||
new Tag("x", "y")
|
||||
])
|
||||
]).optimize()
|
||||
expect(t).deep.eq(new Or([new Tag("a", "b"), new Tag("x", "y")]))
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
it("should not generate a conflict for climbing tags", () => {
|
||||
const club_tags = TagUtils.Tag(
|
||||
{
|
||||
"or": [
|
||||
"club=climbing",
|
||||
{
|
||||
"and": [
|
||||
"sport=climbing",
|
||||
{
|
||||
"or": [
|
||||
"office~*",
|
||||
"club~*"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
})
|
||||
const gym_tags = TagUtils.Tag({
|
||||
"and": [
|
||||
"sport=climbing",
|
||||
"leisure=sports_centre"
|
||||
]
|
||||
})
|
||||
const other_climbing = TagUtils.Tag({
|
||||
"and": [
|
||||
"sport=climbing",
|
||||
"climbing!~route",
|
||||
"leisure!~sports_centre",
|
||||
"climbing!=route_top",
|
||||
"climbing!=route_bottom"
|
||||
]
|
||||
})
|
||||
const together = new Or([club_tags, gym_tags, other_climbing])
|
||||
const opt = together.optimize()
|
||||
|
||||
/*
|
||||
club=climbing | (sport=climbing&(office~* | club~*))
|
||||
OR
|
||||
sport=climbing & leisure=sports_centre
|
||||
OR
|
||||
sport=climbing & climbing!~route & leisure!~sports_centre
|
||||
*/
|
||||
|
||||
/*
|
||||
> When the first OR is written out, this becomes
|
||||
club=climbing
|
||||
OR
|
||||
(sport=climbing&(office~* | club~*))
|
||||
OR
|
||||
(sport=climbing & leisure=sports_centre)
|
||||
OR
|
||||
(sport=climbing & climbing!~route & leisure!~sports_centre & ...)
|
||||
*/
|
||||
|
||||
/*
|
||||
> We can join the 'sport=climbing' in the last 3 phrases
|
||||
club=climbing
|
||||
OR
|
||||
(sport=climbing AND
|
||||
(office~* | club~*))
|
||||
OR
|
||||
(leisure=sports_centre)
|
||||
OR
|
||||
(climbing!~route & leisure!~sports_centre & ...)
|
||||
)
|
||||
*/
|
||||
|
||||
|
||||
expect(opt).deep.eq(
|
||||
TagUtils.Tag({
|
||||
or: [
|
||||
"club=climbing",
|
||||
{
|
||||
and: ["sport=climbing",
|
||||
{or: ["club~*", "office~*"]}]
|
||||
},
|
||||
{
|
||||
and: ["sport=climbing",
|
||||
{
|
||||
or: [
|
||||
"leisure=sports_centre",
|
||||
{
|
||||
and: [
|
||||
"climbing!~route",
|
||||
"climbing!=route_top",
|
||||
"climbing!=route_bottom",
|
||||
"leisure!~sports_centre"
|
||||
]
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
|
||||
],
|
||||
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
|
@ -1,150 +0,0 @@
|
|||
import {describe} from 'mocha'
|
||||
import {expect} from 'chai'
|
||||
import {TagsFilter} from "../../../Logic/Tags/TagsFilter";
|
||||
import {And} from "../../../Logic/Tags/And";
|
||||
import {Tag} from "../../../Logic/Tags/Tag";
|
||||
import {TagUtils} from "../../../Logic/Tags/TagUtils";
|
||||
import {Or} from "../../../Logic/Tags/Or";
|
||||
import {RegexTag} from "../../../Logic/Tags/RegexTag";
|
||||
|
||||
describe("Tag optimalization", () => {
|
||||
|
||||
describe("And", () => {
|
||||
it("with condition and nested and should be flattened", () => {
|
||||
const t = new And(
|
||||
[
|
||||
new And([
|
||||
new Tag("x", "y")
|
||||
]),
|
||||
new Tag("a", "b")
|
||||
]
|
||||
)
|
||||
const opt =<TagsFilter> t.optimize()
|
||||
expect(TagUtils.toString(opt)).eq(`a=b&x=y`)
|
||||
})
|
||||
|
||||
it("with nested ors and commons property should be extracted", () => {
|
||||
|
||||
// foo&bar & (x=y | a=b) & (x=y | c=d) & foo=bar is equivalent too foo=bar & ((x=y) | (a=b & c=d))
|
||||
const t = new And([
|
||||
new Tag("foo","bar"),
|
||||
new Or([
|
||||
new Tag("x", "y"),
|
||||
new Tag("a", "b")
|
||||
]),
|
||||
new Or([
|
||||
new Tag("x", "y"),
|
||||
new Tag("c", "d")
|
||||
])
|
||||
])
|
||||
const opt =<TagsFilter> t.optimize()
|
||||
expect(TagUtils.toString(opt)).eq("foo=bar& (x=y| (a=b&c=d) )")
|
||||
})
|
||||
|
||||
it("should move regextag to the end", () => {
|
||||
const t = new And([
|
||||
new RegexTag("x","y"),
|
||||
new Tag("a","b")
|
||||
])
|
||||
const opt =<TagsFilter> t.optimize()
|
||||
expect(TagUtils.toString(opt)).eq("a=b&x~^y$")
|
||||
|
||||
})
|
||||
|
||||
it("should sort tags by their popularity (least popular first)", () => {
|
||||
const t = new And([
|
||||
new Tag("bicycle","yes"),
|
||||
new Tag("amenity","binoculars")
|
||||
])
|
||||
const opt =<TagsFilter> t.optimize()
|
||||
expect(TagUtils.toString(opt)).eq("amenity=binoculars&bicycle=yes")
|
||||
|
||||
})
|
||||
|
||||
it("should optimize an advanced, real world case", () => {
|
||||
const filter = TagUtils.Tag( {or: [
|
||||
{
|
||||
"and": [
|
||||
{
|
||||
"or": ["amenity=charging_station","disused:amenity=charging_station","planned:amenity=charging_station","construction:amenity=charging_station"]
|
||||
},
|
||||
"bicycle=yes"
|
||||
]
|
||||
},
|
||||
{
|
||||
"and": [
|
||||
{
|
||||
"or": ["amenity=charging_station","disused:amenity=charging_station","planned:amenity=charging_station","construction:amenity=charging_station"]
|
||||
},
|
||||
]
|
||||
},
|
||||
"amenity=toilets",
|
||||
"amenity=bench",
|
||||
"leisure=picnic_table",
|
||||
{
|
||||
"and": [
|
||||
"tower:type=observation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"and": [
|
||||
"amenity=bicycle_repair_station"
|
||||
]
|
||||
},
|
||||
{
|
||||
"and": [
|
||||
{
|
||||
"or": [
|
||||
"amenity=bicycle_rental",
|
||||
"bicycle_rental~*",
|
||||
"service:bicycle:rental=yes",
|
||||
"rental~.*bicycle.*"
|
||||
]
|
||||
},
|
||||
"bicycle_rental!=docking_station"
|
||||
]
|
||||
},
|
||||
{
|
||||
"and": [
|
||||
"leisure=playground",
|
||||
"playground!=forest"
|
||||
]
|
||||
}
|
||||
]});
|
||||
const opt = <TagsFilter> filter.optimize()
|
||||
const expected = "amenity=charging_station|" +
|
||||
"amenity=toilets|" +
|
||||
"amenity=bench|" +
|
||||
"amenity=bicycle_repair_station" +
|
||||
"|construction:amenity=charging_station|" +
|
||||
"disused:amenity=charging_station|" +
|
||||
"leisure=picnic_table|" +
|
||||
"planned:amenity=charging_station|" +
|
||||
"tower:type=observation| " +
|
||||
"( (amenity=bicycle_rental|service:bicycle:rental=yes|bicycle_rental~^..*$|rental~^.*bicycle.*$) &bicycle_rental!~^docking_station$) |" +
|
||||
" (leisure=playground&playground!~^forest$)"
|
||||
|
||||
expect(TagUtils.toString(opt).replace(/ /g, ""))
|
||||
.eq(expected.replace(/ /g, ""))
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe("Or", () => {
|
||||
it("with nested And which has a common property should be dropped", () => {
|
||||
|
||||
const t = new Or([
|
||||
new Tag("foo","bar"),
|
||||
new And([
|
||||
new Tag("foo", "bar"),
|
||||
new Tag("x", "y"),
|
||||
])
|
||||
])
|
||||
const opt =<TagsFilter> t.optimize()
|
||||
expect(TagUtils.toString(opt)).eq("foo=bar")
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
})
|
Loading…
Add table
Add a link
Reference in a new issue