elasticsearch基础

1.elasticsearch基本概念

1.1 elasticsearch数据框架

在Elasticsearch中,文档归属于一种类型(type),而这些类型存在于索引(index)中,我们可以画一些简单的对比图来类比传统关系型数据库:

Relational DB -> Databases -> Tables -> Rows -> Columns
Elasticsearch -> Indices   -> Types  -> Documents -> Fields

1.2 elasticsearch索引

索引这个词在Elasticsearch中有着不同的含义,所以有必要在此做一下区分:

  • 索引(名词) 如上文所述,一个索引就像是传统关系数据库中的数据库,它是相关文档存储的地方,index的复数是indices 或indexes。
  • 索引(动词) 「索引一个文档」表示把一个文档存储到索引(名词)里,以便它可以被检索或者查询。这很像SQL中的INSERT关键字,差别是,如果文档已经存在,新的文档将覆盖旧的文档。
  • 倒排索引 传统数据库为特定列增加一个索引,例如B-Tree索引来加速检索。Elasticsearch和Lucene使用一种叫做倒排索引(inverted index)的数据结构来达到相同目的。默认情况下,文档中的所有字段都会被索引(拥有一个倒排索引),只有这样他们才是可被搜索的。

2.elasticsearch分布式集群

2.1elasticsearch分布式特点

Elasticsearch致力于隐藏分布式系统的复杂性。以下这些分布式操作都是在底层自动完成的:

  • 将你的文档分区到不同的容器或者分片(shards)中,它们可以存在于一个或多个节点中。
  • 将分片均匀的分配到各个节点,对索引和搜索做负载均衡。
  • 冗余每一个分片,防止硬件故障造成的数据丢失。
  • 将集群中任意一个节点上的请求路由到相应数据所在的节点。
  • 无论是增加节点,还是移除节点,分片都可以做到无缝的扩展和迁移。
  • 我们能够与集群中的任何节点通信,每一个节点都知道文档存在于哪个节点上,它们可以转发请求到相应的节点上。我们访问的节点负责收集各节点返回的数据,最后一起返回给客户端。

2.2 elasticsearch集群信息

2.2.1 集群健康

集群健康检查使用

GET /_cluster/health

2.2.2 索引与分片

为了将数据添加到Elasticsearch,我们需要索引(index)——一个存储关联数据的地方。实际上,索引只是一个用来指向一个或多个分片(shards)的“逻辑命名空间(logical namespace)”。

分片是Elasticsearch在集群中分发数据的关键。把分片想象成数据的容器。文档存储在分片中,然后分片分配到你集群中的节点上。当你的集群扩容或缩小,Elasticsearch将会自动在你的节点间迁移分片,以使集群保持平衡。

分片可以是主分片(primary shard)或者是复制分片(replica shard)。复制分片只是主分片的一个副本,它可以防止硬件故障导致的数据丢失,同时可以提供读请求,比如搜索或者从别的shard取回文档。当索引创建完成的时候,主分片的数量就固定了,但是复制分片的数量可以随时调整。

2.2.3 集群横向扩展

假设当前3个分片,一个分片复制集,扩展过程如下,只有一台机器:

154933_KRqV_3291001.png

两台机器:

155521_Y7iU_3291001.png

三台机器:

155721_XHKw_3291001.png

3. elasticsearch增删改查

3.1增加、覆盖更新相应的记录:

PUT /{index}/{type}/{id}
{
  "field": "value",
  ...
}

如果记录存在则会用新的值覆盖以前的值,如果记录不存在则插入

3.2 按照id查找相应的记录

GET /{index}/{type}/{id}

3.3 测试文档是否存在

想做的只是检查文档是否存在——你对内容完全不感兴趣——使用HEAD方法来代替GET。HEAD请求不会返回响应体,只有HTTP头:

curl -i -XHEAD http://localhost:9200/{index}/{type}/{id}

Elasticsearch将会返回200 OK状态如果你的文档存在; 如果不存在返回404 Not Found

3.4 删除文档

DELETE /{index}/{type}/{id} 

如果文档被找到,Elasticsearch将返回200 OK状态码

3.5 版本控制

当elasticsearch中的数据发生变化的时候,相应的数据版本也会增加;当同时存在多个请求共同修改一个文档的时候,就需要通过版本控制保证修改数据的正确性,也就是在修改数据的时候:

1.先拿到当前数据的版本version=1

2.修改数据的时候带上当前的版本作为参数,例如:

PUT /website/blog/1?version=1
{
  "title": "My first blog entry",
  "text":  "Starting to get the hang of this..."
}

如果在修改数据的时候,数据版本不是1,会报

{
  "error" : "VersionConflictEngineException[[website][2] [blog][1]:
             version conflict, current [2], provided [1]]",
  "status" : 409
}

3.6 局部更新

PUT /website/blog/1/_update
{
  "title": "My first blog entry",
  "text":  "Starting to get the hang of this..."
}

 3.7 检索多个文档

使用mget将index、type、_id放入一个数组中请求,例子:

POST /_mget
{
   "docs" : [
      {
         "_index" : "website",
         "_type" :  "blog",
         "_id" :    2
      },
      {
         "_index" : "website",
         "_type" :  "pageviews",
         "_id" :    1,
         "_source": "views"
      }
   ]
}

3.8 批量操作文档

示例(每一行的行末都要加入\n):

POST /_bulk
{ "delete": { "_index": "website", "_type": "blog", "_id": "123" }}\n
{ "create": { "_index": "website", "_type": "blog", "_id": "123" }}\n
{ "title":    "My first blog post" }\n
{ "index":  { "_index": "website", "_type": "blog" }}\n
{ "title":    "My second blog post" }\n
{ "update": { "_index": "website", "_type": "blog", "_id": "123", "_retry_on_conflict" : 3} }\n
{ "doc" : {"title" : "My updated blog post"} }\n

4.elasticsearch分布式增删改查

4.1路由文档

当你索引一个文档,它被存储在单独一个主分片上。Elasticsearch是如何知道文档属于哪个分片的呢?当你创建一个新文档,它是如何知道是应该存储在分片1还是分片2上的呢?进程不能是随机的,因为我们将来要检索文档。事实上,它根据一个简单的算法决定:

shard = hash(routing) % number_of_primary_shards

routing值是一个任意字符串,它默认是_id但也可以自定义。这个routing字符串通过哈希函数生成一个数字,然后除以主切片的数量得到一个余数(remainder),余数的范围永远是0到number_of_primary_shards - 1,这个数字就是特定文档所在的分片。

5.搜索

5.1 空搜索

Get /{index}/{type}/_search

返回hits代表搜索的记录数

分页需要传递from和size,from表示跳过开始的结果数,size代表请求多少个结果,例如:

/_search?from=5&size=10代表跳过5个开头的结果,请求10个结果,但是深度分页通常效率比较低

为了理解为什么深度分页是有问题的,让我们假设在一个有5个主分片的索引中搜索。当我们请求结果的第一页(结果1到10)时,每个分片产生自己最顶端10个结果然后返回它们给请求节点(requesting node),它再排序这所有的50个结果以选出顶端的10个结果。

现在假设我们请求第1000页——结果10001到10010。工作方式都相同,不同的是每个分片都必须产生顶端的10010个结果。然后请求节点排序这50050个结果并丢弃50040个

5.2 简易搜索

将所有参数通过查询字符串定义,完全匹配的例子如下:

GET /_all/tweet/_search?q=tweet:elasticsearch

上面查询语句的意思表示查询tweet字段为elasticsearch

下一个语句查找name字段中包含"john"和tweet字段包含"mary"的结果。实际的查询只需要:

q=+name:john+tweet:mary

6.映射和分析

6.1映射

为了能够把日期字段处理成日期,把数字字段处理成数字,把字符串字段处理成全文本(Full-text)或精确的字符串值,Elasticsearch需要知道每个字段里面都包含了什么类型。这些类型和字段的信息存储(包含)在映射(mapping)中。 索引中每个文档都有一个类型(type)。 每个类型拥有自己的映射(mapping)或者模式定义(schema definition)。

6.1.1查看映射

示例: 查看索引gb类型tweet中的映射:

GET /gb/_mapping/tweet
{
   "gb": {
      "mappings": {
         "tweet": {
            "properties": {
               "date": {
                  "type": "date",
                  "format": "strict_date_optional_time||epoch_millis"
               },
               "name": {
                  "type": "string"
               },
               "tweet": {
                  "type": "string"
               },
               "user_id": {
                  "type": "long"
               }
            }
         }
      }
   }
}

对于string类型的字段,两个最重要的映射参数是index和analyzer,index参数控制字符串以何种方式被索引。它包含以下三个值当中的一个:

194802_v7AV_3291001.png

string类型字段默认值是analyzed。如果我们想映射字段为确切值,我们需要设置它为not_analyzed

6.1.2 创建一个新索引

PUT /gb 
{
  "mappings": {
    "tweet" : {
      "properties" : {
        "tweet" : {
          "type" :    "string",
          "analyzer": "english"
        },
        "date" : {
          "type" :   "date"
        },
        "name" : {
          "type" :   "string"
        },
        "user_id" : {
          "type" :   "long"
        }
      }
    }
  }
}

6.1.3 修改索引

PUT /gb/_mapping/tweet
{
  "properties" : {
    "tag" : {
      "type" :    "string",
      "index":    "not_analyzed"
    }
  }
}

6.2 分析

一个分析器(analyzer)只是一个包装用于将三个功能放到一个包里:

6.2.1 字符过滤器

首先字符串经过字符过滤器(character filter),它们的工作是在标记化前处理字符串。字符过滤器能够去除HTML标记,或者转换"&"为"and"。

6.2.2 分词器

下一步,分词器(tokenizer)被标记化成独立的词。一个简单的分词器(tokenizer)可以根据空格或逗号将单词分开(译者注:这个在中文中不适用)。

6.2.3 标记过滤

最后,每个词都通过所有标记过滤(token filters),它可以修改词(例如将"Quick"转为小写),去掉词(例如停用词像"a"、"and"、"the"等等),或者增加词(例如同义词像"jump"和"leap")

7.结构化查询

7.1查询与过滤

7.1.1 过滤是精确匹配的过程

一条过滤语句会询问每个文档的字段值是否包含着特定值,例如:

created 的日期范围是否在 2013 到 2014 ?
status 字段中是否包含单词 "published" ?
lat_lon 字段中的地理位置与目标点相距是否不超过10km ?

7.1.2 如果索引使用分析器,查询会利用分析器查询匹配度;否则精确匹配

查询语句会询问每个文档的字段值与特定值的匹配程度如何?
查询语句的典型用法是为了找到文档:
    查找与 full text search 这个词语最佳匹配的文档
    查找包含单词 run ,但是也包含runs, running, jog 或 sprint的文档
    同时包含着 quick, brown 和 fox --- 单词间离得越近,该文档的相关性越高
    标识着 lucene, search 或 java --- 标识词越多,该文档的相关性越高
一条查询语句会计算每个文档与查询语句的相关性,会给出一个相关性评分 _score,并且 按照相关性对匹配到的文档进行排序。 这种评分方式非常适用于一个没有完全配置结果的全文本搜索。

7.2 结构化查询

7.2.1 简单的结构化查询

GET /_search
{
    "query": {
        "match": {
            "tweet": "elasticsearch"
        }
    }
}

7.2.2 简单合并多子句

GET /_search
{
    "bool": {
        "must":     { "match": { "tweet": "elasticsearch" }},
        "must_not": { "match": { "name":  "mary" }},
        "should":   { "match": { "tweet": "full text" }}
    }
}

以下实例查询的是邮件正文中含有“business opportunity”字样的星标邮件或收件箱中正文中含有“business opportunity”字样的非垃圾邮件:

{
    "bool": {
        "must": { "match":      { "email": "business opportunity" }},
        "should": [
             { "match":         { "starred": true }},
             { "bool": {
                   "must":      { "folder": "inbox" }},
                   "must_not":  { "spam": true }}
             }}
        ],
        "minimum_should_match": 1
    }
}

7.3 过滤语句

term主要用于精确匹配哪些值,比如数字,日期,布尔值或 not_analyzed的字符串(未经分析的文本数据类型)

7.3.1 普通term语句例子

{ "term": { "age":    26           }}

7.3.2 terms过滤

terms 跟 term 有点类似,但 terms 允许指定多个匹配条件。 如果某个字段指定了多个值,那么文档需要一起去做匹配:

{
    "terms": {
        "tag": [ "search", "full_text", "nosql" ]
        }
}

7.3.3 range过滤

range过滤允许我们按照指定范围查找一批数据:

{
    "range": {
        "age": {
            "gte":  20,
            "lt":   30
        }
    }
}

7.3.4 exists 和 missing 过滤

exists 和 missing过滤可以用于查找文档中是否包含指定字段或没有某个字段,类似于SQL语句中的IS_NULL条件

{
    "exists":   {
        "field":    "title"
    }
}

7.3.5 bool过滤

bool 过滤可以用来合并多个过滤条件查询结果的布尔逻辑,它包含一下操作符:
must :: 多个查询条件的完全匹配,相当于 and。
must_not :: 多个查询条件的相反匹配,相当于 not。
should :: 至少有一个查询条件匹配, 相当于 or。
这些参数可以分别继承一个过滤条件或者一个过滤条件的数组

{
    "bool": {
        "must":     { "term": { "folder": "inbox" }},
        "must_not": { "term": { "tag":    "spam"  }},
        "should": [
                    { "term": { "starred": true   }},
                    { "term": { "unread":  true   }}
        ]
    }
}

7.4 过滤查询

 

查询语句和过滤语句可以放在各自的上下文中。 在 ElasticSearch API 中我们会看到许多带有 query 或 filter 的语句。 这些语句既可以包含单条 query 语句,也可以包含一条 filter 子句。 换句话说,这些语句需要首先创建一个query或filter的上下文关系。
复合查询语句可以加入其他查询子句,复合过滤语句也可以加入其他过滤子句。 通常情况下,一条查询语句需要过滤语句的辅助,全文本搜索除外。
所以说,查询语句可以包含过滤子句,反之亦然。 以便于我们切换 query 或 filter 的上下文。这就要求我们在读懂需求的同时构造正确有效的语句

7.4.1 过滤一条查询语句

比如说我们有这样一条查询语句:

{
    "match": {
        "email": "business opportunity"
    }
}

然后我们想要让这条语句加入 term 过滤,在收信箱中匹配邮件:

{
    "term": {
        "folder": "inbox"
    }
}

search API中只能包含 query 语句,所以我们需要用 filtered 来同时包含 "query" 和 "filter" 子句:

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

我们在外层再加入 query 的上下文关系:

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

7.4.2 单条过滤语句

在 query 上下文中,如果你只需要一条过滤语句,比如在匹配全部邮件的时候,你可以 省略 query 子句:

GET /_search
{
    "query": {
        "filtered": {
            "filter":   { "term": { "folder": "inbox" }}
        }
    }
}

7.4.3 查询语句中的过滤

有时候,你需要在 filter 的上下文中使用一个 query 子句。下面的语句就是一条带有查询功能 的过滤语句, 这条语句可以过滤掉看起来像垃圾邮件的文档:

GET /_search
{
    "query": {
        "filtered": {
            "filter":   {
                "bool": {
                    "must":     { "term":  { "folder": "inbox" }},
                    "must_not": {
                        "query": {
                            "match": { "email": "urgent business proposal" }
                        }
                    }
                }
            }
        }
    }
}

7.5 排序

7.5.1 字段值排序

下面例子中,对结果集按照时间排序,这也是最常见的情形,将最新的文档排列靠前。 我们使用 sort 参数进行排序:

GET /_search
{
    "query" : {
        "filtered" : {
            "filter" : { "term" : { "user_id" : 1 }}
        }
    },
    "sort": { "date": { "order": "desc" }}
}

转载于:https://my.oschina.net/seektechnology/blog/1504517

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值