概述
在开发数据库应用时,快速高效的文本搜索功能常常必不可少。为了提供更友好的用户体验,这些搜索还应支持全文和部分匹配。MongoDB提供了多种方法来利用文本搜索找到相关文档。
本文将深入探讨MongoDB中的文本搜索,包括其特性、使用方法以及如何充分利用它。我们还将创建一个DB客户端,设置文本索引,并演示全文和部分文本搜索的不同方式。
1. MongoDB中的文本搜索
虽然文本搜索查询是一种强大工具,但它们需要特定的设置。为此,我们需要在集合上创建一个文本索引。索引就像特殊的文件夹,包含每个文档中的一小部分信息,与实际文档分开存储。MongoDB允许用户创建不同类型(参阅文档)的索引。
幸运的是,MongoDB提供了专门设计用于在字符串内容内进行搜索的文本索引。这些索引灵活且可以涵盖多个字段,从而实现全面的搜索。此外,它们有助于数据库更快地浏览集合。
首先,通过指定连接字符串、数据库名和集合名创建一个DB客户端:
@Before
public static void setup() {
if (mongoClient == null) {
mongoClient = MongoClients.create("mongodb://localhost:27017");
database = mongoClient.getDatabase("baeldung");
collection = database.getCollection("user");
}
}
接下来,我们在集合的某个字段上创建文本索引:
void createTextIndex(String field) {
IndexOptions indexOptions = new IndexOptions();
indexOptions.name("textIndex").unique(false).background(true);
collection.createIndex(Indexes.text(field), indexOptions);
}
注意:一个集合只能有一个专门用于文本搜索的索引。
2. 全文搜索
简单的全文搜索操作相当直接。我们可以输入关键词或短语,系统会找出包含这些精确词语的文档。
除了基本的全文搜索,还有几种执行全文搜索的方式,各有优劣,适用于不同的场景。常见的方法包括布尔全文搜索、短语搜索和位置搜索。
让我们创建一个执行文本搜索查询的方法:
List<Document> searchUser(String query) {
Document result = new Document("$text", new Document("$search", name));
return collection.find(result).into(new ArrayList<>());
}
$text
在已用文本索引索引过的字段内容上执行文本搜索,而$search
定义要查找的目标文本。$text
会使用空格对搜索字符串进行分词,并对搜索字符串中的所有分词进行逻辑或运算。这意味着搜索“Java Spring”时,会找到包含“Java”、“Spring”或两者都有的所有文档。
我们还可以创建一些记录来探索全文搜索功能:
@Test
void whenSearchingUserWithFullText_thenCorrectCountReturned() {
// WHEN
insertUser("Leonel", "Java Spring");
insertUser("John", "Java Spring MongoDB");
insertUser("Smith", "Java");
createTextIndex("description");
// THEN
assertEquals("All users with term 'Java' or 'Spring'", 3, searchUser("Java Spring").size());
}
我们也可以通过在搜索查询前添加减号排除某个词:
assertEquals("All users with term 'Java' or 'Spring' but not 'MongoDB'", 2, searchUser("Java Spring -MongoDB").size());
同样,我们可以通过双引号包含短语来进行精确搜索:
assertEquals("All users with term Java only", 1, searchUser("\"Java\"").size());
3. 部分文本搜索
MongoDB并未原生支持部分搜索。与全文搜索不同,部分、模糊或子串搜索并不直观。搜索功能会应用特定语言的停用词和词干规则。
支持的语言的词干规则基于标准算法,通常处理常见的动词和名词,但可能不识别专有名词。让我们尝试使用部分搜索查找用户:
@Test
void whenSearchingUserWithPartialText_thenCorrectCountReturned() {
// WHEN
insertUser("LEONEL", "Java Spring");
createTextIndex("name");
// THEN
assertEquals("Search with capital case", 1, mongoDBClient.searchUser("LEONEL").size());
assertEquals("Search with lower case", 1, mongoDBClient.searchUser("leonel").size());
assertEquals("Partial search", 1, mongoDBClient.searchUser("LEONE").size());
}
然而,由于词干规则,系统无法找到“L”、“LEO”或“NEL”:
assertEquals("Partial search with L", 0, searchUser("L").size());
assertEquals("Partial search with LEO", 0, searchUser("LEO").size());
assertEquals("Partial search with NEL", 0, searchUser("NEL").size());
对于部分搜索的一个解决方案是使用$regex
。在这种情况下,我们不需要文本索引,但这可能会降低搜索操作的速度,尤其是在大型集合中。
4. 总结
在这篇简短教程中,我们研究了MongoDB中的全文和部分文本搜索。我们了解了如何使用搜索查询精确查找内容,并排除搜索结果中的某些词。我们还发现,在部分搜索中,前缀和后缀不会匹配文档,并找到了一种解决方案。
如需查看完整的代码示例,请访问GitHub仓库。