1.项目地址

https://github.com/GuardFTC/elasticsearch-test.git

2.创建实体类

  1. import lombok.Data;
  2. import java.util.List;
  3. /**
  4. * @author: 冯铁城 [17615007230@163.com]
  5. * @date: 2022-08-22 15:54:07
  6. * @describe: 查询测试使用Student实体类
  7. */
  8. @Data
  9. public class SearchStudent {
  10. /**
  11. * 名称
  12. */
  13. private String name;
  14. /**
  15. * 年龄
  16. */
  17. private Integer age;
  18. /**
  19. * 成绩
  20. */
  21. private Double grade;
  22. /**
  23. * 描述
  24. */
  25. private String des;
  26. /**
  27. * 标签
  28. */
  29. private List<String> tags;
  30. }

3.查询Api命令整合

term

  1. @Test
  2. void term() throws IOException {
  3. //1.查询名称为宋可心的同学
  4. SearchResponse<SearchStudent> search = primaryClient.search(s -> s
  5. .index(INDEX_NAME)
  6. .query(q -> q.constantScore(c -> c.filter(f -> f.term(t -> t
  7. .field("name")
  8. .value(FieldValue.of("宋可心"))
  9. )))), SearchStudent.class
  10. );
  11. //2.验证
  12. Assert.isTrue(1 == search.hits().hits().size());
  13. Assert.isTrue("宋可心".equals(search.hits().hits().get(0).source().getName()));
  14. }

terms

  1. @Test
  2. void terms() throws IOException {
  3. //1.查询名称为宋可心和王乾雯的同学
  4. SearchResponse<SearchStudent> search = primaryClient.search(s -> s
  5. .index(INDEX_NAME)
  6. .query(q -> q.constantScore(c -> c.filter(f -> f.terms(t -> t
  7. .field("name")
  8. .terms(terms -> terms.value(CollUtil.newArrayList(
  9. FieldValue.of("宋可心"),
  10. FieldValue.of("王乾雯")
  11. )))
  12. )))), SearchStudent.class
  13. );
  14. //2.校验
  15. Assert.isTrue(2 == search.hits().hits().size());
  16. List<String> names = CollUtil.newArrayList(
  17. search.hits().hits().get(0).source().getName(),
  18. search.hits().hits().get(1).source().getName()
  19. );
  20. Assert.isTrue(names.contains("王乾雯"));
  21. Assert.isTrue(names.contains("宋可心"));
  22. }

range

  1. @Test
  2. void range() throws IOException {
  3. //1.查询年龄小于18岁的同学
  4. SearchResponse<SearchStudent> search = primaryClient.search(s -> s
  5. .index(INDEX_NAME)
  6. .query(q -> q.range(r -> r
  7. .field("age")
  8. .lt(JsonData.of(18))
  9. )), SearchStudent.class
  10. );
  11. Assert.isTrue(1 == search.hits().hits().size());
  12. Assert.isTrue(17 == search.hits().hits().get(0).source().getAge());
  13. //2.查询年龄大于17,小于23的同学
  14. search = primaryClient.search(s -> s
  15. .index(INDEX_NAME)
  16. .query(q -> q.range(r -> r
  17. .field("age")
  18. .gt(JsonData.of(17))
  19. .lt(JsonData.of(23))
  20. )), SearchStudent.class
  21. );
  22. Assert.isTrue(4 == search.hits().hits().size());
  23. }

exist

  1. @Test
  2. void exist() throws IOException {
  3. //1.判定文档不包含对应字段
  4. SearchResponse<SearchStudent> search = primaryClient.search(s -> s
  5. .index(INDEX_NAME)
  6. .query(q -> q.constantScore(c -> c.filter(f -> f.exists(e -> e
  7. .field("qqqqq")
  8. )))), SearchStudent.class
  9. );
  10. Assert.isTrue(0 == search.hits().hits().size());
  11. //2.判定文档包含对应字段
  12. search = primaryClient.search(s -> s
  13. .index(INDEX_NAME)
  14. .query(q -> q.constantScore(c -> c.filter(f -> f.exists(e -> e
  15. .field("age")
  16. )))), SearchStudent.class
  17. );
  18. Assert.isTrue(7 == search.hits().hits().size());
  19. }

match

  1. @Test
  2. void match() throws IOException {
  3. //1.查询年龄为17的同学
  4. SearchResponse<SearchStudent> search = secondaryClient.search(s -> s
  5. .index(INDEX_NAME)
  6. .query(q -> q.constantScore(c -> c.filter(f -> f.match(m -> m
  7. .field("age")
  8. .query(query -> query.longValue(17))
  9. )))), SearchStudent.class
  10. );
  11. Assert.isTrue(1 == search.hits().hits().size());
  12. Assert.isTrue(17 == search.hits().hits().get(0).source().getAge());
  13. //2.查询描述中包含性感的同学
  14. search = secondaryClient.search(s -> s
  15. .index(INDEX_NAME)
  16. .query(q -> q.match(m -> m
  17. .field("des")
  18. .query(query -> query.stringValue("性感"))
  19. )), SearchStudent.class
  20. );
  21. Assert.isTrue(2 == search.hits().hits().size());
  22. Assert.isTrue(search.hits().hits().get(0).source().getDes().contains("性感"));
  23. Assert.isTrue(search.hits().hits().get(1).source().getDes().contains("性感"));
  24. //3.查询描述中包含性感小姐姐的同学
  25. search = secondaryClient.search(s -> s
  26. .index(INDEX_NAME)
  27. .query(q -> q.match(m -> m
  28. .field("des")
  29. .query(query -> query.stringValue("性感小姐姐"))
  30. .operator(Operator.And)
  31. )), SearchStudent.class
  32. );
  33. Assert.isTrue(2 == search.hits().hits().size());
  34. Assert.isTrue(
  35. search.hits().hits().get(0).source().getDes().contains("性感") ||
  36. search.hits().hits().get(0).source().getDes().contains("小姐") ||
  37. search.hits().hits().get(0).source().getDes().contains("姐姐")
  38. );
  39. Assert.isTrue(
  40. search.hits().hits().get(1).source().getDes().contains("性感") ||
  41. search.hits().hits().get(1).source().getDes().contains("小姐") ||
  42. search.hits().hits().get(1).source().getDes().contains("姐姐")
  43. );
  44. //4.查询描述中包含性感小姐姐的同学,匹配百分比为100%
  45. search = secondaryClient.search(s -> s
  46. .index(INDEX_NAME)
  47. .query(q -> q.match(m -> m
  48. .field("des")
  49. .query(query -> query.stringValue("性感小姐姐"))
  50. .minimumShouldMatch("100%")
  51. )), SearchStudent.class
  52. );
  53. Assert.isTrue(2 == search.hits().hits().size());
  54. Assert.isTrue(
  55. search.hits().hits().get(0).source().getDes().contains("性感") ||
  56. search.hits().hits().get(0).source().getDes().contains("小姐") ||
  57. search.hits().hits().get(0).source().getDes().contains("姐姐")
  58. );
  59. Assert.isTrue(
  60. search.hits().hits().get(1).source().getDes().contains("性感") ||
  61. search.hits().hits().get(1).source().getDes().contains("小姐") ||
  62. search.hits().hits().get(1).source().getDes().contains("姐姐")
  63. );
  64. }

multiMatch

  1. @Test
  2. void multiMatch() throws IOException {
  3. //1.查询描述中包含性感或标签中包含性感的同学
  4. SearchResponse<SearchStudent> search = secondaryClient.search(s -> s
  5. .index(INDEX_NAME)
  6. .query(q -> q.multiMatch(m -> m
  7. .query("性感")
  8. .fields("des", "tags")
  9. )), SearchStudent.class
  10. );
  11. Assert.isTrue(4 == search.hits().hits().size());
  12. Assert.isTrue(
  13. search.hits().hits().get(0).source().getDes().contains("性感") ||
  14. JSONUtil.toJsonStr(search.hits().hits().get(0).source().getTags()).contains("性感")
  15. );
  16. Assert.isTrue(
  17. search.hits().hits().get(1).source().getDes().contains("性感") ||
  18. JSONUtil.toJsonStr(search.hits().hits().get(1).source().getTags()).contains("性感")
  19. );
  20. Assert.isTrue(
  21. search.hits().hits().get(2).source().getDes().contains("性感") ||
  22. JSONUtil.toJsonStr(search.hits().hits().get(2).source().getTags()).contains("性感")
  23. );
  24. Assert.isTrue(
  25. search.hits().hits().get(3).source().getDes().contains("性感") ||
  26. JSONUtil.toJsonStr(search.hits().hits().get(3).source().getTags()).contains("性感")
  27. );
  28. }

matchPhrase

  1. @Test
  2. void matchPhrase() throws IOException {
  3. //1.查询描述中包含好看的小姐姐的同学
  4. SearchResponse<SearchStudent> search = secondaryClient.search(s -> s
  5. .index(INDEX_NAME)
  6. .query(q -> q.matchPhrase(m -> m
  7. .field("des")
  8. .query("好看的小姐姐")
  9. )), SearchStudent.class
  10. );
  11. Assert.isTrue(2 == search.hits().hits().size());
  12. Assert.isTrue(search.hits().hits().get(0).source().getDes().contains("好看的小姐姐"));
  13. Assert.isTrue(search.hits().hits().get(1).source().getDes().contains("好看的小姐姐"));
  14. //2.查询描述中包含好看的小姐姐的同学,步长为20
  15. search = secondaryClient.search(s -> s
  16. .index(INDEX_NAME)
  17. .query(q -> q.matchPhrase(m -> m
  18. .field("des")
  19. .query("好看的小姐姐")
  20. .slop(20)
  21. )), SearchStudent.class
  22. );
  23. Assert.isTrue(3 == search.hits().hits().size());
  24. }

prefix

  1. @Test
  2. void prefix() throws IOException {
  3. //1.查询所有名称为王的同学
  4. SearchResponse<SearchStudent> search = primaryClient.search(s -> s
  5. .index(INDEX_NAME)
  6. .query(q -> q.constantScore(c -> c.filter(f -> f.prefix(p -> p
  7. .field("name")
  8. .value("王")
  9. )))), SearchStudent.class
  10. );
  11. //2.校验
  12. Assert.isTrue(3 == search.hits().hits().size());
  13. Assert.isTrue(search.hits().hits().get(0).source().getName().startsWith("王"));
  14. Assert.isTrue(search.hits().hits().get(1).source().getName().startsWith("王"));
  15. Assert.isTrue(search.hits().hits().get(2).source().getName().startsWith("王"));
  16. }

wildcard

  1. @Test
  2. void wildcard() throws IOException {
  3. //1.查询所有名称为王的同学
  4. SearchResponse<SearchStudent> search = secondaryClient.search(s -> s
  5. .index(INDEX_NAME)
  6. .query(q -> q.wildcard(w -> w
  7. .field("des")
  8. .value("好看")
  9. )), SearchStudent.class
  10. );
  11. //2.校验
  12. Assert.isTrue(3 == search.hits().hits().size());
  13. Assert.isTrue(search.hits().hits().get(0).source().getDes().contains("好看"));
  14. Assert.isTrue(search.hits().hits().get(1).source().getDes().contains("好看"));
  15. Assert.isTrue(search.hits().hits().get(2).source().getDes().contains("好看"));
  16. }

must

  1. @Test
  2. void must() throws IOException {
  3. //1.查询姓氏为王并且年龄为18的同学
  4. SearchResponse<SearchStudent> search = primaryClient.search(s -> s
  5. .index(INDEX_NAME)
  6. .query(q -> q.bool(b -> b
  7. .must(m -> m.prefix(p -> p
  8. .field("name")
  9. .value("王")
  10. ))
  11. .must(m -> m.term(t -> t
  12. .field("age")
  13. .value(FieldValue.of(18))
  14. ))
  15. )), SearchStudent.class
  16. );
  17. //2.校验
  18. Assert.isTrue(1 == search.hits().hits().size());
  19. Assert.isTrue(search.hits().hits().get(0).source().getName().startsWith("王"));
  20. Assert.isTrue(18 == search.hits().hits().get(0).source().getAge());
  21. }

mustNot

  1. @Test
  2. void mustNot() throws IOException {
  3. //1.查询姓氏不为王并且年龄不为18的同学
  4. SearchResponse<SearchStudent> search = secondaryClient.search(s -> s
  5. .index(INDEX_NAME)
  6. .query(q -> q.bool(b -> b
  7. .mustNot(m -> m.prefix(p -> p
  8. .field("name")
  9. .value("王")
  10. ))
  11. .mustNot(m -> m.term(t -> t
  12. .field("age")
  13. .value(FieldValue.of(18))
  14. ))
  15. )), SearchStudent.class
  16. );
  17. //2.校验
  18. Assert.isTrue(3 == search.hits().hits().size());
  19. Assert.isTrue(!search.hits().hits().get(0).source().getName().startsWith("王"));
  20. Assert.isTrue(search.hits().hits().get(0).source().getAge() != 18);
  21. Assert.isTrue(!search.hits().hits().get(1).source().getName().startsWith("王"));
  22. Assert.isTrue(search.hits().hits().get(1).source().getAge() != 18);
  23. Assert.isTrue(!search.hits().hits().get(2).source().getName().startsWith("王"));
  24. Assert.isTrue(search.hits().hits().get(2).source().getAge() != 18);
  25. }

shouldWithOutMust

  1. @Test
  2. void shouldWithoutMust() throws IOException {
  3. //1.查询年龄为18或标签中包含性感的同学
  4. SearchResponse<SearchStudent> search = primaryClient.search(s -> s
  5. .index(INDEX_NAME)
  6. .query(q -> q.bool(b -> b
  7. .should(should -> should.term(t -> t
  8. .field("age")
  9. .value(FieldValue.of(18))
  10. ))
  11. .should(should -> should.term(t -> t
  12. .field("tags")
  13. .value(FieldValue.of("性感"))
  14. ))
  15. )), SearchStudent.class
  16. );
  17. //2.校验
  18. Assert.isTrue(4 == search.hits().hits().size());
  19. Assert.isTrue(
  20. search.hits().hits().get(0).source().getAge() == 18 ||
  21. search.hits().hits().get(0).source().getTags().contains("性感")
  22. );
  23. Assert.isTrue(
  24. search.hits().hits().get(1).source().getAge() == 18 ||
  25. search.hits().hits().get(1).source().getTags().contains("性感")
  26. );
  27. Assert.isTrue(
  28. search.hits().hits().get(2).source().getAge() == 18 ||
  29. search.hits().hits().get(2).source().getTags().contains("性感")
  30. );
  31. Assert.isTrue(
  32. search.hits().hits().get(3).source().getAge() == 18 ||
  33. search.hits().hits().get(3).source().getTags().contains("性感")
  34. );
  35. }

shouldWithMust

  1. @Test
  2. void shouldWithMust() throws IOException {
  3. //1.查询描述匹配好看的小姐姐短语且年龄为18的同学,同时标签中包含风骚的同学优先级高
  4. SearchResponse<SearchStudent> search = primaryClient.search(s -> s
  5. .index(INDEX_NAME)
  6. .query(q -> q.bool(b -> b
  7. .must(m -> m.match(match -> match
  8. .field("des")
  9. .query(query -> query.stringValue("好看的小姐姐"))
  10. ))
  11. .must(m -> m.term(t -> t
  12. .field("age")
  13. .value(FieldValue.of(18))
  14. ))
  15. .should(should -> should.term(t -> t
  16. .field("tags")
  17. .value(FieldValue.of("风骚"))
  18. ))
  19. )), SearchStudent.class
  20. );
  21. //2.校验
  22. Assert.isTrue(2 == search.hits().hits().size());
  23. Assert.isTrue(search.hits().hits().get(0).source().getTags().contains("风骚"));
  24. Assert.isTrue(!search.hits().hits().get(1).source().getTags().contains("风骚"));
  25. }

mustAndShould

  1. @Test
  2. void mustAndShould() throws IOException {
  3. //1.查询姓氏为王,并且标签包含性感或可爱的同学
  4. SearchResponse<SearchStudent> search = primaryClient.search(s -> s
  5. .index(INDEX_NAME)
  6. .query(q -> q.bool(b -> b
  7. .filter(f -> f.prefix(p -> p
  8. .field("name")
  9. .value("王")
  10. ))
  11. .filter(f -> f.bool(fb -> fb.should(fbs -> fbs.terms(t -> t
  12. .field("tags")
  13. .terms(tt -> tt.value(CollUtil.newArrayList(
  14. FieldValue.of("性感"),
  15. FieldValue.of("可爱")
  16. )))
  17. ))))
  18. )), SearchStudent.class
  19. );
  20. //2.校验
  21. Assert.isTrue(2 == search.hits().hits().size());
  22. Assert.isTrue(search.hits().hits().get(0).source().getName().startsWith("王"));
  23. Assert.isTrue(
  24. search.hits().hits().get(0).source().getTags().contains("性感") ||
  25. search.hits().hits().get(0).source().getTags().contains("可爱")
  26. );
  27. Assert.isTrue(search.hits().hits().get(1).source().getName().startsWith("王"));
  28. Assert.isTrue(
  29. search.hits().hits().get(1).source().getTags().contains("性感") ||
  30. search.hits().hits().get(1).source().getTags().contains("可爱")
  31. );
  32. //3.查询年龄为18或标签中包含长腿和性感的同学
  33. search = primaryClient.search(s -> s
  34. .index(INDEX_NAME)
  35. .query(q -> q.bool(b -> b
  36. .should(should -> should.term(t -> t
  37. .field("age")
  38. .value(FieldValue.of(18))
  39. ))
  40. .should(should -> should.bool(sb -> sb.must(sbm -> sbm.terms(t -> t
  41. .field("tags")
  42. .terms(terms -> terms.value(CollUtil.newArrayList(
  43. FieldValue.of("长腿"),
  44. FieldValue.of("性感")
  45. )))
  46. ))))
  47. )), SearchStudent.class
  48. );
  49. //4.校验
  50. Assert.isTrue(4 == search.hits().hits().size());
  51. Assert.isTrue(
  52. 18 == search.hits().hits().get(0).source().getAge() || (
  53. search.hits().hits().get(0).source().getTags().contains("长腿") &&
  54. search.hits().hits().get(0).source().getTags().contains("性感")
  55. ));
  56. Assert.isTrue(
  57. 18 == search.hits().hits().get(1).source().getAge() || (
  58. search.hits().hits().get(1).source().getTags().contains("长腿") &&
  59. search.hits().hits().get(1).source().getTags().contains("性感")
  60. ));
  61. Assert.isTrue(
  62. 18 == search.hits().hits().get(2).source().getAge() || (
  63. search.hits().hits().get(2).source().getTags().contains("长腿") &&
  64. search.hits().hits().get(2).source().getTags().contains("性感")
  65. ));
  66. Assert.isTrue(
  67. 18 == search.hits().hits().get(3).source().getAge() || (
  68. search.hits().hits().get(3).source().getTags().contains("长腿") &&
  69. search.hits().hits().get(3).source().getTags().contains("性感")
  70. ));
  71. }

matchAll

  1. @Test
  2. void matchAll() throws IOException {
  3. //1.全量查询
  4. SearchResponse<SearchStudent> search = primaryClient.search(s -> s
  5. .index(INDEX_NAME)
  6. .query(q -> q.matchAll(m -> m)), SearchStudent.class
  7. );
  8. //2.校验
  9. Assert.isTrue(7 == search.hits().hits().size());
  10. }

sort

  1. @Test
  2. void sort() throws IOException {
  3. //1.按照年龄排序
  4. SearchResponse<SearchStudent> search = primaryClient.search(s -> s
  5. .index(INDEX_NAME)
  6. .sort(sort -> sort.field(f -> f.field("age").order(SortOrder.Asc)))
  7. .sort(sort -> sort.field(f -> f.field("grade").order(SortOrder.Asc))),
  8. SearchStudent.class
  9. );
  10. //2.验证
  11. Assert.isTrue(7 == search.hits().hits().size());
  12. Assert.isTrue(17 == search.hits().hits().get(0).source().getAge());
  13. Assert.isTrue(28 == search.hits().hits().get(6).source().getAge());
  14. Assert.isTrue(16.7 == search.hits().hits().get(1).source().getGrade());
  15. Assert.isTrue(22.7 == search.hits().hits().get(2).source().getGrade());
  16. }

fromSize

  1. @Test
  2. void fromSize() throws IOException {
  3. //1.每页3个,查询第3页数据
  4. SearchResponse<SearchStudent> search = secondaryClient.search(s -> s
  5. .index(INDEX_NAME)
  6. .from(6)
  7. .size(3), SearchStudent.class
  8. );
  9. //2.验证
  10. Assert.isTrue(1 == search.hits().hits().size());
  11. }

scroll

  1. @Test
  2. void scroll() throws IOException {
  3. //1.定义结果集
  4. List<SearchStudent> students = CollUtil.newArrayList();
  5. //2.查询快照数据
  6. SearchResponse<SearchStudent> search = secondaryClient.search(s -> s
  7. .index(INDEX_NAME)
  8. .scroll(scroll -> scroll.time("10m"))
  9. .sort(sort -> sort.field(f -> f
  10. .field("grade")
  11. .order(SortOrder.Asc)
  12. ))
  13. .size(2), SearchStudent.class
  14. );
  15. //3.初始数据加入结果集
  16. students.addAll(CollStreamUtil.toList(search.hits().hits(), Hit::source));
  17. //4.定义count值
  18. int count = Integer.MIN_VALUE;
  19. String scrollId = search.scrollId();
  20. //5.循环从快照获取值
  21. while (count != 0) {
  22. //6.查询快照数据
  23. search = secondaryClient.scroll(s -> s
  24. .scrollId(scrollId)
  25. .scroll(scroll -> scroll.time("10m")), SearchStudent.class
  26. );
  27. //7.重新复制count
  28. count = search.hits().hits().size();
  29. //8.数据继续加入结果集
  30. students.addAll(CollStreamUtil.toList(search.hits().hits(), Hit::source));
  31. }
  32. //9.校验
  33. Assert.isTrue(7 == students.size());
  34. }

4.完整版单元测试

  1. import cn.hutool.core.collection.CollStreamUtil;
  2. import cn.hutool.core.collection.CollUtil;
  3. import cn.hutool.core.lang.Assert;
  4. import cn.hutool.core.util.ObjectUtil;
  5. import cn.hutool.json.JSONUtil;
  6. import co.elastic.clients.elasticsearch.ElasticsearchClient;
  7. import co.elastic.clients.elasticsearch._types.FieldValue;
  8. import co.elastic.clients.elasticsearch._types.Refresh;
  9. import co.elastic.clients.elasticsearch._types.SortOrder;
  10. import co.elastic.clients.elasticsearch._types.query_dsl.Operator;
  11. import co.elastic.clients.elasticsearch.core.BulkRequest;
  12. import co.elastic.clients.elasticsearch.core.BulkResponse;
  13. import co.elastic.clients.elasticsearch.core.DeleteByQueryResponse;
  14. import co.elastic.clients.elasticsearch.core.SearchResponse;
  15. import co.elastic.clients.elasticsearch.core.search.Hit;
  16. import co.elastic.clients.elasticsearch.indices.CreateIndexResponse;
  17. import co.elastic.clients.json.JsonData;
  18. import com.ftc.elasticsearchtest.entity.SearchStudent;
  19. import org.junit.jupiter.api.BeforeEach;
  20. import org.junit.jupiter.api.Test;
  21. import org.springframework.beans.factory.annotation.Qualifier;
  22. import org.springframework.boot.test.context.SpringBootTest;
  23. import javax.annotation.Resource;
  24. import java.io.IOException;
  25. import java.util.List;
  26. /**
  27. * @author: 冯铁城 [17615007230@163.com]
  28. * @date: 2022-08-22 15:32:17
  29. * @describe: ElasticSearch查询单元测试
  30. */
  31. @SpringBootTest
  32. public class ElasticSearchSearchTest {
  33. @Resource
  34. @Qualifier("primaryElasticsearchClient")
  35. private ElasticsearchClient primaryClient;
  36. @Resource
  37. @Qualifier("secondaryElasticsearchClient")
  38. private ElasticsearchClient secondaryClient;
  39. /**
  40. * 测试Mock索引名称常量
  41. */
  42. private static final String INDEX_NAME = "test_index";
  43. /**
  44. * 测试数据
  45. */
  46. private static final String TEST_DATA = "[{\"name\":\"王乾雯\",\"age\":\"18\",\"grade\":16.7,\"des\":\"好看性感的小姐姐\",\"tags\":[\"性感\",\"风骚\",\"很好上\"]},{\"name\":\"宋可心\",\"age\":\"18\",\"grade\":22.7,\"des\":\"她也是一个性感小姐姐\",\"tags\":[\"性感\",\"声音好听\",\"美脚\"]},{\"name\":\"郭楠\",\"age\":\"28\",\"grade\":15.2,\"des\":\"长腿小姐姐\",\"tags\":[\"腿长\"]},{\"name\":\"王可菲\",\"age\":\"17\",\"grade\":68.2,\"des\":\"清纯好看的小姐姐\",\"tags\":[\"高颜值\"]},{\"name\":\"王彦舒\",\"age\":\"22\",\"grade\":11.2,\"des\":\"闷骚的小姐姐\",\"tags\":[\"可爱\",\"闷骚\"]},{\"name\":\"宁娜\",\"age\":\"25\",\"grade\":44.4,\"des\":\"特别骚的一个小姐姐\",\"tags\":[\"骚\",\"长腿\",\"我想上她\",\"性感\"]},{\"name\":\"lulu\",\"age\":\"19\",\"grade\":16.4,\"des\":\"脚特别好看的小姐姐\",\"tags\":[\"骚\",\"长腿\",\"我想上她\",\"极品美脚\",\"性感\"]}]";
  47. @BeforeEach
  48. void createIndexAndAddData() throws IOException {
  49. //1.判定索引是否存在
  50. boolean exist = primaryClient.indices().exists(e -> e.index(INDEX_NAME)).value();
  51. //2.不存在创建索引
  52. if (!exist) {
  53. //3.创建索引
  54. CreateIndexResponse createIndexResponse = primaryClient.indices().create(c -> c
  55. .index(INDEX_NAME + "_1")
  56. .aliases(INDEX_NAME, a -> a)
  57. .mappings(m -> m
  58. .properties("name", p -> p.keyword(k -> k))
  59. .properties("age", p -> p.integer(in -> in))
  60. .properties("grade", p -> p.halfFloat(s -> s))
  61. .properties("des", p -> p.text(t -> t.analyzer("ik_max_word")))
  62. .properties("tags", p -> p.keyword(k -> k))
  63. )
  64. .settings(s -> s
  65. .refreshInterval(t -> t.time("1s"))
  66. .numberOfReplicas("1")
  67. .numberOfShards("3")
  68. )
  69. );
  70. Assert.isTrue(Boolean.TRUE.equals(createIndexResponse.acknowledged()));
  71. }
  72. //4.获取索引中文档数量
  73. long count = primaryClient.count(c -> c.index(INDEX_NAME)).count();
  74. if (0 != count) {
  75. //5.文档数据不为空,全量删除
  76. DeleteByQueryResponse deleteByQueryResponse = primaryClient.deleteByQuery(d -> d
  77. .index(INDEX_NAME)
  78. .query(q -> q.matchAll(m -> m))
  79. .refresh(true)
  80. );
  81. Assert.isTrue(ObjectUtil.isNotNull(deleteByQueryResponse.deleted()));
  82. }
  83. //6.准备测试数据
  84. List<SearchStudent> students = JSONUtil.toList(TEST_DATA, SearchStudent.class);
  85. BulkRequest.Builder builder = new BulkRequest.Builder();
  86. builder.index(INDEX_NAME);
  87. students.forEach(s -> builder.operations(o -> o.create(c -> c.document(s))));
  88. builder.refresh(Refresh.True);
  89. //7.存入数据
  90. BulkResponse bulk = primaryClient.bulk(builder.build());
  91. Assert.isFalse(bulk.errors());
  92. Assert.isTrue(7 == bulk.items().size());
  93. }
  94. @Test
  95. void term() throws IOException {
  96. //1.查询名称为宋可心的同学
  97. SearchResponse<SearchStudent> search = primaryClient.search(s -> s
  98. .index(INDEX_NAME)
  99. .query(q -> q.constantScore(c -> c.filter(f -> f.term(t -> t
  100. .field("name")
  101. .value(FieldValue.of("宋可心"))
  102. )))), SearchStudent.class
  103. );
  104. //2.验证
  105. Assert.isTrue(1 == search.hits().hits().size());
  106. Assert.isTrue("宋可心".equals(search.hits().hits().get(0).source().getName()));
  107. }
  108. @Test
  109. void terms() throws IOException {
  110. //1.查询名称为宋可心和王乾雯的同学
  111. SearchResponse<SearchStudent> search = primaryClient.search(s -> s
  112. .index(INDEX_NAME)
  113. .query(q -> q.constantScore(c -> c.filter(f -> f.terms(t -> t
  114. .field("name")
  115. .terms(terms -> terms.value(CollUtil.newArrayList(
  116. FieldValue.of("宋可心"),
  117. FieldValue.of("王乾雯")
  118. )))
  119. )))), SearchStudent.class
  120. );
  121. //2.校验
  122. Assert.isTrue(2 == search.hits().hits().size());
  123. List<String> names = CollUtil.newArrayList(
  124. search.hits().hits().get(0).source().getName(),
  125. search.hits().hits().get(1).source().getName()
  126. );
  127. Assert.isTrue(names.contains("王乾雯"));
  128. Assert.isTrue(names.contains("宋可心"));
  129. }
  130. @Test
  131. void range() throws IOException {
  132. //1.查询年龄小于18岁的同学
  133. SearchResponse<SearchStudent> search = primaryClient.search(s -> s
  134. .index(INDEX_NAME)
  135. .query(q -> q.range(r -> r
  136. .field("age")
  137. .lt(JsonData.of(18))
  138. )), SearchStudent.class
  139. );
  140. Assert.isTrue(1 == search.hits().hits().size());
  141. Assert.isTrue(17 == search.hits().hits().get(0).source().getAge());
  142. //2.查询年龄大于17,小于23的同学
  143. search = primaryClient.search(s -> s
  144. .index(INDEX_NAME)
  145. .query(q -> q.range(r -> r
  146. .field("age")
  147. .gt(JsonData.of(17))
  148. .lt(JsonData.of(23))
  149. )), SearchStudent.class
  150. );
  151. Assert.isTrue(4 == search.hits().hits().size());
  152. }
  153. @Test
  154. void exist() throws IOException {
  155. //1.判定文档不包含对应字段
  156. SearchResponse<SearchStudent> search = primaryClient.search(s -> s
  157. .index(INDEX_NAME)
  158. .query(q -> q.constantScore(c -> c.filter(f -> f.exists(e -> e
  159. .field("qqqqq")
  160. )))), SearchStudent.class
  161. );
  162. Assert.isTrue(0 == search.hits().hits().size());
  163. //2.判定文档包含对应字段
  164. search = primaryClient.search(s -> s
  165. .index(INDEX_NAME)
  166. .query(q -> q.constantScore(c -> c.filter(f -> f.exists(e -> e
  167. .field("age")
  168. )))), SearchStudent.class
  169. );
  170. Assert.isTrue(7 == search.hits().hits().size());
  171. }
  172. @Test
  173. void match() throws IOException {
  174. //1.查询年龄为17的同学
  175. SearchResponse<SearchStudent> search = secondaryClient.search(s -> s
  176. .index(INDEX_NAME)
  177. .query(q -> q.constantScore(c -> c.filter(f -> f.match(m -> m
  178. .field("age")
  179. .query(query -> query.longValue(17))
  180. )))), SearchStudent.class
  181. );
  182. Assert.isTrue(1 == search.hits().hits().size());
  183. Assert.isTrue(17 == search.hits().hits().get(0).source().getAge());
  184. //2.查询描述中包含性感的同学
  185. search = secondaryClient.search(s -> s
  186. .index(INDEX_NAME)
  187. .query(q -> q.match(m -> m
  188. .field("des")
  189. .query(query -> query.stringValue("性感"))
  190. )), SearchStudent.class
  191. );
  192. Assert.isTrue(2 == search.hits().hits().size());
  193. Assert.isTrue(search.hits().hits().get(0).source().getDes().contains("性感"));
  194. Assert.isTrue(search.hits().hits().get(1).source().getDes().contains("性感"));
  195. //3.查询描述中包含性感小姐姐的同学
  196. search = secondaryClient.search(s -> s
  197. .index(INDEX_NAME)
  198. .query(q -> q.match(m -> m
  199. .field("des")
  200. .query(query -> query.stringValue("性感小姐姐"))
  201. .operator(Operator.And)
  202. )), SearchStudent.class
  203. );
  204. Assert.isTrue(2 == search.hits().hits().size());
  205. Assert.isTrue(
  206. search.hits().hits().get(0).source().getDes().contains("性感") ||
  207. search.hits().hits().get(0).source().getDes().contains("小姐") ||
  208. search.hits().hits().get(0).source().getDes().contains("姐姐")
  209. );
  210. Assert.isTrue(
  211. search.hits().hits().get(1).source().getDes().contains("性感") ||
  212. search.hits().hits().get(1).source().getDes().contains("小姐") ||
  213. search.hits().hits().get(1).source().getDes().contains("姐姐")
  214. );
  215. //4.查询描述中包含性感小姐姐的同学,匹配百分比为100%
  216. search = secondaryClient.search(s -> s
  217. .index(INDEX_NAME)
  218. .query(q -> q.match(m -> m
  219. .field("des")
  220. .query(query -> query.stringValue("性感小姐姐"))
  221. .minimumShouldMatch("100%")
  222. )), SearchStudent.class
  223. );
  224. Assert.isTrue(2 == search.hits().hits().size());
  225. Assert.isTrue(
  226. search.hits().hits().get(0).source().getDes().contains("性感") ||
  227. search.hits().hits().get(0).source().getDes().contains("小姐") ||
  228. search.hits().hits().get(0).source().getDes().contains("姐姐")
  229. );
  230. Assert.isTrue(
  231. search.hits().hits().get(1).source().getDes().contains("性感") ||
  232. search.hits().hits().get(1).source().getDes().contains("小姐") ||
  233. search.hits().hits().get(1).source().getDes().contains("姐姐")
  234. );
  235. }
  236. @Test
  237. void multiMatch() throws IOException {
  238. //1.查询描述中包含性感或标签中包含性感的同学
  239. SearchResponse<SearchStudent> search = secondaryClient.search(s -> s
  240. .index(INDEX_NAME)
  241. .query(q -> q.multiMatch(m -> m
  242. .query("性感")
  243. .fields("des", "tags")
  244. )), SearchStudent.class
  245. );
  246. Assert.isTrue(4 == search.hits().hits().size());
  247. Assert.isTrue(
  248. search.hits().hits().get(0).source().getDes().contains("性感") ||
  249. JSONUtil.toJsonStr(search.hits().hits().get(0).source().getTags()).contains("性感")
  250. );
  251. Assert.isTrue(
  252. search.hits().hits().get(1).source().getDes().contains("性感") ||
  253. JSONUtil.toJsonStr(search.hits().hits().get(1).source().getTags()).contains("性感")
  254. );
  255. Assert.isTrue(
  256. search.hits().hits().get(2).source().getDes().contains("性感") ||
  257. JSONUtil.toJsonStr(search.hits().hits().get(2).source().getTags()).contains("性感")
  258. );
  259. Assert.isTrue(
  260. search.hits().hits().get(3).source().getDes().contains("性感") ||
  261. JSONUtil.toJsonStr(search.hits().hits().get(3).source().getTags()).contains("性感")
  262. );
  263. }
  264. @Test
  265. void matchPhrase() throws IOException {
  266. //1.查询描述中包含好看的小姐姐的同学
  267. SearchResponse<SearchStudent> search = secondaryClient.search(s -> s
  268. .index(INDEX_NAME)
  269. .query(q -> q.matchPhrase(m -> m
  270. .field("des")
  271. .query("好看的小姐姐")
  272. )), SearchStudent.class
  273. );
  274. Assert.isTrue(2 == search.hits().hits().size());
  275. Assert.isTrue(search.hits().hits().get(0).source().getDes().contains("好看的小姐姐"));
  276. Assert.isTrue(search.hits().hits().get(1).source().getDes().contains("好看的小姐姐"));
  277. //2.查询描述中包含好看的小姐姐的同学,步长为20
  278. search = secondaryClient.search(s -> s
  279. .index(INDEX_NAME)
  280. .query(q -> q.matchPhrase(m -> m
  281. .field("des")
  282. .query("好看的小姐姐")
  283. .slop(20)
  284. )), SearchStudent.class
  285. );
  286. Assert.isTrue(3 == search.hits().hits().size());
  287. }
  288. @Test
  289. void prefix() throws IOException {
  290. //1.查询所有名称为王的同学
  291. SearchResponse<SearchStudent> search = primaryClient.search(s -> s
  292. .index(INDEX_NAME)
  293. .query(q -> q.constantScore(c -> c.filter(f -> f.prefix(p -> p
  294. .field("name")
  295. .value("王")
  296. )))), SearchStudent.class
  297. );
  298. //2.校验
  299. Assert.isTrue(3 == search.hits().hits().size());
  300. Assert.isTrue(search.hits().hits().get(0).source().getName().startsWith("王"));
  301. Assert.isTrue(search.hits().hits().get(1).source().getName().startsWith("王"));
  302. Assert.isTrue(search.hits().hits().get(2).source().getName().startsWith("王"));
  303. }
  304. @Test
  305. void wildcard() throws IOException {
  306. //1.查询所有名称为王的同学
  307. SearchResponse<SearchStudent> search = secondaryClient.search(s -> s
  308. .index(INDEX_NAME)
  309. .query(q -> q.wildcard(w -> w
  310. .field("des")
  311. .value("好看")
  312. )), SearchStudent.class
  313. );
  314. //2.校验
  315. Assert.isTrue(3 == search.hits().hits().size());
  316. Assert.isTrue(search.hits().hits().get(0).source().getDes().contains("好看"));
  317. Assert.isTrue(search.hits().hits().get(1).source().getDes().contains("好看"));
  318. Assert.isTrue(search.hits().hits().get(2).source().getDes().contains("好看"));
  319. }
  320. @Test
  321. void must() throws IOException {
  322. //1.查询姓氏为王并且年龄为18的同学
  323. SearchResponse<SearchStudent> search = primaryClient.search(s -> s
  324. .index(INDEX_NAME)
  325. .query(q -> q.bool(b -> b
  326. .must(m -> m.prefix(p -> p
  327. .field("name")
  328. .value("王")
  329. ))
  330. .must(m -> m.term(t -> t
  331. .field("age")
  332. .value(FieldValue.of(18))
  333. ))
  334. )), SearchStudent.class
  335. );
  336. //2.校验
  337. Assert.isTrue(1 == search.hits().hits().size());
  338. Assert.isTrue(search.hits().hits().get(0).source().getName().startsWith("王"));
  339. Assert.isTrue(18 == search.hits().hits().get(0).source().getAge());
  340. }
  341. @Test
  342. void mustNot() throws IOException {
  343. //1.查询姓氏不为王并且年龄不为18的同学
  344. SearchResponse<SearchStudent> search = secondaryClient.search(s -> s
  345. .index(INDEX_NAME)
  346. .query(q -> q.bool(b -> b
  347. .mustNot(m -> m.prefix(p -> p
  348. .field("name")
  349. .value("王")
  350. ))
  351. .mustNot(m -> m.term(t -> t
  352. .field("age")
  353. .value(FieldValue.of(18))
  354. ))
  355. )), SearchStudent.class
  356. );
  357. //2.校验
  358. Assert.isTrue(3 == search.hits().hits().size());
  359. Assert.isTrue(!search.hits().hits().get(0).source().getName().startsWith("王"));
  360. Assert.isTrue(search.hits().hits().get(0).source().getAge() != 18);
  361. Assert.isTrue(!search.hits().hits().get(1).source().getName().startsWith("王"));
  362. Assert.isTrue(search.hits().hits().get(1).source().getAge() != 18);
  363. Assert.isTrue(!search.hits().hits().get(2).source().getName().startsWith("王"));
  364. Assert.isTrue(search.hits().hits().get(2).source().getAge() != 18);
  365. }
  366. @Test
  367. void shouldWithoutMust() throws IOException {
  368. //1.查询年龄为18或标签中包含性感的同学
  369. SearchResponse<SearchStudent> search = primaryClient.search(s -> s
  370. .index(INDEX_NAME)
  371. .query(q -> q.bool(b -> b
  372. .should(should -> should.term(t -> t
  373. .field("age")
  374. .value(FieldValue.of(18))
  375. ))
  376. .should(should -> should.term(t -> t
  377. .field("tags")
  378. .value(FieldValue.of("性感"))
  379. ))
  380. )), SearchStudent.class
  381. );
  382. //2.校验
  383. Assert.isTrue(4 == search.hits().hits().size());
  384. Assert.isTrue(
  385. search.hits().hits().get(0).source().getAge() == 18 ||
  386. search.hits().hits().get(0).source().getTags().contains("性感")
  387. );
  388. Assert.isTrue(
  389. search.hits().hits().get(1).source().getAge() == 18 ||
  390. search.hits().hits().get(1).source().getTags().contains("性感")
  391. );
  392. Assert.isTrue(
  393. search.hits().hits().get(2).source().getAge() == 18 ||
  394. search.hits().hits().get(2).source().getTags().contains("性感")
  395. );
  396. Assert.isTrue(
  397. search.hits().hits().get(3).source().getAge() == 18 ||
  398. search.hits().hits().get(3).source().getTags().contains("性感")
  399. );
  400. }
  401. @Test
  402. void shouldWithMust() throws IOException {
  403. //1.查询描述匹配好看的小姐姐短语且年龄为18的同学,同时标签中包含风骚的同学优先级高
  404. SearchResponse<SearchStudent> search = primaryClient.search(s -> s
  405. .index(INDEX_NAME)
  406. .query(q -> q.bool(b -> b
  407. .must(m -> m.match(match -> match
  408. .field("des")
  409. .query(query -> query.stringValue("好看的小姐姐"))
  410. ))
  411. .must(m -> m.term(t -> t
  412. .field("age")
  413. .value(FieldValue.of(18))
  414. ))
  415. .should(should -> should.term(t -> t
  416. .field("tags")
  417. .value(FieldValue.of("风骚"))
  418. ))
  419. )), SearchStudent.class
  420. );
  421. //2.校验
  422. Assert.isTrue(2 == search.hits().hits().size());
  423. Assert.isTrue(search.hits().hits().get(0).source().getTags().contains("风骚"));
  424. Assert.isTrue(!search.hits().hits().get(1).source().getTags().contains("风骚"));
  425. }
  426. @Test
  427. void mustAndShould() throws IOException {
  428. //1.查询姓氏为王,并且标签包含性感或可爱的同学
  429. SearchResponse<SearchStudent> search = primaryClient.search(s -> s
  430. .index(INDEX_NAME)
  431. .query(q -> q.bool(b -> b
  432. .filter(f -> f.prefix(p -> p
  433. .field("name")
  434. .value("王")
  435. ))
  436. .filter(f -> f.bool(fb -> fb.should(fbs -> fbs.terms(t -> t
  437. .field("tags")
  438. .terms(tt -> tt.value(CollUtil.newArrayList(
  439. FieldValue.of("性感"),
  440. FieldValue.of("可爱")
  441. )))
  442. ))))
  443. )), SearchStudent.class
  444. );
  445. //2.校验
  446. Assert.isTrue(2 == search.hits().hits().size());
  447. Assert.isTrue(search.hits().hits().get(0).source().getName().startsWith("王"));
  448. Assert.isTrue(
  449. search.hits().hits().get(0).source().getTags().contains("性感") ||
  450. search.hits().hits().get(0).source().getTags().contains("可爱")
  451. );
  452. Assert.isTrue(search.hits().hits().get(1).source().getName().startsWith("王"));
  453. Assert.isTrue(
  454. search.hits().hits().get(1).source().getTags().contains("性感") ||
  455. search.hits().hits().get(1).source().getTags().contains("可爱")
  456. );
  457. //3.查询年龄为18或标签中包含长腿和性感的同学
  458. search = primaryClient.search(s -> s
  459. .index(INDEX_NAME)
  460. .query(q -> q.bool(b -> b
  461. .should(should -> should.term(t -> t
  462. .field("age")
  463. .value(FieldValue.of(18))
  464. ))
  465. .should(should -> should.bool(sb -> sb.must(sbm -> sbm.terms(t -> t
  466. .field("tags")
  467. .terms(terms -> terms.value(CollUtil.newArrayList(
  468. FieldValue.of("长腿"),
  469. FieldValue.of("性感")
  470. )))
  471. ))))
  472. )), SearchStudent.class
  473. );
  474. //4.校验
  475. Assert.isTrue(4 == search.hits().hits().size());
  476. Assert.isTrue(
  477. 18 == search.hits().hits().get(0).source().getAge() || (
  478. search.hits().hits().get(0).source().getTags().contains("长腿") &&
  479. search.hits().hits().get(0).source().getTags().contains("性感")
  480. ));
  481. Assert.isTrue(
  482. 18 == search.hits().hits().get(1).source().getAge() || (
  483. search.hits().hits().get(1).source().getTags().contains("长腿") &&
  484. search.hits().hits().get(1).source().getTags().contains("性感")
  485. ));
  486. Assert.isTrue(
  487. 18 == search.hits().hits().get(2).source().getAge() || (
  488. search.hits().hits().get(2).source().getTags().contains("长腿") &&
  489. search.hits().hits().get(2).source().getTags().contains("性感")
  490. ));
  491. Assert.isTrue(
  492. 18 == search.hits().hits().get(3).source().getAge() || (
  493. search.hits().hits().get(3).source().getTags().contains("长腿") &&
  494. search.hits().hits().get(3).source().getTags().contains("性感")
  495. ));
  496. }
  497. @Test
  498. void matchAll() throws IOException {
  499. //1.全量查询
  500. SearchResponse<SearchStudent> search = primaryClient.search(s -> s
  501. .index(INDEX_NAME)
  502. .query(q -> q.matchAll(m -> m)), SearchStudent.class
  503. );
  504. //2.校验
  505. Assert.isTrue(7 == search.hits().hits().size());
  506. }
  507. @Test
  508. void sort() throws IOException {
  509. //1.按照年龄排序
  510. SearchResponse<SearchStudent> search = primaryClient.search(s -> s
  511. .index(INDEX_NAME)
  512. .sort(sort -> sort.field(f -> f.field("age").order(SortOrder.Asc)))
  513. .sort(sort -> sort.field(f -> f.field("grade").order(SortOrder.Asc))),
  514. SearchStudent.class
  515. );
  516. //2.验证
  517. Assert.isTrue(7 == search.hits().hits().size());
  518. Assert.isTrue(17 == search.hits().hits().get(0).source().getAge());
  519. Assert.isTrue(28 == search.hits().hits().get(6).source().getAge());
  520. Assert.isTrue(16.7 == search.hits().hits().get(1).source().getGrade());
  521. Assert.isTrue(22.7 == search.hits().hits().get(2).source().getGrade());
  522. }
  523. @Test
  524. void fromSize() throws IOException {
  525. //1.每页3个,查询第3页数据
  526. SearchResponse<SearchStudent> search = secondaryClient.search(s -> s
  527. .index(INDEX_NAME)
  528. .from(6)
  529. .size(3), SearchStudent.class
  530. );
  531. //2.验证
  532. Assert.isTrue(1 == search.hits().hits().size());
  533. }
  534. @Test
  535. void scroll() throws IOException {
  536. //1.定义结果集
  537. List<SearchStudent> students = CollUtil.newArrayList();
  538. //2.查询快照数据
  539. SearchResponse<SearchStudent> search = secondaryClient.search(s -> s
  540. .index(INDEX_NAME)
  541. .scroll(scroll -> scroll.time("10m"))
  542. .sort(sort -> sort.field(f -> f
  543. .field("grade")
  544. .order(SortOrder.Asc)
  545. ))
  546. .size(2), SearchStudent.class
  547. );
  548. //3.初始数据加入结果集
  549. students.addAll(CollStreamUtil.toList(search.hits().hits(), Hit::source));
  550. //4.定义count值
  551. int count = Integer.MIN_VALUE;
  552. String scrollId = search.scrollId();
  553. //5.循环从快照获取值
  554. while (count != 0) {
  555. //6.查询快照数据
  556. search = secondaryClient.scroll(s -> s
  557. .scrollId(scrollId)
  558. .scroll(scroll -> scroll.time("10m")), SearchStudent.class
  559. );
  560. //7.重新复制count
  561. count = search.hits().hits().size();
  562. //8.数据继续加入结果集
  563. students.addAll(CollStreamUtil.toList(search.hits().hits(), Hit::source));
  564. }
  565. //9.校验
  566. Assert.isTrue(7 == students.size());
  567. }
  568. }