在php中搜索大量文本

时间:2014-08-19 19:32:22

标签: php mysql symfony elasticsearch

我有一个项目,它基于Symfony2,允许用户创建一些规则,用于将项目分配到不同的类别。它是一个后端项目,因此只有少数用户可以访问它。

规则基于短语,项目是文本对象。我试图找出一种最好的方法来尽可能快速,平稳地搜索和应用规则到那些文本对象。

例如,如果用户创建了5条规则(短语:basketballfootballbaseballswimmingrunning)以及所有文字对象匹配任何这些短语都应该分配到SPORTS类别,我想我可以使用ElasticSearch快速返回这些对象的ID,然后使用简单的INSERT或UPDATE mysql查询保存赋值。

我担心性能,如果在索引中有例如1百万个文本对象,并且说有50k个对象匹配这些规则,在部分中运行搜索查询,例如将范围限制为50k ElasticSearch查询(迭代整个索引),然后更新/插入数据到MySQL是一种可接受的方法吗?

所以,运行查询(伪):

$ids = elasticSearch->setPhrases('basketball OR baseball OR football')->find()->limit(1, 50000);
$ids = elasticSearch->setPhrases('basketball OR baseball OR football')->find()->limit(50000, 100000);

ElasticSearch是否适合进行此类处理?或者我应该坚持MySQL并使用regexp运行查询(例如当然是块)?

也许我可以检查现有的解决方案?不幸的是,我只限于PHP和Symfony2,但如果有更好的解决方案值得检查,我可能会建议客户端。

希望有人能帮助我,欢迎任何帮助。

2 个答案:

答案 0 :(得分:1)

假设您使用的是text字段,则可以在桌面上创建FULLTEXT索引:

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');

或找到所有条款

SELECT * FROM texts 
WHERE MATCH (text_field1,text_field2,text_field3) 
AGAINST ('+basketball +baseball +football');

现在,在您的项目中,您可以将规则转换为全文搜索表达式,并使用常规的symfony查询执行它。

有关FULLTEXT搜索的更多信息: http://dev.mysql.com/doc/refman/5.0/en/fulltext-boolean.html

答案 1 :(得分:0)

这绝对是Elasticsearch所擅长的。例如,我的笔记本电脑上有一个索引(Macbook Air,所以没什么好吃的),它有4,095,005个代表不同名字的文件。

使用过滤器

您请求的用例基本上只是完全匹配过滤。为此,我们可以使用非常快的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,
 ....

took: 85意味着需要85毫秒来过滤400万个文档并找到匹配的3755.如果我再次运行相同的过滤器,我会得到:

{
 "took": 4,
 "timed_out": false,
 "_shards": {
    "total": 5,
    "successful": 5,
    "failed": 0
 },
 "hits": {
    "total": 3755,
    "max_score": 1,
 ....

现在执行时间只有4毫秒。这是因为Elasticsearch缓存了过滤器位集,因此后续搜索" miguel"," ella"," almeta"," garret"或者"西蒙"将以极快的速度执行(直到这些值从缓存中逐出)

使用查询

查询提供更强大的全文搜索功能,并根据文档的匹配程度对文档进行排名。所以你可以这样做:

curl -XGET "http://localhost:9200/test_names/_search" -d'
{
  "query" : {
    "match": {
      "first_name": "miguel ella almeta garret simon"
    }
  }
}'

这基本上相当于" miguel OR ella OR almeta或garret或simon",但加权使得匹配更多术语的文档得分更高。它在54ms内执行(再次在我的笔记本电脑上搜索4m文件):

{
   "took": 54,
   "timed_out": false,
   "_shards": {
      "total": 5,
      "successful": 5,
      "failed": 0
   },
   "hits": {
      "total": 3755,
      "max_score": 0.8923058,
    ...

这只是触及Elasticsearch可以做的事情的表面。但我肯定会说它可以提供你所描述的功能,而且非常非常快。