ElasticSearch入门教程

概念

全文检索

试想一下结构化数据的查询方式,例如数据库的查找方式,我们知道在数据库中构建索引可以极大程度的提高查询速度,这是因为结构化数据有一定程度固定的结构使得我们可以采取某些搜索算法加速查询速度,那既然如此,为什么我们不可以尝试在非结构化数据中,将一部分结构化信息抽取出来,重新组织,然后针对这部分有结构的数据进行索引建立,从而达到加速查询的目的;

上述想法很天然,但却构成了全文检索的基本思路

从非结构化数据中抽取部分结构化数据,并建立索引,再对索引进行搜索的过程,我们成为全文索引

顺序扫描法

比如我们想要在成千上万的文档中,查找包含某一字符串的所有文档,顺序扫描法就必须逐个的扫描每个文档,并且每个文档都要从头看到尾,如果找到,就继续找下一个,直至遍历所有的文档;这种方法通常应用于数据量较小的场景,比如经常使用的grep命令就是这种查找方式

 

windows安装ElasticSearch

版本对应关系:

springboot2.1.5

spring-boot-starter-data-elasticsearch

elasticsearch-6.4.3

ik-6.4.3

  1. 下载es

    https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.4.3.zip

    解压到D:\Java\

  2. 下载ik

    https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.4.3/elasticsearch-analysis-ik-6.4.3.zip

    解压文件到D:\Java\elasticsearch-6.4.3\plugins

    创建ik文件夹

    解压

  3. 修改D:\Java\elasticsearch-6.4.3\config\elasticsearch.yml配置文件

    cluster.name: nowcoder
    path.data: D:/data/path/to/data
    path.logs: D:/data/path/to/logs
  4. 运行D:\Java\elasticsearch-6.4.3\bin\elasticsearch.bat

linux安装es

  1. 下载

    https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.4.3.tar.gz
  2. 修改配置文件

    cd /opt/elasticsearch-6.4.0/config
    vim jvm.options
    {
    -Xmx256m
    -Xmx256m
    }
    vim elasticsearch.yml
    {
    path.data: /tmp/es/data
    cluster.name: nowcoder
    path.logs: /tmp/es/logs
    }

     

  3. 添加ik

    mkdir opt/elasticsearch-6.4.0/plugins/ik && cd opt/elasticsearch-6.4.0/plugins/ik
    yum install unzip
    unzip /root/elasticsearch-analysis-ik-6.4.0.zip /opt/elasticsearch-6.4.0/plugins/ik
  4. 创建用户

groupadd nowcoder
useradd nowcoder1 -p 123456 -g nowcoder
  1. 设置权限并登陆

cd /opt
chown -R nowcoder1:nowcoder *
cd /tmp
chown -R nowcoder1:nowcoder *
sudo - nowcoder1
{
Enter Password:
...
}
  1. 启动es

cd opt/elasticsearch-6.4.0/bin
./elasticsearch
  1. 测试es

su -
{
Enter Password:
..
}
action1: curl -X Get "localhost:9200/_cat/health?v"
action2: curl http://localhost:9200/

 

使用PostMan操作ES

GET localhsot:9200                          查询es状态
PUT localhsot:9200/test 创建索引
GET localhost:9200/_cat/indices?v 查看索引状态
DELETE localhsot:9200/test 删除索引
PUT 创建文档
localhsot:9200/test/_doc/1
row-JSON
{
"title":"互联网求职",
"content":"我这里需要招聘运营岗位数名"
}
GET localhsot:9200/test/_search 查询文档

Spring整合ElasticSearch

sql

CREATE TABLE `discuss_post` (
`id` int NOT NULL AUTO_INCREMENT,
`user_id` varchar(45) DEFAULT NULL,
`title` varchar(100) DEFAULT NULL,
`content` text,
`type` int DEFAULT NULL COMMENT '0-普通; 1-置顶;',
`status` int DEFAULT NULL COMMENT '0-正常; 1-精华; 2-拉黑;',
`create_time` timestamp NULL DEFAULT NULL,
`comment_count` int DEFAULT NULL,
`score` double DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `index_user_id` (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=281 DEFAULT CHARSET=utf8mb3;

 

  1. 添加maven依赖项

            <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
    </dependency>

     

  2. 添加properties配置项

    #elasticsearchProperties
    spring.data.elasticsearch.cluster-name=nowcoder
    spring.data.elasticsearch.cluster-nodes=127.0.0.1:9300

     

  3. 解决springboot整合redis+es冲突问题

    @SpringBootApplication
    @MapperScan("com.nowcoder.community.dao")
    public class CommunityApplication {
    @PostConstruct
    public void init() {
    // 解决netty启动冲突问题
    // see Netty4Utils.setAvailableProcessors()
    System.setProperty("es.set.netty.runtime.available.processors", "false");
    }
    public static void main(String[] args) {
    SpringApplication.run(CommunityApplication.class, args);
    }

    }

     

  4. 单元测试项

    //建立es与实体类的关系
    @Document(indexName = "discuss",type = "docs",shards = 6,replicas = 3)
    @Data
    public class Discuss {
       @Id
       private int id;

       @Field(type = FieldType.Integer)
       private int userId;

       @Field(type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_smart")
       private String title;

       @Field(type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_smart")
       private String content;

       @Field(type = FieldType.Integer)
       private int type;

       @Field(type = FieldType.Integer)
       private int status;

       @Field(type = FieldType.Integer)
       private int commentCount;

       @Field(type = FieldType.Date)
       private Date createTime;

       @Field(type = FieldType.Double)
       private double score;

       public Discuss(){};
    }
  5. 创建es仓库

    @Repository
    public interface ElasticSearchRepository extends ElasticsearchCrudRepository<Discuss,Integer> {
    }
  6. 基于es的全文检索第一种方法

    @RunWith(SpringRunner.class)
    @SpringBootTest
    @ContextConfiguration(classes = CommunityApplication.class)
    public class ElasticsearchTests1 {

       @Autowired
       private DiscussPostMapper discussMapper;

       @Autowired
       private ElasticSearchRepository discussRepository;

       @Autowired
       private ElasticsearchTemplate elasticTemplate;

       @Test
       public void testInsert() {
           discussRepository.save(discussMapper.selectDiscussById(241));
           discussRepository.save(discussMapper.selectDiscussById(242));
           discussRepository.save(discussMapper.selectDiscussById(243));
      }

       @Test
       public void testInsertList() {
           discussRepository.saveAll(discussMapper.getListAll(101, 0, 100));
           discussRepository.saveAll(discussMapper.getListAll(102, 0, 100));
           discussRepository.saveAll(discussMapper.getListAll(103, 0, 100));
           discussRepository.saveAll(discussMapper.getListAll(111, 0, 100));
           discussRepository.saveAll(discussMapper.getListAll(112, 0, 100));
           discussRepository.saveAll(discussMapper.getListAll(131, 0, 100));
           discussRepository.saveAll(discussMapper.getListAll(132, 0, 100));
           discussRepository.saveAll(discussMapper.getListAll(133, 0, 100));
           discussRepository.saveAll(discussMapper.getListAll(134, 0, 100));
      }

       @Test
       public void testUpdate() {
           Discuss post = discussMapper.selectDiscussById(231);
           post.setContent("我是新人,使劲灌水.");
           discussRepository.save(post);
      }

       @Test
       public void testDelete() {
           // discussRepository.deleteById(231);
           discussRepository.deleteAll();
      }

       @Test
       public void testSearchByRepository() {
           SearchQuery searchQuery = new NativeSearchQueryBuilder()
                  .withQuery(QueryBuilders.multiMatchQuery("互联网寒冬", "title", "content"))
                  .withSort(SortBuilders.fieldSort("type").order(SortOrder.DESC))
                  .withSort(SortBuilders.fieldSort("score").order(SortOrder.DESC))
                  .withSort(SortBuilders.fieldSort("createTime").order(SortOrder.DESC))
                  .withPageable(PageRequest.of(0, 10))
                  .withHighlightFields(
                           new HighlightBuilder.Field("title").preTags("<em>").postTags("</em>"),
                           new HighlightBuilder.Field("content").preTags("<em>").postTags("</em>")
                  ).build();

           // elasticTemplate.queryForPage(searchQuery, class, SearchResultMapper)
           // 底层获取得到了高亮显示的值, 但是没有返回.

           Page<Discuss> page = discussRepository.search(searchQuery);
           System.out.println(page.getTotalElements());
           System.out.println(page.getTotalPages());
           System.out.println(page.getNumber());
           System.out.println(page.getSize());
           for (Discuss post : page) {
               System.out.println(post);
          }
      }

       @Test
       public void testSearchByTemplate() {
           SearchQuery searchQuery = new NativeSearchQueryBuilder()
                  .withQuery(QueryBuilders.multiMatchQuery("互联网寒冬", "title", "content"))
                  .withSort(SortBuilders.fieldSort("type").order(SortOrder.DESC))
                  .withSort(SortBuilders.fieldSort("score").order(SortOrder.DESC))
                  .withSort(SortBuilders.fieldSort("createTime").order(SortOrder.DESC))
                  .withPageable(PageRequest.of(0, 10))
                  .withHighlightFields(
                           new HighlightBuilder.Field("title").preTags("<em>").postTags("</em>"),
                           new HighlightBuilder.Field("content").preTags("<em>").postTags("</em>")
                  ).build();

           Page<Discuss> page = elasticTemplate.queryForPage(searchQuery, Discuss.class, new SearchResultMapper() {
               @Override
               public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> aClass, Pageable pageable) {
                   SearchHits hits = response.getHits();
                   if (hits.getTotalHits() <= 0) {
                       return null;
                  }

                   List<Discuss> list = new ArrayList<>();
                   for (SearchHit hit : hits) {
                       Discuss post = new Discuss();

                       String id = hit.getSourceAsMap().get("id").toString();
                       post.setId(Integer.valueOf(id));

                       String userId = hit.getSourceAsMap().get("userId").toString();
                       post.setUserId(Integer.valueOf(userId));

                       String title = hit.getSourceAsMap().get("title").toString();
                       post.setTitle(title);

                       String content = hit.getSourceAsMap().get("content").toString();
                       post.setContent(content);

                       String status = hit.getSourceAsMap().get("status").toString();
                       post.setStatus(Integer.valueOf(status));

                       String createTime = hit.getSourceAsMap().get("createTime").toString();
                       post.setCreateTime(new Date(Long.valueOf(createTime)));

                       String commentCount = hit.getSourceAsMap().get("commentCount").toString();
                       post.setCommentCount(Integer.valueOf(commentCount));

                       // 处理高亮显示的结果
                       HighlightField titleField = hit.getHighlightFields().get("title");
                       if (titleField != null) {
                           post.setTitle(titleField.getFragments()[0].toString());
                      }

                       HighlightField contentField = hit.getHighlightFields().get("content");
                       if (contentField != null) {
                           post.setContent(contentField.getFragments()[0].toString());
                      }

                       list.add(post);
                  }

                   return new AggregatedPageImpl(list, pageable,
                           hits.getTotalHits(), response.getAggregations(), response.getScrollId(), hits.getMaxScore());
              }
          });

           System.out.println(page.getTotalElements());
           System.out.println(page.getTotalPages());
           System.out.println(page.getNumber());
           System.out.println(page.getSize());
           for (Discuss post : page) {
               System.out.println(post);
          }
      }

    }
  7. 基于es的全文检索第二种方法

    @RunWith(SpringRunner.class)
    @SpringBootTest
    @ContextConfiguration(classes = CommunityApplication.class)
    public class ElasticSearchTest {
       @Autowired
       private DiscussPostMapper discussPostMapper;
       @Autowired
       private ElasticSearchRepository elasticSearchRepository;

       @Test
       //添加
       public void contextLoads() {
           elasticSearchRepository.save(discussPostMapper.selectDiscussById(265));
      }

       //批量添加
       @Test
       public void contextLoads1() {
    //       elasticSearchRepository.saveAll(discussPostMapper.getListAll(101,0,100));
           elasticSearchRepository.saveAll(discussPostMapper.getListAll(102,0,100));
           elasticSearchRepository.saveAll(discussPostMapper.getListAll(103,0,100));
    //       elasticSearchRepository.saveAll(discussPostMapper.getListAll(104,0,100));

      }
       @Test
       //删除
       public void contextLoads2() {
           elasticSearchRepository.delete(discussPostMapper.selectDiscussById(265));
      }
       @Test
       //修改
       public void contextLoads3() {
           Discuss discuss = discussPostMapper.selectDiscussById(265);
           discuss.setTitle("今年招聘计划");
           elasticSearchRepository.save(discuss);
      }
       @Test
       //删除所有
       public void contextLoads4() {
           elasticSearchRepository.deleteAll();
      }
       //检索;title:互联网|content:寒冬
       //测试按title模糊匹配
       @Test
       public void titleMatch(){
           Iterable<Discuss> items=
                   elasticSearchRepository
                          .queryItemsByTitleMatches("互联网");
           items.forEach(item -> System.out.println(item));
      }

       //检索;title:互联网|content:跳水
       @Test
       public void contextLoads6(){
           Iterable<Discuss> items2=
                   elasticSearchRepository
                          .queryDiscussByTitleAndContentMatches("互联网","跳水");
           items2.forEach(item -> System.out.println(item));

      }

       //按照type排序
       @Test
       public void orderBy(){
           Iterable<Discuss> items=
                   elasticSearchRepository
                          .queryDiscussByTitleMatchesOrderByTypeDesc("求职");
           items.forEach(item -> System.out.println(item));
      }

       //分页查询
       @Test
       public void page() {
           //PageRequest.of这个方法第一个参数是页码
           //但是和PageHelper不同的是从0开始
           int pageNum = 2;
           int pageSize = 2;
           Page<Discuss> page = elasticSearchRepository
                  .queryDiscussByTitleMatchesOrderByCreateTimeDesc(
                           "求职"
                          , PageRequest.of(pageNum - 1, pageSize));
           List<Discuss> list = page.getContent();
           list.forEach(item -> System.out.println(item));
           //Page对象中的属性
           System.out.println("总页数:" + page.getTotalPages());
           System.out.println("当前页码:" + page.getNumber());
           System.out.println("页面条数:" + page.getSize());
           System.out.println("是不是第一页:" + page.isFirst());
           System.out.println("是不是最后一页:" + page.isLast());

           System.out.println("下一页页码:" + page.nextPageable().next().getPageNumber());
           System.out.println("上一页页码:" + page.previousPageable().first().getPageNumber());
      }
posted @ 2022-02-24 18:30  简讯_A  阅读(66)  评论(0)    收藏  举报