1. Ecql概述
CQL(通用查询语言)是为OGC目录规范创建的纯文本语言。GeoServer已将其调整为易于使用的过滤机制。GeoServer实际上实现了一个名为ECQL(Extended CQL)的功能更强大的扩展,它允许表达OGC Filter1.1可以编码的所有过滤器。
而geotools对于Ecql的解析有一套基于FilterFactory的解析机制,通过这套机制,可以生成语法解析树。
2. FilterFactory体系
FilterFactory体系结构如下:
FilterFactory体系最上层为FilterFactory接口,定义了Ecql当中所有的过滤方法,整理如下:
分类 | 过滤方法 |
---|---|
逻辑关系 | and、or、not、isNull |
属性赋值 | id、property |
比较关系 | between、equal、notequal、greater、greaterOrEqual、less、lessOrEqual |
模糊查询 | like |
空间查询 | bbox、beyond、contains、crosses、disjoint、dwithin、equals、intersects、overlaps、touches、with |
时间查询 | after、anyIntersects、before、begins、begunBy、during、endBy、ends、meets、metBy、overlappedBy、toverlaps、tcontains、tequals |
表达式 | add、divide、multiply、subtract、function、literal |
排序 | sort |
而FilterFactory2是继承了FilterFactory的一个接口,这个接口是为了能够更好地扩展FilterFactory的功能。FilterFactory只是对于真实的查询对象进行了描述,而在FilterFactory2中,将每个查询对象封装成为了具体的Expression,而Expression体系则通过访问者模式,实现了更为复杂的查询样式。
例如:crosses方法,在FilterFactory接口当中,局部变量定义为具体的String类型以及Geometry类型。
- /** Checks if the the first geometric operand contains the second. */
- Contains contains(String propertyName, Geometry geometry);
-
- /** Checks if the the first geometric operand contains the second. */
- Contains contains(String propertyName, Geometry geometry, MatchAction matchAction);
在FilterFactory2接口当中,则对于这个方法进行了扩展,将局部变量封装成为了Expression对象:
- /** Checks if the the first geometric operand contains the second. */
- Contains contains(Expression geometry1, Expression geometry2);
-
- /** Checks if the the first geometric operand contains the second. */
- Contains contains(Expression geometry1, Expression geometry2, MatchAction matchAction);
这样能够让相关的查询类支持更加复杂的查询条件。
FilterFactory2的两个实现类为FilterFactoryImpl和FastFilterFactory,二者都对于上述接口的方法进行了具体的实现。FilterFactoryImpl是更为通用的一种方式,而FastFilterFactory类则有一些查询优化的机制,可以使得查询更为高效。但是FastFilterFactory类是私有类,因此无法进行继承和开发,所以在此案例当中,通过自定义的FilterFactoryImpl子类来完成Ecql的改写工作。
在这两个方法当中,还有两个细节需要注意的。
一个是MatchAction参数,这个参数定义了对于两个表达式进行比对时,满足条件的情况。如下图所示,在ecql当中,对于两个条件进行比对时,两个条件可能会有多个子条件,例如A条件有n种情况,B条件又m种情况,对于每种情况可能会有n*m种比对的过程。我们可以设定MatchAction参数来控制其匹配的规则,MatchAction有三个状态:ANY、ALL、ONE,当我们设置为ANY时,表示只要这n*m种情况,有一种以上满足条件就为true;当我们设置为ALL时,表示这n*m种情况需要全部满足才能为true;当我们设置为ONE时,表示这些情况只能有一种满足,才为true。
另一个细节就是matchCase参数,这个参数主要是为了控制查询条件是否对大小写敏感。
3. 改写实践
新建一个TestFilterFactory类,继承FilterFactoryImpl类,重写其中的方法,在其中添加判断条件,此处展示的是比较简单的样例,如果需要更为复杂的改写,也可以再次写相关的更加复杂的逻辑。
- import org.geotools.filter.{AttributeExpressionImpl, FilterFactoryImpl}
- import org.opengis.filter.expression.Expression
- import org.opengis.filter.spatial.Within
-
- class TestFilterFactory extends FilterFactoryImpl{
-
- override def within(geometry1: Expression, geometry2: Expression): Within = {
- val e1 = if (geometry1.toString == "aaa") new AttributeExpressionImpl("bb") else geometry1
- super.within(e1, geometry2)
- }
-
- }
在最终调用这个类时,需要用到ECQL类的静态方法,将ecql利用自定义的Factory来进行转化。
- val ecql = "WITHIN(aaa, POLYGON ((116.54 39.75, 116.54 40, 117 40, 117 39.75, 116.54 39.75)))"
- val ff = new TestFilterFactory
- val filter = ECQL.toFilter(ecql, ff)
- val resultEcql = ECQL.toCQL(filter)
最后输出的就是改造过后的ecql语句了,已将aaa换成了bb
WITHIN(bb, POLYGON ((116.54 39.75, 116.54 40, 117 40, 117 39.75, 116.54 39.75)))