【问题标题】:Document word index database structure?文档词索引数据库结构?
【发布时间】:2012-04-01 02:35:15
【问题描述】:

假设我希望识别文档的几页(可能大约 500 多页)中出现的所有单词。我已经完成了查找单词出现在哪些页面上的工作。因此,例如,我想列出出现计算机一词的所有页面。

存储这些数据以便通过网络服务快速搜索的最佳方式是什么?我的直觉是做类似的事情:

表结构: varchar(30) WORD, blob PAGES

并且让 PAGES 字段是一个逗号分隔的列表,包含该单词出现的所有页面,然后在查询匹配 WORD 字段时将其展开并列出所有页面。我想知道是否有更有效的方法来实现这一目标?我可能会使用 MySQL 和 PHP/Zend,因为那是我最熟悉的。但是,如果您有任何更好的想法,我绝对愿意听取他们的意见。

表格可能会变得非常长,因为我需要为文档中的每个唯一单词留一行。也许我会设置一个不超过 3 或 4 个字符的限制,但我仍然会想象超过 10-20k 个单词。如果我按字母顺序排列行列表,我能以某种方式使我的数据库服务器更容易吗? (即苹果、苹果、分支是升序排列的?) MySQL 可以处理这个吗?其他东西能更好地处理它吗?

最后,是否有更好的结构模式可以让我以后收集/提供有趣的数据? (即给用户经常出现在附近的相关词等)

【问题讨论】:

    标签: mysql database-design indexing


    【解决方案1】:

    您必须规范化您的数据库。

    首先是一个表来存储页面

    table pages (
      id unsigned integer auto_increment primary key,
      page blob,
      other_interesting_data_about_a_page )
    

    然后是一个存放字数的表格

    table wc (
      id unsigned integer auto_increment primary key
      word varchar(20) unique key,
      count unsigned integer default 1,
      other_interesting_data_about_a_word.... )
    

    然后是一个将单词链接到页面的表格

    table word_page (
      word_id unsigned integer,
      page_id unsiged integer,
      pos_in_page unsigned integer,  /*position*/
      primary key pk (word_id, page_id, pos_in_page) )
    

    现在可以查询一页的字数了:

    SELECT COUNT(*) 
    FROM word_page 
    WHERE page_id = 123
    

    或单词“the”在页面中重复的次数。

    SELECT COUNT(*)
    FROM word_page wp 
    INNER JOIN wc ON (wp.word_id = wc.id)
    WHERE wp.page_id = 123 AND wc.word = 'the'
    

    一句警告

    并让 PAGES 字段成为所有页面的逗号分隔列表.....

    永远不要在数据库中使用 CSV,它是你能用过的最糟糕的反模式,如果你爱上它,它会一遍又一遍地咬你。
    如果您觉得有必要,请猛烈抨击自己的头部,直到冲动消失,然后改用一两张单独的桌子。

    【讨论】:

    • 我永远不会在数据库中使用 CSV。 :) 感谢您的提示!
    【解决方案2】:

    与标准化相反,标准化通常是一种很好的做法,但对于这个特定问题来说空间效率很低,您可能希望坚持您的结构,但将 blob 中的页面列表替换为位向量(仍在blob 列),每个位代表一个页面。好处是对于 500 个页面,即使这个词出现在所有页面中(500/8=62.5),一个词的这个向量的最大大小也是 63 字节。

    在位域内部,每一页对应一个位号:如果位号N为ON,则表示该字出现在N页,否则不出现在N页。 这是DBIx SQL text indexing implementation基本使用的结构 位从右到左编号,可以删除不重要的 0。

    例如,如果“计算机”一词出现在第 3,4 和 12 页中,则值为: 100000001100 二进制(=2060 十进制表示)。

    如果它仅出现在第 400 页,则它将是数字 1 后跟 399 0's。如果它出现在每一页上,则该值将是数字 1 的 500 倍。

    我一直在使用该表示(加上分区)来为 postgresql 数据库中的全文索引邮件内容,我发现它的扩展性非常好,这与只在非常小的情况下表现良好的天真规范化实现相反数据集。

    【讨论】:

    • 从来没有考虑过这样的事情,谢谢!我认为 Johan 的回答虽然成本更高,但可以让我用存储的数据做更多有趣的事情。
    • @Emeka(同样),David 的好主意,但是,在遇到速度/空间问题之前,不要试图去规范化。我想知道您如何处理在一个页面上出现多次的单词(但我现在太深入了)。
    【解决方案3】:

    为了便于维护和索引,我将使用计算的主键设置一个映射表: id BIGINT AUTO_INCREMENT, 单词 VARCHAR(30), 页面INT, ... 并为单词和页面建立索引。 这样一来,您就更加灵活,无需展开列表,甚至可以访问一些统计信息(哪些页面使用了更多独特的词等)。

    MySQL(以及所有其他关系数据库引擎)使用树结构构建自己的内部索引,无需对数据进行预先排序。

    这个表很容易被 MySQL 处理。可能还有其他更快的数据库引擎,但这是一个好的开始。

    当然你可以添加更多的表,即 word、other_word、distance,这一切都取决于你的规范和你的解析器的可能性。

    如果你有时间浏览,看看搜索引擎是如何处理这些事情的,例如 solr/lucene

    【讨论】:

    • 感谢 Solr/Lucene 的提示。可能最终会朝这个方向发展!
    猜你喜欢
    • 1970-01-01
    • 2013-02-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多