ES基本查詢總結
ES與資料庫比較
查詢操作
Elasticsearch中當我們設定Mapping(分詞器、欄位型別)完畢後,就可以按照設定的方式匯入資料。
有了資料後,我們就需要對資料進行檢索操作。根據實際開發需要,往往我們需要支援包含但不限於以下型別的檢索:
1)精確匹配,類似mysql中的 “=”操作;
2)模糊匹配,類似mysql中的”like %關鍵詞% “查詢操作;
3)字首匹配;
4)萬用字元匹配;
5)正則表示式匹配;
6)跨索引匹配;
7)提升精讀匹配。
細數一下,我們的痛點在於:
1)ES究竟支援哪些檢索操作?
2)如何實現ES精確值檢索、指定索引檢索、全文檢索?
這些就是本文著重參考ES最新官方文件,針對ES5.X版本探討的內容。
0、檢索概覽
檢索子句的行為取決於查詢應用於過濾(filter)上下文還是查詢/分析(query)上下文。
過濾上下文——對應於結構化檢索
1)核心回答的問題是:“這個文件是否符合這個查詢條款?”
2)答案是簡單的是或否,不計算分數。
3)過濾器上下文主要用於過濾結構化資料。類似於Mysql中判定某個欄位是否存在:
例如:
a. 時間戳欄位:是否屬於2015年或2016年?
b. 狀態欄位:是否設定為“已釋出”?
經常使用的過濾器將被Elasticsearch**自動快取,以加快效能**。
分析上下文——對應於全文檢索
1)核心回答了“本文件與此查詢子句是否匹配?”的問題。
2)除了決定文件是否匹配之外,查詢子句還會計算一個_score,表示文件與其他文件的匹配程度。
綜合應用場景如下:
GET /_search { "query": { "bool": { "must": [ { "match": { "title": "Search" }}, { "match": { "content": "Elasticsearch" }} ], "filter": [ { "term": { "status": "published" }}, { "range": { "publish_date": { "gte": "2015-01-01" }}} ] } } }
以上檢索,title中包含”Search”並且content中包含 “Elasticsearch”,status中精確匹配”published”,並且publish_date 大於“2015-01-01”的全部資訊。
以下,以“腦圖”的形式直觀展示檢索分類。
其中,3-7根據我隨著我開發深入再做更新。
以下內容的原文需要參考ES官方文件(隨著版本變化,後續會有更新)
1、結構化檢索
針對欄位型別: 日期、時間、數字型別,以及精確的文字匹配。
結構化檢索特點:
* 1)結構化查詢,我們得到的結果 總是 非是即否,要麼存於集合之中,要麼存在集合之外。
* 2)結構化查詢不關心檔案的相關度或評分;它簡單的對文件包括或排除處理。
1.1 精確值查詢
1.1.1 單個精確值查詢(term query)
term 查詢會查詢我們指定的精確值。term 查詢是簡單的,它接受一個欄位名以及我們希望查詢的數值。
想要類似mysql中如下sql語句的查詢操作:
SELECT document FROM products WHERE price = 20;
DSL寫法:
GET /my_store/products/_search
{
"query" : {
"term" : {
"price" : 20
}
}
}
當進行精確值查詢時, 我們會使用過濾器(filters)。過濾器很重要,因為它們執行速度非常快,不會計算相關度(直接跳過了整個評分階段)而且很容易被快取。如下: 使用 constant_score 查詢以非評分模式來執行 term 查詢並以一作為統一評分。
GET /my_store/products/_search
{
"query" : {
"constant_score" : {
"filter" : {
"term" : {
"price" : 20
}
}
}
}
}
注意:5.xES中,對於字串型別,要進行精確值匹配。需要講型別設定為text和keyword兩種型別。mapping設定如下:
POST testindex/testtype/_mapping
{
"testtype ":{
"properties":{
"title":{
"type":"text",
"analyzer":"ik_max_word",
"search_analyzer":"ik_max_word",
"fields":{
"keyword":{
"type":"keyword"
}
}
}
}
}
精確值java api jest使用方法:
searchSourceBuilder.query(QueryBuilders.termQuery(“text.keyword”, “來自新華社的報道”));
1.1.2 布林過濾器
一個 bool 過濾器由三部分組成:
{
"bool" : {
"must" : [],
"should" : [],
"must_not" : [],
"filter": []
}
}
must ——所有的語句都 必須(must) 匹配,與 AND 等價。
must_not ——所有的語句都 不能(must not) 匹配,與 NOT 等價。
should ——至少有一個語句要匹配,與 OR 等價。
filter——必須匹配,執行在非評分&過濾模式。
就這麼簡單! 當我們需要多個過濾器時,只須將它們置入 bool 過濾器的不同部分即可。
舉例:
GET /my_store/products/_search
{
"query" : {
"filtered" : {
"filter" : {
"bool" : {
"should" : [
{ "term" : {"price" : 20}},
{ "term" : {"productID" : "XHDK-A-1293-#fJ3"}}
],
"must_not" : {
"term" : {"price" : 30}
}
}
}
}
}
}
1.1.3 多個值精確查詢(terms query)
{
"terms" : {
"price" : [20, 30]
}
}
如上,terms是包含的意思,包含20或者包含30。
如下實現嚴格意義的精確值檢索, tag_count代表必須匹配的次數為1。
GET /my_index/my_type/_search
{
"query": {
"constant_score" : {
"filter" : {
"bool" : {
"must" : [
{ "term" : { "tags" : "search" } },
{ "term" : { "tag_count" : 1 } }
]
}
}
}
}
}
1.2 範圍檢索(range query)
range 查詢可同時提供包含(inclusive)和不包含(exclusive)這兩種範圍表示式,可供組合的選項如下:
gt: > 大於(greater than)
lt: < 小於(less than)
gte: >= 大於或等於(greater than or equal to)
lte: <= 小於或等於(less than or equal to)
類似Mysql中的範圍查詢:
SELECT document
FROM products
WHERE price BETWEEN 20 AND 40
ES中對應的DSL如下:
GET /my_store/products/_search
{
"query" : {
"constant_score" : {
"filter" : {
"range" : {
"price" : {
"gte" : 20,
"lt" : 40
}
}
}
}
}
}
1.3 存在與否檢索(exist query)
mysql中,有如下sql:
SELECT tags FROM posts WHERE tags IS NOT NULL;
ES中,exist查詢某個欄位是否存在:
GET /my_index/posts/_search
{
"query" : {
"constant_score" : {
"filter" : {
"exists" : { "field" : "tags" }
}
}
}
}
若想要exist查詢能匹配null型別,需要設定mapping:
"user": {
"type": "keyword",
"null_value": "_null_"
}
missing查詢在5.x版本已經不存在,改成如下的判定形式:
GET /_search
{
"query": {
"bool": {
"must_not": {
"exists": {
"field": "user"
}
}
}
}
}
1.4 字首檢索( Prefix Query )
匹配包含 not analyzed 的字首字元:
GET /_search
{ "query": {
"prefix" : { "user" : "ki" }
}
}
1.5 萬用字元檢索( wildcard query)
匹配具有匹配萬用字元表示式( (not analyzed )的欄位的文件。 支援的萬用字元:
1)*,它匹配任何字元序列(包括空字元序列);
2)?,它匹配任何單個字元。
請注意,此查詢可能很慢,因為它需要遍歷多個術語。
為了防止非常慢的萬用字元查詢,萬用字元不能以任何一個萬用字元*或?開頭。
舉例:
GET /_search
{
"query": {
"wildcard" : { "user" : "ki*y" }
}
}
1.6 正則表示式檢索(regexp query)
正則表示式查詢允許您使用正則表示式術語查詢。
舉例如下:
GET /_search
{
"query": {
"regexp":{
"name.first": "s.*y"
}
}
}
注意: *的匹配會非常慢,你需要使用一個長的字首,
通常類似.*?+萬用字元查詢的正則檢索效能會非常低。
1.7 模糊檢索(fuzzy query)
模糊查詢查詢在模糊度中指定的最大編輯距離內的所有可能的匹配項,然後檢查術語字典,以找出在索引中實際存在待檢索的關鍵詞。
舉例如下:
GET /_search
{
"query": {
"fuzzy" : { "user" : "ki" }
}
}
1.8 型別檢索(type query)
舉例:
GET /my_index/_search
{
"query": {
"type" : {
"value" : "xext"
}
}
}
已驗證,檢索索引my_index中,type為xext的全部資訊。
1.9 Ids檢索(ids query)
返回指定id的全部資訊。
GET /my_index/_search
{
"query": {
"ids" : {
"type" : "xext",
"values" : ["2", "4", "100"]
}
}
}
2、全文檢索
高階全文查詢通常用於在全文字欄位(如電子郵件正文)上執行全文查詢。他們瞭解如何對被查詢的欄位進行分析,並在執行前將每個欄位的分析器(或search_analyzer)應用於查詢字串。
2.1 匹配檢索(match query)
匹配查詢接受文字/數字/日期型別,分析它們,並構造查詢。
1)匹配查詢的型別為boolean。 這意味著分析所提供的文字,並且分析過程從提供的文字構造一個布林查詢,
可以將運算子標誌設定為或以控制布林子句(預設為或);
2)文字分析取決於mapping中設定的analyzer(中文分詞,我們預設選擇ik分詞器);
3) fuzziness——模糊性允許基於被查詢的欄位的型別進行模糊匹配;
4)”operator”: “and”——匹配與操作(預設或操作);
5) “minimum_should_match”: “75%”——這讓我們可以指定必須匹配的詞項數用來表示一個文件是否相關。
舉例:
GET /_search
{
"query": {
"match" : {
"message" : {
"query" : "this is a test",
"operator" : "and"
}
}
}
}
2.2 匹配解析檢索 match_phrase query
match_phrase查詢分析文字,並從分析文字中建立短語查詢。
類似 match 查詢, match_phrase 查詢首先將查詢字串解析成一個詞項列表,然後對這些詞項進行搜尋,但只保留那些包含 全部 搜尋詞項,且 位置 與搜尋詞項相同的文件。
舉例如下:對於 quick fox 的短語搜尋可能不會匹配到任何文件,因為沒有文件包含的 quick 詞之後緊跟著 fox 。
GET /my_index/my_type/_search
{
"query": {
"match_phrase": {
"title": "quick brown fox"
}
}
}
2.3 匹配解析字首檢索(match_phrase_prefix)
使用者已經漸漸習慣在輸完查詢內容之前,就能為他們展現搜尋結果,這就是所謂的 即時搜尋(instant search) 或 輸入即搜尋(search-as-you-type) 。
不僅使用者能在更短的時間內得到搜尋結果,我們也能引導使用者搜尋索引中真實存在的結果。
例如,如果使用者輸入 johnnie walker bl ,我們希望在它們完成輸入搜尋條件前就能得到:
Johnnie Walker Black Label 和 Johnnie Walker Blue Label 。
match_phrase_prefix與match_phrase相同,除了它允許文字中最後一個術語的字首匹配。
舉例:
GET / _search
{
“query”:{
“match_phrase_prefix”:{
“message”:“quick brown f”
}
}
}
2.4 多欄位匹配檢索( multi_match query)
multi_match 查詢為能在多個欄位上反覆執行相同查詢提供了一種便捷方式。
預設情況下,查詢的型別是 best_fields, 這表示它會為每個欄位生成一個 match 查詢。
舉例1:”fields”: “*_title”
——任何與模糊模式正則匹配的欄位都會被包括在搜尋條件中, 例如可以左側的方式同時匹配 book_title 、 chapter_title 和 section_title (書名、章名、節名)這三個欄位。
舉例2: “fields”: [ “*_title”, “chapter_title^2” ]
——可以使用 ^ 字元語法為單個欄位提升權重,在欄位名稱的末尾新增 ^boost , 其中 boost 是一個浮點數。
舉例3:”fields”: [ “first_name”, “last_name” ],
“operator”: “and”
——兩個欄位必須都包含。
GET /_search
{
"query": {
"multi_match" : {
"query": "this is a test",
"fields": [ "subject", "message" ]
}
}
}
2.5 字串檢索(query_string)
一個使用查詢解析器解析其內容的查詢。
query_string查詢提供了以簡明的簡寫語法執行多匹配查詢 multi_match queries ,布林查詢 bool queries ,提升得分 boosting ,模糊匹配 fuzzy matching ,萬用字元 wildcards ,正則表示式 regexp 和範圍查詢 range queries 的方式。
支援引數達10幾種。
GET /_search
{
"query": {
"query_string" : {
"default_field" : "content",
"query" : "this AND that OR thus"
}
}
}
2.6 簡化字串檢索(simple_query_string)
一個使用SimpleQueryParser解析其上下文的查詢。 與常規query_string查詢不同,simple_query_string查詢永遠不會丟擲異常,並丟棄查詢的無效部分。
舉例:
GET /_search
{
"query": {
"simple_query_string" : {
"fields" : ["content"],
"query" : "foo bar -baz"
}
}
}
支援的操作如下:
1)+表示AND操作
2)| 表示OR操作
3)- 否定操作
4)*在術語結束時表示字首查詢
5)(和)表示優先
3 小結
有的博友可能會問,這和ES官網API有什麼區別。
仔細對比你會發現,ES的中文文件是根據2.X版本翻譯的,ES的英文文件一個版本是沒有更新到5.X版本,另一個已經更新
===========================================================================
本節介紹以下 CRUD API:
單文件 APIs
多文件 APIs
Multi Get API
Bulk API
注意:所有的單文件的CRUD API,index引數只能接受單一的索引庫名稱,或者是一個指向單一索引庫的alias。
Index API
Index API 允許我們儲存一個JSON格式的文件,使資料可以被搜尋。文件通過index、type、id唯一確定。我們可以自己提供一個id,或者也使用Index API 為我們自動生成一個。
這裡有幾種不同的方式來產生JSON格式的文件(document):
- 手動方式,使用原生的byte[]或者String
- 使用Map方式,會自動轉換成與之等價的JSON
- 使用第三方庫來序列化beans,如Jackson
- 使用內建的幫助類 XContentFactory.jsonBuilder()
手動方式
String json = "{" +
"\"user\":\"kimchy\"," +
"\"postDate\":\"2013-01-30\"," +
"\"message\":\"trying out Elasticsearch\"" +
"}";
例項
/**
* 手動生成JSON
*/
@Test
public void CreateJSON(){
String json = "{" +
"\"user\":\"fendo\"," +
"\"postDate\":\"2013-01-30\"," +
"\"message\":\"Hell word\"" +
"}";
IndexResponse response = client.prepareIndex("fendo", "fendodate")
.setSource(json)
.get();
System.out.println(response.getResult());
}
Map方式
Map是key:value資料型別,可以代表json結構.
Map<String, Object> json = new HashMap<String, Object>();
json.put("user","kimchy");
json.put("postDate",new Date());
json.put("message","trying out Elasticsearch");
例項
/**
* 使用集合
*/
@Test
public void CreateList(){
Map<String, Object> json = new HashMap<String, Object>();
json.put("user","kimchy");
json.put("postDate","2013-01-30");
json.put("message","trying out Elasticsearch");
IndexResponse response = client.prepareIndex("fendo", "fendodate")
.setSource(json)
.get();
System.out.println(response.getResult());
}
序列化方式
ElasticSearch已經使用了jackson,可以直接使用它把javabean轉為json.
import com.fasterxml.jackson.databind.*;
// instance a json mapper
ObjectMapper mapper = new ObjectMapper(); // create once, reuse
// generate json
byte[] json = mapper.writeValueAsBytes(yourbeaninstance);
例項
/**
* 使用JACKSON序列化
* @throws Exception
*/
@Test
public void CreateJACKSON() throws Exception{
CsdnBlog csdn=new CsdnBlog();
csdn.setAuthor("fendo");
csdn.setContent("這是JAVA書籍");
csdn.setTag("C");
csdn.setView("100");
csdn.setTitile("程式設計");
csdn.setDate(new Date().toString());
// instance a json mapper
ObjectMapper mapper = new ObjectMapper(); // create once, reuse
// generate json
byte[] json = mapper.writeValueAsBytes(csdn);
IndexResponse response = client.prepareIndex("fendo", "fendodate")
.setSource(json)
.get();
System.out.println(response.getResult());
}
XContentBuilder幫助類方式
ElasticSearch提供了一個內建的幫助類XContentBuilder來產生JSON文件
// Index name
String _index = response.getIndex();
// Type name
String _type = response.getType();
// Document ID (generated or not)
String _id = response.getId();
// Version (if it's the first time you index this document, you will get: 1)
long _version = response.getVersion();
// status has stored current instance statement.
RestStatus status = response.status();
例項
/**
* 使用ElasticSearch 幫助類
* @throws IOException
*/
@Test
public void CreateXContentBuilder() throws IOException{
XContentBuilder builder = XContentFactory.jsonBuilder()
.startObject()
.field("user", "ccse")
.field("postDate", new Date())
.field("message", "this is Elasticsearch")
.endObject();
IndexResponse response = client.prepareIndex("fendo", "fendodata").setSource(builder).get();
System.out.println("建立成功!");
}
綜合例項
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.transport.client.PreBuiltTransportClient;
import org.junit.Before;
import org.junit.Test;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class CreateIndex {
private TransportClient client;
@Before
public void getClient() throws Exception{
//設定叢集名稱
Settings settings = Settings.builder().put("cluster.name", "my-application").build();// 叢集名
//建立client
client = new PreBuiltTransportClient(settings)
.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"), 9300));
}
/**
* 手動生成JSON
*/
@Test
public void CreateJSON(){
String json = "{" +
"\"user\":\"fendo\"," +
"\"postDate\":\"2013-01-30\"," +
"\"message\":\"Hell word\"" +
"}";
IndexResponse response = client.prepareIndex("fendo", "fendodate")
.setSource(json)
.get();
System.out.println(response.getResult());
}
/**
* 使用集合
*/
@Test
public void CreateList(){
Map<String, Object> json = new HashMap<String, Object>();
json.put("user","kimchy");
json.put("postDate","2013-01-30");
json.put("message","trying out Elasticsearch");
IndexResponse response = client.prepareIndex("fendo", "fendodate")
.setSource(json)
.get();
System.out.println(response.getResult());
}
/**
* 使用JACKSON序列化
* @throws Exception
*/
@Test
public void CreateJACKSON() throws Exception{
CsdnBlog csdn=new CsdnBlog();
csdn.setAuthor("fendo");
csdn.setContent("這是JAVA書籍");
csdn.setTag("C");
csdn.setView("100");
csdn.setTitile("程式設計");
csdn.setDate(new Date().toString());
// instance a json mapper
ObjectMapper mapper = new ObjectMapper(); // create once, reuse
// generate json
byte[] json = mapper.writeValueAsBytes(csdn);
IndexResponse response = client.prepareIndex("fendo", "fendodate")
.setSource(json)
.get();
System.out.println(response.getResult());
}
/**
* 使用ElasticSearch 幫助類
* @throws IOException
*/
@Test
public void CreateXContentBuilder() throws IOException{
XContentBuilder builder = XContentFactory.jsonBuilder()
.startObject()
.field("user", "ccse")
.field("postDate", new Date())
.field("message", "this is Elasticsearch")
.endObject();
IndexResponse response = client.prepareIndex("fendo", "fendodata").setSource(builder).get();
System.out.println("建立成功!");
}
}
你還可以通過startArray(string)和endArray()方法新增陣列。.field()方法可以接受多種物件型別。你可以給它傳遞數字、日期、甚至其他XContentBuilder物件。
Get API
根據id檢視文件:
GetResponse response = client.prepareGet("twitter", "tweet", "1").get();
更多請檢視 rest get API 文件
配置執行緒
operationThreaded
設定為 true
是在不同的執行緒裡執行此次操作
下面的例子是operationThreaded
設定為 false
:
GetResponse response = client.prepareGet("twitter", "tweet", "1")
.setOperationThreaded(false)
.get();
Delete API
根據ID刪除:
DeleteResponse response = client.prepareDelete("twitter", "tweet", "1").get();
更多請檢視 delete API 文件
配置執行緒
operationThreaded
設定為 true
是在不同的執行緒裡執行此次操作
下面的例子是operationThreaded
設定為 false
:
GetResponse response = client.prepareGet("twitter", "tweet", "1")
.setOperationThreaded(false)
.get();
DeleteResponse response = client.prepareDelete("twitter", "tweet", "1")
.setOperationThreaded(false)
.get();
Delete By Query API
通過查詢條件刪除
BulkByScrollResponse response =
DeleteByQueryAction.INSTANCE.newRequestBuilder(client)
.filter(QueryBuilders.matchQuery("gender", "male")) //查詢條件
.source("persons") //index(索引名)
.get(); //執行
long deleted = response.getDeleted(); //刪除文件的數量
如果需要執行的時間比較長,可以使用非同步的方式處理,結果在回撥裡面獲取
DeleteByQueryAction.INSTANCE.newRequestBuilder(client)
.filter(QueryBuilders.matchQuery("gender", "male")) //查詢
.source("persons") //index(索引名)
.execute(new ActionListener<BulkByScrollResponse>() { //回撥監聽
@Override
public void onResponse(BulkByScrollResponse response) {
long deleted = response.getDeleted(); //刪除文件的數量
}
@Override
public void onFailure(Exception e) {
// Handle the exception
}
});
Update API
有兩種方式更新索引:
- 建立
UpdateRequest
,通過client傳送; - 使用
prepareUpdate()
方法;
使用UpdateRequest
UpdateRequest updateRequest = new UpdateRequest();
updateRequest.index("index");
updateRequest.type("type");
updateRequest.id("1");
updateRequest.doc(jsonBuilder()
.startObject()
.field("gender", "male")
.endObject());
client.update(updateRequest).get();
使用 prepareUpdate()
方法
這裡官方的示例有問題,new Script()引數錯誤,所以一下程式碼是我自己寫的(2017/11/10)
client.prepareUpdate("ttl", "doc", "1")
.setScript(new Script("ctx._source.gender = \"male\"" ,ScriptService.ScriptType.INLINE, null, null))//指令碼可以是本地檔案儲存的,如果使用檔案儲存的指令碼,需要設定 ScriptService.ScriptType.FILE
.get();
client.prepareUpdate("ttl", "doc", "1")
.setDoc(jsonBuilder() //合併到現有文件
.startObject()
.field("gender", "male")
.endObject())
.get();
Update by script
使用指令碼更新文件
UpdateRequest updateRequest = new UpdateRequest("ttl", "doc", "1")
.script(new Script("ctx._source.gender = \"male\""));
client.update(updateRequest).get();
Update by merging documents
合併文件
UpdateRequest updateRequest = new UpdateRequest("index", "type", "1")
.doc(jsonBuilder()
.startObject()
.field("gender", "male")
.endObject());
client.update(updateRequest).get();
Upsert
更新插入,如果存在文件就更新,如果不存在就插入
IndexRequest indexRequest = new IndexRequest("index", "type", "1")
.source(jsonBuilder()
.startObject()
.field("name", "Joe Smith")
.field("gender", "male")
.endObject());
UpdateRequest updateRequest = new UpdateRequest("index", "type", "1")
.doc(jsonBuilder()
.startObject()
.field("gender", "male")
.endObject())
.upsert(indexRequest); //如果不存在此文件 ,就增加 `indexRequest`
client.update(updateRequest).get();
如果 index/type/1
存在,類似下面的文件:
{
"name" : "Joe Dalton",
"gender": "male"
}
如果不存在,會插入新的文件:
{
"name" : "Joe Smith",
"gender": "male"
}
Multi Get API
一次獲取多個文件
MultiGetResponse multiGetItemResponses = client.prepareMultiGet()
.add("twitter", "tweet", "1") //一個id的方式
.add("twitter", "tweet", "2", "3", "4") //多個id的方式
.add("another", "type", "foo") //可以從另外一個索引獲取
.get();
for (MultiGetItemResponse itemResponse : multiGetItemResponses) { //迭代返回值
GetResponse response = itemResponse.getResponse();
if (response.isExists()) { //判斷是否存在
String json = response.getSourceAsString(); //_source 欄位
}
}
更多請瀏覽REST multi get 文件
Bulk API
Bulk API,批量插入:
import static org.elasticsearch.common.xcontent.XContentFactory.*;
BulkRequestBuilder bulkRequest = client.prepareBulk();
// either use client#prepare, or use Requests# to directly build index/delete requests
bulkRequest.add(client.prepareIndex("twitter", "tweet", "1")
.setSource(jsonBuilder()
.startObject()
.field("user", "kimchy")
.field("postDate", new Date())
.field("message", "trying out Elasticsearch")
.endObject()
)
);
bulkRequest.add(client.prepareIndex("twitter", "tweet", "2")
.setSource(jsonBuilder()
.startObject()
.field("user", "kimchy")
.field("postDate", new Date())
.field("message", "another post")
.endObject()
)
);
BulkResponse bulkResponse = bulkRequest.get();
if (bulkResponse.hasFailures()) {
// process failures by iterating through each bulk response item
//處理失敗
}
使用 Bulk Processor
BulkProcessor 提供了一個簡單的介面,在給定的大小數量上定時批量自動請求
建立BulkProcessor
例項
首先建立BulkProcessor
例項
import org.elasticsearch.action.bulk.BackoffPolicy;
import org.elasticsearch.action.bulk.BulkProcessor;
import org.elasticsearch.common.unit.ByteSizeUnit;
import org.elasticsearch.common.unit.ByteSizeValue;
import org.elasticsearch.common.unit.TimeValue;
BulkProcessor bulkProcessor = BulkProcessor.builder(
client, //增加elasticsearch客戶端
new BulkProcessor.Listener() {
@Override
public void beforeBulk(long executionId,
BulkRequest request) { ... } //呼叫bulk之前執行 ,例如你可以通過request.numberOfActions()方法知道numberOfActions
@Override
public void afterBulk(long executionId,
BulkRequest request,
BulkResponse response) { ... } //呼叫bulk之後執行 ,例如你可以通過request.hasFailures()方法知道是否執行失敗
@Override
public void afterBulk(long executionId,
BulkRequest request,
Throwable failure) { ... } //呼叫失敗拋 Throwable
})
.setBulkActions(10000) //每次10000請求
.setBulkSize(new ByteSizeValue(5, ByteSizeUnit.MB)) //拆成5mb一塊
.setFlushInterval(TimeValue.timeValueSeconds(5)) //無論請求數量多少,每5秒鐘請求一次。
.setConcurrentRequests(1) //設定併發請求的數量。值為0意味著只允許執行一個請求。值為1意味著允許1併發請求。
.setBackoffPolicy(
BackoffPolicy.exponentialBackoff(TimeValue.timeValueMillis(100), 3))//設定自定義重複請求機制,最開始等待100毫秒,之後成倍更加,重試3次,當一次或多次重複請求失敗後因為計算資源不夠丟擲 EsRejectedExecutionException 異常,可以通過BackoffPolicy.noBackoff()方法關閉重試機制
.build();
BulkProcessor 預設設定
- bulkActions 1000
- bulkSize 5mb
- 不設定flushInterval
- concurrentRequests 為 1 ,非同步執行
- backoffPolicy 重試 8次,等待50毫秒
增加requests
然後增加requests
到BulkProcessor
bulkProcessor.add(new IndexRequest("twitter", "tweet", "1").source(/* your doc here */));
bulkProcessor.add(new DeleteRequest("twitter", "tweet", "2"));
關閉 Bulk Processor
當所有文件都處理完成,使用awaitClose
或 close
方法關閉BulkProcessor
:
bulkProcessor.awaitClose(10, TimeUnit.MINUTES);
或
bulkProcessor.close();
在測試中使用Bulk Processor
如果你在測試種使用Bulk Processor
可以執行同步方法
BulkProcessor bulkProcessor = BulkProcessor.builder(client, new BulkProcessor.Listener() { /* Listener methods */ })
.setBulkActions(10000)
.setConcurrentRequests(0)
.build();
// Add your requests
bulkProcessor.add(/* Your requests */);
// Flush any remaining requests
bulkProcessor.flush();
// Or close the bulkProcessor if you don't need it anymore
bulkProcessor.close();
// Refresh your indices
client.admin().indices().prepareRefresh().get();
// Now you can start searching!
client.prepareSearch().get();
所有例項 參見Git
===============================================================================================
為了講解不同型別ES檢索,我們將要對包含以下型別的文件集合進行檢索:
1. title 標題;
2. authors 作者;
3. summary 摘要;
4. release data 釋出日期;
5. number of reviews 評論數。
首先,讓我們藉助 bulk API批量建立新的索引並提交資料。
PUT /bookdb_index
{ "settings": { "number_of_shards": 1 }}
POST /bookdb_index/book/_bulk
{ "index": { "_id": 1 }}
{ "title": "Elasticsearch: The Definitive Guide", "authors": ["clinton gormley", "zachary tong"], "summary" : "A distibuted real-time search and analytics engine", "publish_date" : "2015-02-07", "num_reviews": 20, "publisher": "oreilly" }
{ "index": { "_id": 2 }}
{ "title": "Taming Text: How to Find, Organize, and Manipulate It", "authors": ["grant ingersoll", "thomas morton", "drew farris"], "summary" : "organize text using approaches such as full-text search, proper name recognition, clustering, tagging, information extraction, and summarization", "publish_date" : "2013-01-24", "num_reviews": 12, "publisher": "manning" }
{ "index": { "_id": 3 }}
{ "title": "Elasticsearch in Action", "authors": ["radu gheorge", "matthew lee hinman", "roy russo"], "summary" : "build scalable search applications using Elasticsearch without having to do complex low-level programming or understand advanced data science algorithms", "publish_date" : "2015-12-03", "num_reviews": 18, "publisher": "manning" }
{ "index": { "_id": 4 }}
{ "title": "Solr in Action", "authors": ["trey grainger", "timothy potter"], "summary" : "Comprehensive guide to implementing a scalable search engine using Apache Solr", "publish_date" : "2014-04-05", "num_reviews": 23, "publisher": "manning" }
1、基本匹配檢索( Basic Match Query)
1.1 全文檢索
有兩種方式可以執行全文檢索:
1)使用包含引數的檢索API,引數作為URL的一部分。
舉例:以下對”guide”執行全文檢索。
GET /bookdb_index/book/_search?q=guide
[Results]
"hits": [
{
"_index": "bookdb_index",
"_type": "book",
"_id": "1",
"_score": 0.28168046,
"_source": {
"title": "Elasticsearch: The Definitive Guide",
"authors": [
"clinton gormley",
"zachary tong"
],
"summary": "A distibuted real-time search and analytics engine",
"publish_date": "2015-02-07",
"num_reviews": 20,
"publisher": "manning"
}
},
{
"_index": "bookdb_index",
"_type": "book",
"_id": "4",
"_score": 0.24144039,
"_source": {
"title": "Solr in Action",
"authors": [
"trey grainger",
"timothy potter"
],
"summary": "Comprehensive guide to implementing a scalable search engine using Apache Solr",
"publish_date": "2014-04-05",
"num_reviews": 23,
"publisher": "manning"
}
}
]
2)使用完整的ES DSL,其中Json body作為請求體。
其執行結果如方式1)結果一致。
{
"query": {
"multi_match" : {
"query" : "guide",
"fields" : ["_all"]
}
}
}
解讀:使用multi_match關鍵字代替match關鍵字,作為對多個欄位執行相同查詢的方便的簡寫方式。 fields屬性指定要查詢的欄位,在這種情況下,我們要對文件中的所有欄位進行查詢。
1.2 指定特定欄位檢索
這兩個API也允許您指定要搜尋的欄位。 例如,要在標題欄位中搜索帶有“in action”字樣的圖書,
1)URL檢索方式
如下所示:
GET /bookdb_index/book/_search?q=title:in action
[Results]
"hits": [
{
"_index": "bookdb_index",
"_type": "book",
"_id": "4",
"_score": 0.6259885,
"_source": {
"title": "Solr in Action",
"authors": [
"trey grainger",
"timothy potter"
],
"summary": "Comprehensive guide to implementing a scalable search engine using Apache Solr",
"publish_date": "2014-04-05",
"num_reviews": 23,
"publisher": "manning"
}
},
{
"_index": "bookdb_index",
"_type": "book",
"_id": "3",
"_score": 0.5975345,
"_source": {
"title": "Elasticsearch in Action",
"authors": [
"radu gheorge",
"matthew lee hinman",
"roy russo"
],
"summary": "build scalable search applications using Elasticsearch without having to do complex low-level programming or understand advanced data science algorithms",
"publish_date": "2015-12-03",
"num_reviews": 18,
"publisher": "manning"
}
}
]
2)DSL檢索方式
然而,full body的DSL為您提供了建立更復雜查詢的更多靈活性(我們將在後面看到)以及指定您希望的返回結果。 在下面的示例中,我們指定要返回的結果數、偏移量(對分頁有用)、我們要返回的文件欄位以及屬性的高亮顯示。
結果數的表示方式:size;
偏移值的表示方式:from;
指定返回欄位 的表示方式 :_source;
高亮顯示 的表示方式 :highliaght。
POST /bookdb_index/book/_search
{
"query": {
"match" : {
"title" : "in action"
}
},
"size": 2,
"from": 0,
"_source": [ "title", "summary", "publish_date" ],
"highlight": {
"fields" : {
"title" : {}
}
}
}
[Results]
"hits": {
"total": 2,
"max_score": 0.9105287,
"hits": [
{
"_index": "bookdb_index",
"_type": "book",
"_id": "3",
"_score": 0.9105287,
"_source": {
"summary": "build scalable search applications using Elasticsearch without having to do complex low-level programming or understand advanced data science algorithms",
"title": "Elasticsearch in Action",
"publish_date": "2015-12-03"
},
"highlight": {
"title": [
"Elasticsearch <em>in</em> <em>Action</em>"
]
}
},
{
"_index": "bookdb_index",
"_type": "book",
"_id": "4",
"_score": 0.9105287,
"_source": {
"summary": "Comprehensive guide to implementing a scalable search engine using Apache Solr",
"title": "Solr in Action",
"publish_date": "2014-04-05"
},
"highlight": {
"title": [
"Solr <em>in</em> <em>Action</em>"
]
}
}
]
}
注意:對於 multi-word 檢索,匹配查詢允許您指定是否使用‘and’運算子,
而不是使用預設’or’運算子。
您還可以指定minimum_should_match選項來調整返回結果的相關性。
詳細資訊可以在Elasticsearch指南中查詢Elasticsearch guide. 獲取。
2、多欄位檢索 (Multi-field Search)
如我們已經看到的,要在搜尋中查詢多個文件欄位(例如在標題和摘要中搜索相同的查詢字串),請使用multi_match查詢。
POST /bookdb_index/book/_search
{
"query": {
"multi_match" : {
"query" : "elasticsearch guide",
"fields": ["title", "summary"]
}
}
}
[Results]
"hits": {
"total": 3,
"max_score": 0.9448582,
"hits": [
{
"_index": "bookdb_index",
"_type": "book",
"_id": "1",
"_score": 0.9448582,
"_source": {
"title": "Elasticsearch: The Definitive Guide",
"authors": [
"clinton gormley",
"zachary tong"
],
"summary": "A distibuted real-time search and analytics engine",
"publish_date": "2015-02-07",
"num_reviews": 20,
"publisher": "manning"
}
},
{
"_index": "bookdb_index",
"_type": "book",
"_id": "3",
"_score": 0.17312013,
"_source": {
"title": "Elasticsearch in Action",
"authors": [
"radu gheorge",
"matthew lee hinman",
"roy russo"
],
"summary": "build scalable search applications using Elasticsearch without having to do complex low-level programming or understand advanced data science algorithms",
"publish_date": "2015-12-03",
"num_reviews": 18,
"publisher": "manning"
}
},
{
"_index": "bookdb_index",
"_type": "book",
"_id": "4",
"_score": 0.14965448,
"_source": {
"title": "Solr in Action",
"authors": [
"trey grainger",
"timothy potter"
],
"summary": "Comprehensive guide to implementing a scalable search engine using Apache Solr",
"publish_date": "2014-04-05",
"num_reviews": 23,
"publisher": "manning"
}
}
]
}
注意:以上結果3匹配的原因是guide在summary存在。
3、 Boosting提升某欄位得分的檢索( Boosting)
由於我們正在多個欄位進行搜尋,我們可能希望提高某一欄位的得分。 在下面的例子中,我們將“摘要”欄位的得分提高了3倍,以增加“摘要”欄位的重要性,從而提高文件 4 的相關性。
POST /bookdb_index/book/_search
{
"query": {
"multi_match" : {
"query" : "elasticsearch guide",
"fields": ["title", "summary^3"]
}
},
"_source": ["title", "summary", "publish_date"]
}
[Results]
"hits": [
{
"_index": "bookdb_index",
"_type": "book",
"_id": "1",
"_score": 0.31495273,
"_source": {
"summary": "A distibuted real-time search and analytics engine",
"title": "Elasticsearch: The Definitive Guide",
"publish_date": "2015-02-07"
}
},
{
"_index": "bookdb_index",
"_type": "book",
"_id": "4",
"_score": 0.14965448,
"_source": {
"summary": "Comprehensive guide to implementing a scalable search engine using Apache Solr",
"title": "Solr in Action",
"publish_date": "2014-04-05"
}
},
{
"_index": "bookdb_index",
"_type": "book",
"_id": "3",
"_score": 0.13094766,
"_source": {
"summary": "build scalable search applications using Elasticsearch without having to do complex low-level programming or understand advanced data science algorithms",
"title": "Elasticsearch in Action",
"publish_date": "2015-12-03"
}
}
]
注意:Boosting不僅意味著計算得分乘法以增加因子。 實際的提升得分值是通過歸一化和一些內部優化。參考 Elasticsearch guide.檢視更多。
4、Bool檢索( Bool Query)
可以使用AND / OR / NOT運算子來微調我們的搜尋查詢,以提供更相關或指定的搜尋結果。
在搜尋API中是通過bool查詢來實現的。
bool查詢接受”must”引數(等效於AND),一個must_not引數(相當於NOT)或者一個should引數(等同於OR)。
例如,如果我想在標題中搜索一本名為“Elasticsearch”或“Solr”的書,AND由“clinton gormley”創作,但NOT由“radu gheorge”創作:
POST /bookdb_index/book/_search
{
"query": {
"bool": {
"must": {
"bool" : { "should": [
{ "match": { "title": "Elasticsearch" }},
{ "match": { "title": "Solr" }} ] }
},
"must": { "match": { "authors": "clinton gormely" }},
"must_not": { "match": {"authors": "radu gheorge" }}
}
}
}
[Results]
"hits": [
{
"_index": "bookdb_index",
"_type": "book",
"_id": "1",
"_score": 0.3672021,
"_source": {
"title": "Elasticsearch: The Definitive Guide",
"authors": [
"clinton gormley",
"zachary tong"
],
"summary": "A distibuted real-time search and analytics engine",
"publish_date": "2015-02-07",
"num_reviews": 20,
"publisher": "oreilly"
}
}
]
注意:您可以看到,bool查詢可以包含任何其他查詢型別,包括其他布林查詢,以建立任意複雜或深度巢狀的查詢。
5、 Fuzzy 模糊檢索( Fuzzy Queries)
在 Match檢索 和多匹配檢索中可以啟用模糊匹配來捕捉拼寫錯誤。 基於與原始詞的Levenshtein距離來指定模糊度。
POST /bookdb_index/book/_search
{
"query": {
"multi_match" : {
"query" : "comprihensiv guide",
"fields": ["title", "summary"],
"fuzziness": "AUTO"
}
},
"_source": ["title", "summary", "publish_date"],
"size": 1
}
[Results]
"hits": [
{
"_index": "bookdb_index",
"_type": "book",
"_id": "4",
"_score": 0.5961596,
"_source": {
"summary": "Comprehensive guide to implementing a scalable search engine using Apache Solr",
"title": "Solr in Action",
"publish_date": "2014-04-05"
}
}
]
“AUTO”的模糊值相當於當欄位長度大於5時指定值2。但是,設定80%的拼寫錯誤的編輯距離為1,將模糊度設定為1可能會提高整體搜尋效能。 有關更多資訊, Typos and Misspellingsch 。
6、 Wildcard Query 萬用字元檢索
萬用字元查詢允許您指定匹配的模式,而不是整個片語(term)檢索。
- ? 匹配任何字元;
- *匹配零個或多個字元。
舉例,要查詢具有以“t”字母開頭的作者的所有記錄,如下所示:
POST /bookdb_index/book/_search
{
"query": {
"wildcard" : {
"authors" : "t*"
}
},
"_source": ["title", "authors"],
"highlight": {
"fields" : {
"authors" : {}
}
}
}
[Results]
"hits": [
{
"_index": "bookdb_index",
"_type": "book",
"_id": "1",
"_score": 1,
"_source": {
"title": "Elasticsearch: The Definitive Guide",
"authors": [
"clinton gormley",
"zachary tong"
]
},
"highlight": {
"authors": [
"zachary <em>tong</em>"
]
}
},
{
"_index": "bookdb_index",
"_type": "book",
"_id": "2",
"_score": 1,
"_source": {
"title": "Taming Text: How to Find, Organize, and Manipulate It",
"authors": [
"grant ingersoll",
"thomas morton",
"drew farris"
]
},
"highlight": {
"authors": [
"<em>thomas</em> morton"
]
}
},
{
"_index": "bookdb_index",
"_type": "book",
"_id": "4",
"_score": 1,
"_source": {
"title": "Solr in Action",
"authors": [
"trey grainger",
"timothy potter"
]
},
"highlight": {
"authors": [
"<em>trey</em> grainger",
"<em>timothy</em> potter"
]
}
}
]
7、正則表示式檢索( Regexp Query)
正則表示式能指定比萬用字元檢索更復雜的檢索模式。
舉例如下:
POST /bookdb_index/book/_search
{
"query": {
"regexp" : {
"authors" : "t[a-z]*y"
}
},
"_source": ["title", "authors"],
"highlight": {
"fields" : {
"authors" : {}
}
}
}
[Results]
"hits": [
{
"_index": "bookdb_index",
"_type": "book",
"_id": "4",
"_score": 1,
"_source": {
"title": "Solr in Action",
"authors": [
"trey grainger",
"timothy potter"
]
},
"highlight": {
"authors": [
"<em>trey</em> grainger",
"<em>timothy</em> potter"
]
}
}
]
8、匹配短語檢索( Match Phrase Query)
匹配短語查詢要求查詢字串中的所有詞都存在於文件中,按照查詢字串中指定的順序並且彼此靠近。
預設情況下,這些詞必須完全相鄰,但您可以指定偏離值(slop value),該值指示在仍然考慮文件匹配的情況下詞與詞之間的偏離值。
POST /bookdb_index/book/_search
{
"query": {
"multi_match" : {
"query": "search engine",
"fields": ["title", "summary"],
"type": "phrase",
"slop": 3
}
},
"_source": [ "title", "summary", "publish_date" ]
}
[Results]
"hits": [
{
"_index": "bookdb_index",
"_type": "book",
"_id": "4",
"_score": 0.22327082,
"_source": {
"summary": "Comprehensive guide to implementing a scalable search engine using Apache Solr",
"title": "Solr in Action",
"publish_date": "2014-04-05"
}
},
{
"_index": "bookdb_index",
"_type": "book",
"_id": "1",
"_score": 0.16113183,
"_source": {
"summary": "A distibuted real-time search and analytics engine",
"title": "Elasticsearch: The Definitive Guide",
"publish_date": "2015-02-07"
}
}
]
注意:在上面的示例中,對於非短語型別查詢,文件_id 1通常具有較高的分數,並且顯示在文件_id 4之前,因為其欄位長度較短。
然而,作為一個短語查詢,詞與詞之間的接近度被考慮在內,所以文件_id 4分數更好。
9、匹配片語字首檢索
匹配片語字首查詢在查詢時提供搜尋即時型別或“相對簡單”的自動完成版本,而無需以任何方式準備資料。
像match_phrase查詢一樣,它接受一個斜率引數,使得單詞的順序和相對位置沒有那麼“嚴格”。 它還接受max_expansions引數來限制匹配的條件數以減少資源強度。
POST /bookdb_index/book/_search
{
"query": {
"match_phrase_prefix" : {
"summary": {
"query": "search en",
"slop": 3,
"max_expansions": 10
}
}
},
"_source": [ "title", "summary", "publish_date" ]
}
[Results]
"hits": [
{
"_index": "bookdb_index",
"_type": "book",
"_id": "4",
"_score": 0.5161346,
"_source": {
"summary": "Comprehensive guide to implementing a scalable search engine using Apache Solr",
"title": "Solr in Action",
"publish_date": "2014-04-05"
}
},
{
"_index": "bookdb_index",
"_type": "book",
"_id": "1",
"_score": 0.37248808,
"_source": {
"summary": "A distibuted real-time search and analytics engine",
"title": "Elasticsearch: The Definitive Guide",
"publish_date": "2015-02-07"
}
}
]
注意:查詢時間搜尋型別具有效能成本。
一個更好的解決方案是將時間作為索引型別。
更多相關API查詢 Completion Suggester API 或者 Edge-Ngram filters 。
10、字串檢索( Query String)
query_string查詢提供了以簡明的簡寫語法執行多匹配查詢 multi_match queries ,布林查詢 bool queries ,提升得分 boosting ,模糊匹配 fuzzy matching ,萬用字元 wildcards ,正則表示式 regexp 和範圍查詢 range queries 的方式。
在下面的例子中,我們對“ search algorithm ”一詞執行模糊搜尋,其中一本作者是“ grant ingersoll ”或“tom morton”。 我們搜尋所有欄位,但將提升應用於文件2的摘要欄位。
POST /bookdb_index/book/_search
{
"query": {
"query_string" : {
"query": "(saerch~1 algorithm~1) AND (grant ingersoll) OR (tom morton)",
"fields": ["_all", "summary^2"]
}
},
"_source": [ "title", "summary", "authors" ],
"highlight": {
"fields" : {
"summary" : {}
}
}
}
[Results]
"hits": [
{
"_index": "bookdb_index",
"_type": "book",
"_id": "2",
"_score": 0.14558059,
"_source": {
"summary": "organize text using approaches such as full-text search, proper name recognition, clustering, tagging, information extraction, and summarization",
"title": "Taming Text: How to Find, Organize, and Manipulate It",
"authors": [
"grant ingersoll",
"thomas morton",
"drew farris"
]
},
"highlight": {
"summary": [
"organize text using approaches such as full-text <em>search</em>, proper name recognition, clustering, tagging, information extraction, and summarization"
]
}
}
]
11、簡化的字串檢索 (Simple Query String)
simple_query_string查詢是query_string查詢的一個版本,更適合用於暴露給使用者的單個搜尋框,
因為 它分別用+ / | / - 替換了AND / OR / NOT的使用,並放棄查詢的無效部分,而不是在使用者出錯時丟擲異常。
POST /bookdb_index/book/_search
{
"query": {
"simple_query_string" : {
"query": "(saerch~1 algorithm~1) + (grant ingersoll) | (tom morton)",
"fields": ["_all", "summary^2"]
}
},
"_source": [ "title", "summary", "authors" ],
"highlight": {
"fields" : {
"summary" : {}
}
}
}
12、Term/Terms檢索(指定欄位檢索)
上面1-11小節的例子是全文搜尋的例子。 有時我們對結構化搜尋更感興趣,我們希望在其中找到完全匹配並返回結果。
在下面的例子中,我們搜尋Manning Publications釋出的索引中的所有圖書(藉助 term和terms查詢 )。
POST /bookdb_index/book/_search
{
"query": {
"term" : {
"publisher": "manning"
}
},
"_source" : ["title","publish_date","publisher"]
}
[Results]
"hits": [
{
"_index": "bookdb_index",
"_type": "book",
"_id": "2",
"_score": 1.2231436,
"_source": {
"publisher": "manning",
"title": "Taming Text: How to Find, Organize, and Manipulate It",
"publish_date": "2013-01-24"
}
},
{
"_index": "bookdb_index",
"_type": "book",
"_id": "3",
"_score": 1.2231436,
"_source": {
"publisher": "manning",
"title": "Elasticsearch in Action",
"publish_date": "2015-12-03"
}
},
{
"_index": "bookdb_index",
"_type": "book",
"_id": "4",
"_score": 1.2231436,
"_source": {
"publisher": "manning",
"title": "Solr in Action",
"publish_date": "2014-04-05"
}
}
]
Multiple terms可指定多個關鍵詞進行檢索。
{
"query": {
"terms" : {
"publisher": ["oreilly", "packt"]
}
}
}
13、Term排序檢索-(Term Query - Sorted)
Term查詢和其他查詢一樣,輕鬆的實現排序。多級排序也是允許的。
POST /bookdb_index/book/_search
{
"query": {
"term" : {
"publisher": "manning"
}
},
"_source" : ["title","publish_date","publisher"],
"sort": [
{ "publish_date": {"order":"desc"}},
{ "title": { "order": "desc" }}
]
}
[Results]
"hits": [
{
"_index": "bookdb_index",
"_type": "book",
"_id": "3",
"_score": null,
"_source": {
"publisher": "manning",
"title": "Elasticsearch in Action",
"publish_date": "2015-12-03"
},
"sort": [
1449100800000,
"in"
]