---
# System prepended metadata

title: ElasticSearchr學習
tags: [Elasticsearch學習]

---

---
tags: Elasticsearch學習
---
# ElasticSearchr學習

## ElasticSearchr 介紹

### 引言

當我們在購物網站搜尋＝＞＂手機殼＂，它如果在SQL中執行可能會使用這樣的語法
```sql=
select *form item where name like %手機殼%
```
但是如果用SQL搜尋使用％索引會失效，效率是極低的

![](https://i.imgur.com/7nUJcH6.png)

這時候如果再把關鍵字改成搜尋=>＂殼手機＂，並且還是搜尋到了，搜尋的方式疑似會將我們輸入的關鍵字
拆分開來，再去匹配商品內容，以我們學習到的sql是無法做到這件事情的
![](https://i.imgur.com/WrMQbdL.png)

ElasticSearchr能做到：

* 在大數據中執行搜索功能時，如果使用MySql，效率太低
* 如果關鍵字輸入不準確，一樣可以搜索到想要的資料
* 將搜索關鍵字，以紅色字體展示

### ES的介紹

ES是一個使用Java語言並且基於[Lucene](https://lucene.apache.org/)編寫的搜索引擎框架，他提供了分布式的全文搜索功能，提供了一個統一的基於RESTful風格的WebApi接口，官方客戶也對多種語言都提供了相應的API

Lucene:本身就是一個搜索引擎的底層。

Lucene官方網站也不推薦直接使用Lucene，再它官方網站中有提供一個框架叫做Solr。

Solr也是基於Lucene去開發的一個框架，因此很多人會拿Solr跟ES做一個對比。

分布式：ES主要是為了突出它的橫向擴展能力（搭建集群）。

全文檢索：將一段詞語進行分詞，並且將分出的單個詞語統一的放到一個分詞庫中，在搜索時，根據關鍵字去分詞庫中檢索，找到匹配的內容。（倒排索引）

![](https://i.imgur.com/86twhX2.png)


RESTful風格的Ｗeb風格接口：操作ES很簡單，只需要發送一個HTTP請求，並且根據請求的方式的不同，攜帶參數的不同，執行相應的功能。

應用廣泛：Github.com,WIKI,GoldMan用ES每天維護將近10TB的數據。

ES官方學習網站：
[官方Document](https://www.elastic.co/guide/index.html)
[Elasticsearch: 权威指南](https://www.elastic.co/guide/cn/elasticsearch/guide/2.x/index.html)

### ES和Solr區別

1. Solr在查詢死數據時，速度相對ES更快一些。但是數據如果是常常改變的，Solr的查詢速度會降低很多,ES的查詢效率基本沒有變化。
2. Solr搭建基於需要依賴Zookeeper來幫助管理。ES本身就是支持集群的搭建，不需要第三方的介入。
3. 最開始Solr的社區可以說是非常火爆，針對中文的資料並不是很多。在ES出現之後，ES的討論社區火爆程度直線上升，ES的文件非常健全。
4. ES對現在云計算和大數據支持的特別好。

### 倒排索引
1. 將存放的數據，已依定的方式進行分詞，並且將分詞的內容存放倒一個單獨的分詞庫中。
2. 當用戶去查詢數據時，會將用戶的查詢關鍵字進行分詞。
3. 然後去分詞庫中匹配內容，最終得到數據id標示。
4. 根據id標示去存放數據的位置拉取到指定的數據。
![](https://img2020.cnblogs.com/blog/2139373/202009/2139373-20200904101427847-1957525614.png)

## ElasticSearchr的安裝

### 安裝ES＆Kibana

```yaml=
version:"3.1"
services:
elasticsearch:
    image: elasticsearch:6.5.4
    restart: always
    container_name: elasticsearch
    potrts:
        -9200:9200

kibana:
    image: kibana:6.5.4
    restart: always
    container_name: kibana
    potrts:
        -5601:5601
    enviroment:
        -elasticsearch_url=http://192.168.199.109:9200
    depens_on:
        -elasticsearch
```

Kevin版本：
```yaml=
version: "3.2"

services:
  elasticsearch:
    image: elasticsearch:6.5.4
    container_name: elasticsearch-654
    ports:
      - "9200:9200"
      - "9300:9300"
    environment:
      - cluster.name=docker-cluster
      - bootstrap.memory_lock=true
      - http.host=0.0.0.0
      - http.port=9200
      - transport.host=127.0.0.1
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
      - "http.cors.allow-origin=http://127.0.0.1:1358"
      - "http.cors.enabled=true"
      - "http.cors.allow-headers=X-Requested-With,X-Auth-Token,Content-Type,Content-Length,Authorization"
      - "http.cors.allow-credentials=true"
    ulimits:
      memlock:
        soft: -1
        hard: -1
    volumes:
      - es_data:/usr/share/elasticsearch/data
    networks:
      - esnet

  kibana:
    image: kibana:6.5.4
    container_name: kibana-654
    environment:
      SERVER_NAME: kibana-server
      ELASTICSEARCH_URL: http://elasticsearch:9200
    networks:
      - esnet
    depends_on:
      - elasticsearch
    ports:
      - "5601:5601"

  cerebro:
    image: yannart/cerebro:latest
    container_name: cerebro-073
    networks:
      - esnet
    ports:
      - "9900:9000"
    depends_on:
      - elasticsearch            
  dejavu:
    image: appbaseio/dejavu:latest
    container_name: dejavu
    networks:
      - esnet    
    ports:
     - "1358:1358"

volumes:
  es_data:
    driver: local      

networks:
  esnet:
    driver: bridge
```

在透過`docker-compose up -d` 啟動

![](https://i.imgur.com/W9BaltT.png)

先確認ES有啟動成功，如果有看到下列的JSON格式化面就表示是沒有問題的
![](https://i.imgur.com/1eIVw5M.png)

若ES沒問題再來訪問kibana
`http://127.0.0.1:5601/app/kibana`

進入kibana後，可以進入dev tools執行　webapi操作
![](https://i.imgur.com/y6h33Dn.png)

### 安裝分詞器IK分詞器

ES本身的分詞器對中文的處理不太友善,所以可以去下載[IK分詞器]，不過IK分詞器對繁體中文也不太友善，裡面的字典檔都是簡體字
(https://github.com/medcl/elasticsearch-analysis-ik)，並且版本要選擇跟ES一樣的版本

進去ES的容器內部，進入內部bin目錄文件下，執行腳本文件
`./elasticsearch-plugin  install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.5.4/elasticsearch-analysis-ik-6.5.4.zip`

`https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.5.4/elasticsearch-analysis-ik-6.5.4.zip`

#### 離線安裝
1. 先到GitHub下載，對應版本的IK分詞器
2. 解壓縮到目錄中`E:\es`
3. 到解壓縮的目錄中 執行`docker cp . es的容器編號:/usr/share/elasticsearch/plugins/ik/.`
4. 重啟ES `docker restart 容器編號`
![](https://i.imgur.com/wHZh3AF.png)
#### 在線安裝
1. 執行`docker ps`　查詢容器的編號
2. 執行`docker exec -it 容器編號 bash`
3. 執行`ls`
4. 執行`cd bin/`
5. 執行`ls`
6. 執行`./elasticsearch-plugin  install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.5.4/elasticsearch-analysis-ik-6.5.4.zip
-> Downloading https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.5.4/elasticsearch-analysis-ik-6.5.4.zip`
7. 重啟ES `docker restart 容器編號`

![](https://i.imgur.com/Suk5vfI.png)

![](https://i.imgur.com/kKyrIf3.png)

默認的分詞器
![](https://i.imgur.com/vv5PK4h.png)

可以測試一下IK分詞器
![](https://i.imgur.com/4AGO2m2.png)

## ElasticSearch基本操作

### ES的結構






#### 索引index

:::info
ES的服務中，可以建立多個索引。
每一個索引默認被分成5片儲存。
每一個分片都會存在至少一個備份分片。
備份分片默認不會幫助搜索資料，當ES搜索壓力特別大的時候，備份分片才會幫助搜索資料。
備份的分片必須放在不同的服務器中。
:::
![](https://i.imgur.com/sLBldu3.png)

分片:
ES在默認情況下一個索引會被分成五個分片

![](https://i.imgur.com/82iBnRg.png)

備份(一般情況下，從分片不會去分擔搜索壓力)

![](https://i.imgur.com/N9ZcBrg.png)


#### 類型Type

:::info
一個索引下，可以建立多個類型。
PS:根據版本不同，類型的建立規則也不同。
:::

Type vs Table
![](https://i.imgur.com/N8RB4ep.png)

* ES5.X 版本中，一個index下可以建立多個Type
* ES6.X 版本中，一個index下可以建議一個Type
* ES7.X 版本中，一個index下沒有Type

#### 文件Doc

:::info
一個類型下，可以有多的文件，這個文件就類似於Sql表中的多行資料。
:::

document=>一筆一筆資料
![](https://i.imgur.com/hz2KgO3.png)

#### 屬性Field

::: info
一個文件中，可以包含多個屬性，類似於SQL表中的一行資料存在多個列。
:::

field =>欄位
![](https://i.imgur.com/xKtn4VV.png)

### 操作ES的RESTful語法

Get請求(查詢):
* 查詢索引內容 : `http://ip:port/index`  
* 查詢指定的資料內容: `http://ip:port/index/type/doc_id`    

POST請求:

* 查詢，可以在`Content Types`加入json字串來代表查詢條件:`http://ip:port/index/type/_search` 

* 修改資料，可以在`Content Types`中指定JSON字串代表修改具體訊息:`http://ip:port/index/type/doc_id/_update`    

PUT請求(建立):

* 建立一個索引，需要在`Content Types`中指定索引的訊息:`http://ip:port/index` 

* 代表建立索引時，指定索引資料儲存時的屬性的資訊:`http://ip:port/index/type/_mappings` 

DELETE 請求:

* 刪除:`http://ip:port/index` 

* 刪除指定的資料:`http//ip:port/index/type/doc_id` 

### 索引操作

#### 建立一個索引(index)

```json=
#建立一個索引
PUT /person
{
  "settings": {
    "number_of_shards": 5,    //分片數量
    "number_of_replicas": 1   //備份數量
  }
}
```

![](https://i.imgur.com/ZirvASo.png)


#### 查看索引

可以到management > Index Management 查看剛剛建立的索引
正常情況下健康狀況會是綠色，但這裡的健康狀況是黃色因為目前是單機版啟動，所以沒有其他台主機可以做備份
![](https://i.imgur.com/sKxpKA5.png)

```json=
#查看索引訊息
GET /person
```
![](https://i.imgur.com/M6z3uZM.png)


#### 刪除索引

```json=
#刪除索引
DELETE /person
```
![](https://i.imgur.com/KI7GfbO.png)


### ES中Field可以指定的類型

[elasticsearch6.4參考文件](https://www.elastic.co/guide/en/elasticsearch/reference/6.5/mapping-types.html)

資料型態
:::info
- 字串類型:
   - text: 一般應用於全文檢索，將當前Field進行分詞。
   - keyword:當前Field不會被分詞
- [數字類型](https://www.elastic.co/guide/en/elasticsearch/reference/6.5/number.html):
  - long:
  - interger:
  - short:
  - byte:
  - double:
  - float:
  - half_float:精度比float小一半
  - scaled_float:根據一個long和scaled來表達一個浮點數,long-345,scaled-100 ->3.45
- 時間類型:
  - date:針對時間類型指定具體格式=>"format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis" 
- 布林類型:
  - boolean類型:表達ture & false
- 二進制類型:
  - binary類型暫時支持Base64 encode string
- 範圍類型:
  - long_range:無須指定具體內容，只需要儲存一個範圍即可，指定gt、lt、gte、lte
  - interger_range:
  - float_range:
  - duble_range
  - date_range:
  - ip_range:
- 經緯度類型:
  - geo_point:用來儲存經緯度的
- IP類型
  - ip:可以用來儲存ipv4 or ipv6
- 其他資料型態可以參考官網 
:::

### 建立索引、指定資料結構
```json=
PUT /book
{
  "settings": {
    "number_of_replicas": 1,
   	"number_of_shards": 5
  },

  "mappings": {
    "novel": {
      "properties": {
        "name": {
          "type": "text",
          "analyzer": "ik_max_word",
          "index": true
        },
        "authoor": {
          "type": "keyword"
        },
        "onsale": {
          "type": "date",
          "format": "yyyy-MM-dd"
        }
      }
    }
  }
}
```

![](https://i.imgur.com/2y5M1E0.png)

* settings:設定分片數量、備份數量
* mappings:設定資料表跟欄位
* 設定Field的時候，可以透過index設定這個Field是否可以當作搜索條件
* 若想指定自行安裝的分詞器時也可以在要被指定的Field中使用analyzer來設定
* date型態可以透過format指定格式樣式

### 資料的操作
:::info
文檔在ES服務中的唯一標示,_index,_type,`_id` 三個內容為組合，鎖定一個文檔，操作添加還是修改。
:::
#### 建立資料

**自動產生id**
```json=
# 建立資料、自動產生id
POST /book/novel
{
  "name":"盤龍",
  "authoor":"我愛吃西瓜",
  "onsale":"2000-01-04"
}

```
![](https://i.imgur.com/w1m5WRm.jpg)
這裡可以看出產生的id很詭異

**手動指定id(更推薦)**
```json=
# 建立資料、手動指定id
PUT /book/novel/1
{
  "name":"紅樓夢",
  "authoor":"曹雪晴",
  "onsale":"1985-01-04"
}
```
![](https://i.imgur.com/uPh29Um.png)


#### 修改資料

覆蓋式修改

```json=
# 建立資料、手動指定id
PUT /book/novel/1
{
  "name":"紅樓夢",
  "authoor":"曹雪晴",
  "onsale":"1985-01-04"
}
```

doc修改方式(更推薦)

只需要指定需要修改的field對應的值
```json=
#doc修改方式(更推薦)
POST /book/novel/1/_update
{
  "doc": {
    "authoor":"曹奶奶"
  }
}
```

![](https://i.imgur.com/FEbz1Xm.png)
這裡也可以發現版本從1變成了2


#### 刪除資料

```json=
#根據id刪除檔案
DELETE /book/novel/_id
```
![](https://i.imgur.com/pJ3CBK4.png)

## C#操作ElasticSearch

### C#連接ElasticSearch
* NEST或Elasticsearch.Net，其中NEST是對Elasticsearch.NET的封裝，更易使用，建議使用NEST

建立連線
```csharp=
 public class ESClient
    {
        public static ElasticClient GetEsElasticClient()
        {
            //Uri
            var node = new Uri("http://localhost:9200/");
            //建立連線字串
            var settings = new ConnectionSettings(node);
            //建立ElasticClient
            var client = new ElasticClient(settings);
            return client;
        }
    }
```
### C# 建立索引 (ES6.5的做法)

建立一個Man的類別
```csharp=
    public class Man
    {
        public string Name { get; set; }
        public int Age { get; set; }
        public DateTime Birthday { get; set; }
    }
```
1. 設定索引的setting
2. 設定索引的結構
3. 透過client連接ES並且執行建立索引
```csharp=
static void Main(string[] args)
        {
            string exIndex = "preson";
            //1.準備關於索引的setting
            var settings = new CreateIndexDescriptor(exIndex);
            settings.Settings(s => s.NumberOfShards(5).NumberOfReplicas(1));
            //2.準備關於索引的結構mappings
            settings.Mappings(ms => 
                ms.Map<Man>(m=>m.AutoMap<Man>()));
            //3.透過clinet去連接ES並執行建立索引
            var eSClient = ESClient.GetEsElasticClient();
            eSClient.CreateIndex(settings);
        }
```
mapping的另一種寫法:
```csharp=
  settings.Mappings(ms => 
                ms.Map<Man>(m=>m.AutoMap(typeof(Man))));
```

![](https://i.imgur.com/XkCzvh5.png)
AutoMap 會自動將類別的屬性轉成ES的field
[C#類別轉換成ES的參考資料表](https://www.elastic.co/guide/en/elasticsearch/client/net-api/6.x/auto-map.html)

* string 型別 會轉成 text 和一個子field類型是keyword [詳細至介紹](https://www.elastic.co/guide/en/elasticsearch/client/net-api/6.x/multi-fields.html)
* Int32 型別  會轉成 integer
* UInt16型別  會轉成 integer
* Int16 型別  會轉成 integer
* DateTime型別  會轉成 date

---
也可以將上述的三個動作合併再一起寫
```csharp=
var eSClient = ESClient.GetEsElasticClient();
var createIndexResponse = eSClient.CreateIndex("preson",c=>c.Mappings(ms=>ms
                .Map<Man>(m=>m.AutoMap<Man>())));
```

### C# 檢查索引是否存在&刪除索引 (ES6.5的做法)
1. 準備request
2. 通過clinet去執行
3. 輸出結果

檢查索引

```csharp=
string exIndex = "preson";
var eSClient = ESClient.GetEsElasticClient();
//1.準備request
GetIndexRequest request=new GetIndexRequest(exIndex);
//2.通過clinet去檢查
var exists = eSClient.GetIndex(request);
//3.輸出
Console.WriteLine(exists.IsValid);
```

刪除索引

```csharp=
string exIndex = "preson";
var eSClient = ESClient.GetEsElasticClient();
//1.準備request
DeleteIndexRequest request=new DeleteIndexRequest(exIndex);
//2.通過clinet去刪除
var exists = eSClient.DeleteIndex(request);
//3.輸出
Console.WriteLine(exists.IsValid);
```

### C# 操作document (ES6.5的做法)

#### 單筆資料的 增加&修改(覆蓋的方式)
```csharp=
var eSClient = ESClient.GetEsElasticClient();
string exIndex = "preson";
//1.準備資料
var data = new Man()
{
    Name = "張景嵐",
    Age = 25,
    Birthday = new DateTime(1992, 02, 08)
};

//2.準備request
var request = new IndexRequest<Man>(data, exIndex, "man", 1);
//3.通過client執行建立
var result = eSClient.Index(request);
//4.輸出結果
Console.WriteLine(result.IsValid);
```
![](https://i.imgur.com/UYxW3TN.png)

#### 修改資料以doc的方式
```csharp=
var eSClient = ESClient.GetEsElasticClient();
string exIndex = "preson";
//1.建立一個Map，指定需要修改的內容
Dictionary<string, object> doc = new Dictionary<string, object>();
doc.Add("name","郭雪芙");
//2.準備request
var request = new UpdateRequest<Man,Dictionary<string, object>>(exIndex, "man", 1);
request.Doc = doc;
//3.通過client執行建立
var result = eSClient.Update(request);
//4.輸出結果
Console.WriteLine(result.IsValid);
```
![](https://i.imgur.com/W5RxNZy.png)

#### 刪除資料
```csharp=
var eSClient = ESClient.GetEsElasticClient();
string exIndex = "preson";
//1.準備request
var request = new DeleteRequest(exIndex,"man",1);
//2.通過client執行建立
var result = eSClient.Delete(request);
//3.輸出結果
Console.WriteLine(result.IsValid);
```

#### 批量增加資料
[官方文件](https://www.elastic.co/guide/en/elasticsearch/client/net-api/1.x/bulk.html)
Man類別
```csharp=
    public class Man
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int Age { get; set; }
        public DateTime Birthday { get; set; }
    }
```
```csharp=
var eSClient = ESClient.GetEsElasticClient();
string exIndex = "preson";
//1.準備多個資料
var data = new List<Man>()
{
   new Man(){Id =1,Name = "范冰冰",Age = 22,Birthday = new DateTime(1990,12,05)},
   new Man(){Id =2,Name = "劉詩詩",Age = 15,Birthday = new DateTime(1880,10,01)},
   new Man(){Id =3,Name = "鬼鬼",Age = 30,Birthday = new DateTime(1988,1,06)},
};
//2.準備Request
var request =new BulkRequest();
request.Operations = data.Select(x => new BulkIndexOperation<Man>(x){Index = exIndex,Type = "man",Id = x.Id } as IBulkOperation).ToList();
//3.通過client執行建立
var result = eSClient.Bulk(request);
//4.輸出
Console.WriteLine(result);
```


#### 批量刪除資料


```csharp=
var eSClient = ESClient.GetEsElasticClient();
string exIndex = "preson";
 //1.準備Request
 var request = new BulkRequest(exIndex, "man")
{
  Operations = new List<IBulkOperation>
  {
     new BulkDeleteOperation<Man>(1),
     new BulkDeleteOperation<Man>(2),
     new BulkDeleteOperation<Man>(3),
  }
};
//2.通過client執行建立
var result = eSClient.Bulk(request);
//3.輸出
Console.WriteLine(result);
```
![](https://i.imgur.com/Cav4Qib.png)

## ElasticSearch練習

:::info
索引&類型名稱統一叫做:
String index = "sms_logs_index";
String type   = "sms_logs_type";
:::

![](https://i.imgur.com/fFywdMG.png)

建立一個類別，並透過attribute-mapping [參考網址](https://www.elastic.co/guide/en/elasticsearch/client/net-api/6.x/attribute-mapping.html)

官網有提到如果用attribute-mapping必須使用.AutoMap()
> When you use attributes, you must also call .AutoMap() for the attributes to be applied.


```csharp=
[ElasticsearchType(Name = "sms_logs_type")]
    public class SmsLog
    {
        /// <summary>
        /// 建立時間 
        /// </summary>
        [Date(Name= "createDate")]
        public DateTime CreateDate { get; set; }
        /// <summary>
        /// 發送時間 
        /// </summary>
        [Date(Name = "sendDate")]
        public DateTime SendDate { get; set; }
        /// <summary>
        /// 發送長號碼 如 16092389287811 
        /// </summary>
        [Text(Name = "longCode")]
        public string LongCode { get; set; }
        /// <summary>
        /// 手機號碼 如 0980685666
        /// </summary>
        [Text(Name = "mobile")]
        public string Mobile { get; set; }
        /// <summary>
        /// 公司名稱
        /// </summary>
        [Text(Name = "corpName",Analyzer = "ik_max_word")]
        public string CorpName { get; set; }
        /// <summary>
        /// 簡訊內容
        /// </summary>
        [Text(Name = "smsContent", Analyzer = "ik_max_word")]
        public string SmsContent { get; set; }
        /// <summary>
        /// 簡訊發送狀態 0 成功 1 失败 integer
        /// </summary>
        [PropertyName("state")]
        public int State { get; set; }
        /// <summary>
        /// 電信商編號1中華電信2遠傳3台灣大哥大 integer
        /// </summary>
        [PropertyName("operatorid")]
        public int Operatorid { get; set; }
        /// <summary>
        /// 城市
        /// </summary>
        [Text(Name = "county")]
        public string County { get; set; }
        /// <summary>
        /// 伺服器IP
        /// </summary>
        [Text(Name = "ipAddr")]
        public string IpAddr { get; set; }
        /// <summary>
        /// 簡訊狀態報告傳回時間 
        /// </summary>
        [PropertyName("replyTotal")]
        public int ReplyTotal { get; set; }
        /// <summary>
        /// 費用
        /// </summary>
        [PropertyName("fee")]
        public int Fee { get; set; }
    }
```

**得到的json樣式**
```json=
{
  "mapping": {
    "sms_logs_type": {
      "properties": {
        "corpName": {
          "type": "text",
          "analyzer": "ik_max_word"
        },
        "county": {
          "type": "text"
        },
        "createDate": {
          "type": "date",
        },
        "fee": {
          "type": "integer"
        },
        "ipAddr": {
          "type": "text"
        },
        "longCode": {
          "type": "text"
        },
        "mobile": {
          "type": "text"
        },
        "operatorid": {
          "type": "integer"
        },
        "replyTotal": {
          "type": "integer"
        },
        "sendDate": {
          "type": "date",
        },
        "smsContent": {
          "type": "text",
          "analyzer": "ik_max_word"
        },
        "state": {
          "type": "integer"
        }
      }
    }
  }
}
```

主程式中在執行
```csharp=
var createIndexResponse = eSClient.CreateIndex("sms_logs_index", c => c.Mappings(ms => ms.Map<SmsLog>(m => m.AutoMap<SmsLog>())));
 Console.WriteLine(createIndexResponse.ToString());
```

準備插入的資料

```csharp=
private static string GetPhone()
        {
            string mobile = "09";
            for (int i = 0; i < 8; i++)
            {
                Thread.Sleep(10);
                mobile += new Random(DateTime.Now.Millisecond).Next(0, 9).ToString();
            }

            return mobile;
        }

      
```

```csharp=
            //1.準備資料
            var longcode = "1008687";
            var companies = new List<string>
            {
                "台積電",
                "統一企業集團",
                "大同集團",
                "長榮航空",
                "旺旺集團",
                "星光遠航",
                "潤泰企業集團",
                "鼎泰豐",
                "東森媒體集團"
            };
            var country = new List<string>
            {
                "台北市",
                "新北市",
                "基隆",
                "宜蘭縣",
                "宜蘭市",
                "新竹市",
                "台南縣",
                "台中市",
                "高雄市"
            };

            var smsContents = new List<string>
            {
                "思辨 事實 是 大便",
                "願快樂揮之不去，讓機遇只爭朝夕，願身體健康如一，讓好運春風化雨，願幸福如期而至，讓情誼日積月累，願片言表我心語，願春節你闔家幸福，事事稱意！‎",
                "辭舊迎新，新年問好。祝君：新年新面貌、新年新氣象、新年新起點、新年新開始、新年新心情、新年新運程、新年新局面、新年新收穫、新年新跨越！辭舊迎新，新年問好。‎",
                "福氣多多，快樂連連，萬事圓圓，微笑甜甜，一帆風順，二龍騰飛，三羊開泰，四季平安，五福臨門，六六大順，七星高照，八方來財，九九同心，十全十美！春節愉快！",
                "辭舊迎新要有底，將快樂進行到底，將健康保持到底，將幸福堅持到底，將吉祥陪伴到底，將平安延續到底，將祝福送到心底：祝新年快樂！‎",
                "鍵盤敲敲，拇指點點，短信寫寫，祝福傳傳，鈴聲響響，屏幕閃閃，友情深深，心頭暖暖，除夕鬧鬧，日曆翻翻，心情美美，日子甜甜，祝愿聲聲，共賀新年！",
                "零時的鐘聲響徹天涯，新年的列車準時出發，它托去一個難忘的歲月，應來了又一個火紅的年華，祝您新年快樂，萬事如意，心想事成！",
                "願歡快的歌聲，時刻縈繞你；願歡樂年華，永遠伴隨您；願歡樂的祝福，永遠追隨您。祝福您：春節愉快，身體健康，闔家歡樂，萬事順意！",
                "願你：大財、小財、意外財，財源滾滾；親情、友情、愛情，情情如意官運；福運、財運、桃花運，運運亨通。新春到來之際，送上我最真摯的祝福：春節快樂！",
                "願你天天順，時時順，分分順，秒秒順，月月順，年年順，愛情順，事業順，身體順，生活順，總之一切都能順，最後還要祝你新年順永遠順！",
                "牛轉乾坤、牛運當頭、牛運亨通、牛年財到、牛氣沖天、牛運旺旺來、牛年吉祥、牛年大吉大利、牛年富貴、牛年紅運、牛年牛市、牛年開泰、牛年安康、牛轉新機"
            };


            var operations = new List<IBulkOperation>();
            for (int i = 0; i < 16; i++)
            {
                var s1 = new SmsLog()
                {
                    CreateDate = new DateTime(2020, 10, 16),
                    SendDate = new DateTime(2020, 10, 16),
                    LongCode = longcode + i,
                    Mobile = GetPhone(),
                    CorpName = companies[i % 9],
                    SmsContent = smsContents[i % 9],
                    State = i % 2,
                    Operatorid = i % 3,
                    County = country[i % 9],
                    IpAddr = "127.0.0." + i,
                    ReplyTotal = i * 3,
                    Fee = i * 6
                };

                operations.Add(new BulkIndexOperation<SmsLog>(s1) { Id = i + 1 });
            }
            //2.準備Request
            var request = new BulkRequest("sms_logs_index", "sms_logs_type")
            {
                Operations = operations
            };
            //3.通過client執行建立
            var result = eSClient.Bulk(request);
            //4.輸出
            Console.WriteLine(result.IsValid);
```

## ElasticSearch的各種查詢

### term & terms 查詢

#### [term查詢](https://www.elastic.co/guide/en/elasticsearch/client/net-api/6.x/query-usage.html)
:::info
term的查询是代表完全匹配，搜尋之前不會對你搜尋的關鍵字進行分詞，直接拿 關鍵字 去文件的分詞庫中匹配内容
:::

```json=
# term查詢
POST /sms_logs_index/sms_logs_type/_search
{
  "from": 0,
  "size": 5,
  "query": {
    "term": {
      "county": {
        "value": "台北市"
      }
    }
  }
}
```
C#執行
```csharp=
var eSClient = ESClient.GetEsElasticClient();
//1.建立Request & 指定查詢條件
var request = new SearchRequest<SmsLog>("sms_logs_index", "sms_logs_type")
{
  From =  0,
  Size = 20,
  Query = new TermQuery
  {
     Field = "county",
     Value = "台北市"
  }
};

//2.執行查詢
var result =eSClient.Search<SmsLog>(request);
//3.輸出結果 __source的資料
var list = new List<SmsLog>();
foreach (var item in result.Hits)
{
   list.Add(item.Source);
   Console.WriteLine(item.Source.CorpName);
}
```

#### [terms查詢](https://www.elastic.co/guide/en/elasticsearch/client/net-api/7.x/terms-query-usage.html)

:::info
terms 和 term 查询的機制一样，搜尋之前不會對你搜尋的關鍵字進行分詞，直接拿 關鍵字 去文件分詞庫中匹配内容
terms:是針對一個字串包含多個數值
term : where county = 台北市
terms: where county = 台北市  or  county = 台中市  (類似于mysql 中的 in)
也可針對 text,  只是在分詞庫中查询的時候不會進行分詞 
:::

```json=
# terms查詢
POST /sms_logs_index/sms_logs_type/_search
{
  "from": 0,
  "size": 5,
  "query": {
    "terms": {
      "county": [
        "台北市",
        "台中市"
      ]
    }
  }
}
```
這裡的Size是每一次查詢回傳得數量，FROM是從第幾筆資料開始查詢
```csharp=
//1.建立Request & 指定查詢條件
var request = new SearchRequest<SmsLog>("sms_logs_index", "sms_logs_type")
{
   From =  0,
   Size =  5,
   Query = new TermsQuery
   {
      Field = "county",
      Terms = new List<object>()
      {
         "台北市",
         "台中市"
      }
   }
};

//2.執行查詢
var result =eSClient.Search<SmsLog>(request);
//3.輸出結果 __source的資料
var list = new List<SmsLog>();
foreach (var item in result.Hits)
{
  list.Add(item.Source);
  Console.WriteLine(item.Source.CorpName);
}
```

### [match查詢](https://www.elastic.co/guide/en/elasticsearch/client/net-api/7.x/multi-match-usage.html)

:::info
match 查詢屬於高級查詢，會根據你查詢的字串類型不一樣，採用不同的查詢方式:
-    查詢的是日期或者數字，它會將基於你查詢得字串內容轉換為同等的日期或數字
-    如果查詢的內容是一个不能被分詞的内容（keyword）,match 不會將你指定的關鍵字進行分詞
-    如果查詢的内容是一个可以被分詞的内容（text）,match 查詢會將你指定的内容根據一定的方式進行分詞，去分詞庫中匹配指定的內容
**match 查询，實際上底層就是多個term 查询，將多個term查詢的结果给你封裝到一起**
:::

#### math_all
:::info
查詢全部内容，不指定查詢條件
:::
```json=
POST /sms_logs_index/sms_logs_type/_search
{
    "query":{
    "match_all": {}
     }
}
```

C#執行

```csharp=
//1.建立Request & 指定查詢條件
var request = new SearchRequest<SmsLog>("sms_logs_index", "sms_logs_type")
{
  From = 0,
  Size = 20,//默認值是10
  Query = new MatchAllQuery()
};

//2.執行查詢
var result =eSClient.Search<SmsLog>(request);
//3.輸出結果 __source的資料
var list = new List<SmsLog>();
foreach (var item in result.Hits)
{
  list.Add(item.Source);
  Console.WriteLine(item.Source.CorpName);
}
```

#### match 查询
:::info
指定一個field 作為查詢條件
:::
```json=
#match 查询
POST /sms_logs_index/sms_logs_type/_search
{
  "query": {
    "match": {
      "smsContent": "排時間"
    }
  }
}
```

```csharp=
//1.建立Request & 指定查詢條件
var request = new SearchRequest<SmsLog>("sms_logs_index", "sms_logs_type")
{
  From = 0,
  Size = 20,//默認值是10
  Query = new MatchQuery()
  {
     Field = "smsContent",
     Query = "排時間"
  }
};

//2.執行查詢
var result =eSClient.Search<SmsLog>(request);
//3.輸出結果 __source的資料
var list = new List<SmsLog>();
foreach (var item in result.Hits)
{
  list.Add(item.Source);
  Console.Write(item.Id +" ");
  Console.WriteLine(item.Source.CorpName);
}
```

#### bool match 查询
:::info
基於一個field 匹配的内容，按照 and 或者or的方式連接
:::

> 既包含認為 也必須包含 最壞
```json=
#布林match查询
POST /sms_logs_index/sms_logs_type/_search
{
  "query": {
    "match": {
      "smsContent": {
        "query": "認為 最壞",
        "operator": "and"
      }
    }
  }
}
```

C#
```csharp=
//1.建立Request & 指定查詢條件
            var request = new SearchRequest<SmsLog>("sms_logs_index", "sms_logs_type")
            {
                From = 0,
                Size = 20,//默認值是10
                Query = new MatchQuery()
                {
                    Field = "smsContent",
                    Query = "認為 最壞",
                    Operator = Operator.And
                }
            };

            //2.執行查詢
            var result =eSClient.Search<SmsLog>(request);
            //3.輸出結果 __source的資料
            var list = new List<SmsLog>();
            foreach (var item in result.Hits)
            {
                list.Add(item.Source);
                Console.Write(item.Id +" ");
                Console.WriteLine(item.Source.CorpName);
            }
        }
```

> 既包含認為 或者包含 最壞
```json=
#布林match查询
POST /sms_logs_index/sms_logs_type/_search
{
  "query": {
    "match": {
      "smsContent": {
        "query": "認為 最壞",
        "operator": "or"
      }
    }
  }
}
```
C#
```csharp=
//1.建立Request & 指定查詢條件
            var request = new SearchRequest<SmsLog>("sms_logs_index", "sms_logs_type")
            {
                From = 0,
                Size = 20,//默認值是10
                Query = new MatchQuery()
                {
                    Field = "smsContent",
                    Query = "認為 最壞",
                    Operator = Operator.Or
                }
            };

            //2.執行查詢
            var result =eSClient.Search<SmsLog>(request);
            //3.輸出結果 __source的資料
            var list = new List<SmsLog>();
            foreach (var item in result.Hits)
            {
                list.Add(item.Source);
                Console.Write(item.Id +" ");
                Console.WriteLine(item.Source.CorpName);
            }
        }
```

#### multi_match

:::info
match 針對一個field 做搜索，multi_math 針對多個field 進行搜索，多個field對應一個文本。
:::

```json=
#multi_math 查詢
POST /sms_logs_index/sms_logs_type/_search
{
  "query":{
    "multi_match": {
      "query": "台北市",
      "fields": ["county","smsContent"]
    }
  }
}
```
C# 

```csharp=
//1.建立Request & 指定查詢條件
var request = new SearchRequest<SmsLog>("sms_logs_index", "sms_logs_type")
{
  From = 0,
  Size = 20,//默認值是10
  Query = new MultiMatchQuery()
  {
    Query = "台北市",
    Fields = new Field("county").And("smsContent")
  }
};

//2.執行查詢
var result =eSClient.Search<SmsLog>(request);
//3.輸出結果 __source的資料
var list = new List<SmsLog>();
foreach (var item in result.Hits)
{
  list.Add(item.Source);
  Console.Write(item.Id +" ");
  Console.WriteLine(item.Source.CorpName);
}
```

### 其他查詢

#### id 查詢
```json=
#id 查詢
GET /sms_logs_index/sms_logs_type/1
```

```csharp=
            //1.建立Request & 指定查詢條件
            var request = new GetRequest("sms_logs_index", "sms_logs_type", "1");

            //2.執行查詢
            var result =eSClient.Get<SmsLog>(request);
            //3.輸出結果 __source的資料
            var list = new List<SmsLog>();
            list.Add(result.Source);
            Console.Write(result.Id + " ");
            Console.WriteLine(result.Source.CorpName);
```

#### id 查詢
:::info
根據多個id 查詢,類似 mysql 中的 where in (id1,id2...)
:::

```json=
#ids 查询
POST /sms_logs_index/sms_logs_type/_search
{
  "query": {
    "ids": {
      "values": ["1","2","3"]
    }
  }
}
```

```csharp=
//1.建立Request & 指定查詢條件
            var request = new SearchRequest<SmsLog>("sms_logs_index", "sms_logs_type")
            {
                From = 0,
                Size = 20,//默認值是10
                Query = new IdsQuery()
                {
                    Values = new List<Id>()
                    {
                        new Id("1"),
                        new Id("2"),
                        new Id("3")
                    }
                }
            };

            //2.執行查詢
            var result =eSClient.Search<SmsLog>(request);
            //3.輸出結果 __source的資料
            var list = new List<SmsLog>();
            foreach (var item in result.Hits)
            {
                list.Add(item.Source);
                Console.Write(item.Id + " ");
                Console.WriteLine(item.Source.CorpName);
            }
```

#### prefix 查询
:::info
前缀查詢，可以通過一個關鍵字去指定一個field 的前缀，從而查詢到指定文件
:::

```json=
#prefix 查询
POST /sms_logs_index/sms_logs_type/_search
{
  "query": {
    "prefix": {
      "corpName": {
        "value": "台"
      }
    }
  }
}
```
**在這裡什麼都查不到 和上面的prefix 做比較**
```json=
#match 查詢 
POST /sms_logs_index/sms_logs_type/_search
{
  "query": {
    "match": {
      "corpName": "台"
    }
  }
}
```

```csharp=
 //1.建立Request & 指定查詢條件
            var request = new SearchRequest<SmsLog>("sms_logs_index", "sms_logs_type")
            {
                From = 0,
                Size = 20,//默認值是10
                Query = new PrefixQuery()
                {
                    Field = "corpName",
                    Value = "台"
                }
            };

            //2.執行查詢
            var result =eSClient.Search<SmsLog>(request);
            //3.輸出結果 __source的資料
            var list = new List<SmsLog>();
            foreach (var item in result.Hits)
            {
                list.Add(item.Source);
                Console.Write(item.Id + " ");
                Console.WriteLine(item.Source.CorpName);
            }
        
```

#### fuzzy  查询
:::info
模糊查詢，我们可以输入一個大概的字串，ES 可以根據输入的大概去匹配内容。查尋结果不穩定
:::

prefix_length=>用來指定前面幾個字，是不允許不一樣
```json=
#fuzzy 查询
POST /sms_logs_index/sms_logs_type/_search
{
  "query": {
    "fuzzy": {
      "county": {
        "value": "台北市",
        "prefix_length": 1
      }
    }
  }
}
```

```csharp=
//1.建立Request & 指定查詢條件
var request = new SearchRequest<SmsLog>("sms_logs_index", "sms_logs_type")
{
   From = 0,
   Size = 20,//默認值是10
   Query = new FuzzyQuery()
   {
      Field = "county",
      Value = "台北市",
      PrefixLength = 1
   }
};

//2.執行查詢
var result =eSClient.Search<SmsLog>(request);
//3.輸出結果 __source的資料
var list = new List<SmsLog>();
foreach (var item in result.Hits)
{
   list.Add(item.Source);
   Console.Write(item.Id + " ");
   Console.WriteLine(item.Source.County);
}
```

#### wildcard 查询
:::info
通配查詢，同mysql中的like 是一樣的，可以在查詢时，在字串中指定通配符『*』和佔位符『？』
:::

```json=
#wildcard 查詢
POST /sms_logs_index/sms_logs_type/_search
{
  "query": {
    "wildcard": {
      "county": {
        "value": "台北*"
      }
    }
  }
}
```

```json=
#wildcard 查詢
POST /sms_logs_index/sms_logs_type/_search
{
  "query": {
    "wildcard": {
      "county": {
        "value": "台北?"
      }
    }
  }
}
```

```csharp=
 //1.建立Request & 指定查詢條件
            var request = new SearchRequest<SmsLog>("sms_logs_index", "sms_logs_type")
            {
                From = 0,
                Size = 20,//默認值是10
                Query = new WildcardQuery()
                {
                    Field = "county",
                    Value = "台北*",
                }
            };

            //2.執行查詢
            var result =eSClient.Search<SmsLog>(request);
            //3.輸出結果 __source的資料
            var list = new List<SmsLog>();
            foreach (var item in result.Hits)
            {
                list.Add(item.Source);
                Console.Write(item.Id + " ");
                Console.WriteLine(item.Source.County);
            }
        
```

#### [Rang  查询](https://www.elastic.co/guide/en/elasticsearch/client/net-api/7.x/numeric-range-query-usage.html)
:::info
範圍查詢，只針對數值類型，對一個field 進行大於或者小於的範圍指定
:::


查詢fee價錢在6~18之間的資料
```json=
#rang 查詢
POST /sms_logs_index/sms_logs_type/_search
{
  "query": {
    "range": {
      "fee": {
        "gte": 6,
        "lte": 18
      }
    }
  }
}
```
C#版:
:memo: Field要注意，大小寫是不一樣得
```csharp=
//1.建立Request & 指定查詢條件
            var request = new SearchRequest<SmsLog>("sms_logs_index", "sms_logs_type")
            {
                From = 0,
                Size = 20,//默認值是10
                Query = new NumericRangeQuery()
                {
                    Field = "fee",
                    GreaterThanOrEqualTo = 6,
                    LessThanOrEqualTo = 18
                }
            };

            //2.執行查詢
            var result =eSClient.Search<SmsLog>(request);
            //3.輸出結果 __source的資料
            var list = new List<SmsLog>();
            foreach (var item in result.Hits)
            {
                list.Add(item.Source);
                Console.Write(item.Id + " ");
                Console.WriteLine(item.Source.County);
            }
        }
```
#### [Regexp 查询](https://www.elastic.co/guide/en/elasticsearch/client/net-api/7.x/regexp-query-usage.html)
:::info
正則查詢，通過編寫你編寫得正則表達式去匹配內容
Ps:prefix wildcard  fuzzy 和regexp 查詢效率比較低,在要求效率比較高時，避免使用
:::

```json=
#regexp 查詢
POST /sms_logs_index/sms_logs_type/_search
{
  "query": {
    "regexp": {
      "mobile": "0964[0-9]{6}"
    }
  }
}
```

C# 版本:
```csharp=
//1.建立Request & 指定查詢條件
var request = new SearchRequest<SmsLog>("sms_logs_index", "sms_logs_type")
{
    From = 0,
    Size = 20,//默認值是10
    Query = new RegexpQuery()
    {
        Field = "mobile",
        Value = "0964[0-9]{6}"
    }
};
//2.執行查詢
var result =eSClient.Search<SmsLog>(request);
//3.輸出結果 __source的資料
var list = new List<SmsLog>();
foreach (var item in result.Hits)
{
  list.Add(item.Source);
  Console.Write(item.Id + " ");
  Console.WriteLine(item.Source.County);
}
```

### 深分頁 scroll

:::info
ES 對from +size對是有限制的，什麼限制呢？
from +size 之和 不能大於1W,超過後 效率會十分低
原理：

- from+size  ES查詢數據的方式，
1.   第一步將用户指定的關鍵字進行分詞，
2.   第二步將詞彙去分詞庫中進行搜索，得到多个文档id,
3.   第三步去各個分片中拉取資料，耗時相對比較長
4.   第四步根據score 將資料進行排序， 耗時相對比較長
5.   第五步根據from 和size 的值 將部分資料捨棄，
6.   第六步，返回结果。
  
- scroll +size ES 查詢資料的方式
1.   第一步將用戶指定的關鍵字進行分詞，
2.   第二步將詞彙去分詞庫中進行搜索，得到多個文件id,
3.   第三步將文件的id放在一个上下文中
4.   第四步根據指定的size去ES中搜索指定個數資料，拿完數據的文件id,會從上下文中移除
5.   第五步如果需要下一頁的資料，直接去ES的上下文中找後續内容。
6.   第六步循環第四步和第五步

scroll 不適合做立即查询，因為它會先把資料存到記憶體當中

如果沒有使用scroll ES在查詢下一個分頁的時候會從第一步執行到第六步，
但使用scroll的話每一次查詢下一個分頁的時候，是從第四步到第六步
:::


```json=
#scroll 查詢,傳回第一頁的資料，並將文件id信息存放在ES上下文中，並指定存活時間
POST /sms_logs_index/sms_logs_type/_search?scroll=1m
{
  "query": {
    "match_all": {}
  },
  "size": 2,
  "sort": [
    {
      "fee": {
        "order": "desc"
      }
    }
  ]
}
#根據scroll 查詢下一頁的資料
POST _search/scroll
{
  "scroll_id":"DnF1ZXJ5VGhlbkZldGNoBQAAAAAAAwUbFmVsZS02ejJaUk5lTkwzVlFkMGMwS1EAAAAAAAMFGhZlbGUtNnoyWlJOZU5MM1ZRZDBjMEtRAAAAAAADBRwWZWxlLTZ6MlpSTmVOTDNWUWQwYzBLUQAAAAAAAwUdFmVsZS02ejJaUk5lTkwzVlFkMGMwS1EAAAAAAAMFHhZlbGUtNnoyWlJOZU5MM1ZRZDBjMEtR",
  "scroll":"1m"
}
#刪除scroll上下文中的資料
DELETE _search/scroll/DnF1ZXJ5VGhlbkZldGNoBQAAAAAAAwUbFmVsZS02ejJaUk5lTkwzVlFkMGMwS1EAAAAAAAMFGhZlbGUtNnoyWlJOZU5MM1ZRZDBjMEtRAAAAAAADBRwWZWxlLTZ6MlpSTmVOTDNWUWQwYzBLUQAAAAAAAwUdFmVsZS02ejJaUk5lTkwzVlFkMGMwS1EAAAAAAAMFHhZlbGUtNnoyWlJOZU5MM1ZRZDBjMEtR
```

![](https://i.imgur.com/yYnbk1t.png)

[sort](https://www.elastic.co/guide/en/elasticsearch/client/net-api/7.x/sort-usage.html)

C#版本:
```csharp=
//1.建立Request & 指定查詢條件
            var request = new SearchRequest<SmsLog>("sms_logs_index", "sms_logs_type")
            {
                From = 0,
                Size = 2,//默認值是10
                Sort = new List<ISort>()
                {
                    new SortField() { Field = "fee", Order = SortOrder.Descending }
                },
                Scroll = Time.MinusOne//指定scroll 過期時間
            };
            //2.執行查詢
            var response = eSClient.Search<SmsLog>(request);
            Console.WriteLine("-------------第一筆資料--------------");
            var list = new List<SmsLog>();
            //3.輸出結果 __source的資料
            foreach (var item in response.Hits)
            {
 
                list.Add(item.Source);
                Console.Write(item.Id + " ");
                Console.WriteLine(item.Source.County);
            }
            while (true)
            {
                //5.建立scroll request,指定scroll Id 以及有效時間
                var scrollRequest = new ScrollRequest(response.ScrollId, new Time(1));
                //6.執行查詢，返回查詢結果
                var scroll = eSClient.Scroll<SmsLog>(scrollRequest);
                //7.判斷是否查詢到資料，查詢到輸出
                if (scroll.Documents.Any())
                {
                    Console.WriteLine("-------------下一頁資料------------------");
                    var searchHits = scroll.Hits;
                    foreach (var item in searchHits)
                    {
                        list.Add(item.Source);
                        Console.WriteLine(item.Source.County);
                    }
                }
                else
                {
                    //8.沒有資料，結束
                    Console.WriteLine("----------------结束---------------------");
                    break;
                }
            }
            //刪除scroll上下文中的資料
            //1.建立clearScrollRequest 並指定ScrollId
            var clearScrollRequest = new ClearScrollRequest(response.ScrollId);
            //2.透過client 執行删除scroll
            var clearScrollResponse = eSClient.ClearScroll(clearScrollRequest);
            //3.輸出結果
            Console.WriteLine("刪除scroll: " + clearScrollResponse.IsValid);
```


### delete-by-query
:::info
如果要刪除指定條件的資料，可以使用delete-by-query
PS:如果你要刪除的內容，是index下的大部分的資料，推薦建立一個新的index,然后把保留的資料内容，加入到全新的索引
:::

```json=
#Delet-by-query 刪除
POST /sms_logs_index/sms_logs_type/_delete_by_query
{
   "query": {
    "range": {
      "fee": {
        "lt": 6
      }
    }
  }
}
```

![](https://i.imgur.com/6kHxzQL.png)

```csharp=
//1.建立Request & 指定查詢條件
            var resquest = new DeleteByQueryRequest<SmsLog>("sms_logs_index", "sms_logs_type")
            {
                Query = new QueryContainer(
                    new NumericRangeQuery()
                    {
                        Field = "fee",
                        GreaterThanOrEqualTo = 6,
                    }
                )
            };
            //2.通過client執行建立
            var response = eSClient.DeleteByQuery(resquest);
            //3.輸出結果
            Console.WriteLine(response.IsValid);
```

### [複合查詢](https://www.elastic.co/guide/en/elasticsearch/client/net-api/7.x/bool-queries.html)
:::info
複合過濾器，將你的多個查詢條件 以一定的邏輯組合再一起，

must:所有條件組合在一起，表示 and 的意思
must_not: 將must_not中的條件，全部都不能匹配，表示not的意思
should:所有條件用should 組合再一起，表示or 的意思
:::

#### bool查詢


```json=
#縣市是台北市或高雄市
# operatorid不能是2
#smsContent 包含 事實 和 思辨
POST /sms_logs_index/sms_logs_type/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "term": {
            "county": "台北市"
          }
          
        },
         {
          "term": {
            "county": "高雄市"
            
          }
          
        }
      ],
      "must_not": [
        {
          "term": {
            "operatorid": "2"
          }
        }
      ],
      "must": [
        {
          "match": {
            "smsContent": "事實"
          }
        },
        {
           "match": {
            "smsContent": "思辨"
          }
        }
      ]
    }
  }
}
```
C#版本
```csharp=
            //1.建立Request & 指定查詢條件
            var resquest = new SearchRequest<SmsLog>("sms_logs_index", "sms_logs_type")
            {
                Size = 20,
                Query = (new TermQuery { Field = "county", Value = "台北市" }   ||
                         new TermQuery { Field = "county", Value = "高雄市" } )  & 
                        !new TermQuery { Field = "operatorid", Value = "2" }    &
                         new MatchQuery { Field = "smsContent", Query = "事實" } &
                         new MatchQuery { Field = "smsContent", Query = "思辨" }
            };
            //2.通過client執行建立
            var response = eSClient.Search<SmsLog>(resquest);
            //3.輸出結果
            Console.WriteLine(response.IsValid);
            foreach (var item in response.Hits)
            {
                Console.WriteLine(item.Id);
                Console.WriteLine(item.Source.County);
            }
```

####  boosting 查询(影響分數)

:::info
boosting查詢，可以幫助我們去影響查詢後的分數(score)
-   positive: 只有匹配上positive 查詢的内容，才會被放到返回到結果集合中
-   negative: 如果匹配上了positive 也匹配上了negative，就可以降低資料的分數(score)
-   negative_boost:指定係數,必須小於1=>如果匹配上上述兩個條件分數就會乘上這個係數
   
關於查詢時，分數如何計算的：
-	搜索關鍵字在資料中出現的頻率越高，分數越高
-	指定的資料內容越短，分數越高。
-	我們在搜尋時，指定的關鍵字也會被分詞，這個被分詞的內容，被分詞庫匹配的個數越多，分數越高。
:::

```json=
#boosting 查询
POST /sms_logs_index/sms_logs_type/_search
{
  "query": {
    "boosting": {
      "positive": {
        "match": {
          "smsContent": "思辨"
        }
      }, 
      "negative": {
        "match": {
          "smsContent": "損失"
        }
      },
      "negative_boost": 0.2
    }
  }
}
```
```csharp=
 //1.建立Request & 指定查詢條件
            var resquest = new SearchRequest<SmsLog>("sms_logs_index", "sms_logs_type")
            {
                Size = 20,
                Query = new BoostingQuery
                {
                    PositiveQuery = new MatchQuery()
                    {
                        Field = "smsContent",
                        Query = "思辨"
                    },
                    NegativeQuery = new MatchQuery()
                    {
                        Field = "smsContent",
                        Query = "損失"
                    },
                    NegativeBoost = 0.2
                }
            };
            //2.通過client執行建立
            var response = eSClient.Search<SmsLog>(resquest);
            //3.輸出結果
            Console.WriteLine(response.IsValid);
            foreach (var item in response.Hits)
            {
                Console.WriteLine(item.Id);
                Console.WriteLine(item.Source.County);
            }
```

####  filter  查询
:::info
query 查詢：根據你查詢的條件，去計算資料的匹配度得到一個分數，並根據分數作排序，不會做快取的。

filter 查詢：根據查詢條件去查詢document，不去計算分數，而且filter會對經常被過濾的資料進行快取。
:::

```json=
#filter 查询
POST /sms_logs_index/sms_logs_type/_search
{
  "query": {
    "bool": {
      "filter": [
        {
          "term": {
            "corpName": "台"
           }
        },
        {
          "range":{
            "fee":{
              "lte": 400
            }
          }
        }
      ]
    }
  }
}
```

#### Highlight 查询
:::info
Highlight查詢就是用戶輸入的關鍵字，以一定的特殊樣式展示給用戶看，讓用戶知道為什麼是這個結果被搜索出來

Highlight展示的資料，本身就是document中的一個field,單獨將field以highlight的形式返回給用戶

ES提共了一個highlight 屬性，他和query 同級別。
- frament_size: 指定highlight資料展示多長的字串回來
- pre_tags:指定前綴標籤`<front color="red">`
- post_tags:指定後綴標籤 `</font>`
:::

```json=
#highlight 查询
POST /sms_logs_index/sms_logs_type/_search
{
  "query": {
    "match": {
      "smsContent": "思辨"
    }
  },
  "highlight": {
    "fields": {
      "smsContent":{}
    },
    "pre_tags":"<font color='red'>",
    "post_tags":"</font>",
    "fragment_size":10
  }
}
```


#### 聚合查詢
:::info
ES的聚合查詢和mysql 的聚合查詢類似，ES的聚合查詢相比mysql 要強大的多。ES提供的統計資料的方式多種多樣。
:::

聚合查詢與一般查詢語法略為不同，以下是基本格式:
```json=
#ES 聚合查询的RSTFul 语法
POST /index/type/_search
{
    "aggs":{
        "(名字)agg":{
            "agg_type":{
                "属性"："值"
            }
        }
    }
}
```

##### cardinality  去除重複資料的記數的聚合查询
:::info
去重計數，cardinality 統計document中的一個指定的field，除去重複的資料，統計一共有多少條
:::
譬如:
|Name |居住地   |
|-----|--------|
|張景嵐|台北市   |
|劉詩詩|台南市   |
|郭雪芙|高雄市   |
|林依晨|台南市   |
|溫妮  |台南市   |

這時候如果使用cardinality統計居住地 計算結果會是 3


```json=
# 去重計算 查询 county
POST /sms_logs_index/sms_logs_type/_search
{
  "aggs": {
    "provinceAgg": {
      "cardinality": {
        "field": "county"
      }
    }
  }
}
```

##### 範圍統計
:::info
統計一個範圍出現的資料筆數，譬如，針對某一個field 的值再0~100,100~200,200~300 之間資料出現的各數分別數少

範圍統計 可以針對 普通的數值，針對時間類型，針對ip類型都可以響應。
數值 rang    
時間  date_rang     
IP   ip_rang
:::

以下會統計這三個範圍:
* 小於30以下的資料
* 大於等於30 小於60的資料
* 大於等於60的資料
```json=
#針對數值  from 的意思是 大於等於 ，to 是小於
POST /sms_logs_index/sms_logs_type/_search
{
  "aggs": {
    "agg": {
      "range": {
        "field": "fee",
        "ranges": [
          {
            "to": 30
          },
           {
            "from": 30,
            "to": 60
          },
          {
            "from": 60
          }
        ]
      }
    }
  }
}
```
```json=
#時間方式統計
POST /sms_logs_index/sms_logs_type/_search
{
  "aggs": {
    "agg": {
      "date_range": {
        "field": "sendDate",
        "format": "yyyy", 
        "ranges": [
          {
            "to": "2000"
          },{
            "from": "2000"
          }
        ]
      }
    }
  }
}
```
```json=
#ip 方式 範圍統計
POST /sms_logs_index/sms_logs_type/_search
{
  "aggs": {
    "agg": {
      "ip_range": {
        "field": "ipAddr",
        "ranges": [
          {
            "to": "127.0.0.8"
          },
          {
            "from": "127.0.0.8"
          }
        ]
      }
    }
  }
}
```


##### 統計聚合
:::info
可以查指定的field 的最大值，最小值，平均值，平方和...
使用 extended_stats
:::

這裡的agg 可自由命名
```json=
#統計聚合查詢 extended_stats
POST /sms_logs_index/sms_logs_type/_search
{
  "aggs": {
    "agg": {
      "extended_stats": {
        "field": "fee"
      }
    }
  }
}
```
輸出結果:
![](https://i.imgur.com/qR56Uv9.png)

##### 其他查詢

[官方的document](https://www.elastic.co/guide/en/elasticsearch/reference/6.8/search-aggregations-metrics-weight-avg-aggregation.html)

### 地圖經緯度查詢

#### 準備資料

請注意type要使用geo_point
```json=
#建立一個經緯度索引,指定一個name ,一個location
PUT /map
{
  "settings": {
    "number_of_shards": 5,
    "number_of_replicas": 1
  },
  "mappings": {
    "map":{
      "properties":{
        "name":{
          "type":"text"
        },
        "location":{
          "type":"geo_point"
        }
      }
    }
  }
}

#加入測試數據
PUT /map/map/1
{
 "name":"101購物中心",
  "location":{
    "lon": 121.5765668,
    "lat": 25.0299653
  }
}

PUT /map/map/2
{
  "name":"國立國父紀念館",
  "location":{
    "lon": 121.5757755,
    "lat": 25.0347843
  }
}

PUT /map/map/3
{
  "name":"Miramar美麗華摩天輪",
  "location":{
    "lon": 121.5835182,
    "lat": 25.025217
  }
}
```

#### ES 的地圖搜索方式
:::info
geo_distance :直線距離搜索方式
geo_bounding_box: 以兩個點圍成一個矩形，獲取矩形內的資料
geo_polygon:以多個點，圍成一個多邊形，獲取多邊形內的全部資料
:::

####  基於RESTFul 實現地圖搜索

**geo_distance**

查詢指定地點為圓心，周遭2千公尺內的物件資料
```json=
#geo_distance 
POST /map/map/_search
{
  "query": {
    "geo_distance":{
      "location":{
        "lon":121.5660955,
        "lat":25.0383058
      },
      "distance":2000,
      "distance_type":"arc"
    }
  }
}
```
![經緯度查詢](https://i.imgur.com/F0jPl0d.png)

以兩個點圍成一個矩形，獲取矩形內的資料
```json=
#geo_bounding_box
POST /map/map/_search
{
  "query":{
    "geo_bounding_box":{
      "location":{
        "top_left":{
          "lon": 121.5427135,
          "lat": 25.0490381
        },
        "bottom_right":{
          "lon": 121.584229,
          "lat": 25.027229
        }
      }
    }
  }
}
```
以多個點，圍成一個多邊形，獲取多邊形內的全部資料
```json=
#geo_polygon
POST /map/map/_search
{
  "query":{
    "geo_polygon":{
      "location":{
       "points":[
         {
          "lon": 121.5427135,
          "lat": 25.0490381
         },
          {
           "lon": 121.5795951,
           "lat": 25.0370384

         },
         {
          "lon": 121.584229,
          "lat": 25.027229
         } 
        ]
      }
    }
  }
}
```