【问题标题】:MySQL - Text Search and Database StructureMySQL - 文本搜索和数据库结构
【发布时间】:2016-04-05 14:00:28
【问题描述】:

这是我当前的数据库结构:

CREATE TABLE `books` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `title` varchar(100) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
  `year` year(4) NOT NULL DEFAULT '0000',
  `author` varchar(100) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
  PRIMARY KEY (`id`),
  UNIQUE KEY `title` (`title`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1;

CREATE TABLE `chapters` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `book_id` int(10) unsigned NOT NULL DEFAULT '0',
  `number` int(10) unsigned NOT NULL DEFAULT '0',
  `title` varchar(100) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
  PRIMARY KEY (`id`),
  KEY `book_id` (`book_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1;

ALTER TABLE `chapters`
  ADD CONSTRAINT `chapters_ibfk_1` FOREIGN KEY (`book_id`) REFERENCES `books` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;

CREATE TABLE `pages` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `book_id` int(10) unsigned NOT NULL DEFAULT '0',
  `chapter_id` int(10) unsigned NOT NULL DEFAULT '0',
  `number` int(10) unsigned NOT NULL DEFAULT '0',
  `text` text COLLATE utf8_unicode_ci NOT NULL,
  `words` int(10) unsigned NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`),
  KEY `book_id` (`book_id`),
  KEY `chapter_id` (`chapter_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1;

ALTER TABLE `pages`
  ADD CONSTRAINT `pages_ibfk_1` FOREIGN KEY (`book_id`) REFERENCES `books` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
  ADD CONSTRAINT `pages_ibfk_2` FOREIGN KEY (`chapter_id`) REFERENCES `chapters` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;

结构非常简单...基本上我是逐页提取书籍文本并将所有内容存储到我的数据库中,该数据库被组织成书籍>章节>页面系统。我试图使其尽可能灵活,以便我可以轻松地从整本书或章节的角度汇总数据......但如果你认为我可以做得更好,我愿意接受任何建议!

现在,我想允许用户在书中执行关键字搜索...这样他们就可以在他们从下拉列表中选择的书中搜索所有出现的单个单词,甚至是短语。

我的网络服务器不在存储 MySQL 数据库的同一台机器上(我在短期内无法摆脱的技术问题)......所以为了避免两台机器之间的巨大数据流量,我更喜欢通过 SQL 查询运行文本搜索。检索所有页面并使用 PHP 分析它们每次都会转化为 5-10 Mb 的数据。

现在我的问题是:

  • 是否可以仅使用查询命令(LIKEMATCHREPLACE 等)来执行这种过程?
  • 我会通过以下方式获得按页面格式化的结果:[page 1 | 0 次出现], [第 2 页 | 1 次], [第 3 页 | 1 次出现], [第 4 页 | 2 次]...这可能吗?
  • 您认为在将页面文本存储到字段pages.text 之前从页面文本中去除空格字符(换行符、制表符等)和标点字符是个好主意吗?

感谢您的帮助!

【问题讨论】:

    标签: mysql regex search text full-text-search


    【解决方案1】:

    试试这个:

    SELECT p.number, ROUND (   
            (
                LENGTH(p.text)
                - LENGTH( REPLACE ( p.text, "{your string here}", "") ) 
            ) / LENGTH("{your string here}")        
        ) AS count  
    FROM pages p
    JOIN
    chapters c ON c.id = p.chapter_id
    JOIN
    books b on b.id = c.book_id
    WHERE b.title = {your book title}
    GROUP BY p.number
    
    -- solution inspired by:
    -- http://stackoverflow.com/questions/12344795/count-the-number-of-occurences-of-a-string-in-a-varchar-field
    

    你可以去掉空格,但你可能会遇到几个问题:

    • 您还需要去除搜索查询(这不是真正的问题,只是额外的工作)

    • 更重要的是,如果您想向用户显示页面的全文,甚至只是摘录,您一开始就不会知道空白字符的位置。

    【讨论】:

    • 在任何情况下我都不会显示页面的文本...无论如何我不是在谈论空格...只有回车、制表符等...去除空格不会在任何情况下都有意义。因为如果我保留换行符,我很确定即使应该找到一些搜索也不会找到。
    • 我明白你在说什么。在那种情况下,我认为如果你去掉换行符就可以了。该查询有效吗? @Zarathos
    • 很快就要测试了。另外......有没有办法检查单词边界,比如它是否是一个正则表达式?
    • 字边界在哪里? @Zarathos
    • 好吧,例如,如果有人搜索“我在这儿”,该函数也会返回“我在那儿”之类的匹配项......我想添加一个选项来进行完全匹配而不是部分匹配.
    【解决方案2】:

    不用担心 MySQL 和应用程序在不同的机器上。 (当然,你需要使用TCP,而不是“localhost”。)“大玩家”就是这样分裂的。

    pages 上有FULLTEXT(text),然后说

    FROM pages AS p
    JOIN book AS b ON ...
    WHERE MATCH (p.text) AGAINST (...)
      AND b.id = ...
    

    其他说明:

    使用合适的GROUP BY,您可以按照您的要求接近页面+计数。

    你真的想说“某页出现 0 次吗?如果‘书’有 500 页长怎么办;你真的想要 500 行输出吗?

    注意FULLTEXT 搜索的限制(仅词、词干、最小词大小、“停止”词等),如果用户未能遵循这些限制,请处理它们。

    有时我会这样做:如果用户在查询中输入*%,我会使用REGEXPLIKE 而不是FULLTEXT,并警告用户它会变慢。

    FULLTEXT 索引将在MATCH 存在时首先使用,然后它会在书上过滤。使用LIKEREGEXP,它会在进行繁琐的搜索之前过滤 book_id。注意:这意味着用 PHP(或任何语言)动态“构建”查询。

    完成所有(或大部分)工作会减少网络流量,这是您的担忧之一。

    无需“剥离”。 FULLTEXT 需要单词边界。

    每页计数:

    SELECT p.number AS 'PageNumber',
           COUNT(*) AS 'Occurrences'
        ...
        GROUP BY p.id
    

    【讨论】:

    • 再一次,关于剥离我只想剥离\n、\t、引号、点、冒号、分号、3个点等......所以我只有单词。假设我在数据库中插入了托尔金的书……人们搜索“fly, you fools”,但在我的数据库中有一个换行符,因此短语被拆分为“fly,\nyou傻瓜”…… FULLTEXT 会找到它吗?再说一次,假设人们搜索“靴子”并且只想要确切的行军而不是“靴子”......我应该如何更改我的查询?
    • 大约 0 次出现,不......我可以跳过那个......我假设没有返回匹配的行没有出现,所以我将用零填充初始化我的数组,然后仅插入找到匹配项的值。但目前尚不清楚如何找到返回答案中每页的匹配数。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-02
    • 1970-01-01
    • 2015-03-09
    • 2016-02-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多