PHP使用Elasticsearch-Dsl构造查询

这两天参与公司的Elasticsearch服务接口升级,发现搜索团队的同事在使用Elasticsearch-Dsl库,发现特别好用,所以在这里记录一下相关用法。

背景

此前的做法中,我是直接根据ES的查询语法,使用PHP数组拼装出一个完整的查询请求。

在开发中,为了将一些共性的部分抽象出来,我封装过一些类似TermQuery的对象,传入参数就可以返回一个term过滤子句的PHP数组结构。

后来接触到Elasticsearch-Dsl库,才发现原来已经有人把ES几乎所有的语法全部封装成了对象,通过代码拼装这些语法对象的树形结构,最后由DSL库可以生成一个查询JSON。

其实像JAVA等强类型语言,本身的Elasticsearch官方客户端就是这种对象风格,只是PHP官方客户端并没有实现这一块而已。

下面将模拟一个场景,演示Elasticsearch-Dsl库是如何简化PHP ES开发工作的。

使用

相关代码在github上可以看到:https://github.com/owenliang/elasticsearch-dsl-usage

测试数据

启动一个本地Elasticsearch,然后运行index.php来生成2条测试数据,它们分别代表2篇文章:

  • article_title:标题
  • publish_time:发布时间
  • article_type:文章类型
  • is_anonymous:是否为匿名用户

DSL查询

首先,创建一个ES官方客户端:

然后,创建一个搜索请求:

创建一个布尔查询:

在布尔查询里添加must、must_not、filter条件:

希望返回的文章,标题必须包含”西装”,发布时间必须小于当前时间,文章类型不允许是”食品”和”家居”。

现在把这个布尔查询添加到请求里:

为请求设置翻页:

按照发布时间倒排:

接下来,我希望对上述查询结果顺便做一个聚合+统计

聚合方法是使用bucket agg,它的类型是filters agg。

它将查询结果分成2个桶,一个桶内是所有匿名发布的文章,另外一个桶内是实名发布的文章,代码如下:

这个agg的规则名(自己定义)叫做anonymous_bucketing,然后is_anonymous=1的文章被划入anony_articles桶。

额外的给这个agg设置一个参数叫做other_bucket_key,它的意思是没有被划入任何桶的文章放入no_anony_articles这个桶。

文章被分入2个桶后,现在希望对每个桶再做统计,希望每个桶内只保留前10篇文章,排序规则是按照发布时间倒排。

这里创建了一个metrics agg,它的类型是top hits agg。

它对桶内文章按照publish_time倒排,然后保留从偏移量0开始的10篇文章在桶内;额外的,希望桶内返回的10篇文档只包含article_title字段,因为对其他字段不感兴趣。

因为metrics agg是对桶内进行的统计操作,所以嵌入到bucekt agg内部,也就是先分桶后统计。

最后将search对象序列化一个查询体,提交到ES即可:

查询request的结构

每一部分都可以很清晰的与代码对应,就不展开说明了。

应答response结构

应答中hits对应请求中query部分的查询结果,aggregations对应请求中aggregations部分的聚合统计结果。

可以看出通过filters bucket agg聚合出了2个桶分别叫做anony_articles和no_anony_articles,每个桶内通过top hits metrics agg保留了最近发布的最多10篇文章(我数据库里一共就2条记录),并且只有article_title字段被保留。

总结

Elasticsearch-Dsl库非常好用,也非常利于提升对ES语法体系结构的理解。

如果文章帮助您解决了工作难题,您可以帮我点击屏幕上的任意广告,或者赞助少量费用来支持我的持续创作,谢谢~