Elasticsearch v2.2 快速入门(含curl,Sense,python 3种客户端方式)

Elasticsearch v2.2 快速入门(含curl,Sense,python 3种客户端方式) 作者:AbnerGong

本人花了一周时间看文档和前人的博客,才刚刚达到入门水平。写此文是给各位作垫脚石,希望各位能在入门上少花时间。同时我也希望能与大家互相学习,如果各位写出了更进一步的文章,或补充本文中略去的诸多细节,欢迎将地址贴在回复中,万分感谢!

理解Elasticsearch的基本概念: 建议阅读 Elasticsearch学习笔记 by siddontang@2015.01

  • 索引Index,搜索search是基本概念,也是本文主要关注的方面。
  • Lucene关键概念:Document是一条数据,包含多个Field;每个Field包含name和value;如果分词(Analyzed)则每个值里包含多个Term,如果不分词则整个值即为1个Term;实际存储方式Inverted index是以Term为主键的,每个Term在哪些文档的哪些位置出现,即为一条Token,实际搜索时是在Term表中查找。
  • 将Elasticsearch与MySQL对照更好理解: index = DB数据库; type = table一张表;Mapping = schema表中包含的域及其类型;Document = row一条数据;Field = column;聚合aggs = group by
  • Node,Cluster,Shard,Replica的基本概念。也可参考1.1.1 基本概念
  • Restful API 的基本概念。访问工具:httpieSense编辑器

检索与搜索:强烈推荐Elasticsearch 权威指南 翻译by QQ1350995917@2014/05

假设我们已经作为Megacorp公司HR部门的一份子,接到建立一个employee的目录,这个目录支持雇主和雇员之间的换位思考和实时的动态的协同工作,有如下要求:
1. 数据应该能包含若干值的tags,数值和全文本。
2. 能获取雇员的详细信息。
3. 允许结构化的检索,如找到年龄大于30岁的员工。
4. 允许简单的全文检索和更复杂的短语检索。
5. 能从符合检索条件的文档中高亮检索片段。
6. 能让雇主根据数据生成数据分析图形表格。

创建/删除/验证存在 索引:

#curl
$ curl -XPUT 'http://localhost:9200/megacorp/'   #创建index
$ curl -XPUT 'http://localhost:9200/megacorp/' -d '  #创建Index并设定碎片数和副本数
index :
    number_of_shards : 3   #默认为5,即5个主片
    number_of_replicas : 2   #默认为1,即为每个主片建1个副本
'
$ curl -XDELETE 'http://localhost:9200/twitter/'  #删除index 
$ curl -XHEAD -i 'http://localhost:9200/twitter'

#Sense
PUT megacorp

#Python
es.indices.create(index="megacorp")
es.indices.delete(index="megacorp")
es.indices.exists(index="megacorp")

创建/删除/读取 表结构:Mapping

mapping的意思是制图、图谱,在这里相当于索引里的结构图,简称为结构。请注意,索引、索引的类型、索引类型的字段都有其结构(mapping)。

#curl,创建
curl -XPOST localhost:9200/customer -d '{
    "settings" : {
        "number_of_shards" : 1
    },
    "mappings" : {
        "type1" : {
            "properties" : {
                "field1" : { "type" : "string", "index" : "not_analyzed" }
            }
        }
    }
}'
#读取表结构
curl -XGET 'http://localhost:9200/twitter/_mapping/tweet'

#Sense
#也可以在创建索引时指定表结构
PUT customer
{
  "mappings": {  #指定多个表结构
    "user": {   #第一个表user
      "_all":       { "enabled": false  },  #禁用了meta域_all
      "properties": {   #这个表中有如下属性
        "title":    { "type": "string"  },     #对每个字段,要指定它的类型,类型为string时默认分词
        "name":     { "type": "string"  }, 
        "age":      { "type": "integer" }  
      }
    },
    "blogpost": { 
      "properties": { 
        "title":    { "type": "string"  }, 
        "body":     { "type": "string"  }, 
        "user_id":  {
          "type":   "string", 
          "index":  "not_analyzed"     #指定不分词
        },
        "created":  {
          "type":   "date", 
          "format": "strict_date_optional_time||epoch_millis"   #对时间域要指定格式
        }
      }
    }
  }
}
#读取表结构
GET twitter/_mapping/tweet,user  #指定索引,指定类型名,可以指定多类型
GET twitter/_mapping  #指定索引,不指定类型名
GET _mapping/tweet    #不指定索引,指定类型名
GET _mapping   #不指定索引,不指定类型,取出所有结构
#读取表的字段的结构
GET publications/_mapping/article/field/author.id,abstract,name  #指定索引,关键字_mapping,指定类型,关键字field,指定字段
GET _all/_mapping/tw*/field/*.id  #可以模糊匹配
GET _mapping/field/*       #不指定索引,不指定类型,不指定字段名。  */

#Python
#创建索引时添加表结构,注意将false改为False
q = {
  "mappings": {
    "user2": {
      "_all":       { "enabled": False  },
      "properties": {
        "title":    { "type": "string"  },
        "name":     { "type": "string"  },
        "age":      { "type": "integer" }
      }
    }
  }
}
es.indices.create(index="customer",body=q)  #创建索引并指定它的配置和表结构
#给已有索引添加表结构,这个似乎没法批量添加。注意给已有表结构添加字段也是同样的方式。
requestbody = {
      "_all":       { "enabled": False  },
      "properties": {
        "title":    { "type": "string"  },
        "name":     { "type": "string"  },
        "age":      { "type": "integer" }
      }
    }
print es.indices.put_mapping(index="custom",doc_type="user",body=requestbody)
#读取表结构和字段结构
es.indices.get_mapping(index="custom", doc_type="user") #这两个参数可以都没有
es.indices.get_field_mapping(index="custom",doc_type="user2",fields=["name","age"])  
#确认某类型存在
es.indices.exists_type(index="custom",doc_type="user")  #两个参数必须都有

检索(即插入信息):

#curl
curl -GET 'http://localhost:9200/megacorp/employee/1?pretty'

{
    "first_name":  "John",
    "last_name":   "Smith",
    "age":         25,
    "about":       "I love to go rock climbing",
    "interests":  ["sports","music"]
}

#sense
PUT megacorp/employee/1
{
    "first_name":"John",
    "last_name":  "Smith",
    "age":        25,
    "about":      "I love to go rock climbing",
    "interests":["sports","music"]
}

#python
body={
    "first_name":"John",
    "last_name":  "Smith",
    "age":        25,
    "about":      "I love to go rock climbing",
    "interests":["sports","music"]
}
es.create(index="megacorp", doc_type="employee", id=1,body=body)

简单查找(指定索引/类型/id号)

【注意】请不要在插入0秒后立刻查找,会查不到,得等1s才能查到。下同。

#curl
curl -GET 'http://localhost:9200/megacorp/employee/1?pretty'

#Sense
GET megacorp/employee/1?pretty

#python
es.get(index="megacorp", doc_type="employee", id=1)

简单搜索(查找字符串)

#curl
curl -XGET 'http://localhost:9200/_search?pretty' #显示全部索引的内容
curl -XGET 'http://localhost:9200/megacorp/employee/_search?pretty' #只指定索引/类型
curl -XGET 'http://localhost:9200/_search?q=about:"go rock"&pretty' #只指定搜索内容about必须包含字符串"go rock"完全匹配不分词
curl -XGET 'http://localhost:9200/megacorp/employee/_search?q=about:"go rock"&pretty' #组合起来

#Sense
GET _search?pretty #显示全部索引的内容
GET megacorp/employee/_search?pretty #指定索引/类型,不指定搜索内容
GET _search?q=about:"go rock"&pretty #只指定搜索内容about必须包含字符串"go rock"完全匹配不分词
GET megacorp/employee/_search?q=about:"go rock"&pretty #组合起来

#python
es.search() #不指定任何。返回结果es.search()["hits"]["hits"]是一个数组,每项是一条dict记录,它有"_index""_type""_id""_source""_score"等字段
es.search(filter_path=['hits.hits._id', 'hits.hits._type']) #指定返回结果包含内容,通过这种方式可以使得每条dict记录只包含"_id""_type"字段
es.search(filter_path=['hits.hits._*']) #这样就选出所有字段了
es.search(index='megacorp', doc-type="employee") #指定索引/类型,不指定搜索内容
es.search(from_=3, size=5) #跳过前3个,从下标我3号的结果开始,共取5个结果
#更多参数请见官方[API documentation](http://elasticsearch-py.readthedocs.org/en/master/api.html)

如何使用Search DSL搜索

前面的搜索条件放在网址后面的参数里,这样只能进行简单的搜索。要执行复杂的搜索,需要用到Search DSL,它是将搜索条件放到JSON里,通过数据的方式传递给服务器。举例如下,仍然是搜索about中包含有完整”go rock”的,可以如下书写:

#curl
curl -XGET 'http://localhost:9200/_search?pretty' -d
 {"from" : 0, "size" : 10,
"query":{"match_phrase":{"about":"go rock"}}}

#Sense
GET _search?pretty
 {"from" : 0, "size" : 10,
"query":{"match_phrase":{"about":"go rock"}}}

#python
body = {"from" : 0, "size" : 10,
"query":{"match_phrase":{"about":"go rock"}}}
es.search(body = body) #指定搜索内容 

显然,curl/Sense/python三种方式的JSON部分是完全相同的(即Search DSL),而非JSON的部分与搜索没有关系,也就是说无论我们怎么更改搜索条件,非JSON的部分都是不需要改变的,我们研究的只是JSON部分,所以接下来的代码将只包含JSON部分,而不区分curl,Sense,python。

上面的代码中Search DSL包含”from”、”size”、”query”三个字段,其中”query”表示我们要查询的内容,”from”表示跳过几个结果,”size”表示返回几个结果。这只是举个例子,Search DSL有很多字段,包括Query DSL,from/size,Sort,Source filtering,fields,Script fields,Field Data Fields,Post filter,Highlighting,Rescoring,Seach Type,Scroll。下面将一一阐述这些字段的含义及用法。

Query DSL

0.重要前言

在继续学习以前,还要强调一下Search DSL(搜索DSL)和Query DSL(查询DSL)的区别。
它们的区别在于,Search DSL包含Query DSL,Search DSL是可执行的,Query DSL只表示条件,只是Search DSL中的一个字段。用以上面的例子来说

QueryDSL = {"match_phrase":{"about":"go rock"}}  #这是一个查询,它能表示about中包含"go rock"这个含义
SearchDSL = {"query": {"match_phrase":{"about":"go rock"}} } #这是一个搜索,它最终可以被执行

查询和搜索在中文中的含义差不多,我认为不够直观,所以我将Query翻译为条件。
Query DSL(条件DSL)分为leaf query(原子条件),compound query(复合条件)两类。
显然Query DSL(条件DSL)能表达我们要查询的条件,任意的条件还可以进行组合成为更复杂的条件,但是不管如何组合,条件都不是最终的JSON对象,必须在条件外加一个query(打分)或filter(过滤)才能用来执行。接下来我会介绍原子条件和复杂条件,你也可以参考官方网页,但是测试时请注意,最外层不是query或filter的都只是条件,不是最终JSON对象,不能直接用来查询,在使用前必须外套一层。

1.原子条件

比如我想要,这就是一个条件,{"match_phrase":{"about":"go rock"}}可以表示这个条件。显然,这个条件是JSON中的最小单位,姑且称之为原子条件(leaf query clause),原子条件当然能组合成复合条件,这个后面再说。

原子条件分为match类(Full text queries)term类(Term level queries),它们的概念在两个链接开头有介绍,不过还是再解释一下:
本文开头时提到所有域都会以Term表来存储(分词or不分词),查找时实际上是查找Term表,区别是match会将待搜索字符串先分词再在Terms中查找,而term则是将整个字符串不分词直接在Term表中查找,也就是说查找的字符串分词还是不分只与用match还是term有关,与查找的域分词还是没分没有任何关系。具体样例可参见Term Query

match类(Full text queries))

包括match、multi_match、common_terms、query_string、simple_query_string共五类,下面一一列举

#简单写法
{"match":{"about":"go suck"}} #match要求匹配里面至少1个单词
{"match_phrase":{"about":"go rock"}} #match_phrase要求匹配完整字符串

#复杂写法,但可设置更多参数
{"match":{
     "about" : {
            "query" : "go suck",
            "operator" : "and",
            "zero_terms_query": "all"
        }
}}
{"match_phrase" : {
        "about" : {
            "query" : "go rock",
            "analyzer" : "my_analyzer"
        }
}}
{"match_phrase_prefix" : {
        "message" : {
            "query" : "go rock",
            "max_expansions" : 10
        }
}}

multi_match:match的多域版本,即几个域一起查找字符串
common_terms:更专业的查询,更优先考虑生僻词
query_string
simple_query_string

term类(Term level queries)

全文检索将会在执行前先对query进行分词,而term-level查询则对存储在inverted index的terms进行精确操作。这些查询通常用作结构化数据比如数字、日期、枚举,而不是全文域。另外,它们允许你craft低级查询,在分析过程以前。
term类包括term、terms、range、exists、missing、prefix、wildcard、regexp、fuzzy、type、ids,下面一一列举:
term:找到在指定域中包含指定的完整词(exact term)的文档
terms
range

#简单写法
{ "range":{"age":{"gt":30}}}

#复杂写法,可设置更多参数:
#gte是≥,gt是>,lte是≤,lt是<,boost是设置权重(默认为1)
{"range" : {
        "age" : {
            "gte" : 10,
            "lte" : 20,
            "boost" : 2.0
        }
}}
#针对日期,有如下几种方式
{
    "range" : {
        "date" : {
            "gte" : "now-1d/d",
            "lt" :  "now/d"
        }
    }
}
{
    "range" : {
        "born" : {
            "gte": "01/01/2012",
            "lte": "2013",
            "format": "dd/MM/yyyy||yyyy"
        }
    }
}
{"range" : {
        "timestamp" : {
            "gte": "2015-01-01 00:00:00", 
            "lte": "now", 
            "time_zone": "+01:00"
        }
}}
{"range" : {
         "postDate" : {
              "from" : "2010-03-01",
              "to" : "2010-04-01"
         }
}}

exists
missing
prefix
wildcard
regexp:找到文档,它的指定域包含与特定正则表达式匹配的词
fuzzy:找到文档,它的指定域包含与指定词模糊相似的词。模糊性由Levenshtein编辑距离确定
type:找到指定类型的文档
ids:找到有指定类型和ID的文档

更多内容请参考全文检索(full-text queries)

2.复合条件

一些原子条件可以用联结词连接成为大的复合条件。联结词请见compound queries,下面一一列举:

bool

语法:它先给每个条件加一个限定逻辑词(加must/filter/should/must_not),然后用bool连接在一起
作用:它能综合考虑条件,并将打分相加。must(必须出现并贡献分),filter(必须出现然而分数会被忽略),should(其中的多个原子条件至少要有minimum_should_match个成立),must_not(不能出现)
注意:每个条件可以是match类/term类原子条件,也可以是复合条件。限定逻辑词不可以是query,参考文档中出现query,但是亲测失败

{
    "bool" : {
        "must" : {
            "term" : { "user" : "kimchy" }
        },
        "filter": {
            "term" : { "tag" : "tech" }
        },
        "must_not" : {
            "range" : {
                "age" : { "from" : 10, "to" : 20 }
            }
        },
        "should" : [
            {
                "term" : { "tag" : "wow" }
            },
            {
                "term" : { "tag" : "elasticsearch" }
            }
        ],
        "minimum_should_match" : 1,
        "boost" : 1.0
    }
}
用python构造如下:
atom1={"match":{"data":"really me"}} #原子条件
atom2={"term":{"data":"bore"}} #原子条件
atom3={"match_all":{}} #原子条件
comp={"bool":{"filter":atom2}} #复合条件
comp={"bool":{"filter":comp}} #f复合条件的复合条件
body={"filter":comp}  #最终命令
boosting

语法:它先给每个条件加一个positive/negative限定词,然后用boosting连接在一起。
作用:它可以给部分条件减分。我们知道bool中NOT的内容出现时会被删去,这里negative的内容出现时不会被删去而是会减分

{
    "boosting" : {  
        "positive" : { 
            "term" : {  
                "field1" : "value1"
            }
        },
        "negative" : {  
            "term" : {
                "field2" : "value2"
            }
        },
        "negative_boost" : 0.2 
    }
}
#简化结构为:boosting>(postive>条件+negative>条件+negative_boost:值)
constant_score

用法:它先给每一个条件加一个限定词filter或query,然后用constant_score连在一起
作用:将最终打分变为常数。不论里面用的是query/filter + match/term的哪种搭配,最终的得分均为常数,至于boost为1.2不知道啥用。

{
    "constant_score" : {
        "filter" : {
            "term" : { "user" : "kimchy"}
        },
        "boost" : 1.2
    }
}
dis_max

用法:比较长,没细看
作用:bool是将几项的得分相加,它是取几项中的最好得分。几项条件中有一项满足即可。

{
    "dis_max" : {
        "tie_breaker" : 0.7,
        "boost" : 1.2,
        "queries" : [
            {
                "term" : { "age" : 34 }
            },
            {
                "term" : { "age" : 35 }
            }
        ]
    }
}
function_score
indices

【注意】and, or,filtered已经被废弃,请用bool代替

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值