【lucene】使用docvalues的案例

下面给出一段 可直接跑通 的 Lucene 8.5.0 示例代码,演示如何

 

1. 建索引时为两个字段启用 DocValues(一个 `NumericDocValues`,一个 `SortedDocValues`);  

2. 用 `IndexSearcher` 按 DocValues 排序;  

3. 用 `FacetsCollector` 做分组统计(相当于 SQL 的 `GROUP BY`)。

 

```java

import org.apache.lucene.analysis.standard.StandardAnalyzer;

import org.apache.lucene.codecs.lucene50.Lucene50Codec;

import org.apache.lucene.document.*;

import org.apache.lucene.index.*;

import org.apache.lucene.search.*;

import org.apache.lucene.store.ByteBuffersDirectory;

import org.apache.lucene.util.NumericUtils;

import org.apache.lucene.facet.*;

import org.apache.lucene.facet.sortedset.SortedSetDocValuesFacetCounts;

import org.apache.lucene.facet.sortedset.SortedSetDocValuesFacetField;

 

public class DocValuesDemo {

    public static void main(String[] args) throws Exception {

 

        /* ---------- 1. 创建内存目录 ---------- */

        Directory dir = new ByteBuffersDirectory();

        IndexWriterConfig cfg = new IndexWriterConfig(new StandardAnalyzer());

        // 使用默认 codec 即可,DocValues 默认开启

        cfg.setCodec(new Lucene50Codec());

        IndexWriter writer = new IndexWriter(dir, cfg);

 

        /* ---------- 2. 写入三条示范文档 ---------- */

        Document doc1 = new Document();

        doc1.add(new StringField("id", "1", Field.Store.YES));

        // 商品价:NumericDocValues,可排序、可聚合

        doc1.add(new NumericDocValuesField("price", 2999));

        // 商品品牌:SortedDocValues,可做 faceting

        doc1.add(new SortedSetDocValuesFacetField("brand", "小米"));

 

        Document doc2 = new Document();

        doc2.add(new StringField("id", "2", Field.Store.YES));

        doc2.add(new NumericDocValuesField("price", 3999));

        doc2.add(new SortedSetDocValuesFacetField("brand", "苹果"));

 

        Document doc3 = new Document();

        doc3.add(new StringField("id", "3", Field.Store.YES));

        doc3.add(new NumericDocValuesField("price", 1999));

        doc3.add(new SortedSetDocValuesFacetField("brand", "小米"));

 

        writer.addDocument(doc1);

        writer.addDocument(doc2);

        writer.addDocument(doc3);

        writer.commit();

        writer.close();

 

        /* ---------- 3. 打开搜索器 ---------- */

        DirectoryReader reader = DirectoryReader.open(dir);

        IndexSearcher searcher = new IndexSearcher(reader);

 

        /* ---------- 4. 按价格升序排序 ---------- */

        Sort sort = new Sort(new SortField("price", SortField.Type.LONG));

        TopDocs topDocs = searcher.search(new MatchAllDocsQuery(), 10, sort);

        for (ScoreDoc sd : topDocs.scoreDocs) {

            Document d = searcher.doc(sd.doc);

            System.out.println("id=" + d.get("id"));

        }

        // 输出:

        // id=3 (1999)

        // id=1 (2999)

        // id=2 (3999)

 

        /* ---------- 5. 用 Facets 统计品牌分布 ---------- */

        FacetsCollector fc = new FacetsCollector();

        searcher.search(new MatchAllDocsQuery(), fc);

        Facets facets = new SortedSetDocValuesFacetCounts(fc);

        FacetResult result = facets.getTopChildren(10, "brand");

        for (LabelAndValue lav : result.labelValues) {

            System.out.println(lav.label + " : " + lav.value);

        }

        // 输出:

        // 小米 : 2

        // 苹果 : 1

 

        reader.close();

        dir.close();

    }

}

```

 

---

 

关键 API 回顾

 

目标 用到的 Lucene 类/方法 

声明数值 DocValues `NumericDocValuesField` 

声明字符串 DocValues `SortedSetDocValuesFacetField` 

排序 `SortField("price", Type.LONG)` 

分组/聚合 `FacetsCollector + SortedSetDocValuesFacetCounts` 

 

这样就完整展示了 在 Lucene 8.5.0 中如何启用、读取并利用 DocValues 进行排序和聚合 的完整链路。

下面给出一份 可直接拷贝到 IDE 运行的 Lucene 8.5.0 完整示例,演示如何

1. 索引阶段:  
- 用 NumericDocValues 存价格(可排序/聚合)  
- 用 SortedSetDocValuesFacetField 存品牌(可做分面统计)  

2. 查询阶段:  
- 按价格排序  
- 统计品牌分布  
- 钻取(drill-down)过滤后再统计

---

✅ Maven 依赖(lucene-core + lucene-facet)

```xml
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-core</artifactId>
<version>8.5.0</version>
</dependency>
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-facet</artifactId>
<version>8.5.0</version>
</dependency>
```

---

✅ 代码示例

```java
package demo;

import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.*;
import org.apache.lucene.facet.*;
import org.apache.lucene.facet.sortedset.*;
import org.apache.lucene.index.*;
import org.apache.lucene.search.*;
import org.apache.lucene.store.ByteBuffersDirectory;
import org.apache.lucene.store.Directory;

import java.util.List;

public class DocValuesDemo {
public static void main(String[] args) throws Exception {
Directory dir = new ByteBuffersDirectory();
IndexWriterConfig cfg = new IndexWriterConfig(new StandardAnalyzer());
IndexWriter writer = new IndexWriter(dir, cfg);

        FacetsConfig config = new FacetsConfig();   // 必须

        // 准备 3 条测试数据
addDoc(writer, config, "1", 1999, "小米");
addDoc(writer, config, "2", 3999, "苹果");
addDoc(writer, config, "3", 2999, "小米");

        writer.commit();
writer.close();

        /* ---------- 查询 ---------- */
DirectoryReader reader = DirectoryReader.open(dir);
IndexSearcher searcher = new IndexSearcher(reader);

        /* 1. 按价格排序(NumericDocValues) */
Sort sort = new Sort(new SortField("price", SortField.Type.LONG));
TopDocs topDocs = searcher.search(new MatchAllDocsQuery(), 10, sort);
System.out.println("按价格排序:");
for (ScoreDoc sd : topDocs.scoreDocs) {
Document doc = searcher.doc(sd.doc);
System.out.println("id=" + doc.get("id") +
", 价格=" + doc.get("price") +
", 品牌=" + doc.get("brand"));
}

        /* 2. 品牌分面统计(SortedSetDocValuesFacetField) */
SortedSetDocValuesReaderState state =
new DefaultSortedSetDocValuesReaderState(reader);
FacetsCollector fc = new FacetsCollector();
FacetsCollector.search(searcher, new MatchAllDocsQuery(), 10, fc);
Facets facets = new SortedSetDocValuesFacetCounts(state, fc);
FacetResult brandResult = facets.getTopChildren(10, "brand");
System.out.println("\n品牌统计:");
for (LabelAndValue lv : brandResult.labelValues) {
System.out.println(lv.label + " : " + lv.value);
}

        /* 3. 钻取:只看 2010 年出版的书(示例维度) */
DrillDownQuery dq = new DrillDownQuery(config);
dq.add("brand", "小米");          // 钻取小米
FacetsCollector fc2 = new FacetsCollector();
FacetsCollector.search(searcher, dq, 10, fc2);
Facets facets2 = new SortedSetDocValuesFacetCounts(state, fc2);
FacetResult afterDrill = facets2.getTopChildren(10, "brand");
System.out.println("\n钻取后品牌统计:");
System.out.println(afterDrill);

        reader.close();
dir.close();
}

    private static void addDoc(IndexWriter w,
FacetsConfig config,
String id,
int price,
String brand) throws Exception {
Document doc = new Document();
doc.add(new StringField("id", id, Field.Store.YES));                 // 普通字段
doc.add(new NumericDocValuesField("price", price));                  // 数值 DocValues
doc.add(new SortedSetDocValuesFacetField("brand", brand));           // 分面 DocValues
w.addDocument(config.build(doc));                                    // 必须用 FacetsConfig.build
}
}
```

---

✅ 运行结果示例

```
按价格排序:
id=1, 价格=1999, 品牌=小米
id=3, 价格=2999, 品牌=小米
id=2, 价格=3999, 品牌=苹果

品牌统计:
小米 : 2
苹果 : 1

钻取后品牌统计:
dim=brand path=[小米] value=2 childCount=0
```

---

✅ 小结

- NumericDocValues → 排序、范围聚合  
- SortedSetDocValuesFacetField + FacetsConfig → 分面统计、钻取  
- 代码直接基于 Lucene 8.5.0,无需额外 codec 设置。

在 Lucene 中,`doc.get("price")` 返回 `null` 是因为 `price` 字段被定义为 `NumericDocValuesField`,而 `NumericDocValuesField` 不会存储字段值。

 

它只用于 DocValues 索引,供排序、聚合等操作使用,但不会把值存入 `Document`。

 

---

 

✅ 解释

 

- `NumericDocValuesField`  

  - 用途:用于快速排序、聚合等操作,数据存储在 `.dvd` 文件中。  

  - 特点:不存储字段值,只能通过 `IndexSearcher` 的 `DocValues` 接口访问。

 

- `StoredField`  

  - 用途:用于存储字段值,可以在 `Document` 中直接访问。  

  - 特点:数据存储在 `.fdt` 文件中。

 

---

 

✅ 解决方法

 

如果你想在 `Document` 中直接访问字段值,需要同时添加一个 `StoredField`:

 

```java

doc.add(new NumericDocValuesField("price", price)); // 用于 DocValues 索引

doc.add(new StoredField("price", price)); // 用于存储字段值

```

 

---

 

✅ 完整示例

 

```java

import org.apache.lucene.analysis.standard.StandardAnalyzer;

import org.apache.lucene.document.*;

import org.apache.lucene.facet.*;

import org.apache.lucene.facet.sortedset.*;

import org.apache.lucene.index.*;

import org.apache.lucene.search.*;

import org.apache.lucene.store.ByteBuffersDirectory;

 

public class DocValuesDemo {

    public static void main(String[] args) throws Exception {

        Directory dir = new ByteBuffersDirectory();

        IndexWriterConfig cfg = new IndexWriterConfig(new StandardAnalyzer());

        IndexWriter writer = new IndexWriter(dir, cfg);

 

        FacetsConfig config = new FacetsConfig();

 

        // 添加文档

        addDoc(writer, config, "1", 1999, "小米");

        addDoc(writer, config, "2", 3999, "苹果");

        addDoc(writer, config, "3", 2999, "小米");

 

        writer.commit();

        writer.close();

 

        // 打开索引

        DirectoryReader reader = DirectoryReader.open(dir);

        IndexSearcher searcher = new IndexSearcher(reader);

 

        // 按价格排序

        Sort sort = new Sort(new SortField("price", SortField.Type.LONG));

        TopDocs topDocs = searcher.search(new MatchAllDocsQuery(), 10, sort);

 

        System.out.println("按价格排序:");

        for (ScoreDoc sd : topDocs.scoreDocs) {

            Document doc = searcher.doc(sd.doc);

            System.out.println("id=" + doc.get("id") +

                               ", 价格=" + doc.get("price") + // 这里可以访问 price

                               ", 品牌=" + doc.get("brand"));

        }

 

        reader.close();

        dir.close();

    }

 

    private static void addDoc(IndexWriter w,

                               FacetsConfig config,

                               String id,

                               int price,

                               String brand) throws Exception {

        Document doc = new Document();

        doc.add(new StringField("id", id, Field.Store.YES));

        doc.add(new NumericDocValuesField("price", price)); // 用于 DocValues 索引

        doc.add(new StoredField("price", price)); // 用于存储字段值

        doc.add(new SortedSetDocValuesFacetField("brand", brand));

        w.addDocument(config.build(doc));

    }

}

```

 

---

 

✅ 输出结果

 

```

按价格排序:

id=1, 价格=1999, 品牌=小米

id=3, 价格=2999, 品牌=小米

id=2, 价格=3999, 品牌=苹果

```

 

---

 

✅ 总结

 

- `NumericDocValuesField`:用于 DocValues 索引,不存储字段值。  

- `StoredField`:用于存储字段值,可以在 `Document` 中直接访问。  

- 如果需要同时支持 DocValues 索引和字段值访问,需要同时添加这两个字段。

也就是说docvalues是一种索引,不是存内容的只有storefield设为yes才是存原文的

对,你总结得很准确!

 

✅ DocValues 的本质

 

DocValues 是一种索引结构,用于高效地存储和访问列式数据(如数值、字符串等),主要用于以下场景:

 

1. 排序:快速按字段值排序(如价格、日期等)。

2. 聚合:快速计算字段值的统计信息(如最大值、最小值、平均值等)。

3. 分面统计:快速统计字段值的分布(如品牌、类别等)。

 

DocValues 不存储原始文档内容,而是将字段值编码到索引文件中(如 `.dvd` 和 `.dvm` 文件),供快速读取和计算。

 

---

 

✅ `StoredField` 的作用

 

`StoredField` 用于存储原始字段值,以便在检索时可以直接访问这些值。它的特点如下:

 

- 存储原始值:将字段值存储在 `.fdt` 文件中。

- 可直接访问:通过 `Document` 对象访问存储的字段值。

- 不参与索引:不会影响搜索、排序或聚合操作。

 

---

 

✅ 对比

 

特性 DocValues StoredField 

用途 索引结构,用于排序、聚合、分面统计 存储结构,用于存储原始字段值 

存储位置 `.dvd` 和 `.dvm` 文件 `.fdt` 文件 

是否可直接访问 不可直接访问,需通过 `DocValues` 接口 可直接通过 `Document` 访问 

是否参与索引 参与索引,用于快速读取和计算 不参与索引,仅存储原始值 

 

---

 

✅ 示例代码

 

```java

import org.apache.lucene.analysis.standard.StandardAnalyzer;

import org.apache.lucene.document.*;

import org.apache.lucene.index.*;

import org.apache.lucene.search.*;

import org.apache.lucene.store.ByteBuffersDirectory;

 

public class DocValuesDemo {

    public static void main(String[] args) throws Exception {

        Directory dir = new ByteBuffersDirectory();

        IndexWriterConfig cfg = new IndexWriterConfig(new StandardAnalyzer());

        IndexWriter writer = new IndexWriter(dir, cfg);

 

        // 添加文档

        Document doc1 = new Document();

        doc1.add(new StringField("id", "1", Field.Store.YES));

        doc1.add(new NumericDocValuesField("price", 1999)); // DocValues 索引

        doc1.add(new StoredField("price", 1999)); // 存储原始值

        writer.addDocument(doc1);

 

        Document doc2 = new Document();

        doc2.add(new StringField("id", "2", Field.Store.YES));

        doc2.add(new NumericDocValuesField("price", 3999)); // DocValues 索引

        doc2.add(new StoredField("price", 3999)); // 存储原始值

        writer.addDocument(doc2);

 

        writer.commit();

        writer.close();

 

        // 打开索引

        DirectoryReader reader = DirectoryReader.open(dir);

        IndexSearcher searcher = new IndexSearcher(reader);

 

        // 按价格排序

        Sort sort = new Sort(new SortField("price", SortField.Type.LONG));

        TopDocs topDocs = searcher.search(new MatchAllDocsQuery(), 10, sort);

 

        System.out.println("按价格排序:");

        for (ScoreDoc sd : topDocs.scoreDocs) {

            Document doc = searcher.doc(sd.doc);

            System.out.println("id=" + doc.get("id") +

                               ", 价格=" + doc.get("price")); // 可以直接访问存储的字段值

        }

 

        reader.close();

        dir.close();

    }

}

```

 

---

 

✅ 输出结果

 

```

按价格排序:

id=1, 价格=1999

id=2, 价格=3999

```

 

---

 

✅ 总结

 

- DocValues:用于索引,支持高效排序、聚合和分面统计,但不存储原始值。

- StoredField:用于存储原始字段值,可在检索时直接访问,但不参与索引。

 

通过同时使用 `NumericDocValuesField` 和 `StoredField`,可以兼顾索引性能和原始值访问的需求。

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

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

相关文章

IntelliJ IDEA 配置 Maven 阿里云镜像加速源全流程

1. 为什么要加国内镜像源&#xff1f;国内网络访问 Maven 中央仓库经常超时、依赖下载极慢或失败。配置阿里云等国内镜像后&#xff0c;Java 项目依赖下载飞快&#xff0c;极大提升开发效率&#xff0c;是中国开发者必做优化&#xff01;2. 添加阿里云镜像源的步骤&#xff08;…

【worklist】worklist的hl7、dicom是什么关系

HL7和DICOM在Worklist系统中是互补的关系&#xff0c;它们各自承担不同的角色&#xff0c;但协同工作以实现完整的医疗信息系统集成。HL7与DICOM Worklist的关系1. 功能分工DICOM Worklist (Modality Worklist - MWL)主要用于影像设备获取患者和检查信息基于DICOM协议&#xff…

位运算-面试题01.01.判定字符是否唯一-力扣(LeetCode)

一、题目解析1、s[i]仅包含小写字母2、字符串的长度为[0&#xff0c;100]二、算法原理解法1&#xff1a;哈希表用哈希表记录s[i]的字符&#xff0c;如果有重复的&#xff0c;则返回false优化1&#xff1a;由于s[i]中只有小写字母&#xff0c;所以可以创建一个int hash[26]的数组…

wsl /lib/x86_64-linux-gnu/libc.so.6: version GLIBC_2.28‘ not found

遇到的问题并没有解决&#xff0c;这个 glibc-2.28 应该是安装好了 Ubuntu18 问题描述&#xff1a;Ubuntu18 WSL 无法启动 VS Code &#xff0c;因为node版本问题 rootUbuntu18:~# code . /lib/x86_64-linux-gnu/libc.so.6: version GLIBC_2.28 not found (required by /root…

Windows系统ffmpeg.dll丢失怎么办?从错误分析到永久修复的完整流程

您是否遇到过这样的情况&#xff1a;打开心爱的视频编辑软件时&#xff0c;系统突然提示无法启动此程序&#xff0c;因为计算机中丢失ffmpeg.dll&#xff1f;别担心&#xff0c;这个问题比您想象的要常见得多。作为专业的技术支持团队&#xff0c;我们已经帮助数千用户解决了类…

LaTeX 复杂图形绘制教程:从基础到进阶

系列文章目录 第一章&#xff1a;深入了解 LaTeX&#xff1a;科技文档排版的利器 第二章&#xff1a;LaTeX 下载安装保姆级教程 第三章&#xff1a;LaTeX 创建工程并生成完整文档指南 第四章&#xff1a;LaTeX 表格制作全面指南 文章目录系列文章目录前言一、​LaTeX 绘图工具…

用 Go Typed Client 快速上手 Elasticsearch —— 从建索引到聚合的完整实战

1. 准备工作 go get github.com/elastic/go-elasticsearch/v9小贴士 如果你的集群启用了安全特性&#xff0c;需要在 elasticsearch.Config 中配置 Username/Password 或 APIKey。Typed Client 通过 NewTypedClient 创建&#xff0c;内部复用 *http.Client&#xff0c;建议全局…

《义龙棒球科普》棒球是韩国的国球吗·棒球1号位

⚾ Why Baseball is Koreas NATIONAL SPORT? | KBO热血全解析 ⚾⚾ 1. 历史根源 & 情感纽带 Historical Roots & Emotional Bond美军引入 (1945后): 战后美军将棒球带入韩国&#xff0c;迅速扎根&#xff01;✨1982 KBO成立: 亚洲第二个职业棒球联盟诞生&#xff01;奥…

三坐标测量机路径规划与补偿技术:如何用算法看见微米级误差?

三坐标测量的微米级精度背后&#xff0c;是精密的路径规划算法与实时补偿技术在保驾护航。三坐标测量机的智能避撞算法保障了测量的安全与高效&#xff1b;温度补偿技术消除了环境的无形干扰&#xff1b;点云智能处理则让海量数据蜕变为精准的工程决策依据。 “智能避让路径”&…

Docker设置容器时间

一、前言前言&#xff1a;容器搭建好之后&#xff0c;容器的默认时区于本地时区不一致&#xff0c;这将导致日志文件中保存的时间为错误时间。二、操作1、进入docker 容器docker exec -it <容器名称> bash2、选择时区tzselect3、配置时区根据跳出来的配置选择Asia -> …

德国威乐集团亚太中东非洲PMO负责人和继明受邀为PMO大会主持人

全国PMO专业人士年度盛会德国威乐集团亚太中东非洲PMO负责人 和继明先生 受邀为“PMO评论”主办的2025第十四届中国PMO大会主持人&#xff0c;敬请关注&#xff01;嘉宾介绍&#xff1a;和继明先生&#xff0c;德国威乐集团亚太中东非洲PMO负责人&#xff0c;项目管理硕士MPM&a…

idea 集成飞算Java AI 教程

idea 集成飞算Java AI 教程一、介绍二、下载安装 飞算Java AI 插件方式一&#xff1a;从idea插件市场安装方式二&#xff1a;下载离线安装包三、飞算Java AI插件使用一、介绍 ​ 随着人工智能技术的发展&#xff0c;AI 编程助手逐渐成为提升开发效率的强大工具。上一篇教程是i…

2025.8.1

代码练习 //用指针访问对象成员 #include<iostream> #include<string> using namespace std; class Champion { public:Champion(int id, string nm, int hp, int mn, int dmg) {ID id;name nm;HP hp;mana mn;damage dmg;}void attack(Champion& chmp) {c…

一种新的分布式ID生成方案--ULID

一种新的分布式ID生成方案 ULID: 一种新的分布式ID生成方案 ULID (Universally Unique Lexicographically Sortable Identifier) 是一种较新的分布式ID生成方案&#xff0c;旨在解决传统UUID和雪花算法(Snowflake)的一些局限性。ULID的主要特点 可排序性&#xff1a;ULID按生成…

服务器中涉及节流(Throttle)的硬件组件及其应用注意事项

服务器中涉及节流&#xff08;Throttle&#xff09;的硬件组件及其应用注意事项 在服务器硬件中&#xff0c;“节流”&#xff08;throttling&#xff09;是一种保护机制&#xff0c;当组件温度过高、功耗过大或超出安全阈值时&#xff0c;系统会自动降低性能&#xff08;如时钟…

GitPython07-源码解读

GitPython07-源码解读1 1-核心知识 1&#xff09;从核心代码的第一行作为突破口2&#xff09;从Repo.init方法入手做追踪3&#xff09;subprocess到底做了什么&#xff1f;gitPython是不是执行的脚本&#xff0c;最终还是通过subprocess做到的4&#xff09;代码中貌似并没有实…

Java继承机制详解:从原理到实战应用

一、继承的本质&#xff1a;消除冗余&#xff0c;构建逻辑关系想象一个公司管理系统&#xff1a;普通销售员工&#xff08;CommissionEmployee&#xff09;和带底薪销售员工&#xff08;BasePlusCommissionEmployee&#xff09;共享大部分属性&#xff08;姓名、工号、销售额、…

工业数采引擎-DTU

DTU配置注册包及心跳包(对应设备配置->设备SN)&#xff0c;模块工作方式&#xff1a;TcpClient&#xff0c;首次连接成功后&#xff0c;DTU发送上来的注册包作为链路SessionId1. ModbusRtu设备 -> Dtu -> Server2. DLT645设备 -> Dtu -> Server3. 自定义设备 -&…

AttributeError: ChatGLMTokenizer has no attribute vocab_size

请问运行下面语句tokenizer AutoTokenizer.from_pretrained("THUDM/chatglm-6b", trust_remote_codeTrue) 出现“AttributeError: ChatGLMTokenizer has no attribute vocab_size”是版本不一致&#xff0c;需要旧的版本卸载conda uninstall transformers或者pip un…

14.串口更新FLASH字库

一、简介 在使用STM32等单片机驱动显示屏时&#xff0c;为了显示中文字体&#xff0c;常用FLASH保存字库信息。但是字库的更新通常只能使用SD卡更新&#xff0c;在一些小型单片机系统(如STM32F103C8T6、STC89C52)上&#xff0c;没有增加SD卡支持的必要。为解决此问题&#xff0…