SpringBoot 3.2.5 + ElasticSearch 8.12.0 - SpringData 开发指南

news2024/12/27 11:11:32

 

目录

一、SpringData ElasticSearch

1.1、环境配置

1.2、创建实体类

1.3、ElasticSearchTemplate 的使用

1.3.1、创建索引库,设置映射

1.3.2、创建索引映射注意事项

1.3.3、简单的 CRUD

1.3.4、三种构建搜索条件的方式

1.3.5、NativeQuery 搜索实战

1.3.6、completionSuggestion 自动补全


一、SpringData ElasticSearch


1.1、环境配置

a)依赖如下:

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

        <!--SpringBoot3 之后测试必须引入这个包-->
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-core</artifactId>
            <version>2.23.4</version>
            <scope>test</scope>
        </dependency>

b)配置文件如下:

spring:
  application:
    name: es
  elasticsearch:
    uris: env-base:9200

1.2、创建实体类

a)简单结构如下(后续示例,围绕此结构展开):

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

@Document(indexName = "album_info", )
data class AlbumInfoDo (
    /**
     * @Id: 表示文档中的主键,并且会在保存在 ElasticSearch 数据结构中 {"id": "", "userId": "", "title": ""}
     */
    @Id
    @Field(type = FieldType.Keyword)
    val id: Long? = null,
    /**
     * @Field: 描述 Java 类型中的属性映射
     *      - name: 对应 ES 索引中的字段名. 默认和属性同名
     *      - type: 对应字段类型,默认是 FieldType.Auto (会根据我们数据类型自动进行定义),但是建议主动定义,避免导致错误映射
     *      - index: 是否创建索引. text 类型创建倒排索引,其他类型创建正排索引.  默认是 true
     *      - analyzer: 分词器名称.  中文我们一般都使用 ik 分词器(ik分词器有 ik_smart 和 ik_max_word)
     */
    @Field(name = "user_id", type = FieldType.Long)
    val userId: Long,
    @Field(type = FieldType.Text, analyzer = "ik_max_word")
    var title: String,
    @Field(type = FieldType.Text, analyzer = "ik_smart")
    var content: String,
)

b)复杂嵌套结构如下:

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

@Document(indexName = "album_list")
data class AlbumListDo(
    @Id
    @Field(type = FieldType.Keyword)
    var id: Long,
    @Field(type = FieldType.Nested) // 表示一个嵌套结构
    var userinfo: UserInfoSimp,
    @Field(type = FieldType.Text, analyzer = "ik_max_word")
    var title: String,
    @Field(type = FieldType.Text, analyzer = "ik_smart")
    var content: String,
    @Field(type = FieldType.Nested) // 表示一个嵌套结构
    var photos: List<AlbumPhotoSimp>,
)

data class UserInfoSimp(
    @Field(type = FieldType.Long)
    val userId: Long,
    @Field(type = FieldType.Text, analyzer = "ik_max_word")
    val username: String,
    @Field(type = FieldType.Keyword, index = false)
    val avatar: String,
)

data class AlbumPhotoSimp(
    @Field(type = FieldType.Integer, index = false)
    val sort: Int,
    @Field(type = FieldType.Keyword, index = false)
    val photo: String,
)

对于一个小型系统来说,一般也不会创建这种复杂程度的文档,因为会涉及到很多一致性问题, 需要通过大量的 mq 进行同步,给系统带来一定的开销. 

因此,一般会将需要进行模糊查询的字段存 Document 中(es 就擅长这个),而其他数据则可以在 Document 中以 id 的形式进行存储.   这样就既可以借助 es 高效的模糊查询能力,也能减少为保证一致性而带来的系统开销.  从 es 中查到数据后,再通过其他表的 id 从数据库中拿数据即可(这点开销,相对于从大量数据的数据库中进行 like 查询,几乎可以忽略).

1.3、ElasticSearchTemplate 的使用

1.3.1、创建索引库,设置映射

@SpringBootTest
class ElasticSearchIndexTests {

    @Resource
    private lateinit var elasticsearchTemplate: ElasticsearchTemplate

    @Test
    fun test1() {
        //存在索引库就删除
        if (elasticsearchTemplate.indexOps(AlbumInfoDo::class.java).exists()) {
            elasticsearchTemplate.indexOps(AlbumInfoDo::class.java).delete()
        }
        //创建索引库
        elasticsearchTemplate.indexOps(AlbumInfoDo::class.java).create()
        //设置映射
        elasticsearchTemplate.indexOps(AlbumInfoDo::class.java).putMapping(
            elasticsearchTemplate.indexOps(AlbumInfoDo::class.java).createMapping()
        )
    }

}

效果如下: 

1.3.2、创建索引映射注意事项

a)在没有创建索引库和映射的情况下,也可以直接向 es 库中插入数据,如下代码:

    @Test
    fun test2() {
        val obj = AlbumListDo(
            id = 1,
            userinfo = UserInfoSimp(
                userId = 1,
                username = "cyk",
                avatar = "env-base:9200"
            ),
            title = "今天天气真好",
            content = "早上起来,我要好好学习,然去公园散步~",
            photos = listOf(
                AlbumPhotoSimp(1, "www.photo.com/aaa"),
                AlbumPhotoSimp(2, "www.photo.com/bbb")
            )
        )
        val result = elasticsearchTemplate.save(obj)
        println(result)
    }

即使上述代码中 AlbumListDo 中有各种注解标记,但是不会生效!!! es 会根据插入的数据,自动转化数据结构(无视你的注解).

因此,建议先创建索引库和映射,再进行数据插入!

1.3.3、简单的 CRUD

import jakarta.annotation.Resource
import org.cyk.es.model.AlbumInfoDo
import org.junit.jupiter.api.Test
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.data.elasticsearch.client.elc.ElasticsearchTemplate
import org.springframework.data.elasticsearch.core.document.Document
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates
import org.springframework.data.elasticsearch.core.query.UpdateQuery

@SpringBootTest
class ElasticSearchCRUDTests {

    @Resource
    private lateinit var elasticsearchTemplate: ElasticsearchTemplate

    @Test
    fun testSave() {
        //保存单条数据
        val a1 = AlbumInfoDo(
            id = 1,
            userId = 10000,
            title = "今天天气真好",
            content = "学习完之后,我要出去好好玩"
        )
        val result = elasticsearchTemplate.save(a1)
        println(result)

        //保存多条数据
        val list = listOf(
            AlbumInfoDo(2, 10000, "西安六号线避雷", "前俯后仰。他就一直在那前后动。他背后是我朋友,我让他不要挤了,他直接就急了,开始故意很大力的挤来挤去。"),
            AlbumInfoDo(3, 10000, "字节跳动快上车~", "#内推 #字节跳动内推 #互联网"),
            AlbumInfoDo(4, 10000, "连王思聪也变得低调老实了", "如今的王思聪,不仅交女友的质量下降,在网上也不再像以前那样随意喷这喷那。显然,资金的紧张让他低调了许多")
        )
        val resultList = elasticsearchTemplate.save(list)
        resultList.forEach(::println)
    }

    @Test
    fun testDelete() {
        //根据主键删除,例如删除主键 id = 1 的文档
        elasticsearchTemplate.delete("1", AlbumInfoDo::class.java)
    }

    @Test
    fun testGet() {
        //根据主键获取文档
        val result = elasticsearchTemplate.get("1", AlbumInfoDo::class.java)
        println(result)
    }

    @Test
    fun testUpdate() {
        //例如,修改 id = 1 的文档
        val id = 1
        val title = "今天天气不太好"
        val content = "天气不好,只能在家里学习了。。。"

        val uq = UpdateQuery.builder(id.toString())
            .withDocument(
                Document.create()
                    .append("title", title)
                    .append("content", content)
            ).build()

        val result = elasticsearchTemplate.update(uq, IndexCoordinates.of("album_info")).result
        println(result.ordinal)
        println(result.name)
    }

}

1.3.4、三种构建搜索条件的方式

关于搜索条件的构建,Spring 官网上给出了三种构建方式:Elasticsearch Operations :: Spring Data Elasticsearch

a)CriteriaQuery:允许创建查询来搜索数据,而不需要了解 Elasticsearch 查询的语法或基础知识。它们允许用户通过简单地链接和组合 Criteria 对象来构建查询,Criteria 对象指定被搜索文档必须满足的条件。

Criteria criteria = new Criteria("lastname").is("Miller") 
  .and("firstname").is("James")                           
Query query = new CriteriaQuery(criteria);

b)StringQuery:这个类接受 Elasticsearch 查询作为 JSON String。下面的代码显示了一个搜索名为“ Jack”的人的查询:

Query query = new StringQuery("{ \"match\": { \"firstname\": { \"query\": \"Jack\" } } } ");
SearchHits<Person> searchHits = operations.search(query, Person.class);

c)NativeQuery:当您有一个复杂的查询或者一个无法使用 Criteria API 表示的查询时,例如在构建查询和使用聚合时,可以使用 NativeQuery 类。

d)到底使用哪一种呢?在最新的这一版 SpringDataES 中,NativeQuery 中可以通过大量的 Lambda 来构建条件语句,并且外观上也很符合 ElasticSearch DSL,那么对于比较熟悉原生的 DSL 语句的就建议使用 NativeQuery 啦.  我本人也更倾向 NativeQuery,因此后续的案例都会使用它.

1.3.5、NativeQuery 搜索实战

import co.elastic.clients.elasticsearch._types.SortOrder
import co.elastic.clients.json.JsonData
import jakarta.annotation.Resource
import org.cyk.es.model.AlbumInfoDo
import org.junit.jupiter.api.Test
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.data.domain.PageRequest
import org.springframework.data.elasticsearch.client.elc.ElasticsearchTemplate
import org.springframework.data.elasticsearch.client.elc.NativeQuery
import org.springframework.data.elasticsearch.core.query.HighlightQuery
import org.springframework.data.elasticsearch.core.query.highlight.Highlight
import org.springframework.data.elasticsearch.core.query.highlight.HighlightField
import org.springframework.data.elasticsearch.core.query.highlight.HighlightParameters

@SpringBootTest
class SearchTests {

    @Resource
    private lateinit var elasticsearchTemplate: ElasticsearchTemplate

    /**
     * 全文检索查询(match_all)
     */
    @Test
    fun testMatchAllQuery() {
        val query = NativeQuery.builder()
            .withQuery { q -> q
                .matchAll { it }
            }.build()

        val hits = elasticsearchTemplate.search(query, AlbumInfoDo::class.java)
        hits.forEach { println(it.content) }
    }

    /**
     * 精确查询(match)
     */
    @Test
    fun testMatchQuery() {
        val query = NativeQuery.builder()
            .withQuery { q -> q
                .match {
                    it.field("title").query("天气")
                }
            }.build()

        val hits = elasticsearchTemplate.search(query, AlbumInfoDo::class.java)
        hits.forEach { println(it.content) }
    }

    /**
     * 精确查询(term)
     */
    @Test
    fun testTerm() {
        val query = NativeQuery.builder()
            .withQuery { q -> q
                .term { t -> t
                    .field("id").value("2")
                }
            }.build()

        val hits = elasticsearchTemplate.search(query, AlbumInfoDo::class.java)
        hits.forEach { println(it.content) }
    }

    /**
     * 范围搜索
     */
    @Test
    fun testRangeQuery() {
        val query = NativeQuery.builder()
            .withQuery { q -> q
                .range { r -> r
                    .field("id").gte(JsonData.of(1)).lt(JsonData.of(4)) // 大于等于 1,小于 4
                }
            }.build()
        val hits = elasticsearchTemplate.search(query, AlbumInfoDo::class.java)
        hits.forEach { println(it.content) }
    }

    /**
     * bool 复合搜索
     */
    @Test
    fun testBoolQuery() {
        val query = NativeQuery.builder()
            .withQuery { q -> q
                .bool { b -> b
                    .must { m -> m
                        .range { r -> r
                            .field("id").gte(JsonData.of(1)).lt(JsonData.of(4)) // 大于等于 1,小于 4
                        }
                    }
                    .mustNot { n -> n
                        .match { mc -> mc
                            mc.field("title").query("天气")
                        }
                    }
                    .should { s -> s
                        .matchAll { it }
                    }
                }
            }.build()
        val hits = elasticsearchTemplate.search(query, AlbumInfoDo::class.java)
        hits.forEach { println(it.content) }
    }

    /**
     * 排序 + 分页
     */
    @Test
    fun testSortAndPage() {
        //a) 方式一
//        val query = NativeQuery.builder()
//            .withQuery { q -> q
//                .matchAll { it }
//            }
//            .withPageable(
//                PageRequest.of(0, 3) //页码(从 0 开始),非偏移量
//                    .withSort(Sort.by(Sort.Order.desc("id")))
//            ).build()

        //b) 方式二
        val query = NativeQuery.builder()
            .withQuery { q -> q
                .matchAll { it }
            }
            .withSort { s -> s.field { f->f.field("id").order(SortOrder.Desc) } }
            .withPageable(PageRequest.of(0, 3)) //页码(从 0 开始),非偏移量)
            .build()
        val hits = elasticsearchTemplate.search(query, AlbumInfoDo::class.java)
        hits.forEach { println(it.content) }
    }

    @Test
    fun testHighLight() {
        //所有需要高亮的字段
        val highField = listOf(
            HighlightField("title"),
            HighlightField("content")
        )
        val query = NativeQuery.builder()
            .withQuery { q ->
                q.multiMatch { ma -> ma
                    .fields("title", "content").query("天气")
                }
            }
            .withHighlightQuery(
                HighlightQuery(
                    Highlight(
                        HighlightParameters.builder()
                            .withPreTags("<span style='color:red'>") //前缀标签
                            .withPostTags("</span>") //后缀标签
                            .withFragmentSize(10) //高亮的片段长度(多少个几个字需要高亮,一般会设置的大一些,让匹配到的字段尽量都高亮)
                            .withNumberOfFragments(1) //高亮片段的数量
                            .build(),
                        highField
                    ),
                    String::class.java
                )
            ).build()

        val hits = elasticsearchTemplate.search(query, AlbumInfoDo::class.java)

        //hits.content 本身是没有高亮数据的,因此这里需要手动处理
        hits.forEach {
            val result = it.content
            //根据高亮字段名称,获取高亮数据集合
            val titleList = it.getHighlightField("title")
            val contentList = it.getHighlightField("content")
            if (titleList.size > 0) result.title = titleList[0]
            if (contentList.size > 0) result.content = contentList[0]
            println(result)
        }
    }

}

1.3.6、completionSuggestion 自动补全

a)自动补全的字段必须是 completion 类型. 
这里自动补全的字段为 title,通过 copyTo 将其拷贝到 suggestion 字段中,实现自动补全.
import org.springframework.data.annotation.Id
import org.springframework.data.elasticsearch.annotations.CompletionField
import org.springframework.data.elasticsearch.annotations.Document
import org.springframework.data.elasticsearch.annotations.Field
import org.springframework.data.elasticsearch.annotations.FieldType
import org.springframework.data.elasticsearch.core.suggest.Completion

@Document(indexName = "album_doc")
data class AlbumSugDo (
    @Id
    @Field(type = FieldType.Keyword)
    val id: Long,
    @Field(name = "user_id", type = FieldType.Long)
    val userId: Long,
    @Field(type = FieldType.Text, analyzer = "ik_max_word", copyTo = ["suggestion"]) //注意,copyTo 的字段一定是 var 类型
    val title: String,
    @Field(type = FieldType.Text, analyzer = "ik_smart")
    val content: String,
    @CompletionField(maxInputLength = 100, analyzer = "ik_max_word", searchAnalyzer = "ik_max_word")
    var suggestion: Completion? = null, //注意,被 copyTo 的字段一定要是 var 类型
)

Ps:被 copyTo 的字段一定要是 var 类型

b)需求:在搜索框中输入 “今天”,对其进行自动补全.

import co.elastic.clients.elasticsearch.core.search.FieldSuggester
import co.elastic.clients.elasticsearch.core.search.FieldSuggesterBuilders
import co.elastic.clients.elasticsearch.core.search.Suggester
import jakarta.annotation.Resource
import org.cyk.es.model.AlbumSugDo
import org.junit.jupiter.api.Test
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.data.elasticsearch.client.elc.ElasticsearchTemplate
import org.springframework.data.elasticsearch.client.elc.NativeQuery
import org.springframework.data.elasticsearch.core.suggest.response.Suggest

@SpringBootTest
class SuggestTests {

    @Resource
    private lateinit var elasticsearchTemplate: ElasticsearchTemplate

    @Test
    fun init() {
        if(elasticsearchTemplate.indexOps(AlbumSugDo::class.java).exists()) {
            elasticsearchTemplate.indexOps(AlbumSugDo::class.java).delete()
        }
        elasticsearchTemplate.indexOps(AlbumSugDo::class.java).create()
        elasticsearchTemplate.indexOps(AlbumSugDo::class.java).putMapping(
            elasticsearchTemplate.indexOps(AlbumSugDo::class.java).createMapping()
        )
        elasticsearchTemplate.save(
            listOf(
                AlbumSugDo(1, 10000, "今天发现西安真美", "西安真美丽啊,来到了钟楼...."),
                AlbumSugDo(2, 10000, "今天六号线避雷", "前俯后仰。他就一直在那前后动。他背后是我朋友,我让他不要挤了,他直接就急了,开始故意很大力的挤来挤去。"),
                AlbumSugDo(3, 10000, "字节跳动快上车~", "#内推 #字节跳动内推 #互联网"),
                AlbumSugDo(4, 10000, "连王思聪也变得低调老实了", "如今的王思聪,不仅交女友的质量下降,在网上也不再像以前那样随意喷这喷那。显然,资金的紧张让他低调了许多")
            )
        )
    }

    @Test
    fun suggestTest() {
        //模拟客户端输入的需要自动补全的字段
        val input = "今天"
        val limit = 10

        val fieldSuggester = FieldSuggester.Builder()
            .text(input) //用户输入
            .completion(
                FieldSuggesterBuilders.completion()
                    .field("suggestion") //对哪个字段自动补全
                    .skipDuplicates(true) //如果有重复的词条,自动跳过
                    .size(limit) //最多显示 limit 条数据
                    .build()
            )
            .build()

        val query = NativeQuery.builder()
            .withSuggester(Suggester.of { s -> s.suggesters("sug1", fieldSuggester) }) //参数一: 自定义自动补全名
            .build()

        val hits = elasticsearchTemplate.search(query, AlbumSugDo::class.java)

        val suggestList = hits.suggest
            ?.getSuggestion("sug1")
            ?.entries?.get(0)
            ?.options?.map(::map) ?: emptyList()

        println(suggestList)
    }

    private fun map(hit: Suggest.Suggestion.Entry.Option): String {
        return hit.text
    }

}

上述代码中的 hits 结构如下:

运行结果:

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

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

相关文章

使用socat做端口转发

最近买的云上mongo数据库但是数据库不支持外网访问&#xff0c;准备做iptables转发但是一直不成功&#xff0c;腾讯云官方给予的解释是受服务器内启动的docker影响 做iptables转发会冲突&#xff0c;所以只能另想办法&#xff0c;我发现使用socat做转发也很好用&#xff0c;所以…

01.基本概念

操作系统 为什么要有操作系统&#xff1f; 计算机时一个十分复杂的系统&#xff0c;又cpu、内存、磁盘、IO设备、网络接口等等复杂的硬件组成&#xff0c;人的精力是有限的&#xff0c;不可能了解所有的硬件接口&#xff0c;但是程序可以。 所以我们在计算机上安装了一层软件&…

网易云新玩法:教你赚取第一桶金!

在现今的音乐应用市场中&#xff0c;有几款软件备受广大用户的青睐。 其中&#xff0c;QQ音乐、酷狗音乐以及网易云音乐都是大家耳熟能详的名字。 这些平台不仅提供了丰富的音乐资源&#xff0c;还具备了许多便捷的功能&#xff0c;使得用户们能够享受到更为优质的音乐体验。…

五一 大项目

Docker 中的 Nginx 服务为什么要启用 HTTPS 一安装容器 1 安装docker-20.10.17 2 安装所需的依赖 sudo yum install -y yum-utils device-mapper-persistent-data lvm23 添加Docker官方仓库 sudo yum-config-manager --add-repo https://download.docker.com/linux/centos…

linux安装配置Docker保姆级教程

Docker到底是什么? Docker 是一个开源的应用容器引擎&#xff0c;让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中&#xff0c;然后发布到任何流行的 Linux或Windows操作系统的机器上&#xff0c;也可以实现虚拟化。 容器是完全使用沙箱机制&#xff0c;相互之间…

【MySQL的内置函数】

文章目录 一、日期函数1.current_date()2.current_time()3.current_timestamp4. date_add 穿越未来5.date_sub 回到过去6.datediff案例 二、字符串函数2.1charset2.2 concat ——拼接字符串2.3 ucase——转化成大写2.4 lcase——转化成小写2.5 left&#xff08;&#xff09;2.6…

vue3 element plus el-date-picker组件在日期上做标识

1.先看效果图,带红点的就是我要做标识的日期 2.直接把代码拿出来就可以用 (1)html部分 <el-date-pickerv-model"startTime"type"datetime"placeholder"选择开始日期"format"YYYY-MM-DD HH:mm"value-format"YYYY-MM-DD HH:mm…

测试新人常问:如何开始自动化测试,必须知道的10点!

随着互联网技术的发展&#xff0c;无论哪个公司&#xff0c;哪个团队都在谈论自动化测试、动手实现自动化测试&#xff0c;从而让测试显得更加“高大上”。 那么是不是所有的业务都适合自动化&#xff1f;是不是自动化做的越多&#xff0c;效果越好呢&#xff1f;下面就自己一…

物联网平台之单体架构

介绍本文主要介绍平台的单体架构&#xff0c;包括各个组件之间的数据流描述以及所做的一些架构选择。在单体架构模式下&#xff0c;所有 ThingsKit 组件都在单个 Java 虚拟机 (JVM) 中启动&#xff0c;并共享相同的操作系统资源。由于 ThingsKit 是用 Java 编写的&#xff0c;因…

企业大文件传输之安全数据传输的重要性

企业数据安全性的维护直接关系到企业的市场竞争力乃至其生存的根基。数据的保护不仅涉及到保护企业的商业秘密&#xff0c;也关乎到客户隐私的保护&#xff0c;更触及到国家安全的敏感层面。因此&#xff0c;保障数据在传输过程中的安全&#xff0c;成为了每个企业和组织必须面…

【湿地探秘】守护蓝色星球的绿色之肾

在地球的广阔画卷中&#xff0c;湿地犹如镶嵌其中的翡翠&#xff0c;不仅孕育着丰富的生物多样性&#xff0c;更是自然界不可或缺的调节器。今天&#xff0c;让我们一同深入了解湿地的基本概念、我国湿地的概貌、湿地的多样类型&#xff0c;以及保护湿地对于人类和地球的深远意…

2024.5.8 关于 SpringCloud —— Ribbon 的基本认知

目录 Ribbon 负载均衡原理 工作流程 Ribbon 负载均衡规则 Ribbon 负载均衡自定义化 代码方式修改规则 配置文件方式修改规则 小总结 Ribbon 设定饥饿加载 Ribbon 负载均衡原理 工作流程 order-service 使用 RestTemplate 发送请求&#xff0c;随后该请求将会被 Ribbon 所…

探针流量检测与回溯分析,解密AnaTraf网络流量分析仪的神奇魅力

目录 导言 概述 流量检测探针 流量回溯分析 网络故障解决案例 了解更多 导言 在当今互联网时代&#xff0c;网络性能监测与诊断成为企业发展的关键。为了解决网络故障和提升网络性能&#xff0c;AnaTraf网络流量分析仪应运而生。本文将详细介绍AnaTraf的功能和优势&#…

Python 框架安全:SSTI 模板注入漏洞测试.

什么是 SSTI 模板注入 SSTI (Server-Side Template Injection) 是一种Web应用程序安全漏洞&#xff0c;它发生在应用程序使用模板引擎渲染用户输入时。当应用程序将用户输入直接插入到模板中而不进行充分的过滤和验证时&#xff0c;就可能导致SSTI漏洞。攻击者可以利用这个漏洞…

制造业数字化转型解决方案及应用(125页PPT)

一、资料介绍 《制造业数字化转型解决方案及应用》是一份内容丰富、深入剖析制造业数字化转型的125页PPT资料。这份资料以“智能制造、制造业数字化转型、制造业数字化转型案例”为关键词&#xff0c;全面展现了制造业数字化转型的核心理念、解决方案以及实际应用案例。 关注…

Vue3 - 前端项目代码防止被调试/被爬,阻止浏览器F12开发者工具

项目背景 大家都知道浏览器的开发者工具能干啥&#xff0c;正经的用法&#xff1a;开发时调试代码逻辑&#xff0c;修改布局样式&#xff1b;不正经的用法&#xff1a;改改元素骗骗人&#xff0c;找找网站接口写爬虫&#xff0c;逆向js破解加密等等&#xff0c;所以说前端不安…

MySQL数据库的安装和部署

1.数据库的相关介绍 关系型数据库管理系统&#xff1a;&#xff08;英文简称&#xff1a;RDBMS&#xff09; 为我们提供了一种存储数据的特定格式&#xff0c;所谓的数据格式就是表&#xff0c; 在数据库中一张表就称为是一种关系. 在关系型数据库中表由两部分组成&#xf…

油泼辣子在食品类别可以申请成商标不!

前阵韩国人在美国申请“chili crunch”油泼辣子作为商标&#xff0c;还准备禁止华人餐馆使用投诉侵权并索赔&#xff0c;普推知产老杨在USPTO上面检索发现&#xff0c;这个人申请的主要是30类方便食品的调味品&#xff0c;商标分类是全球通用的。 商标名称不能申请本类所属的通…

详解Python测试框架Pytest的参数化

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 上篇博文介绍过&#xff0c;Pytest是目前比较成熟功能齐全的测试框架&#xff0c;使用率肯定也不…

ECharts系列:基本使用及配置项

目录 基本使用 配置项的写法与位置 配置项option包含属性 各个配置项属性大全 基本使用 在阅读本篇文章时请参考ECharts官网地址中的内容配合了解 首先我们知道ECharts图表中有许多类型&#xff0c;如折线图、柱状图、饼形图等&#xff0c;下面我以折线图为例讲解ECharts图…