PHP/MySQL 更好的用户搜索
PHP/MySQL better user searching
为了重振我 14 多年前编写的代码。我发现我当时写的可爱的小设置是......在某些地方缺乏,即处理用户输入。
教训:永远不要低估用户通过验证器注入垃圾、拼写错误和欺骗的能力。
旧方法正在达到临界质量,因为现在 SELECT 下拉列表中有 470 个项目。我想重新设计流程的这一部分,这样我就不必担心它会遇到瓶颈。
所以我们的想法是构建一个模糊搜索方法,以便在打字员输入搜索字符串后,我们检查五个数据,所有这些数据都位于同一行。
我需要根据舞台名称检查提交的名称,两个也称为名称,以及他们的法定名称,并根据他们的舞台对 soundex() 索引进行最终检查名称(这会发现一些拼写错误,否则会遗漏)
作为 do/while 循环的一部分,我尝试了一个复杂的代码块来检查这些东西(它不起作用,主要是因为我认为我对比较的编码太严格了)。
在下面,var $Rin
将包含用户提供的名称。
$setr = mysql_query("SELECT ID,StageName,AKA1,AKA2,LegalName,SoundEx FROM performers");
IF ($R = mysql_fetch_array($setr)) {
do {
$RT = substr(trim($Rin), 5);
$RT1 = substr($R[1], 5);
$RT2 = substr($R[2], 5);
$RT3 = substr($R[3], 5);
$RT4 = substr($R[4], 5);
$RTx = soundex($RT);
IF ($RT == $RT1) {
$RHits[] = $R[0];
}
IF ($RT == $RT2) {
$RHits[] = $R[0];
}
IF ($RT == $RT3) {
$RHits[] = $R[0];
}
IF ($RT == $RT4) {
$RHits[] = $R[0];
}
IF ($RTx == $R[5]) {
$RHits[] = $R[0];
}
} while ($R = mysql_fetch_array($setr));
}
我的想法是,我将构建一个包含近命中 ID# 的数组,并将其填充到一个 select 下拉列表中,希望它的命中数少于整个 table。这意味着从该数组的内容中查询结果集,以便在 SELECT 下拉列表中显示表演者的姓名并将 ID# 作为这些选项的值传递。
那是我遇到 'I need to use an array in my WHERE clause' 问题的时候,在找到那个答案后,我开始怀疑我运气不好是因为下面的第 2 条规定。所以我开始寻找其他搜索方法,但我不确定我是否找到了任何方法,但更加困惑。
那么,是否有更好的方法来扫描单个 table 的六个字段,根据用户输入检查五个字段并记录第六个以显示在原始 table 的子集中?
思考过程:
针对整个 table,每条记录,按以下顺序针对这些测试测试 $Rin:
$Rin -> StageName
$Rin -> AKA1
$Rin -> AKA2
$Rin -> LegalName
soundex($Rin) -> SoundEx
这五项操作中的任何一项的命中都会将 ID# 添加到结果数组中,该结果数组用于将结果从 470 名表演者缩小到一个合理的列表以供选择。
规定:
1) 正如所写,我知道这很容易受到 SQL 注入攻击。
2) 服务器运行 PHP 4.4.9 和 MySQL 4.0.27-标准版,我无法升级。在花钱之前,我必须证明它有效。
3) 这是爱好级别的东西,不是我的日常工作。
4) 表演者经常在他们的名字中使用非英文名称或元素,这导致了数据输入人员的错别字和重复。
我已经找到了很多关于这类事情的 mysqli 和 PDO 答案,而且我看到很多事情只有一半有意义(比如下面的 link #4)。我正在努力加快处理这些事情的速度,同时尝试修复损坏的内容。
已经看过的地方:
- PHP mysql using an array in WHERE clause
- PHP/MySQL small-scale fuzzy search
- MySQL SubString Fuzzy Search
- Sophisticated Name Lookup
我在评论中提到 Javascript typeahead 库可能是您的不错选择。我发现 Twitter 的 Typeahead 库和 Bloodhound 引擎非常强大。不幸的是,该文档是一个混合包:只要您需要的内容与他们的示例非常相似,您就是黄金,但缺少某些细节(例如标记器的解释)。
在 Stack Overflow 上的 several questions re Typeahead 之一中,@JensAKoch 说:
To be honest, I think twitter gave up on typeahead.js. We look at 13000 stars, a full bugtracker with no maintainer and a broken software, last release 2015. I think that speaks for itself, or not? ... So, try one of the forks: github.com/corejavascript/typeahead.js
坦率地说,在简短的检查中,分叉处的文档看起来好一点,如果没有别的。你不妨看看。
Server-side代码:
使用旧版本 PHP 的所有注意事项均适用。我强烈建议重组以将 PDO 与 PHP 5 一起使用,但此示例按要求使用 PHP 4。
完全未经测试的 PHP 代码。 json_encode()
会更好,但直到 PHP 才出现 5. 您的端点将类似于:
headers("Content-Type: application/json");
$results = mysql_query(
"SELECT ID,StageName,AKA1,AKA2,LegalName,SoundEx FROM performers"
);
$fields = array("ID","StageName","AKA1","AKA2","LegalName","SoundEx");
echo "[";
$first = true;
while ($row = mysql_fetch_array($results)) {
($first) ? $first = false : echo ',';
echo "\n\t,{";
foreach($fields as $f) {
echo "\n\t\t\"{$f}\": \"".$row[$f]."\"";
}
echo "\n\t}";
}
echo "]";
Client-side代码:
这个例子使用了static JSON file as a stub for all of the results. If you anticipate your result set going over 1,000 entries, you should look into the remote
option of Bloodhound。这将需要您编写一些自定义 PHP 代码来处理查询,但它看起来与转储所有(或至少是您最常见的)数据的终点非常相似。
var actors = new Bloodhound({
// Each row is an object, not a single string, so we have to modify the
// default datum tokenizer. Pass in the list of object fields to be
// searchable.
datumTokenizer: Bloodhound.tokenizers.obj.nonword(
'StageName','AKA1','AKA2','LegalName','SoundEx'
),
queryTokenizer: Bloodhound.tokenizers.whitespace,
// URL points to a json file that contains an array of actor JSON objects
// Visit the link to see details
prefetch: 'https://gist.githubusercontent.com/tag/81e4450de8eca805f436b72e6d7d1274/raw/792b3376f63f89d86e10e78d387109f0ad7903fd/dummy_actors.json'
});
// passing in `null` for the `options` arguments will result in the default
// options being used
$('#prefetch .typeahead').typeahead(
{
highlight: true
},
{
name: 'actors',
source: actors,
templates: {
empty: "<div class=\"empty-message\">No matches found.</div>",
// This is simply a function that accepts an object.
// You may wish to consider Handlebars instead.
suggestion: function(obj) {
return '<div class="actorItem">'
+ '<span class="itemStageName">'+obj.StageName+"</span>"
+ ', <em>legally</em> <span class="itemLegalName">'+obj.LegalName+"</span>"
}
//suggestion: Handlebars.compile('<div><strong>{{value}}</strong> – {{year}}</div>')
},
display: "LegalName" // name of object key to display when selected
// Instead of display, you can use the 'displayKey' option too:
// displayKey: function(actor) {
// return actor.LegalName;
// }
});
/* These class names can me specified in the Typeahead options hash. I use the defaults here. */
.tt-suggestion {
border: 1px dotted gray;
padding: 4px;
min-width: 100px;
}
.tt-cursor {
background-color: rgb(255,253,189);
}
/* These classes are used in the suggestion template */
.itemStageName {
font-size: 110%;
}
.itemLegalName {
font-size: 110%;
color: rgb(51,42,206);
}
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
<script src="https://twitter.github.io/typeahead.js/releases/latest/typeahead.bundle.js"></script>
<p>Type something here. A good search term might be 'C'.</p>
<div id="prefetch">
<input class="typeahead" type="text" placeholder="Name">
</div>
为方便起见,这里是Gist of the client-side code。
为了重振我 14 多年前编写的代码。我发现我当时写的可爱的小设置是......在某些地方缺乏,即处理用户输入。
教训:永远不要低估用户通过验证器注入垃圾、拼写错误和欺骗的能力。
旧方法正在达到临界质量,因为现在 SELECT 下拉列表中有 470 个项目。我想重新设计流程的这一部分,这样我就不必担心它会遇到瓶颈。
所以我们的想法是构建一个模糊搜索方法,以便在打字员输入搜索字符串后,我们检查五个数据,所有这些数据都位于同一行。
我需要根据舞台名称检查提交的名称,两个也称为名称,以及他们的法定名称,并根据他们的舞台对 soundex() 索引进行最终检查名称(这会发现一些拼写错误,否则会遗漏)
作为 do/while 循环的一部分,我尝试了一个复杂的代码块来检查这些东西(它不起作用,主要是因为我认为我对比较的编码太严格了)。
在下面,var $Rin
将包含用户提供的名称。
$setr = mysql_query("SELECT ID,StageName,AKA1,AKA2,LegalName,SoundEx FROM performers");
IF ($R = mysql_fetch_array($setr)) {
do {
$RT = substr(trim($Rin), 5);
$RT1 = substr($R[1], 5);
$RT2 = substr($R[2], 5);
$RT3 = substr($R[3], 5);
$RT4 = substr($R[4], 5);
$RTx = soundex($RT);
IF ($RT == $RT1) {
$RHits[] = $R[0];
}
IF ($RT == $RT2) {
$RHits[] = $R[0];
}
IF ($RT == $RT3) {
$RHits[] = $R[0];
}
IF ($RT == $RT4) {
$RHits[] = $R[0];
}
IF ($RTx == $R[5]) {
$RHits[] = $R[0];
}
} while ($R = mysql_fetch_array($setr));
}
我的想法是,我将构建一个包含近命中 ID# 的数组,并将其填充到一个 select 下拉列表中,希望它的命中数少于整个 table。这意味着从该数组的内容中查询结果集,以便在 SELECT 下拉列表中显示表演者的姓名并将 ID# 作为这些选项的值传递。
那是我遇到 'I need to use an array in my WHERE clause' 问题的时候,在找到那个答案后,我开始怀疑我运气不好是因为下面的第 2 条规定。所以我开始寻找其他搜索方法,但我不确定我是否找到了任何方法,但更加困惑。
那么,是否有更好的方法来扫描单个 table 的六个字段,根据用户输入检查五个字段并记录第六个以显示在原始 table 的子集中?
思考过程:
针对整个 table,每条记录,按以下顺序针对这些测试测试 $Rin:
$Rin -> StageName
$Rin -> AKA1
$Rin -> AKA2
$Rin -> LegalName
soundex($Rin) -> SoundEx
这五项操作中的任何一项的命中都会将 ID# 添加到结果数组中,该结果数组用于将结果从 470 名表演者缩小到一个合理的列表以供选择。
规定:
1) 正如所写,我知道这很容易受到 SQL 注入攻击。
2) 服务器运行 PHP 4.4.9 和 MySQL 4.0.27-标准版,我无法升级。在花钱之前,我必须证明它有效。
3) 这是爱好级别的东西,不是我的日常工作。
4) 表演者经常在他们的名字中使用非英文名称或元素,这导致了数据输入人员的错别字和重复。
我已经找到了很多关于这类事情的 mysqli 和 PDO 答案,而且我看到很多事情只有一半有意义(比如下面的 link #4)。我正在努力加快处理这些事情的速度,同时尝试修复损坏的内容。
已经看过的地方:
- PHP mysql using an array in WHERE clause
- PHP/MySQL small-scale fuzzy search
- MySQL SubString Fuzzy Search
- Sophisticated Name Lookup
我在评论中提到 Javascript typeahead 库可能是您的不错选择。我发现 Twitter 的 Typeahead 库和 Bloodhound 引擎非常强大。不幸的是,该文档是一个混合包:只要您需要的内容与他们的示例非常相似,您就是黄金,但缺少某些细节(例如标记器的解释)。
在 Stack Overflow 上的 several questions re Typeahead 之一中,@JensAKoch 说:
To be honest, I think twitter gave up on typeahead.js. We look at 13000 stars, a full bugtracker with no maintainer and a broken software, last release 2015. I think that speaks for itself, or not? ... So, try one of the forks: github.com/corejavascript/typeahead.js
坦率地说,在简短的检查中,分叉处的文档看起来好一点,如果没有别的。你不妨看看。
Server-side代码:
使用旧版本 PHP 的所有注意事项均适用。我强烈建议重组以将 PDO 与 PHP 5 一起使用,但此示例按要求使用 PHP 4。
完全未经测试的 PHP 代码。 json_encode()
会更好,但直到 PHP 才出现 5. 您的端点将类似于:
headers("Content-Type: application/json");
$results = mysql_query(
"SELECT ID,StageName,AKA1,AKA2,LegalName,SoundEx FROM performers"
);
$fields = array("ID","StageName","AKA1","AKA2","LegalName","SoundEx");
echo "[";
$first = true;
while ($row = mysql_fetch_array($results)) {
($first) ? $first = false : echo ',';
echo "\n\t,{";
foreach($fields as $f) {
echo "\n\t\t\"{$f}\": \"".$row[$f]."\"";
}
echo "\n\t}";
}
echo "]";
Client-side代码:
这个例子使用了static JSON file as a stub for all of the results. If you anticipate your result set going over 1,000 entries, you should look into the remote
option of Bloodhound。这将需要您编写一些自定义 PHP 代码来处理查询,但它看起来与转储所有(或至少是您最常见的)数据的终点非常相似。
var actors = new Bloodhound({
// Each row is an object, not a single string, so we have to modify the
// default datum tokenizer. Pass in the list of object fields to be
// searchable.
datumTokenizer: Bloodhound.tokenizers.obj.nonword(
'StageName','AKA1','AKA2','LegalName','SoundEx'
),
queryTokenizer: Bloodhound.tokenizers.whitespace,
// URL points to a json file that contains an array of actor JSON objects
// Visit the link to see details
prefetch: 'https://gist.githubusercontent.com/tag/81e4450de8eca805f436b72e6d7d1274/raw/792b3376f63f89d86e10e78d387109f0ad7903fd/dummy_actors.json'
});
// passing in `null` for the `options` arguments will result in the default
// options being used
$('#prefetch .typeahead').typeahead(
{
highlight: true
},
{
name: 'actors',
source: actors,
templates: {
empty: "<div class=\"empty-message\">No matches found.</div>",
// This is simply a function that accepts an object.
// You may wish to consider Handlebars instead.
suggestion: function(obj) {
return '<div class="actorItem">'
+ '<span class="itemStageName">'+obj.StageName+"</span>"
+ ', <em>legally</em> <span class="itemLegalName">'+obj.LegalName+"</span>"
}
//suggestion: Handlebars.compile('<div><strong>{{value}}</strong> – {{year}}</div>')
},
display: "LegalName" // name of object key to display when selected
// Instead of display, you can use the 'displayKey' option too:
// displayKey: function(actor) {
// return actor.LegalName;
// }
});
/* These class names can me specified in the Typeahead options hash. I use the defaults here. */
.tt-suggestion {
border: 1px dotted gray;
padding: 4px;
min-width: 100px;
}
.tt-cursor {
background-color: rgb(255,253,189);
}
/* These classes are used in the suggestion template */
.itemStageName {
font-size: 110%;
}
.itemLegalName {
font-size: 110%;
color: rgb(51,42,206);
}
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
<script src="https://twitter.github.io/typeahead.js/releases/latest/typeahead.bundle.js"></script>
<p>Type something here. A good search term might be 'C'.</p>
<div id="prefetch">
<input class="typeahead" type="text" placeholder="Name">
</div>
为方便起见,这里是Gist of the client-side code。