升级改造报警系统(3-1)

345 阅读6分钟

使用elasticsearch进行搜索 1


  • elasticsearch 查询

search API就是对存储在elastic search(以下简称es)中数据进行查询的相关API,可以类比MySQL中的select语句。es中的search主要分为URI search 和query dsl语法为主;

es的Query DSL以_search为endpoint,主要分为字段类查询和复合查询()。

字段类查询: 只针对某一个字段进行查询,如 match、term等; 复合查询: 可以进行一个或多个字段的查询,如bool查询等;

字段类查询主要包括两类:单词匹配和全文匹配

-	单词匹配(Term Level Query) 
		不对查询语句进行分词处理,直接匹配该字段的倒排索引;
- 	全文匹配(Full Text Query)
		对指定的text类型的字段进行全文检索,会先对查询语句进行分词处理。例如你输入的查询文本是"我在马路边",es在分词器的作用下就会分词为"我""在""马路"这么几个单词,然后再去匹配。

字段类查询

1. 全文匹配 包括match、match_phrase、query_string、simple_query_string查询语句;

  • match query 是最基本的基于全文检索的字段类查询语法
GET /_search
{
	"query": {
    	"match": {
        	"message": "this is a test"
        }
    }
}

那么这里经过分词后的this、is、a、test各个单词之间就是默认为"或"的匹配关系,可以通过operator关键字来显式设置各个单词间的匹配关系,如下:

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

也可以通过minimum_should_match参数来设置控制需要匹配的单词数,比如你的文档里username存的内容是"this is a java enginer",那么通过下面的查询语句可以控制查询文本最少要匹配到"java"、"enginer":

GET /_search
{
	"query": {
    	"match" : {
        	"username": {
            	"query": "java enginer",
                "minimum_should_match":2
            }
        }
    }
}
  • match_phrase query

match_phrase 也是对字段进行检索,和match_query是有顺序需求的,而match是无序的。

可以通过slop参数来控制单词之间的允许间隔:

GET /_search
{
	"query":{
    	"match_phrase": {
        	"username": {
            	"query": "java php enginer",
                "slop": "2"
                }
        	}
        }
}
  • query_string query
GET /_search
{
	"query": {
    	"query_string": {
        	"default_field": "content",
            "query": "this and that or thus"
        }
    }
}

多字段匹配

GET /_search
{
	"query": {
    	"query_string": {
        	"fields": ["content", "name"],
            "query": "this and that"
        }
    }
}
  • simple_query_string

语法与query_string类似,但是不支持AND、OR、NOT,分别需要用"+"、"|"、"-"号代替。

2. 单词匹配

包括term terms range查询语句:

  • term query

term查询语句不会对查询语句进行分词处理,直接拿查询输入的文本去检索,如下是官方文档测试案例,非常清晰:

PUT my_index
{
	"mappings": {
    	"_doc": {
        	"properties": {
            	"full_text": {
                	"type": "text"
                    },
                 "exact_value": {
                 	"type": "keyword"
                 }
            }
        }
    }
}

PUT my_index/_doc/1 # 创建一个ID 
{
	"full_text": "Quick Foxes!",
    "exact_value": "Quick Foxes!"
}

GET my_index/_search #查看 可以匹配到 类型为keyword的field
{
	"query": {
    	"term" : {
        	"exact_value": "Quick Foxes!"
        }
    }
}

# 不能匹配到结果,因full_text字段类型为text,会					#对原始文本分词为quick 、foxes
  {
	"query": {
    	"term" : {
        	"full_text": "Quick Foxes!"
        }
    }
}

# 可以匹配到结果
{
	"query": {
    	"term" : {
        	"full_text": "Foxes!"
        }
    }
}

# 使用match 会对quick foxes 进行查询时分词
{
	"query": {
    	"match" : {
        	"full_text": "Quick Foxes!"
        }
    }
}

  • terms query

terms和term的查询语法基本类似,但terms支持的查询文本可以多个:

{
	"query": {
    	"terms": {
        	"username": ["java", "elstic"]
        }
    }
}

此外,terms还支持文档元数据信息的几个字段值查询:

  • index
  • type
  • id
  • path
  • routing
GET /index1/_search

{
	"query":{
    	"terms":{
        	"user": {
            	"index": "users",
                "type": "_doc",
                "id": "2",
                "path": "followers"
            }
        }
    }
}
  • range query

范围查询主要用于date或number类型的字段查询中,和term、terms查询一样,不进行查询时分词:

GET _search
{
	"query": {
    	"range": {
        	"age": 10,
            "lte": 20,
            "boost": 2.0
        }
    }
}

其中gte表示大于等于,lte表示小于等于,gt表示大于,lt表示小于,boost表示对此次query进行相关性算分权重,默认是1.0

POST my_test_index1/userinfo/
{
	"name": "simons",
    "age": 24,
    "birth": "2020-10-10"
}

GET my_test_index1/userinfo/_search
{
	"query": {
    	"range": {
        	"brith": {
            	"gt": "now-3y"
            }
        }
    }
}

在range范围查询中,es提供了一种更加简单的日期计算,

now表示当前时间,
y:年,
M:月,
d:天,
H:时,
m:分,
s:秒,

所以now-3y就是三年之前的时间。

query dsl

复合查询

复合查询就是指可以对多个字段过滤筛选,类比mysql的where多条件查询,es的复合查询包括 Constant Score Query、Bool Query、Dis Max Query、Function Score Query、Boosting Query,这里详细说一说用的比较多的Bool Query。

Bool Query是由一个或多个bool子句构成的,包括:

Bool查询的基本语法如下:

GET my_test_index2/userinfo/_search
{
	"query": {
    	"bool": {
        	"must":[
            	"match": {
                	"name": "simons"
                }
            ],
            "must not": [
            	{
                	"term": {
                    	"address": {
                        	"value": "japan"
                        }
                    }
                }
            ],
            "should": [
            {
            	"term": {
                	"age":24
                }
            }
            ],
            "filter":{
            	"term":{
                	"name": "simons"
                }
            }
        }
    }
}

1、must、must_not、should支持数组,同时filter的查询语句,es会对其进行智能缓存,因此执行效率较高,在不需要算分的查询语句中,可以考虑使用filter替代普通的query语句;

2、查询语句同时包含must和should时,可以不满足should的条件,因为must条件优先级高于should,但是如果也满足should的条件,则会提高相关性算分;

3、可以使用minimum_should_match参数来控制应当满足条件的个数或百分比;

4、must、must_not语句里面如果包含多个条件,则各个条件间是且的关系,而should的多个条件是或的关系。

参考文档: Elastic Search之Search API(Query DSL)、字段类查询、复合查询

  • filter 过滤器

尽管我们之前已经涉及了查询DSL,然而实际上存在两种DSL:查询DSL(query DSL)和过滤DSL(filter DSL)。 过滤器(filter)通常用于过滤文档的范围,比如某个字段是否属于某个类型,或者是属于哪个时间区间

  • 创建日期是否在2014-2015年间?
  • status字段是否为success?

查询器(query)的使用方法像极了filter,但query更倾向于更准确的查找。

  • 与full text search的匹配度最高

  • 正则匹配

  • 包含run单词,如果包含这些单词:runs、running、jog、sprint,也被视为包含run单词

  • 包含quick、brown、fox。这些词越接近,这份文档的相关性就越高 查询器会计算出每份文档对于某次查询有多相关(relevant),然后分配文档一个相关性分数:_score。而这个分数会被用来对匹配了的文档进行相关性排序。相关性概念十分适合全文搜索(full-text search),这个很难能给出完整、“正确”答案的领域。

query filter在性能上对比:filter是不计算相关性的,同时可以cache。因此,filter速度要快于query。

如果你想同时使用query和filter查询的话,需要使用 {query:{filtered:{}}} 来包含这两个查询语法。他们的好处是,借助于filter的速度可以快速过滤出文档,然后再由query根据条件来匹配。

    "query": {
        "filtered": {
            "query":  { "match": { "email": "business opportunity" }},
            "filter": { "term": { "folder": "inbox" }}
        }
    }
}

{   "size":0,    
    "query": {
        "filtered": {
            "query": {
                "bool": {
                    "should": [],
                    "must_not": [
                       
                    ],
                    "must": [
                        {
                         "term": {
                             
                                "channel_name":"微信自媒体微信"
                            }
                        }
                   
                    ]
                }
            }

        },
        "filter":{
            "range": {
                "idate": {
                    "gte": "2015-09-01T00:00:00",
                    "lte": "2015-09-10T00:00:00"
                    
                    }
                }
        }
    }
}

python 应用示例:

过滤近两天生成的目标

q_data2 = {"query": {"filtered": {"filter": {"range": {"created_at": {"lt": "","gt": ""}}}}},
                   "sort": {"created_at": {"order": ""}}}
q_data2["query"]["filtered"]["filter"]["range"]["created_at"]["lt"] = "now"
q_data2["query"]["filtered"]["filter"]["range"]["created_at"]["gt"] = "now-1d"
q_data2["sort"]["created_at"]["order"] = "desc"
q_data2["size"] = end_num
q_data2["from"] = start_num

按字段不分词匹配

# ids = [id1, id2 ...idn]
{'query': {'terms': {'alerts_id': ids}}}

query 搜索框 默认搜索所有字段

'query': {
				'multi_match': {
					'query': u '安全',
					'fields': '_all'
				}
			}

方法:

import json
import urllib2
import base64
def query(options, indices, q_data):
  base64string = base64.b64encode(
              '%s:%s' % (options.es_user, options.es_pawd))
  req = urllib2.Request(options.elasticsearch_api + indices + "/_search", json.dumps(q_data))
  req.add_header("Authorization", "Basic %s" % base64string)
  response = urllib2.urlopen(req).read()
  ret = json.loads(response)['hits']
  return ret