PHP 和 MySql 索引的最佳文本搜索逻辑

Best text searching logic on PHP and MySql index

正在尝试为我的 CMS 设计索引和搜索系统。

到目前为止我所做的是:

数据库中的三列 Table (POST table) (InnoDB - utf8mb4_unicode_ci).

  1. 原创内容(JSON格式)。

  2. 标记(从 JSON 中获取所有文本,删除停用词,然后将标记保存到该列),同时在该列上启用索引。

  3. 令牌(直接来自标记字段,Whosebug 就像标记一样。),也索引该字段。

我的 CMS 正在研究这种方法,但是由于原始内容产生的标记,索引和 table 大小增加了。

有什么改进的方法吗?

我来自 JAVA 背景(使用 Lucane)。我很难制作所有停用词的字典。如果有人知道用于删除停用词的预制 API os 脚本。

对于 tag 系统,如堆栈溢出或许多其他系统,我将使用 3 tables。

  • 主要table跟你json
  • 一个标签table
  • 用于多对多关系的交汇点或桥 table。

使用单独的标签 table,您可以使用 IN= 进行搜索。您可以使用 table 作为自动完成。您 "normalize" 您的数据,正在减少它。等等...它确实为查询添加了一些连接和一些复杂性,但我想说它仍然相当微不足道。

我还在 Jquery 中编写了一个文本输入插件来识别文本字段中的标签。它在很大程度上没有记录,但欢迎您使用它。

https://github.com/ArtisticPhoenix/jQuery-Plugins/tree/master/jQuery-Plugins/jqWall

这是一个小项目,我们需要一个像 "twitter" 一样的带有主题标签的墙,因此可以将其设置为在用户键入以 # 开头的标签时识别和自动完成。允许用户使用标签自动完成文本并减少错误或标签拼写错误的发生。您可以定义要匹配的内容,并且它有一些其他功能的回调。使用起来非常简单。

您可以在此 fiddle 中看到基本设置(这是非常标准的多对多设置)

https://www.db-fiddle.com/f/2WSaLjtnuDrZE2CcVhAe9S/1

它为您提供的一些其他功能例如此查询。

SELECT
    t.*
FROM
    tags AS t
LEFT JOIN
    posts_tags AS pt ON t.id = pt.tag_id
WHERE
    pt.tag_id IS NULL;

这将 select 所有未在 post 中使用的标签。而且很容易计算有多少 post 有给定的标签。

SELECT
    count(p.id) AS total
FROM
    posts AS p
JOIN
    posts_tags AS pt ON p.id = pt.post_id
JOIN
    tags AS t ON pt.tag_id = t.id
WHERE
    tag = 'Programming';

如果您不习惯这种关系的工作方式和使用联接,这可能看起来很复杂。但是当你有一个关键字列表时,请考虑计算有多少 post。您将使用 keywords LIKE '%Java%' 并且它可能会损害您的性能(每当您使用 %word 前面的通配符时)。还要注意的是,如果你有像 Javascript 这样的关键字,你会用这个查询来计算它,而 Java 根本不像 Javascript 所以准确计算有多少 posts 你使用了部分字符串匹配。

如果您使用单个字段,我唯一建议的是使用分隔符,并将其包含在内容的前后,就像这样。

 |tag1|tag2|tag|tag10|

这样做的原因是您可以在搜索中包含分隔符,考虑这个部分查询

WHERE tag LIKE "%tag%"

现在,如果您搜索它,您会找到所有标签,但因为我们有分隔符,所以您可以这样做

WHERE tag LIKE "%|tag|%"

这会限制比赛。但要使其正常工作,您需要在列表的开头和结尾使用分隔符。考虑一下这个 tag1|tag10|tag 没有那些我们无法将 |tag|%|tag|% 相匹配的东西,所以它会分崩离析。对于轻度使用的系统,这可能 ok 有效,但单独的 table 仍然更好,因为索引的性质以及它们如何处理通配符搜索。

console.log("type any of these 'Java', 'JavaScript', 'Programming', 'PHP' and press enter to select from list, or use the mouse.");
console.log("press enter to simulate submission, logs contents.");

$('#test').jqWall({
  id: 'jqWall',
  autoComplete: {
    cache: ['Java', 'JavaScript', 'Programming', 'PHP'],
    match: /([^\s]+)$/, //$ ends with is required
    search : function(term, matches, callback){
        if(matches.lenght == 0){
        //get matches array from server by AJAX using `term`
        }
        callback( matches ); //requred
 
      },
  },
  submit : function(wrapper){
    //press enter to submit
    var contents = wrapper.find('textarea').val();
    //you could post contents back to server to save.
    console.log(contents);
  }
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://rawgit.com/ArtisticPhoenix/jQuery-Plugins/master/jQuery-Plugins/jqWall/jqWall.js"></script>
<link href="https://rawgit.com/ArtisticPhoenix/jQuery-Plugins/master/jQuery-Plugins/jqWall/jqWall.css" rel="stylesheet" />
<style type="text/css">
  #jqWall textarea {
    width: 400px;
    height: 200px;
  }
</style>


<div id="test" style=""></div>

按照您描述两个 "tokens" 列的方式,每个列都需要一个 FULLTEXT 索引。然后使用 MATCH(token1) AGAINST('+mysql +index IN BOOLEAN MODE) 查找同时讨论 "mysql" 和 "index"(或索引或​​索引或索引等)的帖子。