ElasticSearch的自动补全功能(拼音分词器、自定义分词器、DSL实现自动补全查询、RestAPI实现自动补全查询)

文章目录

  • 1. 什么是自动补全
  • 2. 拼音分词器
    • 2.1 初识拼音分词器
    • 2.2 下载拼音分词器
    • 2.3 安装拼音分词器
    • 2.4 测试拼音分词器
  • 3. 自定义分词器
    • 3.1 拼音分词器存在的问题
    • 3.2 分词器(analyzer)的组成
    • 3.3 如何自定义分词器
    • 3.4 拼音分词器的可选参数
    • 3.5 配置自定义分词器的tokenizer和filter
    • 3.6 如何使用自定义分词器
    • 3.7 测试自定义分词器
      • 3.7.1 直接测试
      • 3.7.2 插入文档测试
    • 3.8 使用自定义分词器要注意的事项
  • 4. DSL实现自动补全查询
    • 4.1 字段的类型的约束
    • 4.2 查询语法
  • 5. 自动补全案例
    • 5.1 准备工作
      • 5.1.1 创建hotel索引库
      • 5.1.2 导入测试工程
      • 5.1.3 导入酒店数据到数据库中
      • 5.1.4 将数据库中的数据导入到ElasticSearch
    • 5.2 测试自动补全功能
  • 6. RestAPI实现自动补全查询
  • 7. 综合案例:实现搜索框自动补全

视频教程:SpringCloud+RabbitMQ+Docker+Redis+搜索+分布式,系统详解springcloud微服务技术栈课程|黑马程序员Java微服务


阅读本文前可以先阅读以下文章:

  • ElasticSearch快速入门——上篇(认识ElasticSearch、安装ElasticSearch、安装kibana、IK分词器、ElasticSearch中的基本概念、索引库操作、文档操作)
  • ElasticSearch快速入门——下篇(在Java代码中操作ElasticSearch、JavaRestClient、操作索引库、操作文档、DSL查询、JavaRestClient查询、数据聚合)
  • 通过docker启动ElasticSearch后为ElasticSearch设置用户和密码

1. 什么是自动补全

ElasticSearch 中的自动补全跟我们理解的自动补全不太一样,为了大家理解,我们来看一个案例

在这里插入图片描述

当我们在搜索框输入 sj 时,搜索框下方会显示以 sj 拼音首字母开头的词条(如手机、湿巾、数据线、史记、书架等),这个功能被称为自动补全

自动补全功能可以让用户尽可能地搜索到想要的东西,而不需要打出完整的内容

2. 拼音分词器

要想实现自动补全功能,我们需要先学习一下拼音分词器,因为自动补全功能是基于拼音分词器实现的

2.1 初识拼音分词器

拼音分词器的官网:analysis-pinyin

拼音分词器跟我们学过的 IK 分词器相似,都是 ElasticSearch 的一个插件

2.2 下载拼音分词器

下载地址:v7.17.18

在这里插入图片描述


本次演示使用的 ElasticSearch 版本为 7.17.18

其它 ElasticSearch 版本对应的拼音分词器的下载地址:Tags

2.3 安装拼音分词器

解压完成之后,将拼音分词器上传到 ElasticSearch 的 plugin 目录下(本次演示是通过 docker 安装 ElasticSearch 的)


先将拼音分词器上传到服务器,一般是当前用户的目录

cd ~

接着将拼音分词器复制到 ElasticSearch 的 plugin 的目录下

sudo cp elasticsearch-analysis-pinyin-7.17.18 -r /var/lib/docker/volumes/elasticsearch-plugins/_data

最后重启 ElasticSearch 容器

sudo docker restart elasticsearch

2.4 测试拼音分词器

我们在 Kibana 提供的控制台中测试拼音分词器是否生效


在浏览器打开 Kibana 提供的控制台

http://127.0.0.1:5601/app/dev_tools#/console

输入以下内容测试拼音分词器是否生效

POST /_analyze
{
  "text": [
    "练习时长两年半"
  ],
  "analyzer": "pinyin"
}

测试结果如下,主要包含两部分内容:

  1. 每个字的完整拼音
  2. 每个字的拼音首字母的合并

在这里插入图片描述

{
  "tokens" : [
    {
      "token" : "lian",
      "start_offset" : 0,
      "end_offset" : 0,
      "type" : "word",
      "position" : 0
    },
    {
      "token" : "lxsclnb",
      "start_offset" : 0,
      "end_offset" : 0,
      "type" : "word",
      "position" : 0
    },
    {
      "token" : "xi",
      "start_offset" : 0,
      "end_offset" : 0,
      "type" : "word",
      "position" : 1
    },
    {
      "token" : "shi",
      "start_offset" : 0,
      "end_offset" : 0,
      "type" : "word",
      "position" : 2
    },
    {
      "token" : "chang",
      "start_offset" : 0,
      "end_offset" : 0,
      "type" : "word",
      "position" : 3
    },
    {
      "token" : "liang",
      "start_offset" : 0,
      "end_offset" : 0,
      "type" : "word",
      "position" : 4
    },
    {
      "token" : "nian",
      "start_offset" : 0,
      "end_offset" : 0,
      "type" : "word",
      "position" : 5
    },
    {
      "token" : "ban",
      "start_offset" : 0,
      "end_offset" : 0,
      "type" : "word",
      "position" : 6
    }
  ]
}

3. 自定义分词器

3.1 拼音分词器存在的问题

拼音分词器还无法正常用于生产环境,因为拼音分词器存在一些问题

以 “练习时长两年半” 这句话为例,拼音分词器存在以下问题:

  1. “练习时长两年半” 这句话没有被分词,而是作为一个整体出现
  2. 把 “练习时长两年半” 这句话中的每一个字都形成了一个拼音(用处不大)
  3. 分词后的结果只剩下拼音,没有汉字

其实我们很少使用拼音搜索,大多数情况下我们都是使用中文去搜索的,分词后有拼音只是锦上添花,分词后的结果中汉字是必须保留的,所以我们需要对拼音分词器做一些配置,也就是自定义分词器

3.2 分词器(analyzer)的组成

ElasticSearch 中分词器(analyzer)的组成有三部分:

  • character filters:在 tokenizer 之前对文本进行处理,例如删除字符、替换字符
  • tokenizer:将文本按照一定的规则切割成词条(term),例如 keyword(不分词)、ik_smart 等
  • tokenizer filter:将 tokenizer 输出的词条做进一步处理,例如大小写转换、同义词处理、拼音处理等

在这里插入图片描述

3.3 如何自定义分词器

要想自定义分词器,一定要在创建索引库的时候去设置

我们可以在创建索引库时,通过 settings 来配置自定义的 analyzer(分词器)

自定义分词器时可以只设置分词器(analyzer)的某个部分

在这里插入图片描述

PUT /test
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_analyzer": {
          "tokenizer": "ik_max_word",
          "filter": "pinyin"
        }
      }
    }
  }
}

tokenizer 我们使用 ik_max_word,先分词,分好词后再将词条交给拼音分词器处理,这样做可以解决拼音分词器没有分词的问题

但是拼音分词器还存在两个问题:分词后的每一个字都形成了一个拼音、分词后的结果只剩下拼音,没有汉字

3.4 拼音分词器的可选参数

我们需要对拼音分词器做进一步的定制

在拼音分词器的官网上,给出了很多的可选参数(Optional Parameters)

在这里插入图片描述

参数名称含义
keep_first_letter启用后,只保留每个汉字的第一个字母。例如,刘德华变为ldh。默认:true。
keep_separate_first_letter启用后,保留每个汉字的第一个字母,并分别显示。例如,刘德华变为l,d,h。默认:false。注意:这可能会因词频增加查询的模糊度。
limit_first_letter_length设置第一个字母结果的最大长度。默认:16。
keep_full_pinyin启用后,保留每个汉字的完整拼音。例如,刘德华变为[liu,de,hua]。默认:true。
keep_joined_full_pinyin启用后,将每个汉字的完整拼音连接起来。例如,刘德华变为[liudehua]。默认:false。
keep_none_chinese保留结果中的非汉字字母或数字。默认:true。
keep_none_chinese_together保留非汉字字母在一起。默认:true。例如,DJ音乐家变为DJ,yin,yue,jia。当设置为false时,DJ音乐家变为D,J,yin,yue,jia。注意:需要先启用keep_none_chinese。
keep_none_chinese_in_first_letter在首字母中保留非汉字字母。例如,刘德华AT2016变为ldhat2016。默认:true。
keep_none_chinese_in_joined_full_pinyin在连接的完整拼音中保留非汉字字母。例如,刘德华2016变为liudehua2016。默认:false。
none_chinese_pinyin_tokenize如果非汉字字母是拼音,将其拆分为单独的拼音词。默认:true。例如,liudehuaalibaba13zhuanghan变为liu,de,hua,a,li,ba,ba,13,zhuang,han。注意:需要先启用keep_none_chinese和keep_none_chinese_together。
keep_original启用后,保留原始输入。默认:false。
lowercase将非汉字字母转换为小写。默认:true。
trim_whitespace默认:true。
remove_duplicated_term启用后,移除重复的词以节省索引空间。例如,de的变为de。默认:false。注意:可能与位置相关的查询受到影响。
ignore_pinyin_offset在6.0版本之后,偏移量受到严格限制,不允许重叠的词。通过此参数,将允许重叠的词,忽略偏移量。请注意,所有与位置相关的查询或高亮将变得不正确。如果需要偏移量,请设置为false。默认:true。

3.5 配置自定义分词器的tokenizer和filter

在这里插入图片描述

PUT /test
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_analyzer": {
          "tokenizer": "ik_max_word",
          "filter": "py"
        }
      },
      "filter": {
        "py": {
          "type": "pinyin",
          "keep_full_pinyin": false,
          "keep_joined_full_pinyin": true,
          "keep_original": true,
          "limit_first_letter_length": 16,
          "remove_duplicated_term": true,
          "none_chinese_pinyin_tokenize": false
        }
      }
    }
  }
}

创建一个自定义的分词器my_analyzer,使用ik_max_word分词器进行中文分词,并通过pinyin过滤器将中文词条转换为拼音,保留了原始中文词条和连接起来的全拼,同时限制了首字母长度并移除重复的词条

3.6 如何使用自定义分词器

自定义分词器创建好了之后,该怎么使用呢

要使用自定义分词器,我们需要在定义索引库字段(Mapping)的时候使用

在这里插入图片描述

PUT /test
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_analyzer": {
          "tokenizer": "ik_max_word",
          "filter": "py"
        }
      },
      "filter": {
        "py": {
          "type": "pinyin",
          "keep_full_pinyin": false,
          "keep_joined_full_pinyin": true,
          "keep_original": true,
          "limit_first_letter_length": 16,
          "remove_duplicated_term": true,
          "none_chinese_pinyin_tokenize": false
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "name": {
        "type": "text",
        "analyzer": "my_analyzer"
      }
    }
  }
}

3.7 测试自定义分词器

3.7.1 直接测试

POST /test/_analyze
{
  "text": [
    "练习时长两年半"
  ],
  "analyzer": "my_analyzer"
}

测试结果

在这里插入图片描述

{
  "tokens" : [
    {
      "token" : "练习",
      "start_offset" : 0,
      "end_offset" : 2,
      "type" : "CN_WORD",
      "position" : 0
    },
    {
      "token" : "lianxi",
      "start_offset" : 0,
      "end_offset" : 2,
      "type" : "CN_WORD",
      "position" : 0
    },
    {
      "token" : "lx",
      "start_offset" : 0,
      "end_offset" : 2,
      "type" : "CN_WORD",
      "position" : 0
    },
    {
      "token" : "时长",
      "start_offset" : 2,
      "end_offset" : 4,
      "type" : "CN_WORD",
      "position" : 1
    },
    {
      "token" : "shichang",
      "start_offset" : 2,
      "end_offset" : 4,
      "type" : "CN_WORD",
      "position" : 1
    },
    {
      "token" : "sc",
      "start_offset" : 2,
      "end_offset" : 4,
      "type" : "CN_WORD",
      "position" : 1
    },
    {
      "token" : "两年",
      "start_offset" : 4,
      "end_offset" : 6,
      "type" : "CN_WORD",
      "position" : 2
    },
    {
      "token" : "liangnian",
      "start_offset" : 4,
      "end_offset" : 6,
      "type" : "CN_WORD",
      "position" : 2
    },
    {
      "token" : "ln",
      "start_offset" : 4,
      "end_offset" : 6,
      "type" : "CN_WORD",
      "position" : 2
    },
    {
      "token" : "两",
      "start_offset" : 4,
      "end_offset" : 5,
      "type" : "COUNT",
      "position" : 3
    },
    {
      "token" : "liang",
      "start_offset" : 4,
      "end_offset" : 5,
      "type" : "COUNT",
      "position" : 3
    },
    {
      "token" : "l",
      "start_offset" : 4,
      "end_offset" : 5,
      "type" : "COUNT",
      "position" : 3
    },
    {
      "token" : "年半",
      "start_offset" : 5,
      "end_offset" : 7,
      "type" : "CN_WORD",
      "position" : 4
    },
    {
      "token" : "nianban",
      "start_offset" : 5,
      "end_offset" : 7,
      "type" : "CN_WORD",
      "position" : 4
    },
    {
      "token" : "nb",
      "start_offset" : 5,
      "end_offset" : 7,
      "type" : "CN_WORD",
      "position" : 4
    }
  ]
}

3.7.2 插入文档测试

测试数据如下(狮子和虱子的拼音是一样的

POST /test/_doc/1
{
  "id": 1,
  "name": "狮子"
}
POST /test/_doc/2
{
  "id": 2,
  "name": "虱子"
}

我们先通过拼音 shizi 来搜索

GET /test/_search
{
  "query": {
    "match": {
      "name": "shizi"
    }
  }
}

成功搜索出狮子和虱子

在这里插入图片描述

但如果我们搜索的内容是掉入狮子笼怎么办呢

GET /test/_search
{
  "query": {
    "match": {
      "name": "掉入狮子笼怎么办"
    }
  }
}

在这里插入图片描述

从搜索结果中我们可以发现,我们明明搜索的是狮子,怎么虱子也搜索出来了?

这说明我们自定义的分词器有问题,在用拼音搜索时确实没问题,但是在用中文搜索时却搜出了同音词

3.8 使用自定义分词器要注意的事项

拼音分词器适合在创建倒排索引的时候使用,但不能在搜索的时候使用


创建倒排索引时

在这里插入图片描述

在这里插入图片描述

用户搜索狮子,搜索结果中居然出现了虱子


所以,我们在创建倒排索引时使用的分词器要和搜索时使用的分词器分开

怎么分开呢,在创建倒排索引时使用 my_analyzer 分词器,搜索时使用 ik_smart 分词器

在这里插入图片描述

PUT /test
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_analyzer": {
          "tokenizer": "ik_max_word",
          "filter": "py"
        }
      },
      "filter": {
        "py": {
          "type": "pinyin",
          "keep_full_pinyin": false,
          "keep_joined_full_pinyin": true,
          "keep_original": true,
          "limit_first_letter_length": 16,
          "remove_duplicated_term": true,
          "none_chinese_pinyin_tokenize": false
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "name": {
        "type": "text",
        "analyzer": "my_analyzer",
        "search_analyzer": "ik_smart"
      }
    }
  }
}

我们删除 test 索引库之后,重写创建 test 索引库进行测试

DELETE /test
GET /test/_search
{
  "query": {
    "match": {
      "name": "掉入狮子笼怎么办"
    }
  }
}

测试结果如下(可以看到,搜索结果中没有虱子了)

在这里插入图片描述

4. DSL实现自动补全查询

ElasticSearch 提供了 Completion suggester 查询来实现自动补全功能,这个查询会匹配以用户输入内容开头的词条并
返回

4.1 字段的类型的约束

为了提高补全查询的效率,对于文档中字段的类型有一些约束:

  1. 参与补全查询的字段必须是 completion 类型
  2. 字段的内容一般是用来补全的多个词条形成的数组

在这里插入图片描述

4.2 查询语法

在这里插入图片描述

索引库

PUT test2
{
  "mappings": {
    "properties": {
      "title":{
        "type": "completion"
      }
    }
  }
}

测试数据

POST test2/_doc
{
  "title": ["Sony", "WH-1000XM3"]
}

POST test2/_doc
{
  "title": ["SK-II", "PITERA"]
}

POST test2/_doc
{
  "title": ["Nintendo", "switch"]
}

执行查询操作

POST /test2/_search
{
  "suggest": {
    "title_suggest": {
      "text": "s",
      "completion": {
        "field": "title",
        "skip_duplicates": true,
        "size": 10
      }
    }
  }
}

查询结果(查询结果中包含了文档的原始信息)

在这里插入图片描述

5. 自动补全案例

我们来做一个关于酒店数据的自动补全案例

5.1 准备工作

5.1.1 创建hotel索引库

PUT /hotel
{
  "settings": {
    "analysis": {
      "analyzer": {
        "text_anlyzer": {
          "tokenizer": "ik_max_word",
          "filter": "py"
        },
        "completion_analyzer": {
          "tokenizer": "keyword",
          "filter": "py"
        }
      },
      "filter": {
        "py": {
          "type": "pinyin",
          "keep_full_pinyin": false,
          "keep_joined_full_pinyin": true,
          "keep_original": true,
          "limit_first_letter_length": 16,
          "remove_duplicated_term": true,
          "none_chinese_pinyin_tokenize": false
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "id": {
        "type": "keyword"
      },
      "name": {
        "type": "text",
        "analyzer": "text_anlyzer",
        "search_analyzer": "ik_smart",
        "copy_to": "all"
      },
      "address": {
        "type": "keyword",
        "index": false
      },
      "price": {
        "type": "integer"
      },
      "score": {
        "type": "integer"
      },
      "brand": {
        "type": "keyword",
        "copy_to": "all"
      },
      "city": {
        "type": "keyword"
      },
      "starName": {
        "type": "keyword"
      },
      "business": {
        "type": "keyword",
        "copy_to": "all"
      },
      "location": {
        "type": "geo_point"
      },
      "pic": {
        "type": "keyword",
        "index": false
      },
      "all": {
        "type": "text",
        "analyzer": "text_anlyzer",
        "search_analyzer": "ik_smart"
      },
      "suggestion": {
        "type": "completion",
        "analyzer": "completion_analyzer",
        "search_analyzer": "ik_smart"
      }
    }
  }
}

5.1.2 导入测试工程

测试工程的 Gitee 地址:hotel-demo

5.1.3 导入酒店数据到数据库中

SQL 脚本在测试工程的 doc 目录下

在这里插入图片描述

5.1.4 将数据库中的数据导入到ElasticSearch

导入数据前,更改与连接 ElasticSearch 相关的信息(如果 ElasticSearch 没有设置密码,可以去除 setHttpClientConfigCallback 代码)

在这里插入图片描述

运行 HotelDocumentTest 测试类中的 testBulkRequest 方法,将数据库中的数据导入到 ElasticSearch


在 Kibana 提供的控制台检查数据是否导入成功

GET /hotel/_search
{
  "query": {
    "match_all": {}
  }
}

在这里插入图片描述

5.2 测试自动补全功能

在 Kibana 提供的控制台测试自动补全功能

GET /hotel/_search
{
  "suggest": {
    "suggestions": {
      "text": "s",
      "completion": {
        "field": "suggestion",
        "skip_duplicates": true,
        "size": 10
      }
    }
  }
}

测试结果

在这里插入图片描述

6. RestAPI实现自动补全查询

构建请求参数的 API

在这里插入图片描述

结果解析

在这里插入图片描述

import cn.itcast.hotel.service.IHotelService;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.search.suggest.Suggest;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.elasticsearch.search.suggest.SuggestBuilders;
import org.elasticsearch.search.suggest.completion.CompletionSuggestion;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.io.IOException;
import java.util.List;

@SpringBootTest
class HotelSuggestionTest {

    private RestHighLevelClient restHighLevelClient;

    @Autowired
    private IHotelService hotelService;

    @Test
    void testSuggestion() throws IOException {
        // 1.准备SearchRequest
        SearchRequest searchRequest = new SearchRequest("hotel");
        // 2.准备DSL
        searchRequest.source().suggest(new SuggestBuilder().addSuggestion(
                "suggestions",
                SuggestBuilders.completionSuggestion("suggestion")
                        .prefix("h")
                        .skipDuplicates(true)
                        .size(10)
        ));

        // 3.发送请求
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

        // 4.解析结果
        // 4.1.获取suggest对象
        Suggest suggest = searchResponse.getSuggest();
        // 4.2.根据名称获取suggestion对象
        CompletionSuggestion suggestion = suggest.getSuggestion("suggestions");
        // 4.3.获取options
        List<CompletionSuggestion.Entry.Option> options = suggestion.getOptions();
        // 4.4.遍历
        for (CompletionSuggestion.Entry.Option option : options) {
            System.out.println("option.getText().string() = " + option.getText().string());
        }
    }

    @BeforeEach
    void setUp() {
        // 用户名和密码
        String username = "elastic";
        String password = "tF8RGg2vd0FAzgkK";

        final BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
        credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));

        RestClientBuilder restClientBuilder = RestClient
                .builder(new HttpHost("127.0.0.1", 9200, "http"))
                .setHttpClientConfigCallback(httpClientBuilder -> httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider));
        restHighLevelClient = new RestHighLevelClient(restClientBuilder);
    }

    @AfterEach
    void tearDown() throws IOException {
        restHighLevelClient.close();
    }

}

7. 综合案例:实现搜索框自动补全

测试工程已实现搜索框自动补全,启动测试工程后,在浏览器中查看搜索框的自动补全效果

http://localhost:8089/

在这里插入图片描述


前端源代码的 Gitee 地址:auto-complete

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

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

相关文章

AI一键分析小红书对标账号‼️

宝子们&#xff0c;AI小助手近期发现了一款宝藏AI工具&#xff0c;拥有对标账号AI分析功能&#xff0c;只需10秒就能全面掌握对标账号的运营情况&#xff0c;并且可以根据分析结果提供创作方向和灵感&#xff0c;轻松助力1:1复刻起号&#xff01; 功能亮点&#xff1a; &…

【5G】5G的主要架构选项

最初&#xff0c;在3GPP讨论中考虑了所有可能的聚合和核心网络组合&#xff0c;共有八个架构选项。以下重点介绍option2、3、4和7。 1. 独立组网 (Standalone, SA) 架构选项 2 &#xff1a;Standalone architecture with 5G-core 特点&#xff1a; 5G核心网&#xff08;5GC, …

数据分析实战—鸢尾花数据分类

1.实战内容 (1) 加载鸢尾花数据集(iris.txt)并存到iris_df中,使用seaborn.lmplot寻找class&#xff08;种类&#xff09;项中的异常值&#xff0c;其他异常值也同时处理 。 import pandas as pd from sklearn.datasets import load_iris pd.set_option(display.max_columns, N…

鸿蒙项目云捐助第十讲鸿蒙App应用分类页面二级联动功能实现

鸿蒙项目云捐助第十讲鸿蒙App应用分类页面二级联动功能实现 在之前的教程中完成了分类页面的左右两侧的列表结构&#xff0c;如下图所示。 接下来需要实现左侧分类导航项的点击操作&#xff0c;可以友好的提示用户选择了哪一个文字分类导航项。 一、左侧文字分类导航的处理 …

VSCode:Remote-SSH插件安装使用 -- 在VSCode中使用SSH

VSCode&#xff1a;Remote-SSH插件安装使用 1.安装Remote-SSH2.使用Remote-SSH 本文&#xff0c;将在Visual Studio Code中&#xff0c;安装Remote-SSH插件&#xff0c;实现SSH连接远程服务器。 1.安装Remote-SSH 打开VSCode&#xff0c;侧边栏中找到扩展模块(或CtrlShiftX快捷…

【机器人】Graspness 端到端 抓取点估计 | 论文解读

在复杂场景中实现抓取检测&#xff0c;Graspness是一种端到端的方法&#xff1b; 输入点云数据&#xff0c;输出抓取角度、抓取深度、夹具宽度等信息。 开源地址&#xff1a;GitHub - rhett-chen/graspness_implementation: My implementation of Graspnet Graspness. 论文地…

OpenWebUI,RAG+外部知识库+AI写文的开源应用

引言 自从去年AI火起来之后&#xff0c;很多人便热衷于寻找适合自用的AI开源项目&#xff0c;把各家大模型API接入到自己的AI程序里&#xff0c;便可以通过AI辅助完成一系列日常任务&#xff0c;比如内容翻译/润色/总结/撰写、格式转换、数据分类、代码分析、角色扮演等等。 …

力扣-图论-15【算法学习day.65】

前言 ###我做这类文章一个重要的目的还是给正在学习的大家提供方向和记录学习过程&#xff08;例如想要掌握基础用法&#xff0c;该刷哪些题&#xff1f;&#xff09;我的解析也不会做的非常详细&#xff0c;只会提供思路和一些关键点&#xff0c;力扣上的大佬们的题解质量是非…

单元测试知识总结

我们希望每段代码都是自测试的&#xff0c;每次改动之后&#xff0c;都能自动发现对现有功能的影响。 1 测试要求 在对软件单元进行动态测试之前&#xff0c;应对软件单元的源代码进行静态测试&#xff1b; 应建立测试软件单元的环境&#xff0c;如数据准备、桩模块、模拟器…

基于AI对话生成剧情AVG游戏

游戏开发这个领域&#xff0c;一直有较高的学习门槛。作为一个非专业的游戏爱好者&#xff0c;如果想要开发游戏&#xff0c;往往受制于游戏引擎的专业程度&#xff0c;难以完成复杂的游戏项目。 AI IDE的诞生&#xff0c;提供了另外的一种思路&#xff0c;即通过AI 生成项目及…

kubeadm安装K8s高可用集群之集群初始化及master/node节点加入calico网络插件安装

系列文章目录 1.kubeadm安装K8s高可用集群之基础环境配置 2.kubeadm安装K8s集群之高可用组件keepalivednginx及kubeadm部署 3.kubeadm安装K8s高可用集群之集群初始化及master/node节点加入集群calico网络插件安装 kubeadm安装K8s高可用集群之集群初始化及master/node节点加入ca…

java后端环境配置

因为现在升学了&#xff0c;以前本来想毕业干java的&#xff0c;很多java的环境配置早就忘掉了&#xff08;比如mysql maven jdk idea&#xff09;&#xff0c;想写个博客记录下来&#xff0c;以后方便自己快速搭建环境 JAVA后端开发配置 环境配置jdkideamavenMySQLnavicate17…

FPC补强板和软硬结合板有什么区别?

柔性印刷电路&#xff08;Flexible Printed Circuit&#xff0c;简称FPC&#xff09;在现代电子设备中有广泛应用&#xff0c;其设计和应用形式多种多样。在FPC的应用中&#xff0c;补强板和软硬结合板是两种常见的增强结构形式&#xff0c;它们在结构、功能和应用方面存在显著…

windows openssl编译x64版libssl.lib,编译x64版本libcurl.lib,支持https,vs2015编译器

不要纠结&#xff0c;直接选择用perl编译&#xff01; 告诫想要用弄成vs编译版的&#xff0c;暂时先别给自己增加麻烦 告诫&#xff0c;以下执行的每一步&#xff0c;都不要纠结 先安装环境 nasm 64位版本 https://www.nasm.us/pub/nasm/releasebuilds/2.16.01/win64/nasm-…

【sizeof】各种数据类型所占空间大小

各种数据类型所占空间大小 文章目录 前言 一、sizeof是什么&#xff1f; 二、使用步骤 1.整型 2.字符型 总结 前言 ‌sizeof在C语言中是一个运算符&#xff0c;用于获取数据类型或变量在内存中所占的字节数。‌它可以在编译时计算数据类型或变量的内存大小&#xff0c;而…

gitlab仓库API操作

几年没接触gitlab了&#xff0c;新版本装完以后代码提交到默认的main分支&#xff0c;master不再是主分支 项目有几十个仓库&#xff0c;研发提交代码后仓库地址和之前的发生了变化 先修改Group的默认分支&#xff0c;不会影响已存在的项目 修改gitlab全局的默认分支 这就引…

Gitlab服务管理和仓库项目权限管理

Gitlab服务管理 gitlab-ctl start # 启动所有 gitlab 组件&#xff1b; gitlab-ctl stop # 停止所有 gitlab 组件&#xff1b; gitlab-ctl restart # 重启所有 gitlab 组件&#xff1b; gitlab-ctl status …

睡岗和玩手机数据集,4653张原始图,支持YOLO,VOC XML,COCO JSON格式的标注

睡岗和玩手机数据集&#xff0c;4653张原始图&#xff0c;支持YOLO&#xff0c;VOC XML&#xff0c;COCO JSON格式的标注 数据集分割 训练组70&#xff05; 3257图片 有效集20&#xff05; 931图片 测试集10&#xff05; 465图片 预处理 没有采用任何预处…

vue响应式数据-修改对象的属性值,视图不更新

如图&#xff1a; 一&#xff1a;问题是&#xff1a; 我把数据处理后能console.log()打印出来&#xff0c;但是页面的内容不能同步的更新渲染&#xff1b; 二&#xff1a;要求&#xff1a; 在数组循环列表里面&#xff0c;我点击单个的item按钮时&#xff0c;需要实时加载进度…

HuLa——一款基于 Tauri+Vue3 构建的桌面即时通讯应用

文章目录 一、HuLa简介二、技术栈介绍三、安装运行四、界面体验五、开源地址 一、HuLa简介 HuLa 是一个基于 Tauri、Vite 5、Vue 3 和 TypeScript 构建的即时通讯系统。它利用了 Tauri 的跨平台能力和 Vue 3 的响应式设计&#xff0c;结合了 TypeScript 的类型安全特性和 Vite…