Bootstrap

Elasticsearch 安装

Elasticsearch 是一个搜索服务器,特点:分布式、易于扩展、全文检索、索引速度快。 
本篇文章主要介绍 Elasticsearch 的安装和基本使用,假定你有一定的Linux基础(所有命令均在命令行中执行)。

Elasticsearch 版本:2.2.0
服务器:CentOS 6.4 (Win7 下的虚拟机)

一、安装

因为 Elasticsearch 是 Java 开发的,所以要先安装 Java
可用 java -version来查 看是否已安装Java 
若没有安装,且jdk 在 /usr/local/src/jdk-7u79-linux-x64.gz 目录下

tar zxvf /usr/local/src/jdk-7u79-linux-x64.gz
cp -r /usr/local/src/jdk1.7.0_79/ /usr/local/java

添加 java 环境变量,保存并退出

vim /etc/profile
#在最后面添加
export JAVA_HOME=/usr/local/java
export PATH=$JAVA_HOME/bin:$PATH 
export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar

执行 source /etc/profile 使更改生效,再使用 java -version 看一下java 版本

安装 Elasticsearch ,假定安装包在 /usr/local/src/elasticsearch-2.2.0.tar.gz 目录下


curl -L -O https://download.elasticsearch.org/elasticsearch/elasticsearch/elasticsearch-1.4.2.tar.gz

tar -zxvf elasticsearch-2.2.0.tar.gz -C /usr/local

这样在 /usr/local 下就有一个 elasticsearch-2.2.0 的目录 
Elasticsearch 的配置文件在 /usr/local/elasticsearch-2.2.0/config/elasticsearch.yml 
打开配置文件,加入如下配置(可以不用配置)

#集群名称,若有多台服务器
cluster.name: cluster-test
#节点名称,本服务器的名称
node.name: node-136
#监听端口,默认为 9200
http.port: 9200

 
 
  1. # vi /usr/local/elasticsearch/config/elasticsearch.yml  
  2. # 注意冒号后有空格  
  3. http.port: 9200  
  4. node.name: node-1  
  5. cluster.name: es_cluster  
  6. network.host: 192.168.1.222  
  7. bootstrap.memory_lock: false  
  8. path.data: /usr/local/elasticsearch/data  
  9. path.logs: /usr/local/elasticsearch/logs 



打开端口,Elasticsearch 默认监听9200 端口,可在 elasticsearch.yml 中修改

#打开 iptables
vim /etc/sysconfig/iptables
#加入
-A INPUT -p tcp -m state --state NEW -m tcp --dport 9200 -j ACCEPT
-A INPUT -p udp -m state --state NEW -m udp --dport 9200 -j ACCEPT
#重启生效
service iptables restart

启动 Elasticsearch。为安全考虑,Elasticsearch不允许 root 启动,所以你要先创建一个用于启动 Elasticsearch 的用户,并将 elasticsearch-2.2.0 文件的所有者赋予该用户 
假如你已经创建了一个 elastic 的用户(我创建的用户名是 jam)

chown -R elastic:elastic /usr/local/elasticsearch-2.2.0/

启动(-d 表示后台运行)

/usr/local/elasticsearch-2.2.0/bin/elasticsearch -d

查看是否启动

ps aux |grep elastic
#有如下信息表示成功
jam      44292  3.4 17.4 2088296 176320 pts/1  Sl   17:37   0:05 /usr/bin/java -Xms256m -Xmx1g -Djava.awt.headless=true -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly -XX:+HeapDumpOnOutOfMemoryError -XX:+DisableExplicitGC -Dfile.encoding=UTF-8 -Djna.nosys=true -Des.path.home=/usr/local/elasticsearch-2.2.0 -cp /usr/local/elasticsearch-2.2.0/lib/elasticsearch-2.2.0.jar:/usr/local/elasticsearch-2.2.0/lib/* org.elasticsearch.bootstrap.Elasticsearch start -d

停止 Elasticsearch

# 40952 pid,就是上面信息中的 第二个数据
kill -9 44292

二、使用

1、基本概念 
传统关系型数据库(如 MySQL)与 Elasticsearch 对比

Relational DBElasticsearch释义
Databases Indices 索引(名词)即数据库
Tables Types 类型即表名
Rows Documents 文档即每行数据
Columns Fields 字段

2、与 Elasticsearch 通信 
Elasticsearch 支持 RESTful API的访问方式,这里我们使用 curl 访问和操作Elasticsearch 
官方也有很多对应语言的 API 供大家选择 
PHP-API 
JAVA-API 
Python-API

初探:查看 Elasticsearch 的基本信息

#pretty 参数使返回的结果格式化更易读
curl 'http://localhost:9200/?pretty'
#返回
{
  "name" : "node-136",
  "cluster_name" : "cluster-test",
  "version" : {
    "number" : "2.2.0",
    "build_hash" : "8ff36d139e16f8720f2947ef62c8167a888992fe",
    "build_timestamp" : "2016-01-27T13:32:39Z",
    "build_snapshot" : false,
    "lucene_version" : "5.4.1"
  },
  "tagline" : "You Know, for Search"
}

简单介绍一下 curl 命令

curl -X<VERB> '<PROTOCOL>://<HOST>:<PORT>/<PATH>?<QUERY_STRING>' -d '<BODY>'
VERB        HTTP方法:GET, POST, PUT, HEAD, DELETE
PROTOCOL    http或者https协议(只有在Elasticsearch前面有https代理的时候可用)
HOST        Elasticsearch集群中的任何一个节点的主机名
PORT        Elasticsearch HTTP服务所在的端口,默认为9200
PATH        API路径,资源路径(例如_count将返回集群中文档的数量)
QUERY_STRING    一些可选的查询请求参数,例如?pretty参数将返回易读的JSON数据
BODY        一个JSON格式的请求主体(如果请求需要的话)

3、elasticsearch-head 插件 
这里介绍一个 Elasticsearch 可视化的管理插件 elasticsearch-head,可方便的查看,删除,管理数据(生产环境不推荐安装) 
root 安装

cd /usr/local/elasticsearch-2.2.0/bin/
./plugin install mobz/elasticsearch-head

访问(linux 有桌面系统带浏览器的可直接访问):http://127.0.0.1:9200/_plugin/head/ 
那外网如何访问呢,使用 Nginx 代理,修改Nginx 配置文件加入

server
    {
        listen 8080;
        #192.168.1.136 是我虚拟机的 ip 地址
        server_name 192.168.1.136;
        location / {
            #http://127.0.0.1:9200/ 是后端代理的地址
            proxy_pass http://127.0.0.1:9200/;
            proxy_redirect off;
            proxy_set_header   Host             $host;
            proxy_set_header   X-Real-IP        $remote_addr;
            proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
        }
    }

重启nginx,在windows下访问 http://192.168.1.136:8080/_plugin/head/,界面如下 
还未创建索引的情况
可以查看集群信息,节点信息,索引信息…都是以json 形式显示这些信息

4、创建、查看索引 
现在我们来添加一个名叫 test 的索引(可理解为数据库)

curl -XPUT 'http://localhost:9200/test?pretty'
#返回
{
  "acknowledged" : true
}

创建成功后,默认会分配五个主分片(创建后不可更改,通过算法将数据存放在这五个分片中的一个,增删改都是针对主分片的)和一个复制分片(可修改,每个主分片都对应一个复制分片),这两个默认值都可以在配置文件中修改,也可以在创建的时候指定,如

curl -XPUT 'http://localhost:9200/test?pretty' -d '{
    "settings": {
        "number_of_shards" : 2,     #2个主分片
        "number_of_replicas" : 0    #0个复制分片
    }
}'

查看索引

curl -XGET 'http://localhost:9200/test?pretty'

5、创建、查看 类型 
创建一个名叫 article 的类型(即表名),有这几个字段,id、subject、content、author

curl -XPUT 'http://localhost:9200/test/_mapping/article?pretty' -d '{
    "properties": {
        "id": {
            "type":      "integer",
            "index":     "not_analyzed"
        },
        "subject": {
            "type":      "string",
            "analyzer":  "standard"
        },
        "content": {
            "type":      "string",
            "analyzer":  "standard"
        },
        "author": {
            "type":      "string",
            "index":     "not_analyzed"
        }
    }
}'

type:是指定字段的数据类型 
index:有三个选项 analyzed(当成全文来搜索),not_analyzed(当成一个准确的值),no(完全不可被搜索) 
analyzer:索引和搜索时全文字段(即此字段)使用的分析器,standard 为 Elasticsearch 默认的全文字段分析器(对中文不友好,可使用 ik 代替) 
Elasticsearch 字段类型和对应的分类类型

分类类型数据类型
String string
Whole number byte , short , integer , long
Floating point float , double
Boolean boolean
Date date

查看刚才新增的类型

curl -XGET 'http://localhost:9200/test/article/_mapping/?pretty'
#返回
{
  "test" : {
    "mappings" : {
      "article" : {
        "properties" : {
          "author" : {
            "type" : "string",
            "index" : "not_analyzed"
          },
          "content" : {
            "type" : "string",
            "analyzer" : "standard"
          },
          "id" : {
            "type" : "integer"
          },
          "subject" : {
            "type" : "string",
            "analyzer" : "standard"
          }
        }
      }
    }
  }
}

6、创建、查看、修改、删除 文档 
创建一条数据 
指定 _id 的创建

curl -XPUT 'http://localhost:9200/test/article/1?pretty' -d '{
    "id": 1,
    "subject": "第一篇文章标题",
    "content": "第一篇文章内容",
    "author": "jam"
}'
#返回
{
  #文档所在的索引
  "_index" : "test",
  #文档的类型名
  "_type" : "article",
  #文档的字符串 ID,在请求链接中指定的1,若不指定,会自动生成
  "_id" : "1",
  #文档版本号,修改或删除都会增加
  "_version" : 1,
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  #表示创建成功
  "created" : true
}

自增 _id 的创建

curl -XPOST 'http://localhost:9200/test/article?pretty' -d '{
    "id": 2,
    "subject": "第二篇文章标题",
    "content": "第二篇文章内容",
    "author": "jam"
}'
#返回
...
"_id" : "AVf_6fM1vEkwGPLuUJqp",
...

查看指定文档

curl -XGET 'http://localhost:9200/test/article/1?pretty'
#返回
{
  "_index" : "test",
  "_type" : "article",
  "_id" : "1",
  "_version" : 1,
  "found" : true,
  "_source" : {
    "id" : 1,
    "subject" : "第一篇文章标题",
    "content" : "第一篇文章内容",
    "author" : "jam"
  }
}

查看所有文档(_search)

curl -XGET 'http://localhost:9200/test/article/_search?pretty'

更新文档(_update),局部更新(更新指定字段)

curl -XPOST 'http://localhost:9200/test/article/1/_update?pretty' -d '
{
    "doc" : {
        "content" : "第一篇文章内容-更新后"
    }
}'
#返回
{
  "_index" : "test",
  "_type" : "article",
  "_id" : "1",
  "_version" : 2,
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  }
}

_version 版本号变为 2

删除文档

curl -XDELETE 'http://localhost:9200/test/article/1?pretty'
#返回
{
  "found" : true,
  "_index" : "test",
  "_type" : "article",
  "_id" : "1",
  "_version" : 3,
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  }
}

_version 版本号变为 3,说明删除是不会真正删除文档的

7、批量操作——bulk API 介绍 
先看一个批量创建文档的例子

curl -XPOST 'http://localhost:9200/test/article/_bulk?pretty' -d '
{ "index" : { "_id" : "3" } }
{ "id" : 3,"subject" : "第三篇文章标题","content" : "第三篇文章内容" ,"author": "jam"}
{ "index" : { "_id" : "4" } }
{ "id" : 4,"subject" : "第四篇文章标题","content" : "第四篇文章内容" ,"author": "tomi"}
'
#返回
{
  "took" : 36,
  "errors" : false,
  "items" : [ {
    "index" : {
      "_index" : "test",
      "_type" : "article",
      "_id" : "3",
      "_version" : 1,
      "_shards" : {
        "total" : 2,
        "successful" : 1,
        "failed" : 0
      },
      "status" : 201
    }
  }, {
    "index" : {
      "_index" : "test",
      "_type" : "article",
      "_id" : "4",
      "_version" : 1,
      "_shards" : {
        "total" : 2,
        "successful" : 1,
        "failed" : 0
      },
      "status" : 201
    }
  } ]
}

bulk API允许我们使用单一请求来实现多个文档的 create(文档不存在时才创建) 、 index(创建新文档或替换已有文档) 、 update 或 delete 
它的请求体格式如下(也就是 -d 后面的数据)

{ action: { metadata }}\n
{ request body }\n
{ action: { metadata }}\n
{ request body }\n

action 有四个选项,即 create 、 index 、 update 、 delete 
metadata 为元数据,即是 _index 、_type、_id,上面的例子中,因为我在请求链接中指定了 _index (test)和_type(article),所以我只需指定 _id 
request body 这里就是每条数据的具体内容,字段名和数据一一对应就可以了 
需要注意的是每行必须以 “\n” 符号结尾, 包括最后一行。直接敲回车键就可以了 
可以在一条 bulk 的请求体中既执行 index(创建文档),又执行 update(更新文档),还可以执行 delete(删除文档),请求体类似如下

{ "delete": { "_index": "test", "_type": "article", "_id": "1" }}
{ "create": { "_index": "test", "_type": "article", "_id": "123" }}
{ "id" : 123,"subject" : "第123篇文章标题","content" : "第123篇文章内容" ,"author": "jam123"}
{ "index": { "_index": "test", "_type": "article", "_id": "3" }}
{ "id" : 3,"subject" : "第三篇文章标题-替换后","content" : "第三篇文章内容-替换后" ,"author": "jam0"}
{ "update": { "_index": "test", "_type": "article", "_id": "4"} }
{ "doc" : {"content" : "第四篇文章内容-更新后"} }

delete 操作没有请求体,所以后面紧跟另一个操作

三、Elasticsearch 的 DSL(特定领域语言) 查询

以 json 请求体的形式查询数据 
1、基本查询 
查询所有文档 
等同于 curl -XGET ‘http://localhost:9200/test/article/_search?pretty

curl -XGET 'http://localhost:9200/test/article/_search?pretty' -d '
{
    "query": {
        "match_all": {}
    }
}'
#返回
{
  # 用时 毫秒
  "took" : 4,
  "timed_out" : false,
  #分片信息
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "failed" : 0
  },
  "hits" : {
    #文档数
    "total" : 3,
    "max_score" : 1.0,
    "hits" : [ {
      "_index" : "test",
      "_type" : "article",
      "_id" : "AVf_6fM1vEkwGPLuUJqp",
      "_score" : 1.0,
      "_source" : {
        "id" : 2,
        "subject" : "第二篇文章标题",
        "content" : "第二篇文章内容",
        "author" : "jam"
      }
    }, {
      "_index" : "test",
      "_type" : "article",
      "_id" : "4",
      "_score" : 1.0,
      "_source" : {
        "id" : 4,
        "subject" : "第四篇文章标题",
        "content" : "第四篇文章内容-更新后",
        "author" : "tomi"
      }
    }, {
      "_index" : "test",
      "_type" : "article",
      "_id" : "3",
      "_score" : 1.0,
      "_source" : {
        "id" : 3,
        "subject" : "第三篇文章标题",
        "content" : "第三篇文章内容",
        "author" : "jam"
      }
    } ]
  }
}

查询作者是名字包含 “jam” 的文档,返回 id 是 2和3 的文档

curl -XGET 'http://localhost:9200/test/article/_search?pretty' -d '
{
    "query": {
        "match": {
            "author": "jam"
        }
    }
}'

查询文章内容包含 “更新” 的文档,返回 id 是 4 的文档

curl -XGET 'http://localhost:9200/test/article/_search?pretty' -d '
{
    "query": {
        "match": {
            "content": "更新"
        }
    }
}'

2、过滤语句 
1> term 过滤,主要用于精确匹配哪些值,比如数字,日期,布尔值或 not_analyzed 的字符串(未经分析的文本数据类型) 
查询作者是 jam 的文档,返回 id 是 2 和 3 的文档

curl -XGET 'http://localhost:9200/test/article/_search?pretty' -d '
{
    "query": {
        "term": {
            "author": "jam"
        }
    }
}'

2> terms 过滤,跟 term 有点类似,但 terms 允许指定多个匹配条件 
查询作者是 jam 、tomi 的文档,返回 id 是2,3,4 的文档

curl -XGET 'http://localhost:9200/test/article/_search?pretty' -d '
{
    "query": {
        "terms": {
            "author": ["jam","tomi"]
        }
    }
}'

3> range 过滤,指定范围查找 
查找 id 范围是 大于等于2,小余4 的文档,返回 id 是2,3 的文档

curl -XGET 'http://localhost:9200/test/article/_search?pretty' -d '
{
    "query": {
        "range": {
            "id": {
                "gte":  2,
                "lt":  4
            }
        }
    }
}'

gt:大于 
gte :大于等于 
lt:小于 
lte:小于等于

4> bool 过滤 
可以合并多个过滤条件查询结果的布尔逻辑,它有三个操作符

操作符释义
must 多个查询条件的完全匹配,相当于 and
must_not 多个查询条件的相反匹配,相当于 not
should 至少有一个查询条件匹配, 相当于 or

查找作者是 jam (查出 id 为 2,3 的文档),但是 id 不能为 2 的文档,返回 id 为 3的文档(should 就自己测试吧)

curl -XGET 'http://localhost:9200/test/article/_search?pretty' -d '
{
    "query": {
        "bool": {
            "must": { "term": { "author": "jam" }},
            "must_not": { "term": { "id": 2 }}
        }
    }
}'

提示:做精确匹配搜索时,你最好用过滤语句,因为过滤语句可以缓存数据

3、查询语句 
1> multi_match 查询,同时搜索多个字段 
查询 subject 或者 content 字段中有 “更新” 二字的文档,返回 id 为 4 的文档,由于用的分词器是 standard ,它会把 “更新” 拆成 “更”、”新” 来查找

curl -XGET 'http://localhost:9200/test/article/_search?pretty' -d '
{
    "query": {
        "multi_match": {
            "query": "更新",
            "fields": ["subject", "content"]
        }
    }
}'

2> bool 查询 
与 bool 过滤相似,用于合并多个查询子句。 
查询 内容中带有 “第” ,但是不带有 “新” 的文档,返回 id 为 2,3 的文档

curl -XGET 'http://localhost:9200/test/article/_search?pretty' -d '
{
    "query": {
        "bool": {
            "must": { "match": { "content": "" }},
            "must_not": { "match": { "content": "" }}
        }
    }
}'

3、复合查询 
介绍完过滤语句和查询语句,现在我们就来将它们两个组合起来使用 
请求体格式如下

{
    "query" : {
        "filtered" : {
            "query" : {
                "match_all" : {}
            },
            "filter" : {
                "term" : {
                    "author" : "jam" }
            }
        }
    }
}

filtered 中可以只包含 query 或 filter 或者两者都存在,可存在多个 filter ,若不指定���询范围(query),默认为 “match_all” : {} 
query 中可包含 bool 查询或match 查询 
filter 中可包含各种过滤查询

现在来看一个比较复杂的例子

curl -XGET 'http://localhost:9200/test/article/_search?pretty' -d '
{
    "query": {
        "filtered": {
            "query" : {
                "match_all" : {}
            },
            "filter" : {
                "range": {
                    "id": {
                        "gte":  2,
                        "lte":  4
                    }
                }
            },
            "filter" : {
                "bool": {
                    "must": { "term": { "author": "jam" }},
                    "must_not": { "term": { "id": 2 }}
                }
            }
        }
    }
}'

首先是查询所有数据(可省略),可改成有条件的查询

"query" : {
    "match_all" : {}
},

然后进行过滤查询,查询 id 范围 大于等于 2 小于等于 4 的文档

"filter" : {
    "range": {
        "id": {
            "gte":  2,
            "lte":  4
        }
    }
},

最后再过滤,查询作者是 jam ,但是 id 不能是 2 的文档,这样就只有 id 为 3 的文档符合条件

"filter" : {
    "bool": {
        "must": { "term": { "author": "jam" }},
        "must_not": { "term": { "id": 2 }}
    }
}

这条复合查询类似如下 SQL 语句

SELECT * FROM article WHERE id >= 2 AND id <= 4 AND author = ‘jam’ AND id != 2

4、 验证查询语句 
查询语句的请求体越来越大,很容易在书写过程中出现错误,可以使用 validate API 的explain 进行验证 
使用上面的复合查询语句(注意请求链接)

curl -XGET 'http://localhost:9200/test/article/_validate/query?explain&pretty' -d '
{
    "query": {
        "filtered": {
            "query" : {
                "match_all" : {}
            },
            "filter" : {
                "range": {
                    "id": {
                        "gte":  2,
                        "lte":  4
                    }
                }
            },
            "filter" : {
                "bool": {
                    "must": { "term": { "author": "jam" }},
                    "must_not": { "term": { "id": 2 }}
                }
            }
        }
    }
}'
#返回,真是正确的
{
  "valid" : true,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "failed" : 0
  },
  "explanations" : [ {
    "index" : "test",
    "valid" : true,
    "explanation" : "+(+*:* #(+author:jam -id:`\b\u0000\u0000\u0000\u0002)) #ConstantScore(+ConstantScore(_type:article))"
  } ]
}
# 将第一个filter 上面的逗号去掉,再验证。错误信息中会提示第几行,第几列出错,方便修改错误
{
  "valid" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "failed" : 0
  },
  "explanations" : [ {
    "index" : "test",
    "valid" : false,
    "error" : "[test] QueryParsingException[Failed to parse]; nested: JsonParseException[Unexpected character ('\"' (code 34)): was expecting comma to separate OBJECT entries\n at [Source: org.elasticsearch.transport.netty.ChannelBufferStreamInput@3175024d; line: 8, column: 14]];; com.fasterxml.jackson.core.JsonParseException: Unexpected character ('\"' (code 34)): was expecting comma to separate OBJECT entries\n at [Source: org.elasticsearch.transport.netty.ChannelBufferStreamInput@3175024d; line: 8, column: 14]"
  } ]
}

分析match 查询(explanation)

curl -XGET 'http://localhost:9200/test/article/_validate/query?explain&explanation&pretty' -d '
{
    "query": {
        "match": {
            "content": "更新"
        }
    }
}'
#返回
{
  "valid" : true,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "failed" : 0
  },
  "explanations" : [ {
    "index" : "test",
    "valid" : true,
    "explanation" : "+(content:更 content:新) #ConstantScore(+ConstantScore(_type:article))"
  } ]
}

explanations 中描述了 ES 将 “更新” 拆成 “更”、”新” 来查找

5、排序 
默认情况下,结果集以 _score(相关性评分) 进行倒序排列(值越高越靠前) 
可以使用 sort 指定排序字段 
查询 内容中包含 “第二更新” 的文档,并按照 id 倒序排列

curl -XGET 'http://localhost:9200/test/article/_search?pretty' -d '
{
    "query": {
        "match": {
            "content": "第二更新"
        }
    },
    "sort": { 
        "id": { 
            "order": "desc" 
        }
    }
}'
#返回
{
  "took" : 96,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "failed" : 0
  },
  "hits" : {
    "total" : 3,
    "max_score" : null,
    "hits" : [ {
      "_index" : "test",
      "_type" : "article",
      "_id" : "4",
      "_score" : null,
      "_source" : {
        "id" : 4,
        "subject" : "第四篇文章标题",
        "content" : "第四篇文章内容-更新后",
        "author" : "tomi"
      },
      "sort" : [ 4 ]
    }, {
      "_index" : "test",
      "_type" : "article",
      "_id" : "3",
      "_score" : null,
      "_source" : {
        "id" : 3,
        "subject" : "第三篇文章标题",
        "content" : "第三篇文章内容",
        "author" : "jam"
      },
      "sort" : [ 3 ]
    }, {
      "_index" : "test",
      "_type" : "article",
      "_id" : "AVf_6fM1vEkwGPLuUJqp",
      "_score" : null,
      "_source" : {
        "id" : 2,
        "subject" : "第二篇文章标题",
        "content" : "第二篇文章内容",
        "author" : "jam"
      },
      "sort" : [ 2 ]
    } ]
  }
}

每个结果中多了个 sort 字段,就是以此来排序的,而 _score 的值为 null ,是因为计算 _score 是比较消耗性能的,而它通常主要用作排序,既然已经指定排序字段了,就不会计算 _score ,若要强制计算 _score ,可加入如下内容(与 query ,sort 同级)

"track_scores" : true

多级排序 
按照 _score 倒序排序,若分数相同,再按照 id 倒序排序;因为使用了 _score 排序,所以会计算出 _score ,而不需要使用 “track_scores” : true

curl -XGET 'http://localhost:9200/test/article/_search?pretty' -d '
{
    "query": {
        "match": {
            "content": "第二更新"
        }
    },
    "sort": [ 
        { "_score": { "order": "desc" }},
        { "id": { "order": "desc" }}
    ]
}'
#返回
...
"sort" : [ 0.05846126, 4 ]
...
"sort" : [ 0.023869118, 2 ]
...
"sort" : [ 0.0050183414, 3 ]
...

这里简单介绍一下 _score 相关性评分值是如何得来的,这就涉及到了 ElasticSearch 的相似度算法( TF/IDF),即检索词频率/反向文档频率,它包括 
检索词频率:检索词在该字段出现的频率,出现频率越高,相关性也越高 
反向文档频率:一个词在所有文档中出现的频率,出现的越频繁,分数越低,因为像 “的” “我” 这类字出现得很频繁,对相关性贡献很低,相反,如 “ElasticSearch” 这类少见的词对相关性贡献很大,分数也就会越高
字段长度:字段越短,其权重就越高。检索词出现在一个较短的字段中比出现在一个较长的字段中所占的权重越高 
参考文档

我们可以使用 explain 来查看相关性评分 _score 的计算过程,将上面的请求链接改成 http://localhost:9200/test/article/_search?explain&pretty 请求体不变 
这里截取一段返回值做解释

# 截取 id 为 2 的一段分析
{
  "value" : 0.023869118,
  # 分析"第""description" : "weight(content:第 in 0) [PerFieldSimilarity], result of:",
  "details" : [ {
    "value" : 0.023869118,
    "description" : "score(doc=0,freq=1.0), product of:",
    "details" : [ {
      "value" : 0.20743163,
      "description" : "queryWeight, product of:",
      "details" : [ {
        "value" : 0.30685282,
        "description" : "idf(docFreq=1, maxDocs=1)",
        "details" : [ ]
      }, {
        "value" : 0.67599714,
        "description" : "queryNorm",
        "details" : [ ]
      } ]
    }, {
      "value" : 0.11506981,
      "description" : "fieldWeight in 0, product of:",
      "details" : [ {
        "value" : 1.0,
        # 检索词频率 检查
        "description" : "tf(freq=1.0), with freq of:",
        "details" : [ {
          "value" : 1.0,
          "description" : "termFreq=1.0",
          "details" : [ ]
        } ]
      }, {
        "value" : 0.30685282,
        # 反向文档频率 检查
        "description" : "idf(docFreq=1, maxDocs=1)",
        "details" : [ ]
      }, {
        "value" : 0.375,
        # 字段长度 检查
        "description" : "fieldNorm(doc=0)",
        "details" : [ ]
      } ]
    } ]
  } ]
}

6、分页与返回指定字段 
分页 
size 返回的结果数 
from 开始数(偏移量) 
先按照 id 字段顺序排序,然后从第二条(from)开始取,取两条(size)数据,返回 id 为 4 的文档

curl -XGET 'http://localhost:9200/test/article/_search?pretty' -d '
{
    "sort" : { "id" : { "order" : "asc"}},
    "from" : 2,
    "size" : 2
}'

返回指定字段的数据。有时候数据字段太多,而我们使用的只是其中的几个字段,我么可以使用 _source 来指定返回的字段 
查询所有数据, 只返回作者和标题

curl -XGET 'http://localhost:9200/test/article/_search?pretty' -d '
{
    "_source" : ["author" ,"subject"]
}'
;