15

我正在尝试使用 MongoDB 来实现自然语言词典。我有一组词素,每个词素都有许多词形作为子文档。这是单个词素的样子:

{
    "_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"
}

目前我收集了大约 4000 个词位,每个词位平均有大约 1000 个词形的列表(而不是上面的 2 个)。这意味着我在集合中有 4,000,000 个独特的单词形式,我需要能够在合理的时间内搜索它们。

一个普通的查询应该是这样的:

db.lexemes.find({"wordforms.surface_form":"skrejjen"})

我在 上有一个索引wordforms.surface_form,而且这个搜索非常快。但是,如果我想在搜索中使用通配符,则性能非常糟糕。例如:

db.lexemes.find({"wordforms.surface_form":/skrej/})

需要超过 5 分钟(此时我放弃了等待)。正如在这个问题中提到的,已知对索引进行正则表达式搜索是不好的。我知道在正则表达式搜索中添加 ^ 锚点有很大帮助,但它也严重限制了我的搜索能力。即使我愿意做出这种牺牲,我注意到响应时间仍然会因正则表达式而有很大差异。查询

db.lexemes.find({"wordforms.surface_form":/^s/})

需要 35 秒才能完成。

到目前为止,我得到的最好结果实际上是在我使用hint. 在这种情况下,情况似乎有了很大改善。这个查询:

db.lexemes.find({"wordforms.surface_form":/skrej/}).hint('_id_')

大约需要 3 秒才能完成。

我的问题是,我还能做些什么来改善这些搜索时间吗?事实上,它们仍然有点慢,我已经在考虑迁移到 MySQL 以希望获得性能。但我真的很想保持 Mongo 的灵活性并避免 RDBMS 中所有繁琐的规范化。有什么建议么?您认为无论数据库引擎如何,使用这么多的文本数据,我都会遇到一些缓慢的问题吗?

我知道 Mongo 的新文本搜索功能,但它的优点(标记化和词干提取)与我的情况无关(更不用说不支持我的语言)。目前尚不清楚文本搜索是否实际上比使用正则表达式更快。

4

2 回答 2

11

一种可能性是存储您认为可能有用的所有变体作为数组元素 - 但不确定这是否可能!

    {
        "number" : "pl",
        "surface_form" : "skrejjen",
        "surface_forms: [ "skrej", "skre" ],
        "phonetic" : "'skrɛjjɛn",
        "pattern" : "CCCVCCVC"
    }

我可能还建议不要为每个单词存储 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"
}

我也怀疑 MySQL 在这里搜索随机词形式的性能会更好,因为它必须像 MongoDB 一样进行全表扫描。唯一可以帮助的是查询缓存 - 当然,您可以很容易地在应用程序的搜索 UI/API 中构建它。

于 2013-07-30T09:34:07.637 回答
8

正如 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

新模式(见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

对我来说,这是一个很好的证据,表明重构的模式会使搜索更快,并且值得冗余数据(或需要额外的连接)。

于 2013-07-31T06:41:45.080 回答