【问题标题】:Optimising a sentence search across multiple documents跨多个文档优化句子搜索
【发布时间】:2017-07-06 20:58:02
【问题描述】:

我在优化我在 PHP 中创建的搜索时遇到问题,该搜索是一次性使用的,因此灵活性并不那么重要。

我有一系列句子,例如:

$arr = [
   'potato',
   'hi my name is Ivan'
    ..
];

该数组包含大约 1k 个句子。

我有一个包含 200 GB 文件的硬盘。

我需要搜索所有文件,看看这些句子是否在文件中,如果是,则以特定格式打印出路径、校验和等。

我面临的问题是搜索时间,当做这样的事情时,这似乎效率低下:

$objections = [];
foreach ($files as $file) {

    if (!in_array($file->getExtension(), $allowedExt))
        continue;

    $txt = file_get_contents($file);

    foreach ($words as $word) {
        if (stripos($txt, $word) !== false ||
            stripos($file->getFilename(), $word) !== false
            ) {
            $file->c_md5 = getCMD5($file);
            $objections[] = $file;
        }
    }
}

搜索本身需要 1 小时以上的时间,而我正在使用最新 i7 的新 MacBook。使用 PHP 内存等最大化。

在单词数组中匹配哪个单词无关紧要,所以我想知道是否有更聪明的方法来执行搜索,而不是在文件循环中循环单词。带有 OR 的很长的 REGEX 字符串会更快吗?

或者有没有第三种方法非常快。

【问题讨论】:

  • 我不知道一个大的正则表达式是否会更快,但如果你知道如何做一个(你似乎知道),只需尝试它并进行基准测试。有了这么多的数据,无论如何它可能会非常慢。

标签: php search


【解决方案1】:

肯定的答案是正则表达式。您可能应该将其分为三个阶段:

  1. 将您的句子列表转换为一个大的正则表达式或一个正则表达式列表以循环运行。您可以将空格转换为空格\s 并使搜索不贪心/regex/U

  2. 遍历文件并删除行尾,将其替换为空格。以防一个句子跨越多行。

  3. 使用preg_match 查明句子是否在文件中。如果您决定使用多个正则表达式,则可以在循环中执行此操作。

【讨论】:

  • 我认为如果你使用正则表达式,你应该有一个大的正则表达式,否则你基本上只是实现了一种较慢的方式来完成原始代码已经完成的工作。正则表达式对于简单的字符串比较效率不高。 (但如果您不需要遍历每个文件上的每个句子,它会更有效)。
  • @MagnusEriksson 我同意,一个大的正则表达式应该更好。
【解决方案2】:

正则表达式只会为您节省很少或什么也不会。

你可以在命中时跳出循环。

Foreach{
    If(hit){
        Break;
    }
}

这样可以节省搜索其余部分。因此,如果您进行第二次迭代,您将不再需要对该文档进行另外 998 次迭代。

您总是会遇到的问题是打开文档,毫无疑问,您的大部分性能都会丢失。

如果您需要多次执行此操作,则可能值得考虑将所有文件索引到专为全文搜索 ElasticSearch 设计的数据库中。然后,在您的数据库中,您可以简单地保留对物理文件的引用,无论下一个操作是什么。 所有这些文件的初始加载和索引无疑会占用大量资源和时间。但是,一旦完成,您的搜索将真的很快。

【讨论】:

  • 啊,是的,我忘了添加这个,匹配很少,但是是的,应该可以节省一些时间
  • 然后我会认真考虑实现一个数据库,如前面提到的 ElasticSearch。您已经加载了所有文件内容,将其放入 intiba 数据库将是微不足道的,然后搜索将需要几个函数。 ElasticSearch 的文档很棒。
  • 甚至在 MySQL 上进行全文搜索可能会更快。
  • 很有可能,是的。但是,如果您要麻烦地用这么多数据填充数据库,那么合乎逻辑的选择是使用为这种操作设计的数据库,没有关系数据并且写入很少。虽然 MySQL 可能更容易设置。
【解决方案3】:

您可以使用exec

创建一个 test.php 文件并尝试此代码

  <?php
    exec('grep '.escapeshellarg('end').' '."./test.php",$result);              
    print_r($result);
    /* serach for some sentences
     * keep seraching
     * end of comment */
  ?>

当然,您必须先测试其性能,然后根据您的需要进行定制

【讨论】:

    猜你喜欢
    • 2012-02-14
    • 2017-12-25
    • 1970-01-01
    • 2016-03-15
    • 2021-05-16
    • 1970-01-01
    • 2012-04-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多