Preface
搜尋的功能在軟體發展到一個階段,甚至是在軟體建置初期就會被要求加入的一個功能,實作搜尋功能的方式有很多,我接觸過的除了傳統關聯式資料庫的 LIKE,還有 Microsoft Index Service、HP IDOL 到今天我要介紹的 Elasticsearch。
什麼是 Elasticsearch?
Elasticsearch(簡稱ES),是一個 Open Source 的搜尋專案,base on Apache Lucene,專案由 Shay Banon 於 2010 年 2 月啟動,其協定是 Apache 2.0。Elasticsearch 由於其天生的分散式和即時特性,很多人把它作為資料庫使用,也有很多人把它拿來儲存 Log,Elasticsearch 的發佈在 Lucene 和 Solr 社群引起很大的騷動,Solr 4.0+ 版本的 SolrCloud 也吸收了很多 Elasticsearch 的特性。
基本名詞解釋
跟關聯式資料庫不同的是,Elasticsearch 的每一個 Document 並沒有限定 Field 的數量及 Field 的名稱,也就是說放在相同 Type 之下的每一個 Document 都允許不一樣數量的 Field 及不一樣名稱的 Field。
分散式的特性 - 備份不重覆
我畫了一圖,假設我一個 Elasticsearch 的 Cluster 有 3 個 Node,我將 Index 切成 3 個 Shard、1 份 Replica,大致上就會長成下面這個樣子。
從上面這張圖可以看到一個特性,以我假設的情況為例,在任一個 Node 上無論 Shard 是 master 還是 replica,絕對不會有重覆編號的 Shard 出現。如果我的 Shard 太多,Node 太少怎麼辦?Elasticsearch 就不會分配多餘的 Shard 到 Node 裡面,不過至少 master 的 Shard 保證都會有一份。
分散式的特性 - 自動還原資料
假設有一天 Cluster 內的 Node2 掛點了,Cluster 就會啟動重新分配 Shard 的機制,而遺失的 Shard 就會從其他 Node 補足,目的在確保資料的完整性,重新分配後大致上就會長這個樣子,是不是很方便?
在 CentOS 7 安裝 Elasticsearch 分散式搜尋系統 (link)
現在試著將它裝在 CentOS 上,下面就記錄整個過程及所用到的指令。事前的準備當然就是先將 CentOS 安裝起來,目前我是用 CentOS 7,使用最小安裝即可。
安裝有用到的工具
安裝 JDK 1.8.0
安裝 Elasticsearch 2.1.1
修改 network.host 參數值
將 network.host 的值修改為 _non_loopback:ipv4_,network.host 參數值的意義可以參考官網的這篇文章 Network Settings 。
設定 Elasticsearch 為背景服務
開啟防火牆 9200, 9300 埠號
Elasticsearch 需要 2 個 Port:
啟動 Elasticsearch
操作 Elasticsearch
由於 Elasticsearch 是用 Java 寫的,當然有提供它的 Java API,不過在這邊我還是選擇用比較簡單的 RESTful API 來操作。到這裡不懂 RESTful API 沒關係,只要知道等等可以用 curl 來操作就好。不過,你還是看一下什麼叫做 HTTP Method 好了:淺談 HTTP Method:表單中的 GET 與 POST 有什麼差別? – Soul & Shell Blog. (Elasticsearch 預設開啟的 port 是 9200,有需要的話可以到 config/elasticsearch.yml 來修改) 操作 Elasticsearch 的格式基本上是這樣:
# curl -XGET 'http://172.17.0.2:9200/twitter/user/Noob?pretty'
- {
- "_index" : "twitter",
- "_type" : "user",
- "_id" : "Noob",
- "_version" : 1,
- "found" : true,
- "_source":{"name": "Noob"}
- }
既然叫做 Elasticsearch,最重要的應該還是搜尋吧?一樣是用 GET 方法,如果你有多筆資料,你可以這樣搜尋:
Java API
Before we jump straight to how to use the main Java API features, we need to initiate the client using TransportClient:
Indexing Documents
The prepareIndex() function allows to store an arbitrary JSON document and make it searchable (Refer to Index API):
Execution Result:
Querying Indexed Documents
Now that we have a typed searchable JSON document indexed, we can proceed and search using the prepareSearch() method:
The results returned by the
actionGet() method are called Hits, each Hit refers to a JSON document matching a search request. We can enhance the request by adding additional parameters in order to customize the query using the QueryBuilders methods:
Execution output:
Supplement
* 在 CentOS 7 與 ELK(Elasticsearch + Logstash + Kibana)
* Use Elasticsearch in your Java applications
* Guide to Elasticsearch in Java
* How to convert Java object to / from JSON (Gson)
* A Guide to FastJson
Before we jump straight to how to use the main Java API features, we need to initiate the client using TransportClient:
- import java.net.InetAddress;
- import java.util.Arrays;
- import java.util.List;
- import org.elasticsearch.action.search.SearchResponse;
- import org.elasticsearch.client.Client;
- import org.elasticsearch.client.transport.TransportClient;
- import org.elasticsearch.common.transport.InetSocketTransportAddress;
- import org.elasticsearch.search.SearchHit;
- ...
- public static Client GetClient() throws Exception
- {
- Client client = TransportClient.builder().build().addTransportAddress(new InetSocketTransportAddress(
- InetAddress.getByName("220.134.109.53"),
- 9300));
- return client;
- }
The prepareIndex() function allows to store an arbitrary JSON document and make it searchable (Refer to Index API):
- public static class People{
- String name;
- int age;
- public People(String n, int a){this.name=n; this.age=a;}
- public String toJson() throws Exception
- {
- Gson gson = new Gson();
- return gson.toJson(this);
- }
- }
- public static void IndexEx() throws Exception
- {
- Client client = GetClient();
- People p = new People("Mary", 26);
- System.out.printf("\t[Info] Index data as:\n%s\n\n", p.toJson());
- IndexResponse resp = client.prepareIndex("twitter", "user").setSource(p.toJson()).get();
- System.out.printf("\t[Info] Is done? %s\n", resp.isCreated());
- if(resp.isCreated())
- {
- System.out.printf("\t\tid=%s\n", resp.getId());
- System.out.printf("\t\tindex=%s\n", resp.getIndex());
- System.out.printf("\t\ttype=%s\n", resp.getType());
- System.out.printf("\t\tversion=%d\n\n", resp.getVersion());
- }
- //assertTrue(resp.isCreated());
- client.close();
- }
Querying Indexed Documents
Now that we have a typed searchable JSON document indexed, we can proceed and search using the prepareSearch() method:
- public static void QueryEx() throws Exception
- {
- Client client = GetClient();
- SearchResponse resp = client.prepareSearch("twitter").setTypes("user").execute().actionGet();
- List
sh = Arrays.asList(resp.getHits().getHits()); - System.out.printf("\t[Info] %d hit(s)!\n", sh.size());
- for(SearchHit hit:sh)
- {
- System.out.printf("\t[Info] Hit (%s/%s/%s)\n%s\n\n", hit.index(), hit.getType(), hit.getId(), hit.getSourceAsString());
- }
- client.close();
- System.out.printf("\t[Info] Done!\n");
- }
- public static void QueryEx2() throws Exception
- {
- Client client = GetClient();
- SearchResponse resp = client.prepareSearch()
- .setTypes()
- .setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
- .setPostFilter(QueryBuilders.rangeQuery("age").from(10).to(20))
- .execute()
- .actionGet();
- List
sh = Arrays.asList(resp.getHits().getHits()); - System.out.printf("\t[Info] %d hit(s)!\n", sh.size());
- for(SearchHit hit:sh)
- {
- System.out.printf("\t[Info] Hit (%s/%s/%s)\n%s\n", hit.index(), hit.getType(), hit.getId(), hit.getSourceAsString());
- }
- client.close();
- System.out.printf("\t[Info] Done!\n");
- }
Supplement
* 在 CentOS 7 與 ELK(Elasticsearch + Logstash + Kibana)
* Use Elasticsearch in your Java applications
* Guide to Elasticsearch in Java
* How to convert Java object to / from JSON (Gson)
* A Guide to FastJson
沒有留言:
張貼留言