在php中搜索大量文本
我有一个基于Symfony2的项目,它允许用户创建一系列规则,将项目分配到不同的类别。它是一个后端项目,因此只有少数用户访问它 规则基于短语,项目是文本对象。我正试图找出一种最好的方法来搜索这些文本对象,并尽可能快速、顺利地将规则应用于这些文本对象 例如,如果用户创建了5条规则(短语:在php中搜索大量文本,php,mysql,symfony,
elasticsearch,Php,Mysql,Symfony,
elasticsearch,我有一个基于Symfony2的项目,它允许用户创建一系列规则,将项目分配到不同的类别。它是一个后端项目,因此只有少数用户访问它 规则基于短语,项目是文本对象。我正试图找出一种最好的方法来搜索这些文本对象,并尽可能快速、顺利地将规则应用于这些文本对象 例如,如果用户创建了5条规则(短语:篮球,足球,棒球,游泳,跑步),则所有匹配这些短语的文本对象都应分配给运动类别,我想我可以使用ElasticSearch快速返回这些对象的ID,然后使用简单的插入或更新mysql查询保存分配 我担心性能,例如,如果
篮球
,足球
,棒球
,游泳
,跑步
),则所有匹配这些短语的文本对象都应分配给运动类别,我想我可以使用ElasticSearch快速返回这些对象的ID,然后使用简单的插入或更新mysql查询保存分配
我担心性能,例如,如果索引中有一百万个文本对象,比如说,有50k个对象与这些规则匹配,部分运行搜索查询,例如将每个ElasticSearch查询的范围限制为50k(迭代整个索引),那么,将数据更新/插入MySQL将是一种可接受的方法
因此,运行查询(伪):
等等
ElasticSearch是这种处理的好选择吗?或者我应该坚持使用MySQL并使用regexp运行查询(当然是分块)
也许我可以检查一下现有的解决方案?不幸的是,我仅限于PHP和Symfony2,但如果有任何更好的解决方案值得检查,我可能能够向客户推荐
希望有人能帮助我,欢迎任何帮助。如果您使用的是
文本
字段,您可以在表上创建全文
索引:
CREATE TABLE texts(
id int not null auto_increment primary key,
text_field1 text,
text_field2 text,
text_field3 text
)Engine = MyISAM; -- InnoDB supports fulltext indexes since v5.6
CREATE FULLTEXT INDEX itexts on texts(text_field1,text_field2,text_field3);
然后可以使用全文表达式进行搜索(至少查找一个术语):
或者找到所有术语
SELECT * FROM texts
WHERE MATCH (text_field1,text_field2,text_field3)
AGAINST ('+basketball +baseball +football');
现在,在您的项目中,您可以将规则转换为全文搜索表达式,并使用常规symfony查询执行它
有关全文搜索的详细信息:
这绝对是Elasticsearch擅长的。例如,我的笔记本电脑上有一个索引(Macbook Air,所以没什么比这更强大),其中有4095005个代表不同名称的文档 使用过滤器 您请求的用例基本上只是精确的匹配过滤。为此,我们可以使用Elasticsearch过滤器,其速度非常快。下面是一个示例筛选器,用于查找五个不同的名称:
curl -XGET "http://localhost:9200/test_names/_search" -d'
{
"query": {
"filtered": {
"filter": {
"bool": {
"should": [
{
"term": {
"first_name": "miguel"
}
},
{
"term": {
"first_name": "ella"
}
},
{
"term": {
"first_name": "almeta"
}
},
{
"term": {
"first_name": "garret"
}
},
{
"term": {
"first_name": "simon"
}
}
]
}
}
}
}
}'
以及响应的顶部部分:
{
"took": 85,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 3755,
"max_score": 1,
....
take:85
意味着过滤400万个文档并找到匹配的3755需要85毫秒。如果我再次运行相同的筛选器,我会得到:
{
"took": 4,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 3755,
"max_score": 1,
....
现在执行时间仅为4ms。这是因为Elasticsearch缓存过滤器位集,因此后续对“miguel”、“ella”、“almeta”、“garret”或“simon”的搜索将以极快的速度执行(直到这些值从缓存中逐出)
使用查询
查询提供了更健壮的全文搜索,并将根据文档匹配程度对文档进行排序。所以你可以这样做:
curl -XGET "http://localhost:9200/test_names/_search" -d'
{
"query" : {
"match": {
"first_name": "miguel ella almeta garret simon"
}
}
}'
这基本上等同于“米格尔、埃拉、阿尔梅塔、加勒特或西蒙”,但经过加权后,匹配更多术语的文档得分更高。它在54毫秒内执行(再次在我的笔记本电脑上搜索400万个文档):
这只是Elasticsearch所能做的皮毛而已。但是我可以肯定地说,它可以提供您所描述的功能,而且速度非常非常快。谢谢您的回答,伊万,我熟悉MySQL中的全文,虽然我不知道InnoDB表已经支持它,但需要检查文档。谢谢还有,在使用全文搜索时,有没有办法判断哪个短语是匹配的?例如,我搜索至少与一个短语匹配的项目:篮球、足球、棒球。MySQL返回100条记录,有没有办法告诉每个返回的记录哪个短语匹配?您可以添加表达式:
MATCH(text\u field1,text\u field2,text\u field3)对(“+basketball+basketball+football”)
作为列名,并按该列排序以获得匹配率高的记录。谢谢Ivan,但这并不是我所需要的,它给了我分数,而不是匹配每条记录的短语。我在玩全文游戏时遇到的另一个问题是特殊字符的处理,例如,当试图查找包含URL部分的记录时:“nba.com/players”,它返回匹配nba、com或players的记录。有什么办法可以解决这个问题吗?
{
"took": 4,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 3755,
"max_score": 1,
....
curl -XGET "http://localhost:9200/test_names/_search" -d'
{
"query" : {
"match": {
"first_name": "miguel ella almeta garret simon"
}
}
}'
{
"took": 54,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 3755,
"max_score": 0.8923058,
...