Java零基础——Elasticsearch篇

1.Elasticsearch简介

Elasticsearch是一个基于Lucene的一个开源的分布式、RESTful 风格的搜索和数据分析引擎。Elasticsearch是用Java语言开发的,并作为Apache许可条款下的开放源码发布,是一种流行的企业级搜索引擎。Elasticsearch用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。官方客户端在Java、.NET(C#)、PHP、Python、Apache Groovy、Ruby和许多其他语言中都是可用的。根据DB-Engines的排名显示,Elasticsearch是最受欢迎的企业搜索引擎,其次是Apache Solr,也是基于Lucene。

2.Lucene 核心库(红黑二叉树)

Lucene 可以说是当下最先进、高性能、全功能的搜索引擎库——无论是开源还是私有,但它也仅仅只是一个库。为了充分发挥其功能,你需要使用 Java 并将 Lucene 直接集成到应用程序中。 更糟糕的是,您可能需要获得信息检索学位才能了解其工作原理,因为Lucene 非常复杂。

为了解决Lucene使用时的繁复性,于是Elasticsearch便应运而生。它使用 Java 编写,内部采用 Lucene 做索引与搜索,但是它的目标是使全文检索变得更简单,简单来说,就是对Lucene 做了一层封装,它提供了一套简单一致的 RESTful API 来帮助我们实现存储和检索。
在这里插入图片描述

3.和solr对比

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述

ElasticSearch 对比 Solr 总结

  1. es基本是开箱即用,非常简单。Solr安装略微复杂一丢丢

  2. Solr 利用 Zookeeper 进行分布式管理,而 Elasticsearch 自身带有分布式协调管理功能。

  3. Solr 支持更多格式的数据,比如JSON、XML、CSV,而 Elasticsearch 仅支持json文件格式。

  4. Solr 官方提供的功能更多,而 Elasticsearch 本身更注重于核心功能,高级功能多有第三方插件提供,例如图形化界面需要kibana,head等友好支撑,分词插件

  5. Solr 查询快,但更新索引时慢(即插入删除慢),用于电商等查询多的应用(之前);

ES建立索引快(即查询慢),即实时性查询快,用于推特 新浪等搜索。

Solr 是传统搜索应用的有力解决方案,但 Elasticsearch 更适用于新兴的实时搜索应用。

  1. Solr比较成熟,有一个更大,更成熟的用户、开发和贡献者社区,而 Elasticsearch相对开发维护者较少,更新太快,学习使用成本较高。 (现在es也比较火)

4.倒排索引(重点)

正排索引根据id 找到对应的一组数据 (B+tree 聚簇索引)

非聚簇索引:给一个字段建立索引,查询的时候 根据这个字段查到这行数据对应的id

回表 再根据id 去查 聚簇索引 从而拿到一行数据

4.1 正排索引
在这里插入图片描述

4.2 倒排索引
在这里插入图片描述

一个倒排索引由文档中所有不重复词的列表构成,对于其中每个词,有一个包含它的 Term 列表。

5.分词

就是按照一定的规则,将一句话分成组合的单词,按照国人喜欢来进行的

海上生明月 - 如何分成 ----->海上 | 生 | 明月

我想要个女朋友 ---- > 我|想要|要个|女朋友|朋友

6.模拟一个倒排索引

原理步骤:

  1. 将数据存入mysql之前,对其进行分词

  2. 讲分词和存入后得到的id,存放在数据结构中Map<String,Set> index

  3. 查询时先分词,然后从index中拿到Set ids

  4. 再根据ids 查询mysql,从而得到结果,这样借助了mysql的B+tree索引,提高性能

6.1 创建boot项目选择依赖
在这里插入图片描述

6.2 引入分词的依赖

<dependency>
    <groupId>com.huaban</groupId>
    <artifactId>jieba-analysis</artifactId>
    <version>1.0.2</version>
</dependency>

6.3 修改启动类,注入结巴分词器

/**
 * 往IOC容器中入住结巴分词组件
 *
 * @return
 */
@Bean
public JiebaSegmenter jiebaSegmenter() {
    return new JiebaSegmenter();
}

6.4 测试分词

@Autowired
public JiebaSegmenter jiebaSegmenter;

@Test
void testJieBa() {
    String words = "华为 HUAWEI P40 Pro 麒麟990 5G SoC芯片 5000万超感知徕卡四摄 50倍数字变焦 8GB+256GB零度白全网通5G手机";
    // 使用结巴分词,对字符串进行分词,分词类型为搜索类型
    List<SegToken> tokens = jiebaSegmenter.process(words, JiebaSegmenter.SegMode.INDEX);
    // 遍历,拿到SegToken对象中的word属性,打印结果
    tokens.stream()
            .map(token -> token.word)
            .collect(Collectors.toList())
            .forEach(System.out::println);
}

6.5 使用商品搜索案例来展示倒排索引结构

6.5.1 新建Goods类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Goods {
    /**
     * 商品的id
     */
    private Integer goodsId;

    /**
     * 商品的名称(主要分词和检索字段)
     */
    private String goodsName;

    /**
     * 商品的价格
     */
    private Double goodsPrice;

}

6.5.2 模拟数据库,新建DBUtil类

public class DBUtil {
    /**
     * 模拟数据库,key=id,value=商品对象
     * 这里也可以使用List来模拟
     */
    public static Map<Integer, Goods> db = new HashMap<>();

    /**
     * 插入数据库
     *
     * @param goods
     */
    public static void insert(Goods goods) {
        db.put(goods.getGoodsId(), goods);
    }


    /**
     * 根据id得到商品
     *
     * @param id
     * @return
     */
    public static Goods getGoodsById(Integer id) {
        return db.get(id);
    }

    /**
     * 根据ids查询商品集合
     *
     * @param ids
     * @return
     */
    public static List<Goods> getGoodsByIds(Set<Integer> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            return Collections.emptyList();
        }
        List<Goods> goods = new ArrayList<>(ids.size() * 2);
        // 循环ids
        ids.forEach(id -> {
            // 从数据库拿到数据
            Goods g = db.get(id);
            if (!ObjectUtils.isEmpty(g)) {
                goods.add(g);
            }
        });
        return goods;
    }
}

6.5.3 创建倒排索引的数据结构

public class InvertedIndex {
    /**
     * 倒排索引 key = 分词,value= ids
     */
    public static Map<String, Set<Integer>> index = new HashMap<>();

}

6.5.4 创建GoodsService接口

public interface GoodsService {

    /**
     * 添加商品的方法
     *
     * @param goods
     */
    void addGoods(Goods goods);

    /**
     * 根据商品名称查询
     *
     * @param name
     * @return
     */
    List<Goods> findGoodsByName(String name);

    /**
     * 根据关键字查询
     *
     * @param keywords
     * @return
     */
    List<Goods> findGoodsByKeywords(String keywords);

}

6.5.5 创建GoodsServiceImpl实现类

@Service
public class GoodsServiceImpl implements GoodsService {

    @Autowired
    private JiebaSegmenter jiebaSegmenter;

    /**
     * 添加商品的方法
     * 1.先对商品名称进行分词,拿到了List<String> tokens
     * 2.将商品插入数据库 拿到商品id
     * 3.将tokens和id放入倒排索引中index
     *
     * @param goods
     */
    @Override
    public void addGoods(Goods goods) {
        // 分词
        List<String> keywords = fenci(goods.getGoodsName());
        // 插入数据库
        DBUtil.insert(goods);
        // 保存到倒排索引中
        saveToInvertedIndex(keywords, goods.getGoodsId());
    }

    /**
     * 保存到倒排索引的方法
     *
     * @param keywords
     * @param goodsId
     */
    private void saveToInvertedIndex(List<String> keywords, Integer goodsId) {
        // 拿到索引
        Map<String, Set<Integer>> index = InvertedIndex.index;
        // 循环分词集合
        keywords.forEach(keyword -> {
            Set<Integer> ids = index.get(keyword);
            if (CollectionUtils.isEmpty(ids)) {
                // 如果之前没有这个词 就添加进去
                HashSet<Integer> newIds = new HashSet<>(2);
                newIds.add(goodsId);
                index.put(keyword, newIds);
            } else {
                // 说明之前有这个分词 我们记录id
                ids.add(goodsId);
            }
        });
    }

    /**
     * 分词的方法
     *
     * @param goodsName
     * @return
     */
    private List<String> fenci(String goodsName) {
        List<SegToken> tokens = jiebaSegmenter.process(goodsName, JiebaSegmenter.SegMode.SEARCH);
        return tokens.stream()
                .map(token -> token.word)
                .collect(Collectors.toList());
    }

    /**
     * 根据商品名称查询
     *
     * @param name
     * @return
     */
    @Override
    public List<Goods> findGoodsByName(String name) {
        // 查询倒排索引中 是否有这个词
        Map<String, Set<Integer>> index = InvertedIndex.index;
        Set<Integer> ids = index.get(name);
        if (CollectionUtils.isEmpty(ids)) {
            // 查询数据库 模糊匹配去
        } else {
            // 说明分词有 根据ids 查询数据库
            return DBUtil.getGoodsByIds(ids);
        }
        return Collections.emptyList();
    }

    /**
     * 根据关键字查询
     *
     * @param keywords
     * @return
     */
    @Override
    public List<Goods> findGoodsByKeywords(String keywords) {
        // 进来先把关键字分词一下
        List<String> tokens = fenci(keywords);
        // 拿到倒排索引
        Map<String, Set<Integer>> index = InvertedIndex.index;
        Set<Integer> realIds = new HashSet<>();
        // 循环分词集合 查询倒排索引
        tokens.forEach(token -> {
            Set<Integer> ids = index.get(token);
            if (!CollectionUtils.isEmpty(ids)) {
                // 如果局部的ids不为空,就添加到总的ids里面去
                realIds.addAll(ids);
            }
        });
        // 查询数据库
        return DBUtil.getGoodsByIds(realIds);
    }
}

6.5.6 编写测试类

@Autowired
public GoodsService goodsService;

/**
 * 测试我们自己写的倒排索引
 *
 * @throws Exception
 */
@Test
public void testMyIndex() throws Exception {
    // 造数据
    Goods goods = new Goods(1, "苹果手机", 10.00);
    Goods goods1 = new Goods(2, "华为手机", 11.00);
    Goods goods2 = new Goods(3, "红米手机", 5.00);
    Goods goods3 = new Goods(4, "联想手机", 6.00);
    goodsService.addGoods(goods);
    goodsService.addGoods(goods1);
    goodsService.addGoods(goods2);
    goodsService.addGoods(goods3);
    // 查询
    goodsService.findGoodsByName("苹果手机").forEach(System.out::println);
    System.out.println("--------------------------------------------");
    goodsService.findGoodsByKeywords("苹果手机").forEach(System.out::println);
}

7.Elasticsearch安装

下载地址 https://www.elastic.co/cn/downloads/past-releases#elasticsearch

echo 3 > /proc/sys/vm/drop_caches
在这里插入图片描述

1.Docker拉取镜像,我们选7.15.2版本,因为springboot的2.6.3版指定的是这个版本

docker pull elasticsearch:7.15.2

  1. 运行镜像

docker run -d --name elasticsearch --net=host -p 9200:9200 -p 9300:9300 -e “discovery.type=single-node” -e ES_JAVA_OPTS=“-Xms256m -Xmx256m” elasticsearch:7.15.2

  1. 试访问 ip:9200

在这里插入图片描述

  1. 意外

错误:max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144],你最少需要262144的内存

运行:sysctl -w vm.max_map_count=262144

重启容器就ok 了

8.Elasticsearch目录学习

在这里插入图片描述在这里插入图片描述

bin:启动脚本

config:

elasticsearch.yml,ES的集群信息、对外端口、内存锁定、数据目录、跨域访问等属性的配置

jvm.options,ES使用Java写的,此文件用于设置JVM相关参数,如最大堆、最小堆

log4j2.properties,ES使用log4j作为其日志框架

data:数据存放目录(索引数据)

plugins: ES的可扩展插件存放目录,如可以将ik中文分词插件放入此目录,ES启动时会自动加载

9.Elasticsearch可视化插件的安装

9.1 谷歌插件方式
在这里插入图片描述
在这里插入图片描述

9.2 Docker镜像方式安装(和9.1选择一个玩)
在这里插入图片描述

docker run --name eshead -p 9100:9100 -d mobz/elasticsearch-head:5

9.3 解决跨域问题
在这里插入图片描述

1.进入elasticsearch容器

docker exec -it elasticsearch bash

2.进入配置文件

cd /usr/share/elasticsearch/config

  1. 修改elasticsearch.yml配置文件,

vi elasticsearch.yml

  1. 结尾添加

http.cors.enabled: true

http.cors.allow-origin: “*”

  1. 重启elasticsearch后访问即可

docker restart elasticsearch

10.IK分词的安装【重点】**

Ik分词在es 里面也是插件的形式安装的

10.1 没有ik分词的时候
在这里插入图片描述

10.2 IK分词安装(方式一受网速影响)

  1. 找对应的ES版本的IK分词

https://github.com/medcl/elasticsearch-analysis-ik/releases

  1. 进入容器 docker exec -it elasticsearch bash

  2. 进入这个目录 /usr/share/elasticsearch/bin

  3. 执行命令 install 后面的连接就是从上面找到的对应的版本下载链接

./elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.15.2/elasticsearch-analysis-ik-7.15.2.zip

  1. 下载完成
    在这里插入图片描述

10.3 IK分词安装(方式二推荐)

  1. 找对应的ES版本的IK分词 https://github.com/medcl/elasticsearch-analysis-ik/releases

  2. 下载到windows上

  3. 将zip文件拷贝到linux上

  4. docker cp linux的路径 elasticsearch:/usr/share/elasticsearch/plugins
    在这里插入图片描述

  5. 进容器后解压,注意名字unzip elasticsearch-analysis-ik-7.15.2.zip -d ./ik/

  6. 删掉zip文件 elasticsearch-analysis-ik-7.15.2.zip

10.4 重启ES测试
docker restart elasticsearch

Ik分词的两种方式:

ik_smart:分词的粒度较小,也叫智能分词
在这里插入图片描述

ik_max_word:分词的粒度较大,也叫最大力度分词
在这里插入图片描述

10.5 自定义分词【了解】
进入容器
在这里插入图片描述

11.Elasticsearch核心概念【重点】

一个json串 叫文档 es又被叫文档性数据库

11.1 结构说明
在这里插入图片描述

11.2 索引库(indices)
把数据写入elasticsearch 时,会在里面建立索引,索引库里面存储索引,一个index 对应一个database

11.3 文档(document)
就是一条数据,一般使用json 表示,和数据库对比,就是一行数据,在java 里面就是一个一个对象

11.4 字段(field)
一个对象的属性,对应数据库就是一列

11.5 节点
一台运行elasticsearch的服务器,被称为一个节点

11.6 集群
多个节点组成一个集群

11.7 分片
一个索引可以存储在多个主分片上,有负载均衡的作用,还有从分片是主分片的一个副本

11.8 副本
一份数据可以有多个副本,做数据冗余(安全),一般放在从分片里面
在这里插入图片描述

12.Elasticsearch基本使用(重点)

Elasticsearch 是基于restful风格的http应用

Restful风格就是使用http动词形式对url资源进行操作(GET,POST,PUT,DELETE…)

操作格式为:

请求类型 ip:port/索引名/_doc/文档id

{请求体}

12.1 对索引和mappings的操作(建库建表约束)
12.1.1 新增索引
PUT http://192.168.226.128:9200/student 新建一个student索引,给定几个字段约束

索引只能增删,不能修改

{

  "mappings": {

    "properties": {

      "name":{

        "type": "text"

      },

      "age":{

        "type": "integer"

      },

      "birthDay":{

        "type": "date"

      },

      "price":{

        "type": "double"

      }

    }

  }

}

查询索引信息
在这里插入图片描述

查询索引的mappings信息
在这里插入图片描述

12.1.2 删除索引
DELETE http://192.168.226.128:9200/student

12.2 对Document的操作
12.2.1 新增数据(方便后面演示,自己多新增几条)
使用put请求新增时需要自己指定id,使用post请求新增时系统会自动生成一个id

PUT http://192.168.226.128:9200/user/_doc/1 请求解释

user: 索引名称

_doc:类型(即将剔除,官方建议全部使用_doc)

1: 文档id
在这里插入图片描述

从head插件里面查看数据
在这里插入图片描述

12.2.2 修改一个数据

  1. 危险的修改,把其他的字段值都删了

PUT http://192.168.226.128:9200/user/_doc/2
在这里插入图片描述在这里插入图片描述

  1. 安全的修改,其他的字段值会保留

POST http://192.168.226.128:9200/user/_doc/2/_update
在这里插入图片描述在这里插入图片描述

12.2.3 删除一个数据
DELETE http://192.168.226.128:9200/user/_doc/3
在这里插入图片描述在这里插入图片描述

12.2.4 查询一个数据
GET http://192.168.226.128:9200/user/_doc/1
在这里插入图片描述

12.2.5 查询全部数据
GET http://192.168.226.128:9200/user/_doc/_search

13.SpringBoot使用ES【重点】

13.1 新建项目选择依赖
在这里插入图片描述

13.2 修改配置文件
spring:
elasticsearch:
uris: http://192.168.226.129:9200

13.3 测试连接ES
说明我们连接成功,下面开始操作
在这里插入图片描述

13.4 对索引的操作,我们直接使用实体类操作
13.4.1 新建Goods实体类

/**
 * @Document是ES提供的注解 indexName:索引名称
 * createIndex:启动时是否创建
 * shards:分片个数
 * replicas:副本个数
 * refreshInterval:数据导入到索引里面,最多几秒搜索到
 */

@Data
@AllArgsConstructor
@NoArgsConstructor
@Setting(shards = 2,replicas = 1,refreshInterval = "1s")
@Document(indexName = "goods_index")
public class Goods {

    /**
     * 商品ID
     */
    @Id //默认使用keyword关键字模式,不进行分词
    @Field
    private Integer goodsId;
    /**
     * 商品名称
     * analyzer:导入时使用的分词
     * searchAnalyzer:搜索时使用的分词
     */
    @Field(type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_smart")
    private String goodsName;
    /**
     * 商品描述
     */
    @Field(type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_smart")
    private String goodsDesc;
    /**
     * 商品价格
     */
    @Field(type = FieldType.Double)
    private Double goodsPrice;
    /**
     * 商品的销量
     */
    @Field(type = FieldType.Long)
    private Long goodsSaleNum;
    /**
     * 商品的卖点
     */
    @Field(type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_smart")
    private String goodsBrief;
    /**
     * 商品的状态
     */
    @Field(type = FieldType.Integer)
    private Integer goodsStatus;
    /**
     * 商品的库存
     */
    @Field(type = FieldType.Integer)
    private Integer goodsStock;
    /**
     * 商品的标签
     */
    @Field(type = FieldType.Text)
    private List<String> goodsTags;
    /**
     * 上架的时间
     * 指定时间格式化
     */
    private Date goodsUpTime;

}
 

13.5 对文档的操作,我们熟悉的CRUD
13.5.1 创建goodsDao

@Repository
public interface GoodsDao extends ElasticsearchRepository<Goods, Integer> {

}

13.5.2 新增数据

@Autowired
private GoodsDao goodsDao;

/**
 * 描述: 对document的curd
 *
 * @param :
 * @return void
 */
@Test
public void testDocumentCurd() throws Exception {
    //新增商品数据100条
    ArrayList<Goods> goods = new ArrayList<>(200);
    for (int i = 1; i <= 100; i++) {
        goods.add(
                new Goods(i,
                        i % 2 == 0 ? "华为电脑" + i : "联想电脑" + i,
                        i % 2 == 0 ? "轻薄笔记本" + i : "游戏笔记本" + i,
                        4999.9 + i,
                        999L,
                        i % 2 == 0 ? "华为续航强" : "联想性能强",
                        i % 2 == 0 ? 1 : 0,
                        666 + i,
                        i % 2 == 0 ? Arrays.asList("小巧", "轻薄", "续航") : Arrays.asList("炫酷", "畅玩", "游戏"),
                        new Date())
        );
    }
    goodsDao.saveAll(goods);
}

13.5.3 修改数据(注意是危险修改)

@Test
public void testUpdate() throws Exception {
    goodsDao.save(new Goods(1, "小米笔记本", null, null, null, null, null, null, null, null));
}

13.5.4 删除数据

@Test
public void testDelete() throws  Exception{
    goodsDao.deleteById(1);
}
 

13.5.5 根据id查询数据

@Test
public void testSearchById() throws Exception {
    Optional<Goods> byId = goodsDao.findById(502);
    System.out.println(byId.get());
}
 

13.5.6 查询所有数据

@Test
public void testSearchAll() throws Exception {
    Iterable<Goods> all = goodsDao.findAll();
    all.forEach(System.out::println);
}

14.复杂的查询操作【重点】

14.1 查询注意点
match:会通过分词器去模糊匹配 例如:华为电脑,会把包含‘华为’,‘电脑’,都查出来

matchPhrase:不分词查询,弥补了match和term

term:精确查找你的关键字,一般使用keywords的约束,使用term

rang:范围查询

match 和 rang 如果同时出现,需要组合bool查询

分页,排序是通用的查询,不需要通过bool组合使用,直接nativeSearchQueryBuilder使用

14.2 查询常用类
QueryBuilders:构造条件对象,例如matchQuery,rangeQuery,boolQuery等

NativeSearchQueryBuilder:组合条件对象,组合后使用build构建查询对象

HighlightBuilder:高亮的查询类,注意使用它的Field静态内部类

FunctionScoreQueryBuilder:权重类,注意它的FilterFunctionBuilder静态内部类

14.3 关键字,范围,分页,排序

@Test
public void testFuZaSearch() throws Exception {
    //关键字,“华为” ,范围,分页,排序
    MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("goodsName", "华为");
    RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("goodsStock").from(700).to(750);

    //使用bool组合这两个查询
    BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery().must(matchQueryBuilder).must(rangeQueryBuilder);

    //创建组合器
    NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
    //去build()构建查询对象
    NativeSearchQuery nativeSearchQuery = nativeSearchQueryBuilder
            .withQuery(boolQueryBuilder)
            .withPageable(PageRequest.of(0, 20))    //注意范围和分页有关系,可能查出来了,但是当前分页没有
            .withSort(SortBuilders.fieldSort("goodsPrice").order(SortOrder.ASC))
            .build();

    //使用es查询 得到结果集
    SearchHits<Goods> searchHits = elasticsearchRestTemplate.search(nativeSearchQuery, Goods.class);
    searchHits.forEach(goodsSearchHit -> {
        //循环结果集,答应结果
        System.out.println(goodsSearchHit.getContent());
    });
}

14.4 高亮查询

@Test
    public void testHighlight() throws Exception {
        //华为,模糊匹配
        MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("goodsName", "华为");

        HighlightBuilder.Field goodsName = new HighlightBuilder.Field("goodsName").preTags("<span style='color:red'>").postTags("</span>");

        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();

        NativeSearchQuery nativeSearchQuery = nativeSearchQueryBuilder
                .withQuery(matchQueryBuilder)
                .withHighlightFields(goodsName)
                .build();
        //得到结果集 我们需要手动组装高亮字段
        SearchHits<Goods> searchHits = elasticsearchRestTemplate.search(nativeSearchQuery, Goods.class);

        List<Goods> goodsArrayList = new ArrayList<>();

        searchHits.forEach(goodsSearchHit -> {
            //得到goods对象,但是这里面额goodsName属性不是高亮的,所以要改
            Goods goods = goodsSearchHit.getContent();
            List<String> highlightField = goodsSearchHit.getHighlightField("goodsName");
            String highlight = highlightField.get(0);
            goods.setGoodsName(highlight);
            goodsArrayList.add(goods);
        });
        System.out.println(JSON.toJSONString(goodsArrayList));
    }
}

14.5 权重查询

@Test
public void testWeight() throws Exception {
    //手机名称 和卖点 都有的情况下,设置权重查询
    String keyWords = "华为";
    //创建权重数组
    FunctionScoreQueryBuilder.FilterFunctionBuilder[] functionBuilders = new FunctionScoreQueryBuilder.FilterFunctionBuilder[2];

    //设置权重
    functionBuilders[0] = (new FunctionScoreQueryBuilder.FilterFunctionBuilder(
            QueryBuilders.matchQuery("goodsName", keyWords),
            ScoreFunctionBuilders.weightFactorFunction(10)//给名称设置10的权重大小
    ));
    functionBuilders[1] = (
            new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                    QueryBuilders.matchQuery("goodsBrief", keyWords),
                    ScoreFunctionBuilders.weightFactorFunction(4)//给卖点设置4的权重
            ));

    FunctionScoreQueryBuilder functionScoreQueryBuilder = new FunctionScoreQueryBuilder(functionBuilders);

    functionScoreQueryBuilder.setMinScore(2) //设置最小分数
            .scoreMode(FunctionScoreQuery.ScoreMode.FIRST);//设置计分方式

    NativeSearchQuery nativeSearchQuery = new NativeSearchQueryBuilder().withQuery(functionScoreQueryBuilder).build();
    SearchHits<Goods> searchHits = elasticsearchRestTemplate.search(nativeSearchQuery, Goods.class);
    searchHits.forEach(goodsSearchHit -> {
        //循环结果集,答应结果
        System.out.println(goodsSearchHit.getContent());
    });
}

15.ES集群(了解)

这里使用windows方式演示集群

Linux上的集群道理一样,修改配置文件即可

可以参考https://my.oschina.net/u/4353003/blog/4333773

15.1 创建三个es节点
解压出三个来
在这里插入图片描述

15.2 修改配置文件
15.2.1 Node1修改配置文件
进入elasticsearch-7.15.2-node1\config下,修改elasticsearch.yml

# 设置集群名称,集群内所有节点的名称必须一致。

cluster.name: my-esCluster

# 设置节点名称,集群内节点名称必须唯一。

node.name: node1

# 表示该节点会不会作为主节点,true表示会;false表示不会

node.master: true

# 当前节点是否用于存储数据,是:true、否:false

node.data: true

# 索引数据存放的位置

#path.data: /opt/elasticsearch/data

# 日志文件存放的位置

#path.logs: /opt/elasticsearch/logs

# 需求锁住物理内存,是:true、否:false

#bootstrap.memory_lock: true

# 监听地址,用于访问该es

network.host: 0.0.0.0

# es对外提供的http端口,默认 9200

http.port: 9200

# TCP的默认监听端口,默认 9300

transport.tcp.port: 9300

# 设置这个参数来保证集群中的节点可以知道其它N个有master资格的节点。默认为1,对于大的集群来说,可以设置大一点的值(2-4)

discovery.zen.minimum_master_nodes: 2

# es7.x 之后新增的配置,写入候选主节点的设备地址,在开启服务后可以被选为主节点

discovery.seed_hosts: ["192.168.186.1:9300", "192.168.186.1:9301", "192.168.186.1:9302"]

discovery.zen.fd.ping_timeout: 1m

discovery.zen.fd.ping_retries: 5

# es7.x 之后新增的配置,初始化一个新的集群时需要此配置来选举master

cluster.initial_master_nodes: ["node1", "node2", "node3"]

# 是否支持跨域,是:true,在使用head插件时需要此配置

http.cors.enabled: true

# “*” 表示支持所有域名

http.cors.allow-origin: "*"

action.destructive_requires_name: true

action.auto_create_index: .security,.monitoring*,.watches,.triggered_watches,.watcher-history*

xpack.security.enabled: false

xpack.monitoring.enabled: true

xpack.graph.enabled: false

xpack.watcher.enabled: false

xpack.ml.enabled: false

15.2.2 Node2修改配置文件

# 设置集群名称,集群内所有节点的名称必须一致。

cluster.name: my-esCluster

# 设置节点名称,集群内节点名称必须唯一。

node.name: node2

# 表示该节点会不会作为主节点,true表示会;false表示不会

node.master: true

# 当前节点是否用于存储数据,是:true、否:false

node.data: true

# 索引数据存放的位置

#path.data: /opt/elasticsearch/data

# 日志文件存放的位置

#path.logs: /opt/elasticsearch/logs

# 需求锁住物理内存,是:true、否:false

#bootstrap.memory_lock: true

# 监听地址,用于访问该es

network.host: 0.0.0.0

# es对外提供的http端口,默认 9200

http.port: 9201

# TCP的默认监听端口,默认 9300

transport.tcp.port: 9301

# 设置这个参数来保证集群中的节点可以知道其它N个有master资格的节点。默认为1,对于大的集群来说,可以设置大一点的值(2-4)

discovery.zen.minimum_master_nodes: 2

# es7.x 之后新增的配置,写入候选主节点的设备地址,在开启服务后可以被选为主节点

discovery.seed_hosts: ["192.168.186.1:9300", "192.168.186.1:9301", "192.168.186.1:9302"]

discovery.zen.fd.ping_timeout: 1m

discovery.zen.fd.ping_retries: 5

# es7.x 之后新增的配置,初始化一个新的集群时需要此配置来选举master

cluster.initial_master_nodes: ["node1", "node2", "node3"]

# 是否支持跨域,是:true,在使用head插件时需要此配置

http.cors.enabled: true

# “*” 表示支持所有域名

http.cors.allow-origin: "*"

action.destructive_requires_name: true

action.auto_create_index: .security,.monitoring*,.watches,.triggered_watches,.watcher-history*

xpack.security.enabled: false

xpack.monitoring.enabled: true

xpack.graph.enabled: false

xpack.watcher.enabled: false

xpack.ml.enabled: false

15.2.3 Node3修改配置文件

# 设置集群名称,集群内所有节点的名称必须一致。

cluster.name: my-esCluster

# 设置节点名称,集群内节点名称必须唯一。

node.name: node3

# 表示该节点会不会作为主节点,true表示会;false表示不会

node.master: true

# 当前节点是否用于存储数据,是:true、否:false

node.data: true

# 索引数据存放的位置

#path.data: /opt/elasticsearch/data

# 日志文件存放的位置

#path.logs: /opt/elasticsearch/logs

# 需求锁住物理内存,是:true、否:false

#bootstrap.memory_lock: true

# 监听地址,用于访问该es

network.host: 0.0.0.0

# es对外提供的http端口,默认 9200

http.port: 9202

# TCP的默认监听端口,默认 9300

transport.tcp.port: 9302

# 设置这个参数来保证集群中的节点可以知道其它N个有master资格的节点。默认为1,对于大的集群来说,可以设置大一点的值(2-4)

discovery.zen.minimum_master_nodes: 2

# es7.x 之后新增的配置,写入候选主节点的设备地址,在开启服务后可以被选为主节点

discovery.seed_hosts: ["192.168.186.1:9300", "192.168.186.1:9301", "192.168.186.1:9302"]

discovery.zen.fd.ping_timeout: 1m

discovery.zen.fd.ping_retries: 5

# es7.x 之后新增的配置,初始化一个新的集群时需要此配置来选举master

cluster.initial_master_nodes: ["node1", "node2", "node3"]

# 是否支持跨域,是:true,在使用head插件时需要此配置

http.cors.enabled: true

# “*” 表示支持所有域名

http.cors.allow-origin: "*"

action.destructive_requires_name: true

action.auto_create_index: .security,.monitoring*,.watches,.triggered_watches,.watcher-history*

xpack.security.enabled: false

xpack.monitoring.enabled: true

xpack.graph.enabled: false

xpack.watcher.enabled: false

xpack.ml.enabled: false

15.3 启动es

进入bin目录下 逐个启动,三台全部双击启动,注意不要关闭黑窗口

15.4 访问查看集群信息和状态
访问查看:http://127.0.0.1:9200/_cat/nodes
在这里插入图片描述

也可以使用head插件查看
在这里插入图片描述

15.5 SpringBoot连接es集群
spring:
elasticsearch:
uris:
- http://127.0.0.1:9200
- http://127.0.0.1:9201
- http://127.0.0.1:9202

16.Es总结面试

16.1 为什么使用es (结合项目业务来说)
商城中的数据,将来会非常多,所以采用以往的模糊查询,模糊查询前置配置,会放弃索引,(%name%)导致商品查询是全表扫描,在百万级别的数据库中,效率非常低下,而我们使用ES做一个全文索引,我们将经常查询的商品的某些字段,比如说商品名,描述、价格还有id这些字段我们放入我们索引库里,可以提高查询速度。 (数据量过大,而且用户经常使用查询的场景,对用户体验很好)

16.2 es用来做什么(场景)重点

  1. 商品存放 (业务数据的存放) 每个商品都是一个对象

  2. 日志统计 (整个应用的日志非常多,都要收集起来,方便后期做日志分析,数据回溯)

  3. 数据分析(大数据)(只要是电商,就要和tb,jd等对比价格,做数据分析等)

你做一个电商项目 你卖鞋子 衣服 你怎么 定价 鞋子 199 599 398

你写个爬虫 定期采集tb jd的数据库 做数据比对分析 做 竞品 分析

定期的做数据采集和分析 数据采集系统 定时任务去采集别的大型电商网站的数据,进行价格分析,从而定价,后期还要做竞品分析

16.3 什么是倒排索引
通常正排索引是通过id映射到对应的数据

倒排索引是将分词建立索引,通过分词映射到id,在通过id找到数据

详见文档4.2

16.4 es的存储数据的过程
在这里插入图片描述

1:写入请求,分发节点。

2:数据写入同时写入内存和translog各一份,tanslog为保证数据不丢失,每 5 秒,或每次请求操作结束前,会强制刷新 translog 日志到磁盘上

3:确定数据给那个分片,refresh 刷新内存中数据到分片的segment,默认1秒刷新一次,为了提高吞吐量可以增大60s。参数refresh_interval(refresh操作使得写入文档搜索可见)

4:通过flush操作将segment刷新到磁盘中完成持久化,保存成功清除translog,新版本es的 translog 不会在 segment 落盘就删,而是会保留,默认是512MB,保留12小时。每个分片。所以分片多的话 ,要考虑 translog 会带来的额外存储开销(flush操作使得filesystem cache写入磁盘,以达到持久化的目的) (refresh之前搜索不可见)

5:segment过多会进行合并merge为大的segment,消耗大量的磁盘io和网络io (方便数据整理和磁盘优化)

16.5 es的搜索过程
1、搜索被执行成一个两阶段过程,我们称之为 Query Then Fetch;

2、在初始查询阶段时,查询会广播到索引中每一个分片拷贝(主分片或者副本分

片)。 每个分片在本地执行搜索并构建一个匹配文档的大小为 from + size 的

优先队列。

注意:在搜索的时候是会查询 Filesystem Cache 的,但是有部分数据还在 Memory

Buffer,所以搜索是接近实时的。

3、每个分片返回各自优先队列中所有文档的 ID 和排序值给协调节点(主节点),它合并

这些值到自己的优先队列中来产生一个全局排序后的结果列表。

4、接下来就是 取回阶段,协调节点辨别出哪些文档需要被取回并向相关的分片

提交多个 GET 请求。每个分片加载并 丰富 文档,如果有需要的话,接着返回

文档给协调节点。一旦所有的文档都被取回了,协调节点返回结果给客户端。

通俗的将就是:

每个分片先拿到id和排序值,然后整合成一个全局列表

然后通过判断找到相应的节点提交多个get请求,组装数据返回

https://javajl.yuque.com/docs/share/61000afe-fc17-4bb7-9279-63bfba5fd6cc?# 《Elasticsearch 7.x(入门)》密码:yk91

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/232885.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

13. MySQL 日志

目录 错误日志 binlog日志 概述 日志格式 查询日志 慢查询日志 错误日志 错误日志是MySQL中最重要的日志之一&#xff0c;它记录了当mysqld启动和停止时&#xff0c;以及服务器在运行过程中发生任何严重错误时的相关信息。当数据库出现任何故障导致无法正常使用时&#…

用Rust刷LeetCode之26 删除有序数组中的重复项

26. 删除排序数组中的重复项[1] 难度: 简单 老的描述: 新的描述: 注意是 排序数组,非严格递增排列,即已经是排好序的,只不过有重复元素 func removeDuplicates(nums []int) int { if len(nums) 0 { return 0 } i : 0 for j : 1; j < len(nums); j { …

Kubernetes架构及核心部件

文章目录 1、Kubernetes集群概述1.1、概述1.2、通过声明式API即可 2、Kubernetes 集群架构2.1、Master 组件2.1.1、API Server2.1.2、集群状态存储2.1.3、控制器管理器2.1.4、调度器 2.2、Worker Node 组件2.2.1、kubelet2.2.2、容器运行时环境2.2.3、kube-proxy 2.3、图解架构…

AI隆重软件,AI原创文章隆重软件

随着信息量的急剧增加&#xff0c;许多写作者、网站管理员和内容创作者们纷纷感受到了文章降重的压力。原始文本的降重&#xff0c;需要保留关键信息的同时避免重复&#xff0c;这是一项既繁琐又耗时的任务。 改写软件的批量降重功能 147SEO改写软件在降重领域的卓越表现主要体…

PHP基础 - 注释变量

一. 语言开始标识 在PHP中,文件的开头需要使用语言开始标识来指定该文件是PHP代码。标识通常为"<?php",也可以是"<?",但建议使用"<?php"以确保代码的兼容性和可读性。 <?php // PHP代码从这里开始写 二. PHP注释 注释是用…

svn使用步骤

服务器端主要用来创建仓库&#xff0c;然后供客户端去访问与下载。 客户端&#xff1a; 图形化界面的使用&#xff1a;这里使用的是tortoise工具 1.创建一个文件夹作为自己的本地仓库目录 2.鼠标右键文件夹&#xff0c;在菜单中点击SVN checkout 3.找个图 这一步骤相当于git中…

nodejs+vue+微信小程序+python+PHP的基于大数据的家电销售分析系统设计与实现-计算机毕业设计推荐django

系统按照用户的实际需求开发而来&#xff0c;贴近生活。从管理员通过正确的账号的密码进入系统&#xff0c;可以使用相关的系统应用。管理员总体负责整体系统的运行维护&#xff0c;统筹协调。 系统整体模块设计&#xff1a;系统分为管理员和用户两大角色&#xff0c;系统管理员…

Linux 系统上配置 SSH 密钥

1. 生成 SSH 密钥 打开终端&#xff0c;运行以下命令来生成 SSH 密钥&#xff1a; ssh-keygen -t rsa -b 4096 -C "wqzbxhexample.com" 替换 "wqzbxhexample.com" 为你在 GitHub 注册时使用的邮箱地址。 2. 添加 SSH 密钥到 SSH 代理 运行以下命令来启…

贪心算法和动态规划

目录 一、简介 二、贪心算法案例&#xff1a;活动选择问题 1.原理介绍 三、动态规划案例&#xff1a;背包问题 1.原理介绍 四、贪心算法与动态规划的区别 五、总结 作者其他文章链接 正则表达式-CSDN博客 深入理解HashMap&#xff1a;Java中的键值对存储利器-CSDN博客…

知名的Mac系统清理软件CleanMyMac发布了最新的CleanMyMac X 4.14.5 破解版下载

最新版CleanMyMac X 让您的Mac焕然一新&#xff0c;时刻保持安全 CleanMyMac X是一款专业的Mac清理软件&#xff0c;可智能清理mac磁盘垃圾和多余语言安装包&#xff0c;快速释放电脑内存&#xff0c;轻松管理和升级Mac上的应用。同时CleanMyMac X可以强力卸载恶意软件&#x…

Arduino驱动MPX5700AP气压传感器(压力传感器)

目录 1、传感器特性 2、硬件原理图 3、控制器和传感器连线图 4、驱动程序 4.1、采集数据 4.2、校准传感器 MPX5700AP测量范围15~700kPa&#xff0c;支持I2C数字输出&#xff0c;可以根据已知气压值进行标定&#xff0c;可以快速、准确的测量管路或其他环境中的气压值。…

【数据结构】——二叉树功能

前言&#xff1a;我们前面已经了解了二叉树的一些概念&#xff0c;那么我们今天就来了解下二叉树的遍历实现和一些性质。 二叉树的遍历方式有三种&#xff1a;前序&#xff0c;中序&#xff0c;后序。 前序&#xff1a;先根节点&#xff0c;再左子树&#xff0c;最后右子树。 中…

如何用Qt配置git项目并上传Gitee

1.进入到Qt项目文件夹内&#xff0c;打开 “Git Bash Here” 2.初始化&#xff0c;在“Git Bash Here”中输入 git init 3.加入所有文件&#xff0c;在“Git Bash Here”中输入 git add . (需要注意&#xff0c;git add 后面还有一个点) 4.添加备注&#xff0c;git com…

蓝桥杯航班时间

蓝桥杯其他真题点这里&#x1f448; //飞行时间 - 时差 已过去的时间1 //飞行时间 时差 已过去的时间2 //两个式子相加会发现 飞行时间 两段时间差的和 >> 1import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader;public cl…

分类信息发布小程序效果如何

信息发布系统连接信息供需双方&#xff0c;打造信息聚合平台&#xff0c;用户可获取和发布需求信息、参与互动交流&#xff0c;适用于同城、社区交流、客户互动、业务员/经纪人发布信息场景。 制作分类信息小程序后&#xff0c;商家后台设置信息项&#xff0c;发布者填写内容发…

SpringBoot3-创建自定义启动器,使用自定义starter启动器

1、创建自定义启动工程pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.a…

【pip】pip install 无法安装到 conda 环境的另一种问题可能与解决方案

文章目录 1. 发现问题2. 解决思路3. 解决步骤3.1. 删除 ~/.local 中的 pip3.2. 正确换源 pip3.3. 验证问题的解决 1. 发现问题 新装了 ubuntu 系统&#xff0c;使用 sudo 权限在 /usr/local/miniconda3 下安装了 miniconda3&#xff08;配置多用户 conda 环境&#xff09;&…

100基于matlab的双线性变换法设计的切比雪夫II型低通滤波器语音信号

基于matlab的双线性变换法设计的切比雪夫II型低通滤波器语音信号&#xff0c;对加噪的语音信号进行降噪。数据可更换自己的&#xff0c;程序已调通&#xff0c;可直接运行。 100matlab切比雪夫II型低通滤波器 (xiaohongshu.com)

TOMCAT9安装

1、官网下载 2、解压到任意盘符&#xff0c;注意路径不要有中文 3、环境变量 path 下 配置 %CATALINA_HOME%\bin 4、找到tomcat9/bin&#xff0c; 点击 start.bat启动 tomcat

STM32F103

提示&#xff1a;来源正点原子&#xff0c;参考STM32F103 战舰开发指南V1.3PDF资料 文章目录 前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 开发环境硬件普中科技&#xff0c;接…
最新文章