1. 程式人生 > 實用技巧 >Linq 巢狀子查詢

Linq 巢狀子查詢

Elasticsearch

安裝與使用

下載地址

ElasticSearch: https://mirrors.huaweicloud.com/elasticsearch/?C=N&O=D
logstash: https://mirrors.huaweicloud.com/logstash/?C=N&O=D
kibana: https://mirrors.huaweicloud.com/kibana/?C=N&O=D
ik分詞器: https://github.com/medcl/elasticsearch-analysis-ik/releases
視覺化外掛es head: https://github.com/mobz/elasticsearch-head

ElasticSearch

  1. 解壓ElasticSearch

  2. 解壓es head

  3. 在es head目錄下npm install

  4. 啟動ElasticSearch

  5. 訪問http://localhost:9200/檢視是否啟動

  6. 在es head目錄下npm run start

  7. 訪問http://localhost:9100/進行連線

  8. 出現跨域問題,在elasticsearch.yml配置檔案中加入允許跨域

    http.cors.enabled: true
    http.cors.allow-origin: "*"
    
  9. 重新啟動並連線

Kibana

  1. Kibana版本要與Elasticsearch一致

  2. 解壓kibana

  3. 啟動kibana

  4. 訪問http://localhost:5601/

  5. 使用中文介面,在kibana.yml中新增

    i18n.locale: "zh-CN"
    
  6. 重新啟動

ik分詞器

  1. ik分詞器版本要與Elasticsearch一致

  2. 解壓至Elasticsearch的plugins資料夾下

  3. 啟動Elasticsearch

  4. 通過命令可以看到一使用的外掛

    elasticsearch-plugin list 
    
  5. 在kibana的控制檯中測試

  6. 最少切分ik_smart

    GET _analyze
    {
      "analyzer": "ik_smart"
      , "text": "大頭兒子"
    }
    //分詞結果
    {
      "tokens" : [
        {
          "token" : "大頭",
          "start_offset" : 0,
          "end_offset" : 2,
          "type" : "CN_WORD",
          "position" : 0
        },
        {
          "token" : "兒子",
          "start_offset" : 2,
          "end_offset" : 4,
          "type" : "CN_WORD",
          "position" : 1
        }
      ]
    }
    
  7. 最細粒度劃分ik_max_word

    GET _analyze
    {
      "analyzer": "ik_max_word"
      , "text": "大頭兒子"
    }
    //分詞結果
    {
      "tokens" : [
        {
          "token" : "大頭",
          "start_offset" : 0,
          "end_offset" : 2,
          "type" : "CN_WORD",
          "position" : 0
        },
        {
          "token" : "頭兒",
          "start_offset" : 1,
          "end_offset" : 3,
          "type" : "CN_WORD",
          "position" : 1
        },
        {
          "token" : "兒子",
          "start_offset" : 2,
          "end_offset" : 4,
          "type" : "CN_WORD",
          "position" : 2
        }
      ]
    }
    
  8. 新增自定義詞彙,在config目錄下新建my.dic,加入自定義詞彙"肚肚肚"

  9. 在IKAnalyzer.cfg.xml中載入自定義詞典

    <entry key="ext_dict">my.dic</entry>
    
  10. 重啟Elasticsearch

  11. 測試

    GET _analyze
    {
      "analyzer": "ik_smart"
      , "text": "肚肚肚"
    }
    //分詞結果
    {
      "tokens" : [
        {
          "token" : "肚肚肚",
          "start_offset" : 0,
          "end_offset" : 3,
          "type" : "CN_WORD",
          "position" : 0
        }
      ]
    }
    

索引操作

新建索引 PUT

PUT /索引名/型別名/文件id
{請求體}

PUT /test1/type1/1
{
  #資料請求體
  "name": "pinked",
  "age": 7
}


PUT test2
{
  #規則請求體
  "mappings": {
    "properties": {
      "name": {
        "type": "text"		#text屬性的欄位會被分詞器解析,keyword屬性的欄位不會
      },
      "age": {
        "type": "integer"
      },
      "birthday": {
        "type": "date"
      }
    }
  }
}

查詢索引 GET

GET 索引名

GET test1

#查詢結果
{
  "test1" : {
    "aliases" : { },
    "mappings" : {
      "properties" : {
        "age" : {
          "type" : "long"
        },
        "name" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        }
      }
    },
    "settings" : {
      "index" : {
        "creation_date" : "1596707098264",
        "number_of_shards" : "1",
        "number_of_replicas" : "1",
        "uuid" : "V4sVrZnuRS-ip_FHbSdupg",
        "version" : {
          "created" : "7080099"
        },
        "provided_name" : "test1"
      }
    }
  }
}

更新索引 PUT/POST

#通過PUT覆蓋更新
PUT /test1/type1/1
{
  "name": "pinked",
  "age": 8
}

#結果
{
  "_index" : "test1",
  "_type" : "type1",
  "_id" : "1",
  "_version" : 2,   		#版本號會增加
  "result" : "updated",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 1,
  "_primary_term" : 1
}

#通過POST修改更新
POST /test1/_update/1
{
  "doc": {
    "age": "9"
  }
}

#結果
{
  "_index" : "test1",
  "_type" : "type1",
  "_id" : "1",
  "_version" : 3,
  "result" : "updated",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 2,
  "_primary_term" : 1
}

刪除索引 DELETE

DELETE test1

文件操作

#新增資料
PUT /pinked/user/1
{
  "name": "野原新之助",
  "age": 5
}
PUT /pinked/user/2
{
  "name": "野比大雄",
  "age": 10
}
PUT /pinked/user/3
{
  "name": "胖虎",
  "age": 10
}

#簡單搜尋
GET /pinked/user/2

#條件搜尋_search?q=k:v
GET /pinked/user/_search?q=age:10

複雜查詢

GET /pinked/user/_search
{
  "query": {
    "match": {
      "name": "野"
    }
  },
  "_source": ["name"],		#選擇欄位
  "sort": [					#排序
      {
        "age": {
          "order": "desc"
      }
    }
  ],
  "from": 0,				#分頁
  "size": 1
}

#多條件查詢
GET /pinked/user/_search
{
  "query": {
    "bool": {
      "must": [				#must同and, should同or
        {
          "match": {
            "name": "野"
          }
        },
        {
          "match": {
            "age": 5
          }
        }
      ]
    }
  }
}

#
GET /pinked/user/_search
{
  "query": {
    "bool": {
      "must": [				
        {
          "match": {
            "name": "野"
          }
        }
      ],
      "filter": {			#過濾器
        "range": {			#範圍
          "age": {
            "gt": 3,		#大於3
            "lte": 5		#小於等於5
          }
        }
      }
    }
  }
}

#結果高亮
GET /pinked/user/_search
{
  "query": {
    "match": {
      "name": "野 大"
    }
  },
  "highlight": {
  	#"pre_tags": "<span style='color:red'>",  	#自定義標籤字首
    #"post_tags": "</span>", 					#自定義標籤字尾
    "fields": {
      "name": {}			#高亮的欄位,會被<em>標籤包裹
    }
  }
}

與SpringBoot整合

新建一個springboot專案

相關依賴

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-configuration-processor</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
        <exclusions>
            <exclusion>
                <groupId>org.junit.vintage</groupId>
                <artifactId>junit-vintage-engine</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
</dependencies>
spring data中匯入的es依賴版本是7.6.2的,可以自定義匯入的版本,儘量與本地的es版本保持一致
<properties>
    <java.version>1.8</java.version>
    <!--自定義es依賴版本至7.8.0-->
    <elasticsearch.version>7.8.0</elasticsearch.version>
</properties>

配置檔案

@Configuration
public class ElasticSearchClientConfig {
    @Bean
    public RestHighLevelClient restHighLevelClient() {
        RestHighLevelClient client = new RestHighLevelClient(
                RestClient.builder(
                        new HttpHost("localhost", 9200, "http")));
        return client;
    }
}

獲得客戶端

@Autowired
@Qualifier("restHighLevelClient")
private RestHighLevelClient client;

索引操作

//建立索引
@Test
void createIndex() throws IOException {
    CreateIndexRequest request = new CreateIndexRequest("my_index");
    //執行請求
    CreateIndexResponse response = client.indices().create(request, RequestOptions.DEFAULT);
    System.out.println(response.isAcknowledged());
}

//判斷索引是否存在
@Test
void isIndexExists() throws IOException {
    GetIndexRequest request = new GetIndexRequest("my_index");
    boolean b = client.indices().exists(request, RequestOptions.DEFAULT);
    System.out.println(b);
}

//刪除索引
@Test
void deleteIndex() throws IOException {
    DeleteIndexRequest request = new DeleteIndexRequest("my_index");
    AcknowledgedResponse response = client.indices().delete(request, RequestOptions.DEFAULT);
    System.out.println(response.isAcknowledged());
}

文件操作

//新增文件
@Test
void insertDoc() throws IOException {
    User user = new User("野原新之助", 5);
    IndexRequest request = new IndexRequest("my_index");
    //設定規則
    request.id("1")
            .timeout(TimeValue.timeValueSeconds(5))
            .source(JSON.toJSONString(user), XContentType.JSON);
    IndexResponse response = client.index(request, RequestOptions.DEFAULT);
    System.out.println(response.toString());
    System.out.println(response.status());
}

//判斷文件是否存在
@Test
void isDocExists() throws IOException {
    GetRequest request = new GetRequest("my_index", "1");
    boolean b = client.exists(request, RequestOptions.DEFAULT);
    System.out.println(b);
}

//獲取文件
@Test
void getDoc() throws IOException {
    GetRequest request = new GetRequest("my_index", "1");
    GetResponse response = client.get(request, RequestOptions.DEFAULT);
    System.out.println(response);
    System.out.println(response.getSourceAsString());
}

//更新文件
@Test
void updateDoc() throws IOException {
    User user = new User("野比大雄", 10);
    UpdateRequest request = new UpdateRequest("my_index", "1");
    request.timeout(TimeValue.timeValueSeconds(5))
            .doc(JSON.toJSONString(user), XContentType.JSON);
    UpdateResponse response = client.update(request, RequestOptions.DEFAULT);
    System.out.println(response.status());
}

//刪除文件
@Test
void deleteDoc() throws IOException {
    DeleteRequest request = new DeleteRequest("my_index", "1");
    request.timeout(TimeValue.timeValueSeconds(5));
    DeleteResponse response = client.delete(request, RequestOptions.DEFAULT);
    System.out.println(response.status());
}

//批量操作
@Test
void BulkRequest() throws IOException {
    List<User> userList = new ArrayList<>();
    userList.add(new User("野原新之助", 5));
    userList.add(new User("野比大雄", 10));
    userList.add(new User("剛田武", 10));
    BulkRequest request = new BulkRequest();
    request.timeout(TimeValue.timeValueSeconds(10));
    for (int i = 0; i < userList.size(); i++) {
        request.add(new IndexRequest("my_index")
                .id("" + i + 1)
                .source(JSON.toJSONString(userList.get(i)), XContentType.JSON));
    }
    BulkResponse responses = client.bulk(request, RequestOptions.DEFAULT);
    System.out.println(responses.hasFailures());
}

//查詢
@Test
void search() throws IOException {
    SearchRequest request = new SearchRequest("my_index");
    SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
    TermQueryBuilder query = QueryBuilders.termQuery("username.keyword", "野原新之助");
    sourceBuilder.query(query);
    sourceBuilder.timeout(TimeValue.timeValueSeconds(10));
    request.source(sourceBuilder);
    SearchResponse response = client.search(request, RequestOptions.DEFAULT);
    for (SearchHit hit : response.getHits().getHits()) {
        System.out.println(hit.getSourceAsString());
    }
}