PHP 和 MySql 索引的最佳文本搜索逻辑
Best text searching logic on PHP and MySql index
正在尝试为我的 CMS 设计索引和搜索系统。
到目前为止我所做的是:
数据库中的三列 Table (POST table) (InnoDB - utf8mb4_unicode_ci).
原创内容(JSON格式)。
标记(从 JSON 中获取所有文本,删除停用词,然后将标记保存到该列),同时在该列上启用索引。
令牌(直接来自标记字段,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"(或索引或索引或索引等)的帖子。
正在尝试为我的 CMS 设计索引和搜索系统。
到目前为止我所做的是:
数据库中的三列 Table (POST table) (InnoDB - utf8mb4_unicode_ci).
原创内容(JSON格式)。
标记(从 JSON 中获取所有文本,删除停用词,然后将标记保存到该列),同时在该列上启用索引。
令牌(直接来自标记字段,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"(或索引或索引或索引等)的帖子。