ES简单使用

2018-09-27
5分钟阅读时长

常用命令

  1. 查看有哪些indexes
curl -u [user]:[password] -XGET 127.0.0.1:9200/_cat/indices
  1. 删除索引
curl -u *:* -XDELETE 127.0.0.1:9200/story 
  1. 查看type
curl -u *:* -XGET 127.0.0.1:9200/story/_search?pretty
  1. 查看结构
curl -u *:* -XGET 127.0.0.1:9200/luka/_mapping?pretty
  1. 创建index
curl -u *:*  -H 'Content-Type:application/json' -XPUT 127.0.0.1:9200/luka?pretty
  1. 模糊查询
curl -u *:* -XGET '127.0.0.1:9200/story/_search?q=mary&pretty'

查询

https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl.html

Match查询

curl -XGET 'localhost:9200/my_index/my_type/_search' -d '{
    "query" : {
        "match" : {
            "testField" : "abc"
        }
    }
}'
$data = [
    'body' => [
        'query' => [
            'match' => [
                'title' => '黑猫',
            ]
        ],
    ]
];

Bool查询

curl -XGET 'localhost:9200/my_index/my_type/_search' -d '{
    "query" : {
        "bool" : {
            "must": [
                {
                    "match" : { "testField" : "abc" }
                },
                {
                    "match" : { "testField2" : "xyz" }
                }
            ]
        }
    }
}'
 $data = [
    'body' => [
        'query' => [
            'bool' => [
                'must' => [
                    ['match' => ['title' => '黑猫',]],
                    ['match' => ['anchor_nickname' => '可乐']],
                ]
            ]
        ],
    ]
];
curl -XGET 'localhost:9200/my_index/my_type/_search' -d '{
    "query" : {
        "bool" : {
            "filter" : {
                "term" : { "my_field" : "abc" }
            },
            "should" : {
                "match" : { "my_other_field" : "xyz" }
            }
        }
    }
}'

Query DSL

Match Query

GET /_search
{
    "query": {
        "match" : {
            "message" : "this is a test"
        }
    }
}
GET /_search
{
    "query": {
        "match" : {
            "message" : {
                "query" : "this is a test",
                "operator" : "and"
            }
        }
    }
}

Term Query

POST _search
{
  "query": {
    "term" : { "user" : "Kimchy" } 
  }
}

Exists Query

GET /_search
{
    "query": {
        "exists" : { "field" : "user" }
    }
}

Prefix Query

GET /_search
{ "query": {
    "prefix" : { "user" : "ki" }
  }
}

Join Query

Nested Query

PUT /my_index
{
    "mappings": {
        "type1" : {
            "properties" : {
                "obj1" : {
                    "type" : "nested"
                }
            }
        }
    }
}
GET /_search
{
    "query": {
        "nested" : {
            "path" : "obj1",
            "score_mode" : "avg",
            "query" : {
                "bool" : {
                    "must" : [
                    { "match" : {"obj1.name" : "blue"} },
                    { "range" : {"obj1.count" : {"gt" : 5}} }
                    ]
                }
            }
        }
    }
}

Has Child Query

GET /_search
{
    "query": {
        "has_child" : {
            "type" : "blog_tag",
            "query" : {
                "term" : {
                    "tag" : "something"
                }
            }
        }
    }
}

bool联合查询: must,should,must_not

如果我们想要请求"content中带宝马,但是tag中不带宝马"这样类似的需求,就需要用到bool联合查询。 联合查询就会使用到must,should,must_not三种关键词。

这三个可以这么理解

must: 文档必须完全匹配条件 should: should下面会带一个以上的条件,至少满足一个条件,这个文档就符合should must_not: 文档必须不匹配条件

{
  "query": {
    "bool": {
      "must": {
        "term": {
          "content": "宝马"
        }
      },
      "must_not": {
        "term": {
          "tags": "宝马"
        }
      }
    }
  }
}
{
    "index": {
        "analysis": {
            "analyzer": {
                "ik_pinyin_analyzer": {
                    "type": "custom",
                    "tokenizer": "ik_max_word",
                    "filter": ["my_pinyin", "word_delimiter"]
                }
            },
            "filter": {
                "my_pinyin": {
                    "type": "pinyin",
                    "first_letter": "prefix",
                    "padding_char": " "
                }
            }
        }
    }
}

multi match

{
  "query": {
    "multi_match": {
        "query" : "我的宝马多少马力",
        "fields" : ["title", "content"]
    }
  }
}

我们希望完全匹配的文档占的评分比较高,则需要使用best_fields

{
  "query": {
    "multi_match": {
      "query": "我的宝马发动机多少",
      "type": "best_fields",
      "fields": [
        "tag",
        "content"
      ],
      "tie_breaker": 0.3
    }
  }
}

意思就是完全匹配"宝马 发动机"的文档评分会比较靠前,如果只匹配宝马的文档评分乘以0.3的系数

我们希望越多字段匹配的文档评分越高,就要使用most_fields

{
  "query": {
    "multi_match": {
      "query": "我的宝马发动机多少",
      "type": "most_fields",
      "fields": [
        "tag",
        "content"
      ]
    }
  }
}

嵌套查询

GET /my_index/blogpost/_search
{
    "query":{
        "bool":{
            "must":[
                {
                    "match":{
                        "title":"eggs"
                    }
                },
                {
                    "nested":{
                        "path":"comments",
                        "query":{
                            "bool":{
                                "must":[
                                    {
                                        "match":{
                                            "comments.name":"john"
                                        }
                                    },
                                    {
                                        "match":{
                                            "comments.age":28
                                        }
                                    }
                                ]
                            }
                        }
                    }
                }
            ]
        }
    }
}

ES同义词配置

创建index

{
  "index": {
    "analysis": {
      "analyzer": {
        "by_smart": {
          "type": "custom",
          "tokenizer": "ik_smart",
          "filter": ["by_tfr","by_sfr"],
          "char_filter": ["by_cfr"]
        },
        "by_max_word": {
          "type": "custom",
          "tokenizer": "ik_max_word",
          "filter": ["by_tfr","by_sfr"],
          "char_filter": ["by_cfr"]
        }
      },
      "filter": {
        "by_tfr": {
          "type": "stop",
          "stopwords": [" "]
        },
        "by_sfr": {
          "type": "synonym",
          "synonyms_path": "analysis/synonyms.txt"
        }
      },
      "char_filter": {
        "by_cfr": {
          "type": "mapping",
          "mappings": ["| => |"]
        }
      }
    }
  }
}

创建type

{
	"properties": {
	    "title": {
	    	"type": "text",
	    	"fields": {
		    	"keyword": {
		        	"type": "keyword",
		        	"ignore_above": 256
		    	}
	    	},
			"index": "analyzed",
		    "analyzer": "by_max_word",
		    "search_analyzer": "by_smart"
    	},
    	"authors": {
    		"properties":{
    			"name": {
    				"type": "text",
			    	"fields": {
				    	"keyword": {
				        	"type": "keyword",
				        	"ignore_above": 256
				    	}
			    	},
					"index": "analyzed",
				    "analyzer": "by_max_word",
				    "search_analyzer": "by_smart"		
    			}
    		}
    	}
	}
}

分页

from-size"浅"分页

可以理解为简单意义上的分页。它的原理很简单,就是查询前20条数据,然后截断前10条,只返回10-20的数据。这样其实白白浪费了前10条的查询。

{
    "from" : 0,
    "size" : 10,
    "query" : {
        "term" : { "user" : "kimchy" }
    }
}

其中,from定义了目标数据的偏移值,size定义当前返回的事件数目。 默认from为0,size为10,即所有的查询默认仅仅返回前10条数据。

做过测试,越往后的分页,执行的效率越低。

scroll“深”分页

可以把 scroll 理解为关系型数据库里的 cursor,因此,scroll 并不适合用来做实时搜索,而更适用于后台批处理任务,比如群发。

可以把 scroll 分为初始化和遍历两步,初始化时将所有符合搜索条件的搜索结果缓存起来,可以想象成快照,在遍历时,从这个快照里取数据,也就是说,在初始化后对索引插入、删除、更新数据都不会影响遍历结果。

POST ip:port/my_index/my_type/_search?scroll=1m
{
	"query": { "match_all": {}}
}

POST /_search?scroll=1m
{
    "scroll_id":"xxxxxx"
}

分组查询

{
  "size": 20,
   "query": {
      "match": {
        "title" : {
            "query" : "黑猫"
        }
    }
  },
  "aggs": {
    "group_by_title": {
      "terms": {
        "field": "title.keyword"
      }
    }
  }
}

遇到的问题

match 和 term 的区别

  • term再搜索前不会对单词进行分词, 会直接进行进行搜索
  • match在搜索前会对单词进行分词, 然后进行搜索

keyword与text的区别

  • keyword:存储数据时候,不会分词建立索引
  • text:存储数据时候,会自动分词,并生成索引(这是很智能的,但在有些字段里面是没用的,所以对于有些字段使用text则浪费了空间)

中文分词

  • ik_smart会将清华大学整个分为一个词,
  • ik_max_word会将清华大学分为清华大学清华大学,按需选其中之一就可以了。

能否在查询时指定分词器?

阿里云目前不能实现.

不能对已经生成的数据, 重新设置分词

PUT /school_index { “settings” : { “index” : { “analysis.analyzer.default.type”: “ik_max_word” } } }

Fielddat问题

Fielddat is disabled on text fields by default. Set fielddata=true on [title] in order to load fielddata in memory by uninverting the inverted index. Note that this can however use significant memory. Alternatively use a keyword field instead

解决方法: https://www.elastic.co/guide/en/elasticsearch/reference/current/fielddata.html

es既想包含查询,又想精确查询

如果es既想包含查询(即类似数据库like)又想精确查询(完全相等,类似于数据库=),则只要建立mapping中如下设置即可。查包含时直接字段名,=查询时加.raw

{ “properties”: { “user”: { “properties”: { “name”: { “type”: “string”, “fields”: { “raw”: { “type”: “string”, “index”: “not_analyzed” } } } } } } }

参考资料

  1. 19 个很有用的 ElasticSearch 查询语句