如何用PHP轻松打造轻量级搜索引擎?

最近接了一个小项目,客户需要一个简单的搜索引擎框架,用来在自己的网站上实现产品搜索功能。这东西听起来高大上,但其实实现起来并不复杂,尤其用PHP来搞,简直就是小菜一碟。今天就来分享一下我是怎么用PHP搞出一个轻量级搜索引擎框架的。

我们得明确一下需求。客户的需求并不复杂:

1. 支持关键词搜索。

2. 搜索结果需要按相关性排序。

3. 支持简单的分页功能。

4. 搜索速度要快,不能卡顿。

看上去没啥难度,对?但别急着动手,我们在做之前得先想清楚架构。毕竟搜索引擎这东西涉及到大量数据处理和优化,搞不好就翻车了。

架构设计

最简单的搜索引擎框架其实可以分为两个部分:索引构建查询处理。索引构建就是把我们要搜索的数据提前处理好,存到一个高效的数据结构里,方便后续查询。查询处理就是根据用户输入的关键词,在索引中查找匹配的结果,并按相关性排序返回给用户。

这里我们选择用一种叫做倒排索引(Inverted Index)的数据结构来存储数据。倒排索引是搜索引擎中最常用的索引结构,它的核心思想是把每个关键词映射到包含它的文档列表。比如,我们有三个文档:

1. 文档1:"苹果是水果"

2. 文档2:"香蕉是水果"

3. 文档3:"苹果和香蕉都是水果"

那么倒排索引就是:

苹果:文档1, 文档3

香蕉:文档2, 文档3

是:文档1, 文档2, 文档3

这样一来,当用户搜索"苹果"时,我们只需要查找"苹果"对应的文档列表就行了,效率非常高。

数据准备

我们得有一些数据来构建索引。这里假设我们的数据存储在MySQL数据库中,表结构如下:

CREATE TABLE products (

id INT PRIMARY KEY AUTO_INCREMENT,

name VARCHAR(255),

description TEXT

);

为了方便演示,我们插入几条测试数据:

INSERT INTO products (name, description) VALUES ('苹果手机', '这是一款苹果公司出品的智能手机。');

INSERT INTO products (name, description) VALUES ('香蕉牛奶', '这是一款用香蕉和牛奶制作的饮品。');

INSERT INTO products (name, description) VALUES ('苹果和香蕉', '这是一款结合了苹果和香蕉的混合水果。');

数据有了,接下来就是构建索引。

构建倒排索引

我们写一个PHP脚本来读取数据库中的数据,然后构建倒排索引。这里我们用一个简单的数组来存储索引,实际项目中可以考虑使用更高效的数据结构,比如Redis或者Elasticsearch。

代码如下:

$pdo = new PDO('mysql:host=localhost;dbname=test', 'root', 'password');

$stmt = $pdo->query('SELECT FROM products');

$invertedIndex = [];

while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {

$words = array_merge(

explode(' ', $row['name']),

explode(' ', $row['description'])

);

foreach ($words as $word) {

if (!isset($invertedIndex[$word])) {

$invertedIndex[$word] = [];

}

$invertedIndex[$word][] = $row['id'];

}

}

print_r($invertedIndex);

这段代码的作用是读取数据库中的每一条记录,把name和description字段拆分成单词,然后构建倒排索引。运行后,你会得到一个数组,数组的键是单词,值是该单词出现在哪些文档中。

处理查询

有了倒排索引,接下来就是处理用户输入的关键词,并返回匹配的文档。这里我们简单地把用户输入的关键词拆分成多个单词,然后在倒排索引中查找这些单词对应的文档列表,最后把这些列表取交集,得到最终的结果。

代码如下:

function search($query, $invertedIndex) {

$keywords = explode(' ', $query);

$results = [];

foreach ($keywords as $keyword) {

if (isset($invertedIndex[$keyword])) {

if (empty($results)) {

$results = $invertedIndex[$keyword];

} else {

$results = array_intersect($results, $invertedIndex[$keyword]);

}

// 如果有一个关键词没有匹配到任何文档,直接返回空

return [];

}

return $results;

}

$query = '苹果 香蕉';

$results = search($query, $invertedIndex);

print_r($results);

这段代码的作用是把用户输入的关键词拆分成多个单词,然后在倒排索引中查找这些单词对应的文档列表,最后取交集得到结果。比如用户输入"苹果 香蕉",我们会查找"苹果"和"香蕉"对应的文档列表,然后取交集,最终返回文档3。

相关性排序

现在我们已经能返回匹配的文档了,但输出结果是无序的,用户肯定希望能按相关性排序。这里的相关性可以简单地用词频(TF)来衡量,即一个关键词在文档中出现的次数越多,相关性越高。

我们修改一下代码,计算每个文档的相关性得分,然后按得分排序:

$scores = [];

foreach ($invertedIndex[$keyword] as $docId) {

if (!isset($scores[$docId])) {

$scores[$docId] = 0;

}

}

// 按相关性得分排序

arsort($scores);

$rankedResults = array_keys($scores);

return $rankedResults;

}

现在,搜索结果会按相关性得分从高到低排序,用户看到的结果会更加合理。

分页功能

最后我们来加个分页功能。分页其实很简单,就是把结果数组分成多个小数组,每次只返回一页的数据。

代码如下:

function paginate($results, $page, $perPage) {

$offset = ($page - 1)

$perPage;

return array_slice($results, $offset, $perPage);

}

$page = 1;

$perPage = 2;

$paginatedResults = paginate($results, $page, $perPage);

print_r($paginatedResults);

这段代码的作用是把结果数组分成多个小数组,每次只返回一页的数据。比如每页显示2条结果,用户查看第1页时,返回数组中的前2条结果。

性能优化

当然了,这个简单的搜索引擎框架还有很多可以优化的地方。比如:

1. 索引存储:实际项目中,索引数据量可能非常大,用PHP数组存储可能不够高效。可以考虑使用Redis或者Elasticsearch来存储索引。

2. 分词优化:现在我们只是简单地把字符串按空格拆分,实际中文分词需要考虑更多情况,比如停用词、词干提取等。

3. 缓存:每次查询都重新计算相关性得分比较耗时,可以考虑把结果缓存起来,下次查询时直接返回缓存结果。

总结

用PHP打造一个轻量级搜索引擎框架并不复杂,关键是要理解搜索引擎的基本原理,比如倒排索引、相关性排序等。虽然这个框架比较简单,但它已经能满足大部分小网站的需求了。如果你有兴趣,可以继续优化它,比如加入中文分词、缓存、分布式索引等功能,让它变得更加强大。

当然了,如果你不想自己造轮子,直接用现成的搜索引擎框架比如Elasticsearch也是不错的选择。但自己动手搞一个,不仅能让你更深入地理解搜索引擎的工作原理,还能在下次面试时秀一把技术,何乐而不为?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值