概述

在开发数据库应用时,快速高效的文本搜索功能常常必不可少。为了提供更友好的用户体验,这些搜索还应支持全文和部分匹配。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仓库