MongoDB中的完整和部分文本搜索

开发数据库应用程序时经常需要快速高效的文本搜索。他们还应该支持完整和部分文本匹配,以使这些搜索更加用户友好。为此,MongoDB 提供了几种使用文本搜索查找相关文档的方法。

在本教程中,我们将探讨 MongoDB 中的文本搜索、其功能、如何使用它以及如何充分利用它。

MongoDB 中的文本搜索
虽然文本搜索查询是一个强大的工具,但它们需要特定的设置。为了实现这一点,我们需要在集合上创建一个文本索引。

索引就像特殊的文件夹,只保存集合中每个文档的一点信息。换句话说,它们与实际文档本身是分开的。此外,MongoDB 允许用户创建不同类型的索引。

幸运的是,MongoDB 提供了文本索引,旨在促进字符串内容中的搜索。这些索引非常灵活,可以涵盖多个领域,可以进行全面的搜索。此外,这些索引有助于数据库更快地搜索集合。

首先,我们通过指定连接字符串、数据库名称和集合名称来创建数据库客户端:

@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);
}

有一个限制:一个集合只能有一个专用的文本搜索索引。

全文检索
简单的全文搜索非常简单。我们可以输入关键字或短语,然后系统会找到包含这些确切术语的文档。

除了简单的全文搜索之外,还有多种方法可以执行全文搜索。每个都有自己的优点,并且是针对特定用例量身定制的。其中一些常见的方式包括布尔全文搜索、短语搜索和邻近搜索。

让我们创建一个执行文本搜索查询的方法:

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”甚至两者都带有“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());

部分文本搜索
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。因此,我们不需要文本索引,如果集合很大,这可能会减慢搜索操作。