☰
使用 Lucene 搜索你的 Bean — 映射
2026/10/8 6:05:38 网站建设 项目流程

作者:来自 Elastic David Pilato

本教程将Apache Lucene作为进程内搜索索引嵌入,用于搜索领域 Bean —— 这里使用的是 Rekordbox 风格音乐库中的Track记录。同样的模式也适用于任何 Java Bean。

Lucene 作为一个派生缓存位于你的对象旁边,而不是事实来源。将数据库作为系统的权威记录:将 Bean 映射为Document,执行搜索,将命中的 id 与原始列表关联起来,并在成功写入后重新构建或 upsert Lucene。这篇文章只介绍映射 —— 先介绍 analyzer,然后介绍字段。

将 Lucene 添加到 Maven

一个项目,两个 artifact,使用相同版本。实现时,请在 Maven Central 上查找最新的稳定 Lucene 版本;本系列使用10.5.1。

<!-- Index, search, documents, queries --> <dependency> <groupId>org.apache.lucene</groupId> <artifactId>lucene-core</artifactId> <version>10.5.1</version> </dependency> <!-- Tokenizers / filters --> <dependency> <groupId>org.apache.lucene</groupId> <artifactId>lucene-analysis-common</artifactId> <version>10.5.1</version> </dependency>

Lucene 是纯 Java:它可以被打包到 fat-jar 中,无需任何本地库。

从你现有的 Bean 开始

补充现有 Bean 的 Java 代码翻译并补全后续示例内容说明 fat-jar 和本地库

public record Track( String id, String title, Artist artist, Genre genre, MusicalKey key, double bpm, int ratingStars, int year // … album, comment, paths, … ) {}

为需要查找文档的内容建立索引;完整的 Bean 保存在其他地方,并在搜索后通过 id 进行关联。

  • 稳定的 id—Track.id,用于 upsert 和删除。

  • 全文搜索— 用户输入的字符串(title、artist)。

  • 过滤 / 范围查询— 精确的 keyword 或数值(genre、rating、bpm、year)。

选择 analyzer

Around The World经过 StandardTokenizer → LowerCaseFilter → ASCIIFoldingFilter。

对于TextField,analyzer 会在索引时运行,并且应该与查询时生成的 token 保持一致:

补充查询时的 analyzer 示例说明各个 token filter 的作用完善 Java 代码示例

Analyzer analyzer = new Analyzer() { @Override protected TokenStreamComponents createComponents(String fieldName) { Tokenizer source = new StandardTokenizer(); TokenStream filter = new LowerCaseFilter(source); filter = new ASCIIFoldingFilter(filter); return new TokenStreamComponents(source, filter); } }; // Analyze a text TokenStream ts = analyzer.tokenStream("title", "Around The World");

不进行 stemming(artist 名称保持完整),不使用停用词(Around The World仍然可以被搜索)。ASCII folding 会将café/François转换为cafe/francois:

Café del Mar — Around The World (François Kevorkian Mix)— tokenizer → lowercase → ASCII folding;重音符号会在最后一个阶段进行折叠。

最终的 token 会以排序后的形式进入索引(around、cafe、del……)——就像一本书最后的索引一样。字母顺序让人们无需阅读每一页就能快速找到某个词条;Lucene 采用了相同的思路,因此查找时可以直接跳转到所需的 term,而不是扫描整个词典。

在输入和输出时使用相同的analyzer。

将 Bean 映射为 LuceneDocument

补充完整 Bean 映射示例统一术语与格式风格澄清 analyzer 的使用时机

选择一条 track;Lucene 会存储一个可用于搜索的Document(TextField / StringField / 数值字段)。

模式示例Lucene 类型
分析后的文本title、artistTextField
精确 keywordid、genre.rawStringField
数值bpm、rating、yearDoubleField/IntField

TextField会进行 token 化(用于搜索)。StringField不会进行 token 化(用于 id、过滤条件)。数值字段用于范围过滤和排序——暂时还不用于直方图。存储你需要用来呈现匹配结果的数据(Field.Store.YES);无论如何都要存储 id。

这就是一个可用于搜索的Document:

Document doc = new Document(); // stored join key back to the Track bean doc.add(new StringField("id", "172523747", Store.YES)); // title: TextField is analyzed (MUST). .raw keeps the original for display. .raw.normalized is the exact FILTER. doc.add(new TextField("title", "Around The World", Store.YES)); doc.add(new StringField("title.raw", "Around The World", Store.YES)); doc.add(new StringField("title.raw.normalized", "around the world", Store.YES)); // artist: TextField is analyzed (MUST). .raw keeps the original for display. .raw.normalized is the exact FILTER. doc.add(new TextField("artist", "Daft Punk", Store.YES)); doc.add(new StringField("artist.raw", "Daft Punk", Store.YES)); doc.add(new StringField("artist.raw.normalized", "daft punk", Store.YES)); // genre: analyzed text + keyword FILTER (.raw.normalized) doc.add(new TextField("genre", "Club", Store.YES)); doc.add(new StringField("genre.raw", "Club", Store.YES)); doc.add(new StringField("genre.raw.normalized", "club", Store.YES)); // numeric range / sort. numericValue() is IEEE 754 bits; read storedValue().getDoubleValue() doc.add(new DoubleField("bpm", 121.29, Store.YES)); // Camelot key — exact FILTER / MUST_NOT (lowercased) doc.add(new StringField("key.code", "9a", Store.YES)); // rating: numeric filter / sort doc.add(new IntField("rating", 5, Store.YES)); // year: numeric filter / sort doc.add(new IntField("year", 1997, Store.YES)); // album: analyzed free text only — no keyword twin doc.add(new TextField("album", "", Store.YES)); // label: analyzed free text only — no keyword twin doc.add(new TextField("label", "", Store.YES)); // comment: analyzed free text only — no keyword twin doc.add(new TextField("comment", "09A - Energy 7", Store.YES))

完整演示代码位于 GitHub:lucene-search-tracks。

原文:Search your beans with Lucene — Mapping | David Pilato

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询