在 Elasticsearch 中,我们可以使用 bool query 来说实现一种组合的查询。它可以具有如下的一种形式的搜索:
POST _search
{
"query": {
"bool" : {
"must" : {
"term" : { "user.id" : "kimchy" }
},
"filter": {
"term" : { "tags" : "production" }
},
"must_not" : {
"range" : {
"age" : { "gte" : 10, "lte" : 20 }
}
},
"should" : [
{ "term" : { "tags" : "env1" } },
{ "term" : { "tags" : "deployed" } }
],
"minimum_should_match" : 1,
"boost" : 1.0
}
}
}
在上面,我看到了 must 和 must_not 这样的组合,他们表示必须满足以及禁止满足的意思。我们也同时看到了 should 这个 clause,它表示如果满足就可以增加分数,但是我们看到没有 should_not 这样的表达方法。在我们的实际的搜索中这个其实也是蛮有用的,比如我想对某些不满足一定条件的查询进行加分。
在今天的文章中,我们用一个例子来展示如何实现 shoud_not 这样的查询。
我们首先以我们之前的用过的数据来进行展示:
POST _bulk
{ "index" : { "_index" : "twitter", "_id": 1} }
{"user":"双榆树-张三","message":"今儿天气不错啊,出去转转去","uid":2,"age":20,"city":"北京","province":"北京","country":"中国","address":"中国北京市海淀区","location":{"lat":"39.970718","lon":"116.325747"}}
{ "index" : { "_index" : "twitter", "_id": 2 }}
{"user":"东城区-老刘","message":"出发,下一站云南!","uid":3,"age":30,"city":"北京","province":"北京","country":"中国","address":"中国北京市东城区台基厂三条3号","location":{"lat":"39.904313","lon":"116.412754"}}
{ "index" : { "_index" : "twitter", "_id": 3} }
{"user":"东城区-李四","message":"happy birthday!","uid":4,"age":30,"city":"北京","province":"北京","country":"中国","address":"中国北京市东城区","location":{"lat":"39.893801","lon":"116.408986"}}
{ "index" : { "_index" : "twitter", "_id": 4} }
{"user":"朝阳区-老贾","message":"123,gogogo","uid":5,"age":35,"city":"北京","province":"北京","country":"中国","address":"中国北京市朝阳区建国门","location":{"lat":"39.718256","lon":"116.367910"}}
{ "index" : { "_index" : "twitter", "_id": 5} }
{"user":"朝阳区-老王","message":"Happy BirthDay My Friend!","uid":6,"age":50,"city":"北京","province":"北京","country":"中国","address":"中国北京市朝阳区国贸","location":{"lat":"39.918256","lon":"116.467910"}}
{ "index" : { "_index" : "twitter", "_id": 6} }
{"user":"虹桥-老吴","message":"好友来了都今天我生日,好友来了,什么 birthday happy 就成!","uid":7,"age":90,"city":"上海","province":"上海","country":"中国","address":"中国上海市闵行区","location":{"lat":"31.175927","lon":"121.383328"}}
打入上面的 _bulk 指令,我们就创建了一个叫做 twitter 的索引。
我们首先来测试如下的搜索:
GET twitter/_search
{
"query": {
"bool": {
"must": [
{
"range": {
"age": {
"gte": 40
}
}
}
],
"should": [
{
"match": {
"city": "上海"
}
}
]
}
}
}
上面的搜索表示的意思是:搜索出年龄大于40岁的文档,而且来自上海的文档的排名靠前。搜索的结果就是:
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 2,
"relation" : "eq"
},
"max_score" : 2.5404449,
"hits" : [
{
"_index" : "twitter",
"_type" : "_doc",
"_id" : "6",
"_score" : 2.5404449,
"_source" : {
"user" : "虹桥-老吴",
"message" : "好友来了都今天我生日,好友来了,什么 birthday happy 就成!",
"uid" : 7,
"age" : 90,
"city" : "上海",
"province" : "上海",
"country" : "中国",
"address" : "中国上海市闵行区",
"location" : {
"lat" : "31.175927",
"lon" : "121.383328"
}
}
},
{
"_index" : "twitter",
"_type" : "_doc",
"_id" : "5",
"_score" : 1.0,
"_source" : {
"user" : "朝阳区-老王",
"message" : "Happy BirthDay My Friend!",
"uid" : 6,
"age" : 50,
"city" : "北京",
"province" : "北京",
"country" : "中国",
"address" : "中国北京市朝阳区国贸",
"location" : {
"lat" : "39.918256",
"lon" : "116.467910"
}
}
}
]
}
}
从上面的搜索的结果,我们可以看出来 _id 为 6 的来自上海的文档排名靠前。
假如现在我们的问题变为:我们想搜索年龄大于49岁,但是不来自上海的排名得分靠前,我们该如何实现这个目的呢?按照这个要求,我们实现如下的 should_not 方法:
GET twitter/_search
{
"query": {
"bool": {
"must": [
{
"range": {
"age": {
"gte": 40
}
}
}
],
"should": [
{
"bool": {
"must_not": [
{
"match": {
"city": "上海"
}
}
]
}
}
]
}
}
}
请注意上面的 should 部分的写法。它使用了另外一个 bool query 来表达一个来自非上海的查询。上面的查询的结果是:
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 2,
"relation" : "eq"
},
"max_score" : 1.0,
"hits" : [
{
"_index" : "twitter",
"_type" : "_doc",
"_id" : "5",
"_score" : 1.0,
"_source" : {
"user" : "朝阳区-老王",
"message" : "Happy BirthDay My Friend!",
"uid" : 6,
"age" : 50,
"city" : "北京",
"province" : "北京",
"country" : "中国",
"address" : "中国北京市朝阳区国贸",
"location" : {
"lat" : "39.918256",
"lon" : "116.467910"
}
}
},
{
"_index" : "twitter",
"_type" : "_doc",
"_id" : "6",
"_score" : 1.0,
"_source" : {
"user" : "虹桥-老吴",
"message" : "好友来了都今天我生日,好友来了,什么 birthday happy 就成!",
"uid" : 7,
"age" : 90,
"city" : "上海",
"province" : "上海",
"country" : "中国",
"address" : "中国上海市闵行区",
"location" : {
"lat" : "31.175927",
"lon" : "121.383328"
}
}
}
]
}
}
显然这次的结果,我们并没有改变所返回的数据的数量,但是我们确实改变了返回数据的次序,虽然在本例子中分数还是 1.0(这是因为 must_not 不影响分数)。在这次的查询中,我们可以看到来自北京的文档的排名提前了。