MongoDB 全文搜索教程 已翻译 100%

oschina 投递于 2013/01/10 21:27 (共 9 段, 翻译完成于 01-11)
阅读 15446
收藏 70
9
加载中

在我的那篇MongoDB全文检索入门篇一文中,我们已经对MongoDB的基本功能有了一个初步的了解。今天,通过这篇文章,我们来更进一步的讨论MongoDB全文检索功能。

API

你会发现全文搜索并非是通过find()命令实现,而是通过调用

db.foo.runCommand( "text", {search: "bar"} )
请牢记这个命令现在还处于实验阶段。通过这个命令实现find()功能,会在生产环境中掺入危险的代码。通过runCommand()这个命令来执行搜索,运行和测试可以实现分离。

我多么的希望一个新的检索操作符,例如$textor $textsearch 可以和标准的find()命令相结合。

enixyu
enixyu
翻译于 2013/01/10 22:13
2

文本检索语法

在前面的例子中,我们只是搜索一个单词。我们可以搜索的更复杂一些,让我们来看看以下代码:

db.foo.drop()
db.foo.ensureIndex( {txt: "text"} )
db.foo.insert( {txt: "Robots are superior to humans"} )
db.foo.insert( {txt: "Humans are weak"} )
db.foo.insert( {txt: "I, Robot - by Isaac Asimov"} )
搜索单词“robot”, 会得到2个结果,而搜索“human”结果也是2个。
> db.foo.runCommand("text", {search: "robot"}).results.length
2
> db.foo.runCommand("text", {search: "human"}).results.length
2
当我们搜索条件包含多个单词,数据库会执行或的操作,搜索结果会得到3个。
> db.foo.runCommand("text", {search: "human robot"}).results.length
3
我希望搜索的单词之间是与的关系而不是或的关系。
enixyu
enixyu
翻译于 2013/01/10 22:21
2

取反

通过在搜索单词前加上减号'-',可以在搜索的时候,排除包含该单词的记录。也就是说,我们需要搜索包含“robot”,但是不包含“humans”的记录。

> db.foo.runCommand("text", {search: "robot -humans"})
{
        "queryDebugString" : "robot||human||||",
        "language" : "english",
        "results" : [
                {
                        "score" : 0.6666666666666666,
                        "obj" : {
                                "_id" : ObjectId("50ebc484214a1e88aaa4ada0"),
                                "txt" : "I, Robot - by Isaac Asimov"
                        }
                }
        ],
        "stats" : {
                "nscanned" : 2,
                "nscannedObjects" : 0,
                "n" : 1,
                "timeMicros" : 212
        },
        "ok" : 1
}
词组搜索

通过用引号包含由多个单词组成的词组(“foo bar”),就可以实现词组搜索。在词组里面,单词的顺序十分重要,同时搜索结束单词也需要考虑。

> db.foo.runCommand("text", {search: '"robots are"'})
{
        "queryDebugString" : "robot||||robots are||",
        "language" : "english",
        "results" : [
                {
                        "score" : 0.6666666666666666,
                        "obj" : {
                                "_id" : ObjectId("50ebc482214a1e88aaa4ad9e"),
                                "txt" : "Robots are superior to humans"
                        }
                }
        ],
        "stats" : {
                "nscanned" : 2,
                "nscannedObjects" : 0,
                "n" : 1,
                "timeMicros" : 185
        },
        "ok" : 1
}
请查看如下的例子"queryDebugField"
"queryDebugString" : "robot||||robots are||"
我们需要搜索条件中包含"robot"的词根,同时也包含"robots are"的词组。这就是为什么我们只找到一条记录。请比较如下的搜索:
> // order matters inside phrase
> db.foo.runCommand("text", {search: '"are robots"'}).results.length
0
> // no phrase search --> OR query
> db.foo.runCommand("text", {search: 'are robots'}).results.length
2
enixyu
enixyu
翻译于 2013/01/11 09:24
2

多语言支持

分词和停用词过滤都是与语言有关的。如果你希望用英语以外的语言来创建索引和搜索,那么必须告诉MongoDB。MongoDB用的是开源的Snowball分词器,它支持这些语言这些语言

如果希望使用其它语言,需要在创建索引时这样写:

db.de.ensureIndex( {txt: "text"}, {default_language: "german"} )
MongoDB就会认为“txt”中的文本是德语,而且我们搜索的文本也是德语。我们看看是不是这样的:
> db.de.insert( {txt: "Ich bin Dein Vater, Luke." } )
> db.de.validate().keysPerIndex["text.de.$txt_text"]
2

AlfredCheung
AlfredCheung
翻译于 2013/01/11 10:15
2
如你所见,这里只有两个索引关键字,因此停用词过滤就会起效(这里用的是德语的停用词,Vater 是德语中的 father 意思 ) ,我们再试试其他一些搜索:
db.de.insert( {language:"english", txt: "Ich bin ein Berliner" } )

请注意,我们不一定需要在搜索的时候提供语言,因为这是从索引继承而来。我们已经命中了同义词 Vater 和 Luke,但没有命中停用词 ich (意思是 I)

我们还可以在同一个索引中混合多种不同的语言,每个文档都有它独立的语言:

db.de.insert( {language:"english", txt: "Ich bin ein Berliner" } )
红薯
红薯
翻译于 2013/01/11 10:25
2
如果存在 “language” 字段,其内容就相当于为文档的索引数据定义了流数据的语言和停用词过滤。单词 ich 在英语中并不是停用词,因此它被索引了。
// default language: german -> no hits
> db.de.runCommand("text", {search: "ich"})
{
        "queryDebugString" : "||||||",
        "language" : "german",
        "results" : [ ],
        "stats" : {
                "nscanned" : 0,
                "nscannedObjects" : 0,
                "n" : 0,
                "timeMicros" : 96
        },
        "ok" : 1
}
 
// search for English -> one hit
> db.de.runCommand("text", {search: "ich", language: "english"})
{
        "queryDebugString" : "ich||||||",
        "language" : "english",
        "results" : [
                {
                        "score" : 0.625,
                        "obj" : {
                                "_id" : ObjectId("50ed163b1e27d5e73741fafb"),
                                "language" : "english",
                                "txt" : "Ich bin ein Berliner"
                        }
                }
        ],
        "stats" : {
                "nscanned" : 1,
                "nscannedObjects" : 0,
                "n" : 1,
                "timeMicros" : 161
        },
        "ok" : 1
}

这里到底发生什么事情?默认的搜索语言是德语,因此首次搜索没有返回任何结果。而第二次搜索时,我们搜索英语文本,这也是为什么我们能从这个句子中找出 JFK。

这是什么意思呢?嗯,你已经有了这种的多语言文本搜索,你可以在一个集合中存储来自全世界不同语言的文本信息,然后仍然使用你的母语进行搜索。

小编辑
小编辑
翻译于 2013/01/11 10:44
2

多字段

文本索引可以跨越多个字段。在这种情况下,每个字段可以有自己的权重。我们可以利用权重,为文档的不同的部分赋予不同的意义。

> db.mail.ensureIndex( {subject: "text", body: "text"}, {weights: {subject: 10} } )
> db.mail.getIndices()
[
        ...
        {
                "v" : 0,
                "key" : {
                        "_fts" : "text",
                        "_ftsx" : 1
                },
                "ns" : "de.mail",
                "name" : "subject_text_body_text",
                "weights" : {
                        "body" : 1,
                        "subject" : 10
                },
                "default_language" : "english",
                "language_override" : "language"
        }
]
我们创建了一个跨越两个字段的文本索引,“subject”和“body”,它们的权重分别是10和1。我们看下权重有什么影响:
> db.mail.insert( {subject: "Robot leader to minions", body: "Humans suck", prio: 0 } )
> db.mail.insert( {subject: "Human leader to minions", body: "Robots suck", prio: 1 } )
> db.mail.runCommand("text", {search: "robot"})
{
        "queryDebugString" : "robot||||||",
        "language" : "english",
        "results" : [
                {
                        "score" : 6.666666666666666,
                        "obj" : {
                                "_id" : ObjectId("50ed1be71e27d5e73741fafe"),
                                "subject" : "Robot leader to minions",
                                "body" : "Humans suck"
                                "prio" : 0 
                        }
                },
                {
                        "score" : 0.75,
                        "obj" : {
                                "_id" : ObjectId("50ed1bfd1e27d5e73741faff"),
                                "subject" : "Human leader to minions",
                                "body" : "Robots suck"
                                "prio" : 1
                        }
                }
        ],
        "stats" : {
                "nscanned" : 2,
                "nscannedObjects" : 0,
                "n" : 2,
                "timeMicros" : 148
        },
        "ok" : 1
}
可以看到,“subject”字段含有“robot”的文档会有更高的得分,那是因为它有10的权重,作为倍数乘了上去。
AlfredCheung
AlfredCheung
翻译于 2013/01/11 09:40
1

过滤与投射

我们还可以利用过滤来附加额外的搜索条件:

> db.mail.runCommand("text", {search: "robot", filter: {prio:0} } )
{
        "queryDebugString" : "robot||||||",
        "language" : "english",
        "results" : [
                {
                        "score" : 6.666666666666666,
                        "obj" : {
                                "_id" : ObjectId("50ed22621e27d5e73741fb04"),
                                "subject" : "Robot leader to minions",
                                "body" : "Humans suck",
                                "prio" : 0
                        }
                }
        ],
        "stats" : {
                "nscanned" : 2,
                "nscannedObjects" : 2,
                "n" : 1,
                "timeMicros" : 185
        },
        "ok" : 1
}
需要注意的是,过滤并不会使用索引。

如果我们关心的只是一部分字段,可以使用投射(类似于汇聚框架):

> db.mail.runCommand("text", {search: "robot", projection: {_id:0, prio:0} } )
{
        "queryDebugString" : "robot||||||",
        "language" : "english",
        "results" : [
                {
                        "score" : 6.666666666666666,
                        "obj" : {
                                "subject" : "Robot leader to minions",
                                "body" : "Humans suck"
                        }
                },
                {
                        "score" : 0.75,
                        "obj" : {
                                "subject" : "Human leader to minions",
                                "body" : "Robots suck"
                        }
                }
        ],
        "stats" : {
                "nscanned" : 2,
                "nscannedObjects" : 0,
                "n" : 2,
                "timeMicros" : 127
        },
        "ok" : 1
}
过滤和投射是可以一起使用的。
AlfredCheung
AlfredCheung
翻译于 2013/01/11 09:46
1

总结

我们在这篇文章里学习了MongoDB文本搜索的一些有趣功能。它应该对我们实现搜索引擎有很好的帮助。期待大家的反馈。

AlfredCheung
AlfredCheung
翻译于 2013/01/11 09:51
2
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接。
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
加载中

评论(9)

hzjjskuw
hzjjskuw
sphinx+1
大鼻子
大鼻子
不支持中文
桃子他爹
桃子他爹
真的要用到复杂的搜索功能,去用sphinx这些专门的家伙吧
martin_li
martin_li

引用来自“frankiegao123”的评论

总结之前的“Examples”跑哪去了?

对于本译文的建议:

一、用字和用词上

1. 的地得?

译文中含有国人的通病,“的”、“地”、“得”完全不分地使用了“的”。
应使用“地”的地方使用了“的”:“初步的了解”、“更进一步的讨论”、“很好的帮助”、“多么的希望”等等
应使用“得”的地方使用了“的”:“搜索的更复杂”

“的”用于形容词修饰名词之用,比如:红红的苹果、很好的功能
“地”用于副词修饰动词之用,比如:初步地了解、飞快地跑
“得”用于形容程度和状态之用,比如:搜索得更复杂、红得发紫

2. 数词“2”

“会得到2个结果”译为“会得到两个结果”,这样更符合汉语思维。

二、语言与文化差异

1. 德语与德文

德语强调的是语言,德文强调的是书写文字,本文中的应为文字而非语言,建议改为:德文或者德语单词、德语字母。

2. 英文文章中的人称代词

英文文章中由于为了让文章更为贴近读者,让读者感到作者就在身边,在文中拥有大量的:we, we can, you, your, you can 之类的人称代码,这类人称代码译为中文时,由于汉语的思维方式不同,因此应将此类的人称代词删除,否则译文有较为生硬的感觉,不符合中文文章的行为风格。

三、译文语法与用词

这部分的问题很多,不一一列举了

译文:在前面的例子中,我们只是搜索一个单词。我们可以搜索的更复杂一些,让我们来看看以下代码
原文:In the previous examples we just searched for a single word. We can do more than that. Let’s have a look at the following example:

根据上下文,原文中最后一个单词“example”应为示例的意思,并非“代码”之意,建议译为:

前面的例子都是搜索一个单词的,实际上也能进行更为复杂的搜索,可以看看下面的例子:

说的很有条理,赞一个!!!
不过最后的example根据上下文翻译成代码也还好
Xsank
Xsank
连繁体字都不支持,更别说简体了
程明卫
程明卫
mongodb 在实际环境中使用时。发现会莫名其妙的丢数据,而且很没规律。不知,各位有没遇到这样的情况
frankiegao123
frankiegao123
总结之前的“Examples”跑哪去了?

对于本译文的建议:

一、用字和用词上

1. 的地得?

译文中含有国人的通病,“的”、“地”、“得”完全不分地使用了“的”。
应使用“地”的地方使用了“的”:“初步的了解”、“更进一步的讨论”、“很好的帮助”、“多么的希望”等等
应使用“得”的地方使用了“的”:“搜索的更复杂”

“的”用于形容词修饰名词之用,比如:红红的苹果、很好的功能
“地”用于副词修饰动词之用,比如:初步地了解、飞快地跑
“得”用于形容程度和状态之用,比如:搜索得更复杂、红得发紫

2. 数词“2”

“会得到2个结果”译为“会得到两个结果”,这样更符合汉语思维。

二、语言与文化差异

1. 德语与德文

德语强调的是语言,德文强调的是书写文字,本文中的应为文字而非语言,建议改为:德文或者德语单词、德语字母。

2. 英文文章中的人称代词

英文文章中由于为了让文章更为贴近读者,让读者感到作者就在身边,在文中拥有大量的:we, we can, you, your, you can 之类的人称代码,这类人称代码译为中文时,由于汉语的思维方式不同,因此应将此类的人称代词删除,否则译文有较为生硬的感觉,不符合中文文章的行为风格。

三、译文语法与用词

这部分的问题很多,不一一列举了

译文:在前面的例子中,我们只是搜索一个单词。我们可以搜索的更复杂一些,让我们来看看以下代码
原文:In the previous examples we just searched for a single word. We can do more than that. Let’s have a look at the following example:

根据上下文,原文中最后一个单词“example”应为示例的意思,并非“代码”之意,建议译为:

前面的例子都是搜索一个单词的,实际上也能进行更为复杂的搜索,可以看看下面的例子:
缪斯的情人
缪斯的情人
对于我们伟大的母语无效
AlfredCheung
AlfredCheung
多语言支持那部分的原文与代码的搭配貌似有点问题
返回顶部
顶部