快速文本search超过600,000个文件

我有一个PHP,Linux服务器。 它有一个名为notes_docs的文件夹,其中包含60多万个txt文件。 notes_docs的文件夹结构如下所示 –

  - notes_docs - files_txt - 20170831 - 1_837837472_abc_file.txt - 1_579374743_abc2_file.txt - 1_291838733_uridjdh.txt - 1_482737439_a8weele.txt - 1_733839474_dejsde.txt - 20170830 - 20170829 

我必须提供一个可以在浏览器上显示结果的快速文本search工具。 因此,如果我的用户search“纽约”,那么所有在他们中有“纽约”的文件都应该返回到一个数组中。 如果用户search“foo”,则应返回所有带有“foo”的文件。

我已经尝试过使用scandirDirectory Iterator的代码,这太慢了。 search需要一分多钟,即使search没有完成。 我试过Ubuntu的find ,这又慢又花了一分钟才完成。 因为有太多的文件夹迭代,并且notes_docs当前大小超过20 GB。

我可以使用任何解决scheme,使其更快,是值得欢迎的。 我可以进行devise更改,将我的PHP代码整合到另一个语言代码中。 在极端的情况下,我也可以做基础设施的改变(如在内存中使用)。

我想知道工业界人士如何做到这一点? 在确实,邮编招聘人员都提供文件search。

请注意,我有2GB – 4GB的内存,所以在RAM上加载所有的文件是不能接受的。

编辑下面所有的input都很好。 对于那些迟来的人来说,我们最终使用了Lucene进行索引和文本search。 它performance非常好

为了保持简单:每次你想搜索时,没有快捷的方式来打开,搜索和关闭600k文件。 “超过一分钟”的基准可能是单个测试帐户。 如果您打算通过多用户网站搜索这些内容,则可能很快就会忘记这一点,因为disk IO将不在图表中并阻止整个服务器。

所以你唯一的选择是索引所有文件。 就像其他快速搜索工具一样。 无论您是使用评论中提到的Solr还是ElasticSearch,或者构建您自己的东西。 这些文件将被索引。

考虑到txt文件是您收到的pdf文件的文本版本,我打赌最简单的解决方案是将文本写入数据库而不是文件。 它不会占用更多的磁盘空间。

然后你可以在你的数据库上启用full text searchmysqlmssql等支持),我相信响应时间会好很多。 请记住,创建这些indexes确实需要存储空间,但其他解决方案也是如此。

现在,如果你真的想加快速度,你可以尝试更详细的解析简历。 尝试和检索您定期搜索的位置,教育,语言和其他信息,并将其放在单独的表格/列中。 这是一项非常艰巨的任务,几乎是一个独立的项目,但如果您想要一个有价值的搜索结果,这是一条路。 由于在没有上下文的情况下搜索文本会产生非常不同的结果,只要想一想你的例子“纽约”:

  1. 我住在纽约
  2. 我在纽约大学学习
  3. 我喜欢艾丽西亚·凯斯(Alicia Keys)的个人简历“纽约”
  4. 我曾在纽约比萨工作
  5. 我出生在英国纽约郡
  6. 我花了一个夏季繁殖纽约梗犬。

我不会太深,但我会尝试为创建一个概念验证提供指导。

1

首先从这里下载并提取弹性搜索: https : //www.elastic.co/downloads/elasticsearch然后运行它:

 bin/elasticsearch 

2

下载https://github.com/dadoonet/fscrawler#download-fscrawler解压缩并运行它:

 bin/fscrawler myCustomJob 

然后停止(Ctrl-C)并编辑相应的myCustomJob/_settings.json (它已经自动创建,路径被打印在控制台上)。
您可以编辑属性: "url" (要扫描的路径), "update_rate" (可以是1m ), "includes" (例如["*.pdf","*.doc","*.txt"] ), "index_content" (使其为false,只保留在文件名)。

再次运行:

 bin/fscrawler myCustomJob 

注意:索引是您稍后可能想要使用代码执行的操作,但现在将使用直接与弹性对话的fscrawler自动完成。

3

现在开始添加文件到你在"url"属性中指定的目录。

4

下载Chrome的高级休息客户端 ,并进行以下POST

网址: http:// localhost:9200 / _search

原始有效载荷

 { "query": { "wildcard": {"file.filename":"aFileNameToSearchFor*"} } } 

你会得到匹配文件的列表。 注意: fscrawler正在索引key: file.filename下的文件名。

现在,您可以使用PHP来执行此查询,而不是使用高级休息客户端 。 或者通过REST调用上面的url,或者使用php-client api: https : //www.elastic.co/guide/en/elasticsearch/client/php-api/current/_search_operations.html

这同样代表索引: https : //www.elastic.co/guide/en/elasticsearch/client/php-api/current/_indexing_documents.html

如果要将所有文件信息保存到数据库中:

 <?php function deep_scandir( $dir, &$query, &$files) { $count = 0; if(is_dir($dir)) { if ($dh = opendir($dir)) { while (($item = readdir($dh)) !== false) { if($item != '.' && $item != '..') { if(is_dir($dir.'/'.$item)){ deep_scandir($dir.'/'.$item, $query, $files); }else{ $count++; preg_match("/(\d\_\d+)\_(.*)\.txt/i", $item, $matches); if(!empty($matches)){ $no = $matches[1]; $str = $matches[2]; $files[$dir][$no] = $str; $content = addcslashes( htmlspecialchars( file_get_contents($dir.'/'.$item) ), "\\\'\"" ); $query[] = "INSERT INTO `mytable` (id, key, value, path, content) VALUES\n(NULL, '$no', '$str', '$dir/$item', '$content');"; } } } } closedir($dh); } } } echo '<pre>'; $dir = 'notes_docs/files_txt'; $query = []; $files = []; deep_scandir($dir, $query, $files); print_r($files); echo '<br>'; print_r($query); 

现在你可以执行数组中的每一行

 foreach($query as $no=>$line){ mysql_query($line) or trigger_error("Couldn't execute query no: '$no' [$line]"); } 

输出:

 Array ( [notes_docs/files_txt/20170831] => Array ( [1_291838733] => uridjdh [1_482737439] => a8weele [1_579374743] => abc2_file [1_733839474] => dejsde [1_837837472] => abc_file ) ) Array ( [0] => INSERT INTO `mytable` (id, key, value, path, content) VALUES (NULL, '1_291838733', 'uridjdh', 'notes_docs/files_txt/20170831/1_291838733_uridjdh.txt', 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus in nisl quis lectus sagittis ullamcorper at faucibus urna. Suspendisse tristique arcu sit amet ligula cursus pretium vitae eu elit. Nullam sed dolor ornare ex lobortis posuere. Quisque venenatis laoreet diam, in imperdiet arcu fermentum eu. Aenean molestie ligula id sem ultricies aliquet non a velit. Proin suscipit interdum vulputate. Nullam finibus gravida est, et fermentum est cursus eu. Integer sed metus ac urna molestie finibus. Aenean hendrerit ante quis diam ultrices pellentesque. Duis luctus turpis id ipsum dictum accumsan. Curabitur ornare nisi ligula, non pretium nulla venenatis sed. Aenean pharetra odio nec mi aliquam molestie. Fusce a condimentum nisl. Quisque mattis, nulla suscipit condimentum finibus, leo ex eleifend felis, vel efficitur eros turpis nec sem. '); [1] => INSERT INTO `mytable` (id, key, value, path, content) VALUES (NULL, '1_482737439', 'a8weele', 'notes_docs/files_txt/20170831/1_482737439_a8weele.txt', 'Nunc et odio sed odio rhoncus venenatis congue non nulla. Aliquam dictum, felis ac aliquam luctus, purus mi dignissim magna, vitae pharetra risus elit ac mi. Sed sodales dui semper commodo iaculis. Nunc vitae neque ut arcu gravida commodo. Fusce feugiat velit et felis pharetra posuere sit amet sit amet neque. Phasellus iaculis turpis odio, non consequat nunc consectetur a. Praesent ornare nisi non accumsan bibendum. Nunc vel ultricies enim, consectetur fermentum nisl. Sed eu augue ac massa efficitur ullamcorper. Ut hendrerit nisi arcu, a sagittis velit viverra ac. Quisque cursus nunc ac tincidunt sollicitudin. Cras eu rhoncus ante, ac varius velit. Mauris nibh lorem, viverra in porttitor at, interdum vel elit. Aliquam imperdiet lacus eu mi tincidunt volutpat. Vestibulum ut dolor risus. '); [2] => INSERT INTO `mytable` (id, key, value, path, content) VALUES (NULL, '1_579374743', 'abc2_file', 'notes_docs/files_txt/20170831/1_579374743_abc2_file.txt', 'Vivamus aliquet id elit vitae blandit. Proin laoreet ipsum sed tincidunt commodo. Fusce faucibus quam quam, in ornare ex fermentum et. Suspendisse dignissim, tortor at fringilla tempus, nibh lacus pretium metus, vel tempus dolor tellus ac orci. Vestibulum in congue dolor, nec porta elit. Donec pellentesque, neque sed commodo blandit, augue sapien dapibus arcu, sit amet hendrerit felis libero id ante. Praesent vitae elit at eros faucibus volutpat. Integer rutrum augue laoreet ex porta, ut faucibus elit accumsan. Donec in neque sagittis, auctor diam ac, viverra diam. Phasellus vel quam dolor. Nullam nisi tellus, faucibus a finibus et, blandit ac nisl. Vestibulum interdum malesuada sem, nec semper mi placerat quis. Nullam non bibendum sem, vitae elementum metus. Donec non ipsum quis turpis semper lobortis.'); [3] => INSERT INTO `mytable` (id, key, value, path, content) VALUES (NULL, '1_733839474', 'dejsde', 'notes_docs/files_txt/20170831/1_733839474_dejsde.txt', 'Nunc faucibus, enim non luctus rutrum, lorem urna finibus turpis, sit amet dictum turpis ipsum pharetra ex. Donec at leo vitae massa consectetur viverra eget vel diam. Sed in neque tempor, vulputate quam sed, ullamcorper nisl. Fusce mollis libero in metus tincidunt interdum. Cras tempus porttitor nunc nec dapibus. Vestibulum condimentum, nisl eget venenatis tincidunt, nunc sem placerat dui, quis luctus nisl erat sed orci. Maecenas maximus finibus magna in facilisis. Maecenas maximus turpis eget dignissim fermentum. '); [4] => INSERT INTO `mytable` (id, key, value, path, content) VALUES (NULL, '1_837837472', 'abc_file', 'notes_docs/files_txt/20170831/1_837837472_abc_file.txt', 'Integer non ex condimentum, aliquet lectus id, accumsan nibh. Quisque aliquet, ante vitae convallis ullamcorper, velit diam tempus diam, et accumsan metus eros at tellus. Sed lacinia mauris sem, scelerisque efficitur mauris aliquam a. Nullam non auctor leo. In mattis mauris eu blandit varius. Phasellus interdum mi nec enim imperdiet tristique. In nec porttitor erat, tempor malesuada ante. Etiam scelerisque ligula et ex maximus, placerat consequat nunc consectetur. Phasellus suscipit ligula sed elit hendrerit laoreet. Suspendisse ex sem, placerat pharetra ligula eu, accumsan rhoncus ex. Sed luctus nisi vitae metus maximus scelerisque. Suspendisse porta nibh eget placerat tempus. Nunc efficitur gravida sagittis. '); ) 

我会尝试首先创建一个简单的数据库。 主表将具有文件名和文本内容。 通过这种方式,您可以利用数据库引擎的查询功能,在当前的解决方案中可能会大幅度提高性能。

这将是最简单的解决方案,它可以通过添加将文字与文件名相关的互补表格来增长(这可能是动态完成的,例如在最频繁的搜索中)

文件系统还是数据库?
为了处理大量的数据,你需要使用某种数据库。 就像谷歌一样!

那么我应该使用像Mysql这样的关系数据库吗?
在很多情况下(比如你的情况),关系数据库和SQL不是正确的选择。 今天,大多数搜索引擎和大数据分析器都使用NOSQL数据库
NOSQL数据库比SQL数据库要快得多。 但他们通常使用大量的硬件资源(RAM,…)。

你的解决方案

  1. 搜索关于nosql数据库并检查其差异。 你也应该在你的数据库中进行全文搜索,所以搜索一下全文搜索。 选择一个NOSQL数据库: Aerospike (最快的) – CassandraMongoDB
  2. 将您的所有文件数据导入到数据库中。
  3. 选择一个正确和快速的编程语言(我建议不要使用PHP)。
  4. 设计和建立您的搜索引擎作为服务。
  5. 将您的主程序连接到您的搜索引擎。