Elasticsearch搜索引擎(二)——SpringData Elasticsearch

news2024/11/15 11:44:17

SpringData Elasticsearch

SpringData介绍

Spring Data是一个用于简化数据库访问,并支持云服务的开源框架。其主要目标是使得对数据的访问变得方便快捷,并支持map-reduce框架和云计算数据服务。 Spring Data可以极大的简化JPA的写法,可以在几乎不用写实现的情况下,实现对数据的访问和操作。除了CRUD外,还包括如分页、排序等一些常用的功能。

Spring Data的官网:http://projects.spring.io/spring-data/

SpringData ES介绍

Spring Data ElasticSearch 基于 spring data API 简化 elasticSearch操作,将原始操作elasticSearch的客户端API 进行封装 。Spring Data为Elasticsearch项目提供集成搜索引擎。Spring Data Elasticsearch POJO的关键功能区域为中心的模型与Elastichsearch交互文档和轻松地编写一个存储库数据访问层。 官方网站:http://projects.spring.io/spring-data-elasticsearch/

微服搭建

依赖pom.xml
<!-- SpringDataES依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
配置application.yml
spring:
  application:
    name: search

  data:
    elasticsearch:
      cluster-name: my-application  # es配置中的集群名字
      cluster-nodes: 192.168.169.140:9300

cluster-name:Elasticsearch的集群节点名称,这里需要和Elasticsearch集群节点名称保持一致
cluster-nodes:Elasticsearch节点通信地址,端口是9300

启动类
@SpringBootApplication(exclude={DataSourceAutoConfiguration.class})
@EnableEurekaClient
public class SearchApplication {

    public static void main(String[] args) {
        /**
        * Springboot整合Elasticsearch 在项目启动前设置一下的属性,防止报错
        * 解决netty冲突后初始化client时还会抛出异常
        * availableProcessors is already set to [12], rejecting [12]
        ***/
        System.setProperty("es.set.netty.runtime.available.processors", "false");
        SpringApplication.run(SearchApplication.class,args);
    }
}
model层

映射索引库

都是elasticsearch包

import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;

import java.io.Serializable;
import java.util.Date;
import java.util.Map;

/**
 * Title:映射索引库的javabean
 * Description:
 * @author WZQ
 * @version 1.0.0
 * @date 2020/3/5
 */
@Document(indexName = "skuinfo",type = "docs") // 索引库名
@Data
@NoArgsConstructor
public class SkuInfo implements Serializable {

    //商品id,同时也是商品编号
    @Id
    private Long id;

    //SKU名称
    /**
     * Field注解属性
     * type = FieldType.Text:类型,Text文本,适用分词
     * index = true:添加数据的时候,是否分词
     * analyzer = "ik_smart":创建索引分词器
     * store = false:是否存储
     * searchAnalyzer = "ik_smart":搜索的时候是否使用分词
     */
    @Field(type = FieldType.Text, analyzer = "ik_smart")
    private String name;

    //商品价格,单位为:元
    @Field(type = FieldType.Double)
    private Long price;

    //库存数量
    private Integer num;

    //商品图片
    private String image;

    //商品状态,1-正常,2-下架,3-删除
    private String status;

    //创建时间
    private Date createTime;

    //更新时间
    private Date updateTime;

    //是否默认
    private String isDefault;

    //SPUID
    private Long spuId;

    //类目ID
    private Long categoryId;

    //类目名称
    /**
     * FieldType.Keyword:不分词
     */
    @Field(type = FieldType.Keyword)
    private String categoryName;

    //品牌名称
    @Field(type = FieldType.Keyword)
    private String brandName;

    //规格
    private String spec;

    //规格参数
    private Map<String,Object> specMap; // 数据不同地方,可以用map存数据

}
数据导入

查询数据库数据或是独立出来search微服务,则可以利用fegin调用其他微服务的方法查询出来数据,转化为索引映射javaBean,再把数据导入ES,数据太多的话,建议分页查询数据库数据

dao层

import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.stereotype.Repository;

// 给索引映射javabean:skuinfo、该javabean的主键类型:long。自定义
@Repository
public interface SkuEsMapper extends ElasticsearchRepository<SkuInfo, Long> {

}

service层

public interface SkuService {

    /***
     * 导入SKU数据到ES
     */
    void importSku();
    
}
@Service
public class SkuServiceImpl implements SkuService {

    // 这里例子是利用fegin,也可以该微服务对数据库查询,根据业务
    @Resource
    private SkuFeign skuFeign;

    @Resource
    private SkuEsMapper skuEsMapper;

    /**
     * 导入sku数据到es
     */
    @Override
    public void importSku(){
        
        //根据业务拿到数据库数据
        
        //feign调用goods微服务
        ResponseResult<List<TbSku>> skuListResult = skuFeign.findByStatus("1");
        //将json数据转成对应的索引映射类,fastjson
        //字段名字匹配得上就可以转
        List<SkuInfo> skuInfos=  JSON.parseArray(JSON.toJSONString(skuListResult.getData()), SkuInfo.class);
        for(SkuInfo skuInfo:skuInfos){
            Map<String, Object> specMap= JSON.parseObject(skuInfo.getSpec()) ;
            skuInfo.setSpecMap(specMap);
        }
        // es通用mapper方法批量存入到es索引库中
        skuEsMapper.saveAll(skuInfos);
    }
}

controller层:

@RestController
@RequestMapping(value = "/search")
@CrossOrigin
public class SkuController {

    @Resource
    private SkuService skuService;

    /**
     * 导入数据
     * @return
     */
    @GetMapping("/import")
    public ResponseResult<Void> importData(){
        skuService.importSku();
        return new ResponseResult<Void>(true, StatusCode.OK,"导入数据到索引库中成功!");
    }
}

启动类添加:

@EnableElasticsearchRepositories(basePackages = "search.dao")// dao路径

kibana查看数据或者elasticsearch-head

安装如下:

docker pull mobz/elasticsearch-head:5

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

http://192.168.169.140:9100/

在这里插入图片描述

数据搜索

对应DSL方法查询

must多条件

match标准单个关键词

term过滤

prefix前缀

等等

service层实现:

@Service
public class SkuServiceImpl implements SkuService {

    // ElasticsearchTemplate可以实现对索引库的增删改查
    @Resource
    private ElasticsearchTemplate esTemplate;

    @Override
    public Map<String, Object> search(Map<String, String> searchMap) {

        //创建查询对象 构建对象
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();

        //获取关键字的值
        //这里可以不止关键字,多条件也行,判断加条件进去
        String keywords = null;
        if (searchMap != null && searchMap.size() > 0){
            // 不为空
            if (!StringUtils.isEmpty(searchMap.get("keywords"))){
                keywords = searchMap.get("keywords");
                //条件一:设置关键词查询条件
                //match、term等对应dsl语句
                //域名,值
                nativeSearchQueryBuilder.withQuery(QueryBuilders.matchQuery("name", keywords));
            }
        }

        //条件二:设置分组条件addAggregation-->group by  商品分类
        //categoryName是域名,代码自定义skuCategorygroup组名
        nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("skuCategorygroup").field("categoryName").size(50));

        //构建查询对象
        NativeSearchQuery query = nativeSearchQueryBuilder.build();

        //执行查询
        //带上索引映射类
        AggregatedPage<SkuInfo> skuPage = esTemplate.queryForPage(query, SkuInfo.class);

        //获取分组结果,对应分组名
        StringTerms stringTermsCategory = (StringTerms) skuPage.getAggregation("skuCategorygroup");
        List<String> categoryList = getStringsCategoryList(stringTermsCategory);
        
        //获取每页显示个数和当前页码,一起返回
        Pageable pageable = query.getPageable();
        int pageSize = pageable.getPageSize();
        int pageNumber = pageable.getPageNumber()+1; //从0开始

        //返回结果
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("categoryList", categoryList); // 分组结果,几种类型
        resultMap.put("rows", skuPage.getContent()); // 数据
        resultMap.put("total", skuPage.getTotalElements()); // 总记录数
        resultMap.put("totalPages", skuPage.getTotalPages()); // 总页数
        resultMap.put("pageSize", pageSize); // 每页显示个数
        resultMap.put("pageNumber", pageNumber); // 当前页码

        return resultMap;
    }

    /**
     * 获取分类列表数据
     * @param stringTerms
     * @return
     */
    private List<String> getStringsCategoryList(StringTerms stringTerms) {
        List<String> categoryList = new ArrayList<>();
        if (stringTerms != null) {
            for (StringTerms.Bucket bucket : stringTerms.getBuckets()) {
                String keyAsString = bucket.getKeyAsString();//分组的值
                categoryList.add(keyAsString);
            }
        }
        return categoryList;
    }
}

controller层:

    /**
     * 搜索
     * @param searchMap
     * @return
     */
    @PostMapping
    public Map<String, Object> search(@RequestBody(required = false) Map<String, String> searchMap){
        return skuService.search(searchMap);
    }

在这里插入图片描述

在这里插入图片描述

上图是规格的索引存储格式,真实数据在spechMap.规格名字.keyword中,所以找数据也是按照如下格式去找:

spechMap.key值.keyword

域是map类型的数据,这样可以拿到key对应的value值

拿到map数据要:spechMap.keyword

搜索条件

重点

搜索模板
@Service
public class ServiceImpl implements Service {

    // ElasticsearchTemplate可以实现对索引库的增删改查
    @Resource
    private ElasticsearchTemplate esTemplate;

    // 搜索条件和返回数据类型根据业务定义
    @Override
    public Map<String, Object> search(Map<String, String> searchMap) {

        //创建查询对象 构建对象
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();

        //判断前端条件是否为空
        //多条件多个,组合条件得用bool
        //带上条件,看业务
        nativeSearchQueryBuilder.withQuery(QueryBuilders.matchQuery(域名字符串, 前端searchMap中的关键词));

        //设置分组查询addAggregation-->SQL对应group by
        //这里的分组是拿查询出来的数据统计该域的种类并返回
        //可多个,可抽取分组查询方法,多个都是多个addAggregation,换域名组名
        //域名字符串看kibana,可以是值中带值,比如值是Map类型:spec.mapkey
        nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms(组名字符串).field(域名字符串).size(100)); // 默认10条数据,size添加分组的数据数
        nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms(组名字符串).field(域名字符串).size(100)); // 可多个,多个的话最好抽取方法出来

        //构建查询对象
        NativeSearchQuery query = nativeSearchQueryBuilder.build();

        //执行查询
        //带上索引映射类
        AggregatedPage<索引映射类> page = esTemplate.queryForPage(query, 索引映射类.class);

        //获取分组结果,对应分组名
        StringTerms stringTermsCategory = (StringTerms) page.getAggregation(组名字符串);
        List<String> list = getStringsList(stringTermsCategory);

        //返回结果
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("List", list); // 分组结果,几种类型
        resultMap.put("rows", page.getContent()); // 数据
        resultMap.put("total", page.getTotalElements()); // 总记录数
        resultMap.put("totalPages", page.getTotalPages()); // 总页数

        return resultMap;
    }
    
    /**
     * 获取分组后的列表数据
     * 一般String类型
     * @param stringTerms
     * @return
     */
    private List<String> getStringsList(StringTerms stringTerms) {
        List<String> list = new ArrayList<>();
        if (stringTerms != null) {
            for (StringTerms.Bucket bucket : stringTerms.getBuckets()) {
                String keyAsString = bucket.getKeyAsString();//分组的值
                list.add(keyAsString);
            }
        }
        return list;
    }
}

对应dsl语句,field指域名

标准match搜索

可分词搜索,前提是索引映射类上对应的变量域名有searchAnalyzer = “ik_smart”,支持搜索使用分词器,否则只是普通的模糊搜索like。

nativeSearchQueryBuilder.withQuery(QueryBuilders.matchQuery(域名字符串, 关键词));
过滤term搜索

不分词精确匹配

nativeSearchQueryBuilder.withQuery(QueryBuilders.termQuery(域名字符串, 关键词));
组合bool搜索

bool组合过滤可以用来合并多个过滤条件查询结果的布尔逻辑,它包含一下操作符:

  • must : 多个查询条件的完全匹配,相当于 and。
  • must_not : 多个查询条件的相反匹配,相当于 not。
  • should : 至少有一个查询条件匹配, 相当于 or。
    BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

    // 中文分词,如果多个中文分词关键词得用must
    if (!StringUtils.isEmpty(主关键词)) {
        boolQueryBuilder.must(QueryBuilders.matchQuery("域名", 主关键词));
    }

    // 多关键词可用map,或者dto
    if (!StringUtils.isEmpty(关键词1)) {
        boolQueryBuilder.must(QueryBuilders.termQuery("域名1", 关键词1));
    }

    if (!StringUtils.isEmpty(关键词2)) {
        boolQueryBuilder.must(QueryBuilders.termQuery("域名2", 关键词2));
    }

    // ...rangeQuery

    // 构建过滤查询
    // withQuery只能一个
    nativeSearchQueryBuilder.withQuery(boolQueryBuilder);

另一个方式:

PS说明: 以上,我们建议使用filter ,它的搜索效率要优于must.可以参考官方文档说明:

https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-bool-query.html

如果多个中文分词关键词的话得用must(matchQuery),一个一下就用filter。

    //设置主关键字查询,中文分词
    nativeSearchQueryBuilder.withQuery(QueryBuilders.matchQuery("域名", 主关键词));

    BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

    if (!StringUtils.isEmpty(关键词1)) {
        boolQueryBuilder.filter(QueryBuilders.termQuery("域名1", 关键词1));
    }

    if (!StringUtils.isEmpty(关键词2)) {
        boolQueryBuilder.filter(QueryBuilders.termQuery("域名2", 关键词2));
    }

    //...rangeQuery

    //构建过滤查询
    nativeSearchQueryBuilder.withFilter(boolQueryBuilder);
区间range搜索

最好结合bool组合搜索,一般是数字大小,范围

  • gt:>
  • gte:>=
  • lt:<
  • lte:<=
   // 区间1    
   boolQueryBuilder.filter(QueryBuilders.rangeQuery("数字域名").gte(前端int类型参数小);
   boolQueryBuilder.filter(QueryBuilders.rangeQuery("数字域名").lte(前端int类型参数大);
   
   // 区间2
   boolQueryBuilder.filter(QueryBuilders.rangeQuery("数字域名").from(前端int类型参数小, true).to(前端int类型参数大, true));
                           
   // 单个以上,大于或小于选一个
   boolQueryBuilder.filter(QueryBuilders.rangeQuery("数字域名").lte(前端int类型参数);      
分组搜索

group by,可额外添加到搜索,拿到查询数据后该域名的种类。

nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms(组名字符串1).field("域名1").size(100)); 
   nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms(组名字符串2).field("域名2").size(100)); 

   //...

List<String> list1 = getStringsList(page, 组名字符串1);
List<String> list2 = getStringsList(page, 组名字符串2);
 
    /**
     * 获取分组后的列表数据
     * 一般是String类型
     * page是查询后的返回数据
     */
    private List<String> getStringsList(AggregatedPage<索引映射类> page, String 组名字符串) {
        StringTerms stringTermsCategory = (StringTerms) page.getAggregation(组名字符串);
        List<String> list = new ArrayList<>();
        if (stringTerms != null) {
            for (StringTerms.Bucket bucket : stringTerms.getBuckets()) {
                String keyAsString = bucket.getKeyAsString(); //分组的值
                list.add(keyAsString);
            }
        }
        return list;
    }

这样会多次请求es,效率不高,改进如下,分组的合并一起请求

//多个域一起分组,以后多域用这个方法
//修改了getAggregations().get(组名字符串)

/**
     * 获取分组后的列表数据
     * 一般是String类型
     * page是查询后的返回数据
     */
    private List<String> getStringsList(AggregatedPage<索引映射类> page, String 组名字符串) {
        StringTerms stringTermsCategory = (StringTerms) page.getAggregations().get(组名字符串);
        List<String> list = new ArrayList<>();
        if (stringTerms != null) {
            for (StringTerms.Bucket bucket : stringTerms.getBuckets()) {
                String keyAsString = bucket.getKeyAsString(); //分组的值
                list.add(keyAsString);
            }
        }
        return list;
    }
分页查询
        //写在所有条件最后
        //构建分页查询
        int pageNum = 1; // 默认第一页
        if (!StringUtils.isEmpty(searchMap.get("pageNum"))) {
            try {
                // 前端传过来的当前页
                pageNum = Integer.parseInt(searchMap.get("pageNum"));
            } catch (NumberFormatException e) {
                e.printStackTrace();
                pageNum=1;
            }
        }
        int pageSize = 8; // 一页多少条数据,一般后台固定
        nativeSearchQueryBuilder.withPageable(PageRequest.of(pageNum - 1, pageSize));

        //构建查询对象
        NativeSearchQuery query = nativeSearchQueryBuilder.build();
排序查询
//构建排序查询
String sortRule = searchMap.get("sortRule"); //排序的规则,升序还是降序DESC、ASC
String sortField = searchMap.get("sortField"); //前端传过来的指定排序域名
if (!StringUtils.isEmpty(sortRule) && !StringUtils.isEmpty(sortField)) {
    nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort(sortField).order(sortRule.equals("DESC") ? SortOrder.DESC : SortOrder.ASC));
}

//也可以这样
nativeSearchQueryBuilder.withSort(new FieldSortBuilder(sortField).order(SortOrder.valueOf(sortRule)));

//构建查询对象
NativeSearchQuery query = nativeSearchQueryBuilder.build();
高亮设置

某些数据需要高亮显示,比如含有关键词的语句中关键词变色

SkuInfo是对应的索引映射类

数据操作实现类

import com.alibaba.fastjson.JSON;
import com.changgou.model.es.SkuInfo;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.SearchResultMapper;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * Title:获取es搜索后的数据
 * Description:数据进行操作返回给前端
 *             SkuInfo是对应的索引映射类
 * @author WZQ
 * @version 1.0.0
 * @date 2020/3/6
 */
public class SearchResultMapperImpl implements SearchResultMapper {

    @Override
    public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {

        //存放修改后的数据
        List<T> content = new ArrayList<>();

        //如果没有结果返回为空
        if (response.getHits() == null || response.getHits().getTotalHits() <= 0) {
            return new AggregatedPageImpl<T>(content);
        }
        // 遍历数据
        for (SearchHit searchHit : response.getHits()) {
            String sourceAsString = searchHit.getSourceAsString();
            // 默认拿到的是非高亮数据,指定索引映射类
            SkuInfo skuInfo = JSON.parseObject(sourceAsString, SkuInfo.class);

            Map<String, HighlightField> highlightFields = searchHit.getHighlightFields();
            //跟条件设置的域名一致
            HighlightField highlightField = highlightFields.get("域名");

            //有高亮则读取高亮的值
            if (highlightField != null && highlightField.getFragments() != null) {
                StringBuilder stringBuilder = new StringBuilder();
                for (Text text : highlightField.getFragments()) {
                    stringBuilder.append(text.toString());
                }
                // 数据中指定域替换成高亮数据,默认非高亮的
                skuInfo.setName(stringBuilder.toString());
            }
            content.add((T) skuInfo);
        }
        /**
         * 1.携带高亮数据的内容
         * 2.分页对象信息
         * 3.搜索数据的总数
         */
        return new AggregatedPageImpl<T>(content, pageable, response.getHits().getTotalHits(), response.getAggregations(), response.getScrollId());
    }
}

serive中修改:

//高亮配置
HighlightBuilder.Field field = new HighlightBuilder.Field("域名");
//样式前缀
field.preTags("<em style=\"color:red;\">");
//后缀
field.postTags("</em>");
//碎片长度 关键词数据的长度,不超过设置的长度给到前端
field.fragmentSize(100);
//添加到条件
nativeSearchQueryBuilder.withHighlightFields(field); // 参数是可变数组,可多个传进去

//构查询对象
NativeSearchQuery query = nativeSearchQueryBuilder.build();

//记得修改成这个方法,带上实现类
AggregatedPage<SkuInfo> skuPage = esTemplate.queryForPage(query, SkuInfo.class, new SearchResultMapperImpl());

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CSND近期推出的猿如意到底怎么样?

CSND近期推出的猿如意到底怎么样&#xff1f; 投稿测评正文 猿如意传送门 猿如意下载地址&#xff1a;猿如意-程序员的如意兵器,工具代码,一搜就有 猿如意使用了几次了&#xff0c;今天来想分享一下我对于猿如意的使用感受吧&#xff01;&#xff01; 先说结论&#xff1a…

吴恩达《机器学习》——Logistics回归代码实现

Logisitc回归1. Sigmoid与二分类Sigmoid函数为什么Sigmoid函数可以表示二分类概率&#xff1f;2. Logistics回归交叉熵损失函数梯度过拟合与欠拟合正则化3. Python代码实现4. 单维与多维Logistic分类单维数据分类多维数据分类数据集、源文件可以在Github项目中获得 链接: https…

04 kafka 中一些常用的配置的使用

前言 呵呵 也是最近有一些 搭建 kafka 的环境的需求 然后 从新看了一下 一部分的配置情况, 这里 大致理一下 一些我这里比较关心的配置 那些配置关联了 kafka 服务器绑定服务 绑定 tcp 服务的配置来自于这里, 读取的是 config.dataPlaneListeners config.dataPlaneListen…

Java母婴商城母婴店孕妇商城婴幼儿商城网站系统源码

简介 java使用ssm开发的母婴商城系统&#xff0c;用户可以注册浏览商品&#xff0c;加入购物车或者直接下单购买&#xff0c;在个人中心管理收货地址和订单&#xff0c;管理员也就是商家登录后台可以发布商品&#xff0c;上下架商品&#xff0c;处理待发货订单等。 演示视频&…

Allegro如何在PCB上查看pin number的三种方法操作指导

Allegro如何在PCB上查看pin number的三种方法操作指导 Allegro支持快捷的在PCB上查看pin number,如下图 具体操作如下 方法一:show element 选择Show Element命令Find选择Pins

2022年学习机器人和人工智能的一些期待

2022年学习机器人和人工智能的一些体会 2023年即将到来&#xff0c;满满的期待。 做好规划是非常非常重要的&#xff0c;有时候甚至比认真做事本身更为重要。 《礼记中庸》&#xff1a;“凡事豫则立&#xff0c;不豫则废。言前定则不跲&#xff0c;事前定则不困&#xff0c;行…

视频解码学习备忘

媒体文件知识 日常都是播放器直接播&#xff0c;其实这里面还有不少内容的。 首先是视频容器&#xff0c;就是所谓的.mp4 .mkv 这类文件,其目的主要就是用来存放音频视频字幕等内容&#xff0c;所以叫做容器。这些都有一定规范&#xff0c;比如mp4&#xff0c;叫ISO 14496-12…

流程图+BPMN+脑图 JointJS++ 3.6.3 Crack

一个库&#xff0c;‍ 无限的UI选择&#xff0c;直接在您的应用程序中享受交互式流程图、BPMN 和其他图表工作室。利用我们的模板化应用程序&#xff0c;将开发时间缩短至几天。 经过十多年和数百万行代码的编写&#xff0c;我们推出了一组随时可用的功能&#xff0c;以帮助您创…

数据结构进阶 二叉树OJ题二

作者&#xff1a;小萌新 专栏&#xff1a;数据结构进阶 作者简介&#xff1a;大二学生 希望能和大家一起进步&#xff01; 本篇博客简介&#xff1a;介绍几道二叉树的oj题 题目一 二叉搜索树与双向链表 输入一棵二叉搜索树&#xff0c;将该二叉搜索树转换成一个排序的双向链表…

帆软—图表专题

饼图 优点&#xff1a;能很直观的看到每一个部分在整体中所占的比例。 缺点&#xff1a;不适合较大的数据集&#xff08;分类&#xff09;展现&#xff0c;数据项中不能有负值&#xff0c;当比例接近时&#xff0c;人眼很难准确判别。 变形饼图 柱形图 特点 优点&#xff…

SpringBoot HandlerMethodArgumentResolver接口自定义参数解析器

参考资料 【SpringBoot】HandlerMethodArgumentResolver的简单使用HandlerMethodArgumentResolver(参数解析器)的作用使用小案例HandlerMethodArgumentResolver用于统一获取当前登录用户 目录一. 需求二. 前期准备三. 实现HandlerMethodArgumentResolver接口四. 将自定义的方法…

一套javassf门户网站管理系统源码分享

淘源码&#xff1a;国内专业的免费源码下载平台 JAVA SSF项目框架源码 后台管理系统源码 一套以SpringMVCSpringHibernateFreemarkerShiroLuceneHtml5jQuery为技术核心架构的门户网站管理系统源码。 源码免费分享&#xff0c;需要源码学习可私信我。 主要功能&#xff1a; 内…

笑对过往、活在当下、期盼未来

哈喽大家好&#xff0c;我是阿Q。 去年今日&#xff0c;也是安静的午后&#xff0c;拿起铅笔在纸上寥寥草草的写下几个年终关键词&#xff0c;思索良久&#xff0c;迟迟未能下笔。 时至今日&#xff0c;年末将至&#xff0c;再次执笔已时隔一年&#xff0c;再不拿笔记录这转瞬…

Python制作粒子烟花,程序员的跨年姿势

跨年倒计时还有半天&#xff1f;我已经开始整烟花了&#xff0c;虽然不是很好看吧&#xff0c;但是也能将就看看 &#x1f625; 这个的背景图&#xff0c;音乐&#xff0c;还有文字都是可以自己修改的哦 效果展示 依次导入本次需要使用到的模块 import random import pygame…

免费的JPEG 恢复软件 - 照片删除了怎么恢复?

照片删除了怎么恢复? 照片在我们的生活中起着至关重要的作用&#xff0c;因为它们可以承载我们的快乐回忆&#xff0c;记录我们的日常生活&#xff0c;分享我们的快乐等。我们将在智能手机、相机或计算机上拍摄并存储大量 JPEG 图片。但是&#xff0c;无论我们多么小心地保留它…

Python和MySQL对比(2):用Pandas 实现MySQL的 union 和 join 语法效果

文章目录一、前言二、语法对比数据表unionjoin1、两表 inner join2、两表 left join3、两表 right join4、两表 outer join5、多表 join三、小结一、前言 环境&#xff1a; windows11 64位 Python3.9 MySQL8 pandas1.4.2 本文主要介绍 MySQL 中的union和join如何使用pandas实现…

Spring 核心概念 IOC/DI IOC容器 Bean

目录 一&#xff1a;代码书写现状 二&#xff1a;核心思想 一&#xff1a;代码书写现状 常规操作如上,但存在着问题,将项目上线后&#xff0c;需要将数据层进行更换,更换如下&#xff1a; 数据层更换后&#xff0c;业务层也需要进行更换&#xff0c;更换如下&#xff1a; 数据…

NLP学习笔记(五) 注意力机制

大家好&#xff0c;我是半虹&#xff0c;这篇文章来讲注意力机制 (Attention Mechanism) 在序列到序列模型中的应用 在上一篇文章中&#xff0c;我们介绍了序列到序列模型&#xff0c;其工作流程可以概括为以下两个步骤 首先&#xff0c;用编码器将输入序列编码成上下文向量&a…

费控产品之易快报洞察解析

1.费控行业洞察 1.1 概念及管理范畴界定 费控业务的本质是企业支出的管理&#xff0c;因此分析费控要从企业支出管理的宏观范畴来看&#xff0c;企业支出主要包括两方面&#xff0c;一个是企业成本&#xff0c;一个是期间费用。期间费用就是我们常说的管理费用、销售费用和财务…

Centos7的安装与模板机的制作

Centos7的安装与模板机的制作 1.安装环境准备 1.VMware Workstation 2.centos7镜像 本文用的VMware版本为16.2.1 build-18811642 2.开始安装 话不多说&#xff0c;直接上图 3.模板机制作 1.查看防火墙的运行状态 firewall-cmd --state防火墙目前正在启动中 2.关闭防火墙…