MongoDB德语文本索引

时间:2015-02-21 14:00:33

标签: mongodb mongodb-query

在我的文章集上,我有一个文本索引:

{
    "v" : 1,
    "key" : {
            "_fts" : "text",
            "_ftsx" : 1
    },
    "name" : "title_text_abstract_text_body_text",
    "ns" : "foo.articles",
    "weights" : {
            "abstract" : 1,
            "body" : 1,
            "title" : 1
    },
    "default_language" : "english",
    "language_override" : "language",
    "textIndexVersion" : 2
}

在我的文章集中,我有一个这样的条目:

{
    "_id" : ObjectId("5477c28c807a9cd660ccd567"),
    "title" : "Hallo Welt!",
    "author" : "foo",
    "publishDate" : ISODate("2014-11-28T17:00:00Z"),
    "language" : "de",
    "abstract" : "Mein erster Artikel!",
    "body" : "Dieser Artikel ist in deutscher Sprache.",
    "__v" : 0
}

abstractbody实际上有不同的值,为了简洁起见,我们假设它是上面的那些)

当我尝试搜索这篇文章时:

db.articles.find({$text: {$search: 'Welt'}})

确实找到了。

但: 当我尝试搜索这篇文章时:

db.articles.find({$text: {$search: 'Sprache'}})

我没有结果。但是在我将language更改为ennone之后,我确实以完全相同的查询结果获得了这篇文章。

我做错了什么?

修改: 根据评论中的要求,这里是导致上述行为的确切命令。首先应该这样做,道歉。

> db.test.drop()
true
> db.test.insert({language: "de", body: "vermutlich", title: "Artikel"})
WriteResult({ "nInserted" : 1 })
> db.test.ensureIndex({body: "text", title: "text"})
{
        "createdCollectionAutomatically" : false,
        "numIndexesBefore" : 1,
        "numIndexesAfter" : 2,
        "ok" : 1
}
> db.test.find({$text: {$search: 'vermutlich'}})
> db.test.find({$text: {$search: 'Artikel'}})
{ "_id" : ObjectId("54ea86d6c9ec98269e022c67"), "language" : "de", "body" : "vermutlich", "title" : "Artikel" }
> db.version()
2.6.5

我也尝试过再次更改语言:

> db.test.update({}, {$set: {language: "en"}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.test.find({$text: {$search: 'Artikel'}})
{ "_id" : ObjectId("54ea86d6c9ec98269e022c67"), "language" : "en", "body" : "vermutlich", "title" : "Artikel" }
> db.test.find({$text: {$search: 'vermutlich'}})
{ "_id" : ObjectId("54ea86d6c9ec98269e022c67"), "language" : "en", "body" : "vermutlich", "title" : "Artikel" }

修改 好的,所以我只是尝试重建this example. 但我还添加了一个德语引用,所以这就是我所做的:

> db.test.drop()
true
> db.test.insert({ language: "portuguese", original: "A sorte protege os audazes.", translation: [{ language: "english", quote: "Fortune favors the bold."},{ language: "spanish", quote: "La suerte rotege a los audaces."}]})
WriteResult({ "nInserted" : 1 })
> db.test.insert({ language: "spanish", original: "Nada hay más surrealista que la realidad.", translation:[{language: "english",quote: "There is nothing more surreal than reality."},{language: "french",quote: "Il n'y a rien de plus surréaliste que la réalité."}]})
WriteResult({ "nInserted" : 1 })
> db.test.insert({ original: "is thisdagger which I see before me.", translation: {language: "spanish",quote: "Es este un puñal que veo delante de mí." }})
WriteResult({ "nInserted" : 1 })
> db.test.insert({original: "Die Geister, die ich rief...", language: "german", translation: {language: "english", quote: "The spirits that I've cited..."}})
WriteResult({ "nInserted" : 1 })
> db.test.ensureIndex( { original: "text", "translation.quote": "text" } )
{
        "createdCollectionAutomatically" : false,
        "numIndexesBefore" : 1,
        "numIndexesAfter" : 2,
        "ok" : 1
}

然后我尝试了一些问题:

> db.test.count({$text: {$search: "delante"}})
1
> db.test.count({$text: {$search: "spirits"}})
1
> db.test.count({$text: {$search: "Geister"}})
0

结论:mongoDB不适用于德语? 这真是令人沮丧

3 个答案:

答案 0 :(得分:4)

wdberkeley是正确的,但我觉得有必要添加stemming的快速解释,因为我怀疑没有该领域经验的用户会得到这个要点。我还想强调一些替代方案和一般限制。

在许多语言中,由于语法规则,单词会发生显着变化,例如:对于德语单词" Geist" (心灵/幽灵/精神):

"Geist" (singular) -> "Geister" (plural) -> "Geistern" (plural accusative)

这种效果在英语中也是已知的,但它不太明显,例如:

"house" -> "houses" // "mouse" -> "mice" // "notable" -> "notably"

通常,我们希望搜索忽略当地的语法结构,因此如果我们要查找"Geist",它应该找到上面的任何一个词。这样做是非常具有挑战性的,因为语言规则很复杂,没有上下文就无法确定正确的答案。

后缀剥离是一种常见且相对简单的方法,它假设某些结尾很可能确实只是结尾,并且可以被移除以获得干。有时,词干分析器故意删除实际属于词干的字母,例如"notable" -> "notabl"

由于引号的语言是已知的,因此正确的词干分析器将用于引号。这有效 - 使用您的数据:

> db.test.find({$text: {$search: 'Geist'}}).count()
1

现在您遇到的问题是用户可能没有查找词干,但是对于派生表单,因此我们需要将相同的转换应用于输入。关键问题是我们不知道首先应用了哪些转换 。因此,您已经尝试在添加了一个变量的情况下做一些复杂的事情。

好的是there is snowball,它是MongoDB和其他搜索系统(如SolR)使用的词干分析器。它可以在BSD许可下使用并移植到多种语言,因此可以在客户端代码中完成数据库所做的相同操作。这样,我们不会将数据库视为黑盒子,但我们也将客户端代码与数据库的实现细节相结合......选择你的毒药。

例如,我们可以在所有词干分析器上运行并查看哪一个改变了输入,但这可能会导致fals肯定,因为该词可能已经是一个词干而另一种语言的词干缩短了它(德语词干:{ {1}})。

至少,如果我们采用词干分析器的不同响应集合,我们会大大减少我们需要进行的查询次数。

或者,您可以查阅单词列表,以便猜测该查询可能属于哪种语言。

即使付出额外的努力,理解简单后缀剥离所完成的限制也很重要。例如,"老鼠"在寻找" mouse"时,甚至没有使用英语干扰器,都找不到它,因为茎干假设茎更短。如果文本在他们所谓的语言中没有真正,事情会变得更糟(尤利西斯......)

换句话说:一个非常好的自由文本搜索需要的不仅仅是词干,而且跨语言进行查询会增加这一点。一个不同的搜索数据库is not a panacea - 问题根深蒂固在问题空间......

编辑: ElasticSearch has a nice comprehensive explanation of stemming(我在写完答案后继续找到这些

EDIT2:

  

为什么MongoDB只能使用不同的词干?

转换仅在数据库中插入或更新文本时应用。查询只是查找词干的匹配项。从本质上讲,索引是由词干词组成的。你想要的东西每次都要走遍整个系列。这种效率非常低,并且无法实现索引的目的。您可以做的是按照建议在客户端代码中执行该步骤。

  

为什么我只能使用$或两次搜索文本索引

AFAIK,这是查询引擎的一个限制 - 可能与排名有关,因为基于两个不同的输入做出好的评分并没有多大意义。但是你可以简单地运行两个查询并将结果合并到客户端。

答案 1 :(得分:1)

对不起,我太傻了。问题很简单:我们正在尝试将搜索文本"vermutlich"与文档文本"vermutlich"进行匹配,要正确执行此操作,您需要使用相同的语言规则进行解析。如果您执行以下操作:

> db.test.drop()
> db.test.insert({ "language" : "de", "body" : "vermutlich", "title" : "Artikel"})
> db.test.ensureIndex({ "$**" : "text" })
> db.test.count({ "$text" : { "$search" : "vermutlich" } })
0
> db.test.count({ "$text" : { "$search" : "vermutlich", "$language" : "de" } })
1

第一个查询搜索文档,该文档由于language字段而被编入索引,使用"vermutlich"作为英文单词处理。

您可以设置文本索引的默认语言,以避免在每个查询中指定$language

> db.test.drop()
> db.test.insert({ "language" : "de", "body" : "vermutlich", "title" : "Artikel"})
> db.test.ensureIndex({ "$**" : "text" }, { "default_language" : "de" })
> db.test.count({ "$text" : { "$search" : "vermutlich" } })
1

答案 2 :(得分:1)

作为一种解决方法,我使用default_language: "none"language_override: "none"创建了文本索引。 这样就没有停止词语也没有词干。但至少我发现直接匹配,无论语言如何。

> db.test.drop()
true
> db.test.insert({ language: "portuguese", original: "A sorte protege os audazes.", translation: [{ language: "english", quote: "Fortune favors the bold."},{ language: "spanish", quote: "La suerte rotege a los audaces."}]})
WriteResult({ "nInserted" : 1 })
> db.test.insert({ language: "spanish", original: "Nada hay más surrealista que la realidad.", translation:[{language: "english",quote: "There is nothing more surreal than reality."},{language: "french",quote: "Il n'y a rien de plus surréaliste que la réalité."}]})
WriteResult({ "nInserted" : 1 })
> db.test.insert({ original: "is this dagger which I see before me.", translation: {language: "spanish",quote: "Es este un puñal que veo delante de mí." }})
WriteResult({ "nInserted" : 1 })
> db.test.insert({original: "Die Geister, die ich rief...", language: "german", translation: {language: "english", quote: "The spirits that I've cited..."}})
WriteResult({ "nInserted" : 1 })
> db.test.ensureIndex( { original: "text", "translation.quote": "text" }, {default_language: 'none', language_override: 'none'} )
{
        "createdCollectionAutomatically" : false,
        "numIndexesBefore" : 1,
        "numIndexesAfter" : 2,
        "ok" : 1
}
> db.test.find({$text: {$search: 'Geister'}})
{ "_id" : ObjectId("54ed31b2e7ac93c32c760809"), "original" : "Die Geister, die ich rief...", "language" : "german", "translation" : { "language" : "english", "quote" : "The spirits that I've cited..." } }

除非有人找到实际解决方案,否则我认为mongoDB文本索引会被破坏。