使用elasticsearch-java客户端API生成DSL语句

        在Elasticsearch7.15.0之后,官方提供了elasticsearch-java包作为java客户端工具包,用于取代elasticsearch-rest-high-level-client,其底层依然依赖Elasticsearch Low Level REST 客户端,即elasticsearch-rest-client。

        elasticsearch-java客户端的特点:        

  •  对象构造基于生成器模式。
  • 可以使用构建器 lambda 构建嵌套对象,从而允许使用简洁且富有表现力的类似 DSL 的代码。

一、引入依赖包

maven的pom文件

<dependency><groupId>co.elastic.clients</groupId><artifactId>elasticsearch-java</artifactId><version>8.16.1</version>
</dependency>

gradle的build文件

dependencies {implementation 'co.elastic.clients:elasticsearch-java:8.16.1'
}

二、生成DSL语句样例

1、简易match_all

public static String matchAllDsl() {//MatchAllQuery matchAllQuery = MatchAllQuery.of(m -> m);MatchAllQuery matchAllQuery = new MatchAllQuery.Builder().build();SearchRequest searchRequest = SearchRequest.of(s ->    s.query(matchAllQuery._toQuery()).size(10));return convert(searchRequest);
}
{"query":{"match_all":{}},"size":10}

2、sort的match_all

public static String matchAllSortDsl() {MatchAllQuery matchAllQuery = MatchAllQuery.of(m -> m);List<SortOptions> sortOptionsList = new ArrayList<>();sortOptionsList.add(SortOptions.of(s -> s.field(FieldSort.of(f -> f.field("tenantId")))));sortOptionsList.add(SortOptions.of(s -> s.field(FieldSort.of(f -> f.field("id").order(SortOrder.Desc)))));SearchRequest searchRequest = SearchRequest.of(s -> s.query(matchAllQuery._toQuery()).size(10).sort(sortOptionsList));return convert(searchRequest);
}
{"query": {"match_all": {}},"size": 10,"sort": [{"tenantId": {}}, {"id": {"order": "desc"}}]
}

 3、简易match

public static String matchDsl() {MatchQuery matchQuery = MatchQuery.of(m -> m.field("tenantId").query("IDSWVV2W"));SearchRequest searchRequest = SearchRequest.of(s -> s.query(matchQuery._toQuery()).size(10));return convert(searchRequest);
}
{"query":{"match":{"tenantId":{"query":"IDSWVV2W"}}},"size":10}

4、高亮的match

public static String matchHighlightDsl() {MatchQuery matchQuery = MatchQuery.of(m -> m.field("tenantId").query("IDSWVV2W"));Highlight highlight = Highlight.of(h -> h.fields("product_name", f -> f));SearchRequest searchRequest = SearchRequest.of(s -> s.query(matchQuery._toQuery()).size(10).highlight(highlight));return convert(searchRequest);
}
{"highlight": {"fields": {"product_name": {"fragment_offset": 10}}},"query": {"match": {"tenantId": {"query": "IDSWVV2W"}}},"size": 10
}

 5、source的match

public static String matchSourceDsl() {MatchQuery matchQuery = MatchQuery.of(m -> m.field("tenantId").query("IDSWVV2W"));List<SortOptions> sortOptionsList = new ArrayList<>();sortOptionsList.add(SortOptions.of(s -> s.field(FieldSort.of(f -> f.field("tenantId")))));sortOptionsList.add(SortOptions.of(s -> s.field(FieldSort.of(f -> f.field("id").order(SortOrder.Desc)))));SourceConfig sourceConfig = SourceConfig.of(s ->s.filter(f -> f.includes("id", "tenantId").excludes("operateDate")));SearchRequest searchRequest = SearchRequest.of(s -> s.query(matchQuery._toQuery()).size(10).sort(sortOptionsList).source(sourceConfig));return convert(searchRequest);
}
{"_source": {"excludes": ["operateDate"],"includes": ["id", "tenantId"]},"query": {"match": {"tenantId": {"query": "IDSWVV2W"}}},"size": 10,"sort": [{"tenantId": {}}, {"id": {"order": "desc"}}]
}

 6、简易term

public static String termDsl() {TermQuery termQuery = TermQuery.of(t -> t.field("tenantId").value("IDSWVV2W"));SearchRequest searchRequest = SearchRequest.of(s -> s.query(termQuery._toQuery()).size(10));return convert(searchRequest);}
{"query":{"term":{"tenantId":{"value":"IDSWVV2W"}}},"size":10}

 7、简易match_phrase

public static String matchPhraseDsl() {MatchPhraseQuery phraseQuery = MatchPhraseQuery.of(m -> m.field("tenantId").query("IDSWVV2W").slop(0).zeroTermsQuery(ZeroTermsQuery.None).boost(1f));SearchRequest searchRequest = SearchRequest.of(s -> s.query(phraseQuery._toQuery()).size(10));return convert(searchRequest);}
{"query": {"match_phrase": {"tenantId": {"boost": 1.0,"query": "IDSWVV2W","slop": 0,"zero_terms_query": "none"}}},"size": 10
}

 8、简易range

public static String dateRangeDsl() {DateRangeQuery dateRangeQuery = DateRangeQuery.of(d -> d.field("createdDate").gt("1622476800000").lt("1625068799999").boost(1f));RangeQuery rangeQuery = RangeQuery.of(r -> r.date(dateRangeQuery));SearchRequest searchRequest = SearchRequest.of(s -> s.query(rangeQuery._toQuery()).size(10));return convert(searchRequest);}
{"query":{"range":{"createdDate":{"boost":1.0,"gt":"1622476800000","lt":"1625068799999"}}},"size":10}

 9、简易bool

public static String queryBoolDsl() {MatchQuery matchQuery = MatchQuery.of(m -> m.field("tenantId").query("IDSWVV2W"));TermQuery termQuery = TermQuery.of(t -> t.field("ts").value(1732260046231L));BoolQuery boolQuery = BoolQuery.of(b -> b.must(matchQuery._toQuery(), termQuery._toQuery()));SearchRequest searchRequest = SearchRequest.of(s -> s.query(boolQuery._toQuery()).size(10));return convert(searchRequest);}
{"query":{"bool":{"must":[{"match":{"tenantId":{"query":"IDSWVV2W"}}},{"term":{"ts":{"value":1732260046231}}}]}},"size":10}

 10、简易filter

public static String boolFilterDsl() {/*filter查询就是用于精确过滤文档,它只关注文档是否符合条件,将匹配的文档包含在结果中。他们都不进行打分、排序或相关性计算,只担心是否匹配。*/TermQuery termQuery = TermQuery.of(t -> t.field("ts").value(1732260046231L));BoolQuery boolQuery = BoolQuery.of(b -> b.filter(termQuery._toQuery()));SearchRequest searchRequest = SearchRequest.of(s -> s.query(boolQuery._toQuery()).size(10));return convert(searchRequest);}
{"query":{"bool":{"filter":[{"term":{"ts":{"value":1732260046231}}}]}},"size":10}

 11、简易exist,查询字段为空的数据

public static String fieldIsEmptyDsl() {ExistsQuery existsQuery = ExistsQuery.of(e -> e.field("content"));BoolQuery boolQuery = BoolQuery.of(b -> b.mustNot(existsQuery._toQuery()));SearchRequest searchRequest = SearchRequest.of(s -> s.query(boolQuery._toQuery()).size(10));return convert(searchRequest);}
{"query":{"bool":{"must_not":[{"exists":{"field":"content"}}]}},"size":10}

 12、and和or组合

public static String mustShouldDsl() {TermQuery term1 = TermQuery.of(t -> t.field("tenant_id").value("r9au1bbd"));TermQuery term2 = TermQuery.of(t -> t.field("enable").value("1"));ExistsQuery existsQuery1 = ExistsQuery.of(e -> e.field("user_id").boost(1f));MatchPhraseQuery phraseQuery = MatchPhraseQuery.of(m -> m.field("staff_name").query("neng").slop(0).zeroTermsQuery(ZeroTermsQuery.None).boost(1f));TermQuery term3 = TermQuery.of(t -> t.field("user_email").value("玄"));TermQuery term4 = TermQuery.of(t -> t.field("user_name_pinyin").value("玄"));MatchQuery matchQuery1 = MatchQuery.of(m -> m.field("user_name").query("玄"));MatchQuery matchQuery2 = MatchQuery.of(m -> m.field("staff_mobile").query("玄"));RegexpQuery regexpQuery = RegexpQuery.of(r -> r.field("staff_email").value(".*neng.*").flags("65535").maxDeterminizedStates(10000).boost(1f));SortOptions sortOptions = SortOptions.of(s -> s.field(FieldSort.of(f -> f.field("staff_creationtime"))));TrackHits trackHits = TrackHits.of(t -> t.count(2147483647));BoolQuery shouldBool = BoolQuery.of(b -> b.should(phraseQuery._toQuery(), term3._toQuery(), term4._toQuery(), matchQuery1._toQuery(), matchQuery2._toQuery(), regexpQuery._toQuery()));BoolQuery boolQuery = BoolQuery.of(b -> b.must(term1._toQuery(), term2._toQuery(), existsQuery1._toQuery(), shouldBool._toQuery()));SearchRequest searchRequest = SearchRequest.of(s -> s.query(boolQuery._toQuery()).size(10).from(0).sort(sortOptions).trackTotalHits(trackHits));return convert(searchRequest);}
{"from": 0,"query": {"bool": {"must": [{"term": {"tenant_id": {"value": "r9au1bbd"}}}, {"term": {"enable": {"value": "1"}}}, {"exists": {"boost": 1.0,"field": "user_id"}}, {"bool": {"should": [{"match_phrase": {"staff_name": {"boost": 1.0,"query": "neng","slop": 0,"zero_terms_query": "none"}}}, {"term": {"user_email": {"value": "玄"}}}, {"term": {"user_name_pinyin": {"value": "玄"}}}, {"match": {"user_name": {"query": "玄"}}}, {"match": {"staff_mobile": {"query": "玄"}}}, {"regexp": {"staff_email": {"boost": 1.0,"flags": "65535","max_determinized_states": 10000,"value": ".*neng.*"}}}]}}]}},"size": 10,"sort": [{"staff_creationtime": {}}],"track_total_hits": 2147483647
}

 13、聚合-按时间统计排序

public static SearchRequest aggregationsDsl() {TermQuery term1 = TermQuery.of(t -> t.field("tenant_id").value("r9au1bbd"));DateRangeQuery dateRangeQuery = DateRangeQuery.of(d -> d.field("createdDate").gt("1622476800000").lt("1625068799999").boost(1f));RangeQuery rangeQuery = RangeQuery.of(r -> r.date(dateRangeQuery));BoolQuery boolQuery = BoolQuery.of(b -> b.must(term1._toQuery(), rangeQuery._toQuery()).boost(1f));SortOptions sortOptions = SortOptions.of(s -> s.field(FieldSort.of(f -> f.field("createdDate"))));Aggregation valueCountAgg = Aggregation.of(a -> a.valueCount(v -> v.field("id")));NamedValue<SortOrder> namedValue = new NamedValue<>("_key", SortOrder.Asc);Aggregation dateHistogramAgg = Aggregation.of(a -> a.dateHistogram(d -> d.field("createdDate").format("yyyy-MM-dd").calendarInterval(CalendarInterval.Day).timeZone("+08:00").offset(o -> o.offset(0)).keyed(false).minDocCount(0).order(Collections.singletonList(namedValue))));SearchRequest searchRequest = SearchRequest.of(s -> s.query(boolQuery._toQuery()).size(0).sort(sortOptions).aggregations("dateList",Aggregation.of(a -> a.dateHistogram(dateHistogramAgg.dateHistogram()).aggregations("countNum", valueCountAgg))));return searchRequest;}
{"aggregations": {"dateList": {"aggregations": {"countNum": {"value_count": {"field": "id"}}},"date_histogram": {"calendar_interval": "day","field": "createdDate","format": "yyyy-MM-dd","min_doc_count": 0,"offset": 0,"order": [{"_key": "asc"}],"time_zone": "+08:00","keyed": false}}},"query": {"bool": {"boost": 1.0,"must": [{"term": {"tenant_id": {"value": "r9au1bbd"}}}, {"range": {"createdDate": {"boost": 1.0,"gt": "1622476800000","lt": "1625068799999"}}}]}},"size": 0,"sort": [{"createdDate": {}}]
}

 #获取SearchRequest的dsl语句

private static String convert(SearchRequest request) {// JacksonJsonpMapper mapper = new JacksonJsonpMapper();String string = JsonpUtils.toJsonString(request, SimpleJsonpMapper.INSTANCE);System.out.println(string);return string;
}

三、索引API操作

1、查询索引数据

public static void searchTest() {ClusterInfo clusterInfo = new ClusterInfo();clusterInfo.setHosts("172.20.10.14:9200");clusterInfo.setUserName("admin");clusterInfo.setPwd("admin");ElasticsearchClient client = ElasticsearchJavaClient.buildClient(clusterInfo);try {MatchAllQuery matchAllQuery = MatchAllQuery.of(m -> m);SearchRequest searchRequest = SearchRequest.of(s -> s.query(matchAllQuery._toQuery()).size(10).index("logs-2020-01-01"));SearchResponse<LogsDocEntity> response = client.search(searchRequest, LogsDocEntity.class);HitsMetadata<LogsDocEntity> hits = response.hits();for (Hit<LogsDocEntity> hit : hits.hits()) {LogsDocEntity source = hit.source();System.out.println(source.getTimestamp());}} catch (IOException e) {throw new RuntimeException(e);}}

#生成ElasticsearchClient的实现类

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.ElasticsearchTransport;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import com.yonyou.iuap.searchclient.pojo.ClusterInfo;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;import java.io.IOException;public class ElasticsearchJavaClient {public static ElasticsearchClient buildClient(ClusterInfo clusterInfo) {String hosts = clusterInfo.getHosts();String userName = clusterInfo.getUserName();String pwdStr = clusterInfo.getPwd();boolean credentialsFlag = StringUtils.isNoneBlank(userName) && StringUtils.isNoneBlank(pwdStr);final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();if (credentialsFlag) {credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(userName, pwdStr));}RestClientBuilder clientBuilder = RestClient.builder(generateHostArray(hosts, "http"));final int[] connectTimeout = {10000};final int[] socketTimeout = {30000};final int[] connectionRequestTimeout = {10000};// 默认保持30分钟final int[] keepAliveMs = {1800000};// 异步httpclient连接延时配置clientBuilder.setRequestConfigCallback(requestConfigBuilder -> {requestConfigBuilder.setConnectTimeout(connectTimeout[0]);requestConfigBuilder.setSocketTimeout(socketTimeout[0]);requestConfigBuilder.setConnectionRequestTimeout(connectionRequestTimeout[0]);return requestConfigBuilder;});// 异步httpclient配置clientBuilder.setHttpClientConfigCallback(httpClientBuilder -> {if (credentialsFlag) {httpClientBuilder.disableAuthCaching();httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);}// httpclient保活策略httpClientBuilder.setKeepAliveStrategy((resp, context) -> keepAliveMs[0]);return httpClientBuilder;});RestClient client = clientBuilder.build();if (client.isRunning()) {ElasticsearchTransport transport = new RestClientTransport(client, new JacksonJsonpMapper());return new ElasticsearchClient(transport);}throw new RuntimeException("Cluster unable to connect!!!");}public static void closeClient(ElasticsearchClient restClient) {try {if (restClient != null && restClient.ping().value()) {restClient.close();}} catch (IOException e) {throw new RuntimeException(e);}}public static HttpHost[] generateHostArray(String hosts, String scheme) {String[] hostsArray = hosts.split(",");HttpHost[] httpHostArray = new HttpHost[hostsArray.length];for (int i = 0; i < hostsArray.length; i++) {
//            String[] hp = hostsArray[i].split(CommonConstants.COLON_STR);String[] hp = getIpAndPort(false, hostsArray[i]);String host = null, port = null;if (hp.length == 2) {host = hp[0];port = hp[1];} else if (hp.length == 1) {host = hp[0];port = "9200";}httpHostArray[i] = new HttpHost(host, Integer.parseInt(port), scheme);}return httpHostArray;}private static String[] getIpAndPort(boolean isIpv6, String address) {String[] ippt = address.split(":");if (isIpv6) {String[] stv6 = new String[2];stv6[1] = ippt[ippt.length - 1];stv6[0] = address.substring(0, address.length() - stv6[1].length() - 1);return stv6;} else {return ippt;}}}

待更新。。。

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

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

相关文章

Python OpenCV按照像素点图片切割

图像分割是从图像处理到图像分析的关键步骤&#xff0c;在目标检测、特征提取、图像识别等领域具有广泛应用。OpenCV是一个强大的计算机视觉库&#xff0c;提供了多种图像分割方法。本文将详细介绍如何使用Python和OpenCV进行基于像素点的图像分割&#xff0c;包括阈值分割、自…

selenium:新窗口切换、关闭

背景 当前页面A&#xff0c;点击跳转到页面B 句柄就是你点击一个页面,跳转了一个新的窗口。你要操作的元素可能在原窗口上,也有可能在新窗口上。 通过索引定位 window_handles driver.window_handlesdriver.switch_to.window(window_handles[1])#定位到第二个通过当前页面的…

Kubernetes和ZStack分配CPU的机制

在 Kubernetes (k8s) 环境中限制 CPU 资源与在 ZStack 虚拟机中分配 CPU 资源的机制存在一些关键差异。以下是这两种环境下 CPU 分配机制的详细对比&#xff1a; Kubernetes 中的 CPU 管理 资源请求与限制&#xff1a;Kubernetes 允许你为每个容器指定 CPU 请求&#xff08;re…

深度学习中的损失函数

损失函数是深度学习模型训练过程中不可或缺的一部分&#xff0c;是模型预测值与真实值之间差异的客观衡量标准。它们是模型训练的基础&#xff0c;指导算法调整模型参数&#xff0c;以最小化损失并提高预测准确性。它们衡量了模型预测值与真实值的吻合程度。通过最小化这种损失…

#自定义数据类型-简单模拟进程执行

#include <iostream> #include <string> #include <iomanip> using namespace std;//定义一个名为process的结构体&#xff0c;用于表示进程相关信息 //包含进程名、进程ID、进程优先级、进程状态、进程所需CPU时间、进程描述等成员 struct process {string …

KNN算法做预测的几个例子

准备工作 # 安装机器学习算法所用到的第三方库 pip install numpy pip install pandas pip install scikit-learn pip install matplotlibKNN算法 示例一&#xff1a; 鸢尾花数据集分类 Iris也称鸢尾花卉数据集&#xff0c;是一类多重变量分析的数据集。数据集包含150个数据…

【论文阅读】处理器芯片敏捷设计方法:问题与挑战

作者&#xff1a;包云岗老师 包云岗老师是计算机体系结构方向的大牛&#xff0c;推动了体系结构方面的开源事业! 欢迎对本栏目感兴趣的人学习"一生一芯"~ 学习体会&#xff1a; 已有的软硬件生态系统和开发成本制约了对新结构的探索。但目前仍在几种路线上做尝试~ 1…

20. 内置模块

一、random模块 random 模块用来创建随机数的模块。 random.random() # 随机生成一个大于0且小于1之间的小数 random.randint(a, b) # 随机生成一个大于等于a小于等于b的随机整数 random.uniform(a, b) …

嵌入式的软实时和硬实时系统

我觉得嵌入式的软实时和硬实时系统&#xff0c;一定要在应用场景里面去判断。假如你的需求是一个任务的执行时间&#xff0c;必须在2ms内响应&#xff0c;那么你选择的一个系统&#xff0c;系统的设计最晚响应时间是10us&#xff0c;那么这个系统对你来说就是硬实时的。 而且这…

Redis原理—4.核心原理摘要

大纲(9870字) 1.Redis服务器的Socket网络连接建立 2.Redis多路复用监听与文件事件模型 3.基于队列串行化的文件事件处理机制 4.完整的Redis Server网络通信流程 5.Redis串行化单线程模型为什么能高并发 6.Redis内核级请求处理流程与原理 7.Redis通信协议与内核级请求数据…

【UE5】制作插件 并调试【vs2022】

视频教程&#xff1a;好看视频-轻松有收获 https://www.youtube.com/watch?vIjpa9mI2b5I 官方&#xff1a;https://dev.epicgames.com/documentation/zh-cn/unreal-engine/plugins-in-unreal-engine 原文&#xff1a;【UE】制作插件_ue插件-CSDN博客 C制作插件 1. 我们可…

cocos creator 的 widget组件的使用及踩坑

以下的内容基于cocos creator 3.8版本&#xff0c;如有错误&#xff0c;恳请指出。 &#x1f449;官方文档的指引 应用&#xff1a;以上官方指引有非常清晰的使用方式&#xff0c;接下来说明一些注意事项&#xff1a; 1、与canvas搭配的使用&#xff0c;解决多分别率适配问题。…

十五、K8s计划任务JobCronJob

K8s计划任务CronJob&Job 一、Job可以干什么 Job 控制器用于管理 Pod 对象运行一次性任务,比方说我们对数据库备份,可以直接在 k8s 上启动一个 mysqldump 备份程序,也可以启动一个 pod,这个 pod 专门用来备份用的,备份结束 pod 就可以终止了,不需要重启,而是将 Pod…

关于LinuxWindows双系统在八月更新后出现的问题

问题描述类似于&#xff1a;Verifying shim SBAT data failed: If you are, this is caused by a reported problem in the August update if you can get into Windows, either uninstall the August update, or open Command Prompt as administrator and run this command,…

【Elasticsearch 中间件】Elasticsearch 客户端使用案例

文章目录 一、安装 Elasticsearch1.1 启动 Elasticsearch1.2 启动 Kibana 二、客户端代码2.1 导入依赖2.2 配置 application.yaml2.3 定义实体类2.4 连接 Elasticserach2.5 定义 Service 层接口2.6 实现 Service 层功能 三、测试项目3.1 添加数据3.2 搜索数据3.3 更新数据3.4 删…

一文掌握 OpenGL 几何着色器的使用

学习本文需要具备 OpenGL ES 编程基础,如果看起来比较费劲,可以先看入门文章 OpenGL ES 3.0 从入门到精通系统性学习教程 。 什么是几何着色器 几何着色器(Geometry Shader) OpenGL 管线中的可选着色器阶段,位于顶点着色器(Vertex Shader) 和光栅化阶段 之间。 其核心…

C++中多态

1) 什么是多态性&#xff1f;C中如何实现多态&#xff1f; 多态性是指通过基类指针或引用调用派生类的函数&#xff0c;实现不同的行为 多态性可以提高代码的灵活性和可扩展性&#xff0c;使程序能够根据不同的对象类型执行不同的操作。 2&#xff09;C中如何实现多态&#…

PHP语法学习(第十天)—PHP 表单

各位&#x1f9cd;‍♂️ 周一愉快♪٩(ω)و♪ 我是练小杰&#xff0c;今天开启新的篇章——PHP 表单 另外&#xff0c;想要学习更多PHP语法相关内容点击 “PHP专栏”~~~ 今天由我强哥来带领大家学习~~ 文章目录 PHP 表单简介表单的基本结构表单数据操作PHP 中的 $_POST 和 $_…

跟李笑来学美式俚语(Most Common American Idioms): Part 67

Most Common American Idioms: Part 67 前言 本文是学习李笑来的Most Common American Idioms这本书的学习笔记&#xff0c;自用。 Github仓库链接&#xff1a;https://github.com/xiaolai/most-common-american-idioms 使用方法: 直接下载下来&#xff08;或者clone到本地…

卫生巾检测项目、检测标准梳理

为了确保卫生巾在使用过程中具备良好的吸收性、舒适性、透气性&#xff0c;并且符合卫生安全标准&#xff0c;避免对人体健康造成不良影响&#xff0c;需要对其进行一系列检测。 名找我 一、检测项目 外观质量 检查卫生巾的形状、尺寸是否与标识相符&#xff0c;表面是否平整…