过滤器

过滤器简介

Ultipa 过滤器是一种高性能树形结构过滤器,而树形的节点由条件和逻辑运算符组成,过滤器可以在逻辑上完整的支持各类组合判断。

作用

Ultipa过滤器作用于各类搜索当中,包括node和edge元数据搜索,路径搜索,K邻搜索和模板搜索。在全图中通过Ultipa过滤器可以灵活的筛选子图或元数据。

构建条件

在构建条件之前,需要先做一些评估与分析,确保条件的合理性。 我们列出以下几条建议:

  • 条件尽量明确,如直接指定id,或指定某个属性值
  • 命中几率更高的条件在前
  • 用最少的条件的数量完成过滤
  • 避免构造过深的逻辑树

条件操作符

Ultipa过滤器包含了常用的操作符:

  • $eq 相等
  • $neq 不等
  • $lt 小于
  • $gt 大于
  • $lte 小于等于
  • $gte 大于等于
  • $bt 区间操作符
  • $in 存在于
  • $nin 不存在于

$eq

相等操作符有两种写法,在之前的示例当中其实大量的使用了简化的相等操作符。 可以参考下边两种使用规范:

<property> : <value>
 
<property> : { $eq : <value> }

大多数情况下,我们只会使用第一种方式

为了方便用户书写,Ultipa增加了一种特殊的操作符,ID匹配操作符,具体如下:

<id>

[]<id>s

示例1:查找node (id = 12)所有年龄为20的邻居并返回10个结果:

t().n(12).e().n(a{age: 20}).limit(10).select(name)

n(12) 中包含了 ID匹配操作符,相同于:

n ({_id : 12})


n(a{age:20})

相同于

n( a { age : { $eq : 20}})

示例2:查找node (id = [12,23,30])的三个点

find().nodes([12, 23, 30]).limit(3)

上面的点查询使用了ID匹配操作符,相同于

find().nodes({_id: { $in: [ 12, 23, 30] } } ).limit(3)

$neq

不等操作符,相同于判断符号:!=

使用方式:

<property> : { $neq : <string | number> }

示例:查找年龄不等于0的节点

find().nodes( { age: { $neq : 0 } } ).limit(10)

$lt

小于操作符,相同于判断符号 : <

使用方式:

<property> : { $lt : <number> }

示例:查找年年龄小于18周岁的节点

find().nodes( { age: { $lt : 10 } } ).limit(10)

$lte

小于等于操作符, 相同于判断符号: <=

使用方式:

<property> : { $lte : <number> }

示例:查找年龄小于等于18岁的节点

find().nodes( { age : { $lte : 18 } } ).limit(10)

$gt

大于操作符,相同于判断符号:>

使用方式:

<property> : { $gt: <number> }

示例:查找年龄大于18岁的节点

find().nodes( { age : { $gt : 18 } } ).limit(10)

$gte

大于操作符,相同于判断符号:>=

使用方式:

<property> : { $gte: <number> }

示例:查找年龄大于等于18岁的节点

find().nodes( { age : { $gte : 18 } } ).limit(10)

$bt

区间操作符

使用方式:

<property> : { $bt: [<number1>, <number2>] }

其中number2 必须 大于等于 number1

示例:查找年龄大于等于18岁并且小于等于30的节点

find().nodes( { age : { $bt: [18,30] } } ).limit(10)

$in

属于操作符,表示符合一组值中的某个值,

<property> : { $nin : [ <value1>,<value2>... ] }

示例: 查找年龄在10岁,20岁和30岁的节点

find().nodes( { age: { $in : [ 10, 20, 30 ] } } ).limit(10)

$nin

不属于操作符,表示不等于一组值中的任意一个值

<property> : { $nin : [ <value1>,<value2>... ] }

示例: 查找年龄不等于10岁,20岁和30岁的节点

find().nodes( { age: { $nin : [ 10, 20, 30 ] } } ).limit(10)

逻辑类型

逻辑操作符可以帮助组合不同的条件。逻辑结构以树形为基础,从叶子节点层层向上执行,最终到根逻辑。

如 图 所示:

$or

或操作符, 由或操作符连接的条件中,有任一判断为真,视为判断成功,返回真值,并按照逻辑树继续向上执行。

示例:查找年龄为20 或者 国籍是 China 的点

find().nodes( {$or : [ { age : 20 }, { country : "China" } ] })
.limit(10)

$and

与操作符, 与操作符连接的条件必须全都返回真,视为判断成功,返回真值,并按照逻辑树继续向上执行。

示例:查找年龄呢为20 并且 国际是 China的点

find().nodes( {$and : [ { age : 20 }, { country : "China" } ] })
.limit(10)

更多示例

接下来我们看下一个两层的逻辑判断:

  • 找到 国家是 China 的 Human
  • 或者 国家是 France 的 Company
find().nodes( { 
$or :[ 
{$and: [{country : "China"},{type: "Human"}]},
{$and: [{country : "France"},{type: "Company"}]}
]
})

// 也可以简写成:

find().nodes( { 
$or :[ 
{country : "China" , type: "Human"},
{country : "France", type: "Company"}
]
 })

简写的形式代表:当不明确规定逻辑规则的时候,默认会使用 $and操作符

步间过滤器

步间过滤器目前处于验证运行阶段,主体功能逻辑已经确定,语法未来将会有轻微调整。

步间过滤器,指在寻找路径的过程当中,从当前的节点或者边向前面已经经过的点或边的属性值进行比较判断。

我们用下面的例子来讲述,步间过滤器的使用:

向前匹配

边匹配:与上一条边进行匹配

关键字:prev_e

示例:在银行转账交易中,按照时序升序查找从卡A到卡B的两步转账路径。

t().n({card_id:"<card_a_id>"}).re({type: "transaction"}).n()
.re({timestamp:{$gt: prev_e.timestamp},type: "transaction"})
.n({card_id:"<card_b_id>"}).limit(10).select(*)
关键字prev_e 示意图

示例:在转账交易中,按时序升序查找5步以内从卡A到卡B的转账记录

t().n({card_id:"<card_a_id>"})
.re({type: "transaction", timestamp: {$gt: prev_e.timestamp}})[:5]
.n({card_id:"<card_b_id>"}).limit(10).select(*)
关键字prev_e 示意图

点匹配:与上一个点进行匹配

关键字:prev_n

示例:在公司组织架构中,按照职级降序查找从 员工A 到起两步下属的组织架构

t().n({employee:"A"}).e().n({level: {$lt: prev_n.level}})
.e().n(({level: {$lt: prev_n.level}}).limit(10).select(*)

别名向前匹配 与前面已经定义的别名进行匹配

示例:查找从A经过两步到达B,且B不等于A的路径

t().n(a{employee:"A"}).e()[2].n({_id: {$neq: a._id}}).limit(10).select(*)