query_string查询疑问

es2.2版本。我有一个字段name,string类型不分词,我存储的值是"test nihao";
用wildCard查询 "test a*"能够搜索出这条记录:
QueryBuilders.wildcardQuery("nick_name","test n*")
 
但是用query_string查询"+name:test a*"无法搜索出这条记录:
QueryBuilders.queryStringQuery("+nick_name:test n*")
 
望各位大神赐教。另外analyze_wildcard这个参数的含义我不太明白,是不是设置了true以后,test n* 会被分词成test 和 n* ?
已邀请:

kennywu76 - wood@Ctrip

赞同来自: Cheetah novia lingerchouzi wudoz

澄清一下,QueryString Query的查询串是否分词,是取决于查询的字段mapping怎么设置的。 如果是不分词字段,查询串也不会经过analyze过程。以下测试过程可以证明:
 
1. 创建一个空索引,name字段为keyword不分词, desc字段为text分词
PUT test_index/
{
"mappings": {
"typeme": {
"properties": {
"name": {
"type": "keyword"
},
"desc": {
"type": "text"
}
}
}
}
}

对name 字段执行一个query string query,带上"profile":true选项,看ES怎么解析这个查询的。
POST test_index/_search
{
"profile": true,
"query": {
"query_string": {
"default_field": "name",
"query": "John Wall"
}
}
}
结果:
query": [
              {
                "type": "BooleanQuery",
                "description": "name:John name:Wall",
                "time": "0.1274150000ms",
被解析成一个查询和John和Wall,关系为OR的boolean Query,看起来似乎查询串被分词了,但实际上是对结果的误读。 仔细看John和Wall的首字母大些被保留着,而默认的分词器是是含有lower case filter的。 
 
那么为什么John Wall被分成两个词查询了,实际上起作用的是默认的split_on_whitespace: true这个参数,查询串被按照空格分割了。
 
如果这个参数设置为false,
POST test_index/_search
{
"profile": true,
"query": {
"query_string": {
"default_field": "name",
"query": "John Wall",
"split_on_whitespace": false
}
}
}
得到的查询是这样的:
"query": [
{
"type": "TermQuery",
"description": "name:John Wall",
"time": "0.04509200000ms",

查询的是"John Wall"这个完整的Term。 
 
如果查询的是"John,Wall",结果则是这样:
query": [
{
"type": "TermQuery",
"description": "name:John,Wall",
也可以证明,对于不分词字段做Query string 查询,查询串是不会分词的。
 
对比以下,如果对desc这个分词字段做同样的查询,会是怎样:
POST test_index/_search
{
"profile": true,
"query": {
"query_string": {
"default_field": "desc",
"query": "John,Wall"
}
}
}

从结果可以看到,查询串被从逗号处进行了分割,并且term都被lower case转换掉了。
 "query": [
{
"type": "BooleanQuery",
"description": "desc:john desc:wall",
"time": "0.1125100000ms",

 不理解查询是如何执行的时候,多用"profile":true这个参数去看看,对比文档理解会比较好。 否则靠对文档的一知半解去猜测,很容易搞成一团浆糊。
 

kennywu76 - wood@Ctrip

赞同来自: novia

既然name字段设置成不分词,test nihao在倒排索引里是作为一个完整的term保存的。当你用wildcard查询的时候,*是作为通配符使用的,所以可以匹配。 如果使用query string query,查询会根据字段的类型和查询字符串的内容和查询参数来决定rewrite成什么类型的查询。因为name字段是不分词的,所以查询串不会被分词。当analyze_wildcard设置为true时,意思是*这样的通配符会当作通配符来解析,实际效果就是rewrite成了一个wildcard quey,所以能匹配到结果。 而analyze_wildcard如果设置成false,则*会被当作一个普通字符,查询被rewrite成一个term query。尝试在倒排表里查找” test *n”这个term,但这个term显然不存在,所以无法匹配。

Cheetah

赞同来自: novia

queryStringQuery是需要设置分词模式的,也就是查询分词,所以你的test n*会被置为test 和 n*
analyze_wildcard设置为true之后,可以让你查询时与wildcard等效
你这里查询不出来的原因就是你有空格,你实际查询的是test和n*,默认是or的关系,你可以将test* n*,这样肯定能查询出结果

kennywu76 - wood@Ctrip

赞同来自: lingerchouzi

"analyze_wildcard": true这个参数别我理解的更复杂一些,实际上执行的方式是这样的:
y setting analyze_wildcard to true, queries that end with a * will be analyzed and a boolean query will be built out of the different tokens, by ensuring exact matches on the first N-1 tokens, and prefix match on the last token
 
测试了一下,这个参数打开后,所有的词会从空格处分开,最后一个term用*统配,前面N-1个用term查询。split_on_whitespace也会影响查询。
 
例如,"split_on_whitespace":false  
POST test_index/_search
{
"profile": true,
"query": {
"query_string": {
"default_field": "name",
"query": "John allen wall*",
"split_on_whitespace":false
}
}
}

得到的是:
"query": [
{
"type": "BooleanQuery",
"description": "name:John allen name:wall*",

如果设置"split_on_whitespace": true, 会有细微差别:
"query": [
{
"type": "BooleanQuery",
"description": "name:John name:allen name:wall*",

要回复问题请先登录注册