MultiFieldQueryParser

本文介绍了Lucene中多字段查询解析的方法,通过指定不同字段的查询字符串、搜索字段及布尔标志来构造复杂的查询条件。例如,可以设置某些字段为必须包含(and),某些字段为禁止包含(not),以及其他字段为可选包含(or)。这种方式适用于需要对多个字段进行精确控制的场景。
部署运行你感兴趣的模型镜像

摘自lucene官网

public static Query parse(String[] queries,
          String[] fields,
          BooleanClause.Occur[] flags,
          Analyzer analyzer)
                   throws ParseException
Parses a query, searching on the fields specified. Use this if you need to specify certain fields as required, and others as prohibited.

Usage:

String[] query ={"query1","query2","query3"};String[] fields ={"filename","contents","description"};BooleanClause.Occur[] flags ={BooleanClause.Occur.SHOULD,BooleanClause.Occur.MUST,BooleanClause.Occur.MUST_NOT};MultiFieldQueryParser.parse(query, fields, flags, analyzer);

The code above would construct a query:

 
 (filename:query1) +(contents:query2) -(description:query3)
 
 
Parameters:
queries - Queries string to parse
fields - Fields to search on
flags - Flags describing the fields
analyzer - Analyzer to use
Throws:
ParseException - if query parsing fails
IllegalArgumentException - if the length of the queries, fields, and flags array differ
/**
 * BooleanClause.Occur.MUST表示and
 * BooleanClause.Occur.MUST_NOT表示not
 * BooleanClause.Occur.SHOULD表示or
 */

您可能感兴趣的与本文相关的镜像

Stable-Diffusion-3.5

Stable-Diffusion-3.5

图片生成
Stable-Diffusion

Stable Diffusion 3.5 (SD 3.5) 是由 Stability AI 推出的新一代文本到图像生成模型,相比 3.0 版本,它提升了图像质量、运行速度和硬件效率

<think> 我们使用Spring Boot 3整合Lucene和MyBatis-Flex来实现全文检索功能。Lucene是一个强大的全文搜索引擎库,我们将用它来创建索引和进行搜索。MyBatis-Flex是一个灵活的MyBatis增强框架,用于数据库操作。 步骤: 1. 创建Spring Boot项目并添加依赖。 2. 配置MyBatis-Flex和数据源。 3. 创建实体类和Mapper(使用MyBatis-Flex注解)。 4. 集成Lucene,创建索引工具类和搜索工具类。 5. 在服务层实现索引的创建和搜索。 6. 控制器层暴露API。 注意:由于Lucene本身不是分布式,这里我们使用单个索引示例。 一、添加依赖(pom.xml) ```xml <dependencies> <!-- Spring Boot Starter Web --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- MyBatis-Flex --> <dependency> <groupId>com.mybatis-flex</groupId> <artifactId>mybatis-flex-spring-boot-starter</artifactId> <version>1.7.5</version> </dependency> <!-- 数据库驱动(以MySQL为例) --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <!-- Lucene --> <dependency> <groupId>org.apache.lucene</groupId> <artifactId>lucene-core</artifactId> <version>9.8.0</version> </dependency> <dependency> <groupId>org.apache.lucene</groupId> <artifactId>lucene-queryparser</artifactId> <version>9.8.0</version> </dependency> <dependency> <groupId>org.apache.lucene</groupId> <artifactId>lucene-analyzers-common</artifactId> <version>8.11.2</version> <!-- 注意:9.x版本中analyzers-common已经合并到lucene-core,但这里我们使用8.x的common包,因为9.x之后分词器有改动,常用分词器在9.x中可能还未更新 --> </dependency> <!-- 其他可能需要的依赖,比如lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> </dependencies> ``` 注意:Lucene 9.x版本中,一些分词器已经迁移到不同的模块,如果你需要使用IKAnalyzer等第三方分词器,需要单独引入。这里我们使用标准分词器(StandardAnalyzer),它在lucene-core中已经包含。 二、配置数据源和MyBatis-Flex application.yml: ```yaml spring: datasource: url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8 username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver mybatis-flex: mapper-locations: classpath*:/mapper/*.xml configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 可选,打印SQL ``` 三、创建实体类和Mapper 实体类(Article.java): ```java import com.mybatisflex.annotation.Column; import com.mybatisflex.annotation.Id; import com.mybatisflex.annotation.Table; @Table("article") public class Article { @Id private Long id; private String title; private String content; // getter和setter省略,可以使用lombok } ``` Mapper接口(ArticleMapper.java): ```java import com.mybatisflex.core.BaseMapper; import com.example.demo.entity.Article; public interface ArticleMapper extends BaseMapper<Article> { } ``` 四、Lucene工具类 创建索引工具类(LuceneIndexUtil.java)和搜索工具类(LuceneSearchUtil.java)。我们将索引存储在文件系统中,例如在项目的index目录下。 首先,配置索引存放路径(可以在application.yml中自定义,这里我们写死为"./index")。 创建索引工具类: ```java import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.standard.StandardAnalyzer; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; import org.apache.lucene.document.TextField; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.index.IndexWriterConfig; import org.apache.lucene.store.Directory; import org.apache.lucene.store.FSDirectory; import org.springframework.stereotype.Component; import java.io.IOException; import java.nio.file.Paths; import java.util.List; @Component public class LuceneIndexUtil { private final String indexDir = "./index"; /** * 创建索引 * @param articles 文章列表 */ public void createIndex(List<Article> articles) throws IOException { // 索引存放路径 Directory directory = FSDirectory.open(Paths.get(indexDir)); // 标准分词器 Analyzer analyzer = new StandardAnalyzer(); IndexWriterConfig config = new IndexWriterConfig(analyzer); config.setOpenMode(IndexWriterConfig.OpenMode.CREATE); // 每次都重新创建 IndexWriter indexWriter = new IndexWriter(directory, config); for (Article article : articles) { Document doc = new Document(); doc.add(new TextField("id", article.getId().toString(), Field.Store.YES)); doc.add(new TextField("title", article.getTitle(), Field.Store.YES)); doc.add(new TextField("content", article.getContent(), Field.Store.YES)); indexWriter.addDocument(doc); } indexWriter.close(); directory.close(); } } ``` 搜索工具类: ```java import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.standard.StandardAnalyzer; import org.apache.lucene.document.Document; import org.apache.lucene.index.DirectoryReader; import org.apache.lucene.index.IndexReader; import org.apache.lucene.queryparser.classic.ParseException; import org.apache.lucene.queryparser.classic.QueryParser; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; import org.apache.lucene.search.ScoreDoc; import org.apache.lucene.search.TopDocs; import org.apache.lucene.store.Directory; import org.apache.lucene.store.FSDirectory; import org.springframework.stereotype.Component; import java.io.IOException; import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; @Component public class LuceneSearchUtil { private final String indexDir = "./index"; public List<String> search(String field, String keyword) throws IOException, ParseException { Directory directory = FSDirectory.open(Paths.get(indexDir)); IndexReader reader = DirectoryReader.open(directory); IndexSearcher searcher = new IndexSearcher(reader); Analyzer analyzer = new StandardAnalyzer(); QueryParser parser = new QueryParser(field, analyzer); Query query = parser.parse(keyword); TopDocs topDocs = searcher.search(query, 10); // 取前10个 ScoreDoc[] scoreDocs = topDocs.scoreDocs; List<String> results = new ArrayList<>(); for (ScoreDoc scoreDoc : scoreDocs) { Document doc = searcher.doc(scoreDoc.doc); results.add(doc.get("title") + ": " + doc.get("content")); } reader.close(); directory.close(); return results; } } ``` 五、服务层 ArticleService.java: ```java import com.example.demo.entity.Article; import com.example.demo.mapper.ArticleMapper; import com.example.demo.util.LuceneIndexUtil; import com.example.demo.util.LuceneSearchUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.io.IOException; import java.util.List; @Service public class ArticleService { @Autowired private ArticleMapper articleMapper; @Autowired private LuceneIndexUtil luceneIndexUtil; @Autowired private LuceneSearchUtil luceneSearchUtil; public void createIndex() throws IOException { List<Article> articles = articleMapper.selectAll(); luceneIndexUtil.createIndex(articles); } public List<String> search(String keyword) throws Exception { // 这里我们对标题和内容都搜索,所以使用"content"字段,也可以使用多字段查询,但这里简单起见只搜索内容 return luceneSearchUtil.search("content", keyword); } } ``` 六、控制器层 ArticleController.java: ```java import com.example.demo.service.ArticleService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.List; @RestController public class ArticleController { @Autowired private ArticleService articleService; @GetMapping("/createIndex") public String createIndex() { try { articleService.createIndex(); return "索引创建成功"; } catch (Exception e) { e.printStackTrace(); return "索引创建失败"; } } @GetMapping("/search") public List<String> search(@RequestParam String keyword) { try { return articleService.search(keyword); } catch (Exception e) { e.printStackTrace(); return null; } } } ``` 七、启动类 ```java import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ``` 八、测试 1. 启动项目。 2. 访问`http://localhost:8080/createIndex`创建索引。 3. 访问`http://localhost:8080/search?keyword=spring`进行搜索。 注意:上述代码中,索引存储在当前运行目录下的index文件夹中。每次调用createIndex都会重新创建整个索引。 优化点: 1. 索引增量更新:可以使用IndexWriterConfig.OpenMode.CREATE_OR_APPEND,并记录最后更新时间。 2. 使用更合适的分词器(如IKAnalyzer中文分词)。 3. 多字段查询(可以使用MultiFieldQueryParser)。 4. 分页查询。 这里只是一个基础示例。 §§相关问题§§: 1. Lucene索引的增量更新如何实现? 2. 如何集成中文分词器(如IKAnalyzer)到Lucene? 3. 在Lucene中如何实现多字段同时检索? 4. 如何对搜索结果进行分页? </think> # Spring Boot3 + Lucene + MyBatis-Flex 构建全文检索方案 下面我将详细讲解如何使用Spring Boot 3整合Lucene和MyBatis-Flex构建全文检索系统。 ## 整体方案设计 ### 技术栈 - **Spring Boot 3**:基础框架 - **Lucene 9.x**:全文搜索引擎库 - **MyBatis-Flex**:高性能ORM框架 - **IK Analyzer**:中文分词器 - **MySQL**:数据存储 ### 功能模块 1. 实体类映射数据库表 2. Lucene索引创建与维护 3. 全文检索服务实现 4. RESTful API接口 ## 实现步骤 ### 1. 添加依赖 (pom.xml) ```xml <dependencies> <!-- Spring Boot Starter --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- MyBatis-Flex --> <dependency> <groupId>com.mybatis-flex</groupId> <artifactId>mybatis-flex-spring-boot-starter</artifactId> <version>1.7.5</version> </dependency> <!-- MySQL Driver --> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <scope>runtime</scope> </dependency> <!-- Lucene Core --> <dependency> <groupId>org.apache.lucene</groupId> <artifactId>lucene-core</artifactId> <version>9.8.0</version> </dependency> <!-- Lucene Query Parser --> <dependency> <groupId>org.apache.lucene</groupId> <artifactId>lucene-queryparser</artifactId> <version>9.8.0</version> </dependency> <!-- IK Analyzer (中文分词) --> <dependency> <groupId>com.github.magese</groupId> <artifactId>ik-analyzer</artifactId> <version>8.5.0</version> </dependency> </dependencies> ``` ### 2. 数据模型与Mapper #### 实体类 (Article.java) ```java import com.mybatisflex.annotation.Column; import com.mybatisflex.annotation.Id; import com.mybatisflex.annotation.Table; @Table("articles") public class Article { @Id private Long id; @Column("title") private String title; @Column("content") private String content; @Column("create_time") private LocalDateTime createTime; // getters and setters } ``` #### Mapper接口 (ArticleMapper.java) ```java import com.mybatisflex.core.BaseMapper; import org.apache.ibatis.annotations.Mapper; @Mapper public interface ArticleMapper extends BaseMapper<Article> { } ``` ### 3. Lucene服务实现 #### Lucene配置类 (LuceneConfig.java) ```java import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.store.Directory; import org.apache.lucene.store.FSDirectory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.wltea.analyzer.lucene.IKAnalyzer; import java.io.IOException; import java.nio.file.Paths; @Configuration public class LuceneConfig { @Bean public Directory directory() throws IOException { // 存储索引的目录 return FSDirectory.open(Paths.get("lucene-index")); } @Bean public Analyzer analyzer() { // 使用IK中文分词器 return new IKAnalyzer(); } } ``` #### Lucene索引服务 (LuceneIndexService.java) ```java import lombok.RequiredArgsConstructor; import org.apache.lucene.document.*; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.index.IndexWriterConfig; import org.apache.lucene.index.Term; import org.springframework.stereotype.Service; import java.io.IOException; import java.util.List; @Service @RequiredArgsConstructor public class LuceneIndexService { private final Directory directory; private final Analyzer analyzer; // 添加文档到索引 public void addDocument(Article article) throws IOException { try (IndexWriter writer = new IndexWriter(directory, new IndexWriterConfig(analyzer))) { Document doc = new Document(); doc.add(new StringField("id", article.getId().toString(), Field.Store.YES)); doc.add(new TextField("title", article.getTitle(), Field.Store.YES)); doc.add(new TextField("content", article.getContent(), Field.Store.YES)); writer.addDocument(doc); } } // 更新索引中的文档 public void updateDocument(Article article) throws IOException { try (IndexWriter writer = new IndexWriter(directory, new IndexWriterConfig(analyzer))) { Document doc = new Document(); doc.add(new StringField("id", article.getId().toString(), Field.Store.YES)); doc.add(new TextField("title", article.getTitle(), Field.Store.YES)); doc.add(new TextField("content", article.getContent(), Field.Store.YES)); writer.updateDocument(new Term("id", article.getId().toString()), doc); } } // 删除索引中的文档 public void deleteDocument(Long id) throws IOException { try (IndexWriter writer = new IndexWriter(directory, new IndexWriterConfig(analyzer))) { writer.deleteDocuments(new Term("id", id.toString())); } } // 批量创建索引 public void batchIndex(List<Article> articles) throws IOException { try (IndexWriter writer = new IndexWriter(directory, new IndexWriterConfig(analyzer))) { for (Article article : articles) { Document doc = new Document(); doc.add(new StringField("id", article.getId().toString(), Field.Store.YES)); doc.add(new TextField("title", article.getTitle(), Field.Store.YES)); doc.add(new TextField("content", article.getContent(), Field.Store.YES)); writer.addDocument(doc); } writer.commit(); } } } ``` #### 全文检索服务 (SearchService.java) ```java import lombok.RequiredArgsConstructor; import org.apache.lucene.document.Document; import org.apache.lucene.index.DirectoryReader; import org.apache.lucene.index.IndexReader; import org.apache.lucene.queryparser.classic.MultiFieldQueryParser; import org.apache.lucene.queryparser.classic.QueryParser; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; import org.apache.lucene.search.ScoreDoc; import org.apache.lucene.search.TopDocs; import org.springframework.stereotype.Service; import java.io.IOException; import java.util.ArrayList; import java.util.List; @Service @RequiredArgsConstructor public class SearchService { private final Directory directory; private final Analyzer analyzer; public List<Document> search(String keyword, int maxResults) throws Exception { List<Document> results = new ArrayList<>(); try (IndexReader reader = DirectoryReader.open(directory)) { IndexSearcher searcher = new IndexSearcher(reader); // 在title和content字段中搜索 String[] fields = {"title", "content"}; QueryParser parser = new MultiFieldQueryParser(fields, analyzer); Query query = parser.parse(keyword); TopDocs topDocs = searcher.search(query, maxResults); for (ScoreDoc scoreDoc : topDocs.scoreDocs) { Document doc = searcher.doc(scoreDoc.doc); results.add(doc); } } return results; } } ``` ### 4. 业务逻辑层与控制器 #### 文章服务 (ArticleService.java) ```java import com.github.yulichang.wrapper.MPJLambdaWrapper; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; @Service @RequiredArgsConstructor public class ArticleService { private final ArticleMapper articleMapper; private final LuceneIndexService indexService; @Transactional public void addArticle(Article article) throws IOException { articleMapper.insert(article); indexService.addDocument(article); } @Transactional public void updateArticle(Article article) throws IOException { articleMapper.updateById(article); indexService.updateDocument(article); } @Transactional public void deleteArticle(Long id) throws IOException { articleMapper.deleteById(id); indexService.deleteDocument(id); } // 初始化所有文章的索引 public void initAllIndex() throws IOException { List<Article> articles = articleMapper.selectAll(); indexService.batchIndex(articles); } } ``` #### 搜索控制器 (SearchController.java) ```java import lombok.RequiredArgsConstructor; import org.apache.lucene.document.Document; import org.springframework.web.bind.annotation.*; import java.util.List; @RestController @RequestMapping("/search") @RequiredArgsConstructor public class SearchController { private final SearchService searchService; @GetMapping public List<Document> search(@RequestParam String keyword, @RequestParam(defaultValue = "10") int size) { try { return searchService.search(keyword, size); } catch (Exception e) { throw new RuntimeException("搜索失败", e); } } } ``` ### 5. 索引初始化与维护 在应用启动时初始化索引: ```java import jakarta.annotation.PostConstruct; import lombok.RequiredArgsConstructor; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication @RequiredArgsConstructor public class Application { private final ArticleService articleService; public static void main(String[] args) { SpringApplication.run(Application.class, args); } @PostConstruct public void init() { try { articleService.initAllIndex(); } catch (Exception e) { System.err.println("索引初始化失败: " + e.getMessage()); } } } ``` ## 代码解释 ### 关键组件说明 1. **Lucene索引服务**: - 负责文档的增删改操作 - 使用IKAnalyzer进行中文分词 - 索引存储在文件系统的"lucene-index"目录 2. **全文检索服务**: - 使用MultiFieldQueryParser在多个字段中搜索 - 支持分页(通过maxResults参数) - 返回包含匹配文档的列表 3. **MyBatis-Flex整合**: - 简化数据库操作 - 提供高效的ORM映射 - 支持事务管理 ### 索引维护策略 1. **初始化索引**:应用启动时全量同步 2. **实时同步**: - 新增文章时自动创建索引 - 更新文章时更新索引 - 删除文章时删除索引 3. **定期间隔**:可添加定时任务增量更新索引 ## 性能优化建议 1. **索引优化**: - 定期使用`IndexWriter.forceMerge()`减少索引段数量 - 启用复合文件格式减少文件数量 2. **搜索优化**: - 使用`BooleanQuery`组合多个查询条件 - 缓存常用查询结果 3. **并发处理**: - 使用`NIOFSDirectory`替代`FSDirectory`提高并发性能 - 实现读写锁机制
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值