Joh*_*eri 11 regex indexing mongodb mongodb-query
我正在尝试使用MongoDB来实现自然语言字典.我有一个lexemes集合,每个lexemes都有许多wordforms作为子文档.这就是一个单一的lexeme看起来像:
{
"_id" : ObjectId("51ecff7ee36f2317c9000000"),
"pos" : "N",
"lemma" : "skrun",
"gloss" : "screw",
"wordforms" : [
{
"number" : "sg",
"surface_form" : "skrun",
"phonetic" : "?skru?n",
"gender" : "m"
},
{
"number" : "pl",
"surface_form" : "skrejjen",
"phonetic" : "'skr?jj?n",
"pattern" : "CCCVCCVC"
}
],
"source" : "Mayer2013"
}
Run Code Online (Sandbox Code Playgroud)
目前我有大约4000个lexemes的集合,每个lexemes平均有一个1000个字形的列表(而不是上面的2个).这意味着我在集合中有效地拥有4,000,000个独特的单词形式,我需要能够在合理的时间内搜索它们.
普通查询看起来像这样:
db.lexemes.find({"wordforms.surface_form":"skrejjen"})
Run Code Online (Sandbox Code Playgroud)
我有一个索引wordforms.surface_form,这个搜索非常快.但是,如果我想在搜索中使用通配符,则性能非常差.例如:
db.lexemes.find({"wordforms.surface_form":/skrej/})
Run Code Online (Sandbox Code Playgroud)
花了5分多钟(此时我放弃了等待).正如在这个问题中提到的,对索引进行正则表达式搜索是不好的.我知道在正则表达式搜索中添加^锚点有很大帮助,但它也严重限制了我的搜索功能.即使我愿意做出这样的牺牲,我也注意到响应时间仍然可以根据正则表达式而变化很大.查询
db.lexemes.find({"wordforms.surface_form":/^s/})
Run Code Online (Sandbox Code Playgroud)
需要35秒才能完成.
到目前为止,我所获得的最佳结果实际上是在我关闭索引时使用的hint.在这种情况下,事情似乎有了很大改善.这个查询:
db.lexemes.find({"wordforms.surface_form":/skrej/}).hint('_id_')
Run Code Online (Sandbox Code Playgroud)
需要大约3秒才能完成.
我的问题是,我还能做些什么来改善这些搜索时间吗?虽然它们仍然有点慢,但我已经在考虑迁移到MySQL以期获得性能.但我真的希望保持Mongo的灵活性并避免RDBMS中所有繁琐的规范化.有什么建议?您是否认为无论使用数据库引擎,我都会遇到一些缓慢的问题?
我知道Mongo的新文本搜索功能,但这个(标记化和词干化)的优点与我的情况无关(更不用说我的语言不受支持).目前尚不清楚文本搜索实际上是否比使用正则表达式更快.
一种可能性是存储您认为可能作为数组元素有用的所有变体 - 不确定这是否可能!
{
"number" : "pl",
"surface_form" : "skrejjen",
"surface_forms: [ "skrej", "skre" ],
"phonetic" : "'skr?jj?n",
"pattern" : "CCCVCCVC"
}
Run Code Online (Sandbox Code Playgroud)
我可能还建议不要在每个单词中存储1000个单词表单,但要将其转换为包含较小的单词.文档越小,MongoDB每次搜索就必须读入内存的次数越少(只要搜索条件不需要完整扫描):
{
"word": {
"pos" : "N",
"lemma" : "skrun",
"gloss" : "screw",
},
"form" : {
"number" : "sg",
"surface_form" : "skrun",
"phonetic" : "?skru?n",
"gender" : "m"
},
"source" : "Mayer2013"
}
{
"word": {
"pos" : "N",
"lemma" : "skrun",
"gloss" : "screw",
},
"form" : {
"number" : "pl",
"surface_form" : "skrejjen",
"phonetic" : "'skr?jj?n",
"pattern" : "CCCVCCVC"
},
"source" : "Mayer2013"
}
Run Code Online (Sandbox Code Playgroud)
我也怀疑MySQL在搜索随机字形式时会表现得更好,因为它必须像MongoDB那样进行全表扫描.唯一可以帮助的是查询缓存 - 但是当然,您可以在应用程序中轻松地在搜索UI/API中构建.
正如Derick所建议的那样,我在我的数据库中重构了数据,以便我将"wordforms"作为集合而不是"lexemes"下的子文档.结果实际上更好!这是一些速度比较.最后一个使用示例hint是故意绕过索引surface_form,在旧模式中实际上更快.
旧架构(见原始问题)
Query Avg. Time
db.lexemes.find({"wordforms.surface_form":"skrun"}) 0s
db.lexemes.find({"wordforms.surface_form":/^skr/}) 1.0s
db.lexemes.find({"wordforms.surface_form":/skru/}) > 3mins !
db.lexemes.find({"wordforms.surface_form":/skru/}).hint('_id_') 2.8s
Run Code Online (Sandbox Code Playgroud)
新架构(见Derick的答案)
Query Avg. Time
db.wordforms.find({"surface_form":"skrun"}) 0s
db.wordforms.find({"surface_form":/^skr/}) 0.001s
db.wordforms.find({"surface_form":/skru/}) 1.4s
db.wordforms.find({"surface_form":/skru/}).hint('_id_') 3.0s
Run Code Online (Sandbox Code Playgroud)
对我来说,这是一个很好的证据,证明重构的模式可以使搜索更快,并且值得冗余数据(或需要额外的连接).
| 归档时间: |
|
| 查看次数: |
10396 次 |
| 最近记录: |