PHP全文检索中间件,可索引上千万数据

前言

使用的是倒排索引结构,独立开发的中文分词,使用sqlite进行持久化存储,加上各种算法,实测可索引5000万数据。
这样一个PHP版的ES,在中小型项目中,就无需额外安装es软件,直接引入即可使用。

整个插件是独立运行的,不依赖任何其它插件,完全的接口化操作,使用方式也很简单:配置mapping、创建索引库 -> 导入数据 -> 开始搜索。下面简单介绍一下使用流程。

安装中间件

require 'yourdirname/wind-search-engine-professional/vendor/autoload.php';

配置mapping

// mapping字段配置
$field = [
   [
        'name' => 'id',//主键名称
        'type' => 'primarykey' //数据类型为主键 int 必须
   ],
   [
        'name' => 'title', //字段名称
        'index' => true, //代表需要索引
        'type'=>'text',//数据类型:文本类型,会根据analyzer配置的方式进行分词
        'analyzer' => 'segment', // 分词模式
    ],
    [
        'name' => 'tags',//字段名称
        'index' => true,
        'type'=>'keyword',//数据类型:关键词 不会分词
        'analyzer' => 'segment', // 分词模式
    ],
    [
        'name'=>'descr', //不索引
    ],
    [
        'name'=>'link', //不索引
    ],
    [
        'name'=>'time', //不索引
    ],
]; 

导入数据

//导入数据
$Wind = new \WindSearch\Core\Wind($indexName);
// 开启分词
$Wind->loadAnalyzer(true);
//$result 数组 从数据库中查询的数据
foreach ($result as $v) {
    $Wnd->indexer($v);
}
//每组$result数据导入后,进行批量保存,请合理设置分批查询的$result数据量
$Wind->batchWrite();

检索接口

match 单字段检索
match_phrase 短句检索
multi_match 多字段检索
match_prefix 前缀匹配
match_range 范围查询
match_terms 精确搜索
aggs_group 聚合搜索
geo_distance 地理空间搜索
match_bool 布尔搜索

DSL语法示例

多字段检索

//实例化引擎
$Wind = new \WindSearch\Core\Wind($indexName);
//开启分词功能
$Wind->loadAnalyzer();
// 开启同义词功能
$Wind->onNearSynonym();
//开启缓存功能
$Wind->onCache();

// 搜索多个字段
$query = [
    'multi_match' => [
        'field' => [
            [
                'name' => 'title',
                'query' => $textTitle,
                'analyzer' => 'segment', // 分词模式
                'highlight' => [ // 结果高亮
                    'is_cut' => 'cut', // 是否将未匹配的地方截取掉
                    'fixed_length' => '', // 保留多少字符,若为空,则保持原长度
                ],
                // 'weight' => 1, //字段权重
                'minimum_should_match' => '75%', //控制精度 最少应该匹配几个,operator为空时,此项会起作用 值可以为整型或百分比
            ],
            [
                'name' => 'tags',
                'query' => '蔬菜 水果',
                'analyzer' => 'segment', /// 分词模式 complete/not,代表不分词 整体匹配; segment 中文分词;
                'highlight' => [ //结果高亮
                    'is_cut' => 'not_cut', // 是否将未匹配的地方截取掉
                    'fixed_length' => '', // 保留多少字符,若为空,则保持原长度
                ],
                'weight' => 2,
                'minimum_should_match' => '15%',//控制精度 最少应该匹配几个,operator为空时,此项会起作用 值可以为整型或百分比

            ]

        ],
        // 结果排序
        'sort' => [
            'time' => 'asc' //asc 按字段值正序 desc 按字段值倒序
        ],
        'list_rows' => $listRows, // 每页多少条数据
        'page' => $page, // 第几页

    ]
];

// 开始搜索
$res = $Wind->search($query);
// $res 返回的最终结果,可直接渲染到前台页面
// ...

bool搜索

// 布尔查询
$query = [
    'match_bool' => [
        'bool' => [
            'must' => [
                'match' => [
                    [
                    'title' => $text,
                    // 'level'=>'二级', //可以有多个条件
                    ],
                ],

            ],
            'should' => [
                'match' => [
                    [
                        'area' => '中国',
                    ],
                    [
                        'area' => '韩国',
                    ],
                ],
                'minimum_should_match' => 1, //最少应该同时满足几个

            ],
            //必须不包含
            'must_not' => [
                'match' => [
                    ['level' => '三级',],
                    ['level' => '二级',],
                ],

            ],
            //筛选过滤
            'filter' => [
                'range' => [//可多个过滤字段
                    'score' => [ //每个字段一个或两个条件 lt lte gt gte
                        'lt' => 1000,
                        'gt' => 9,
                    ],
                    'time' => [
                        'lt' => 1710288994
                    ],
                ],
            ],
        ],
        'highlight' => [
            'is_cut' => 'cut', // 是否将未匹配的地方截取掉
            'fixed_length' => '', //保留多少字符,若为空,则保持原长度
        ],
        // 结果排序
        'sort' => [
             'time' => 'desc' //asc 按字段值正序 desc 按字段值倒序
        ],
        '_source'=>['id','title','descr'],//返回自定义字段,为空则全部返回
        'list_rows' => $listRows, //每页多少条数据
        'page' => $page, //第几页
    ],
];

// 搜索接口
$res = $Wind->search($query);
// $res 返回的最终结果,可直接渲染到前台页面
// ...

可以看到,整个过程跟使用es是差不多的,引入即可使用,插件内部自动完成所有功能(分词、索引、存储、搜索、排序等等),返回的结果可直接显示到前台页面,对于中小型网站的数据量,完全没有难度。

本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 15

你先把他打包成一个应用网站,用户上传 5M 以内文档免费使用,再出一个Pro版本

2周前 评论
rock365 (楼主) 2周前
Alone88 2周前
rock365 (楼主) 2周前

如果说做一个cms之类的产品,把这个全文检索插件内嵌进去,以此来吸引用户,大佬们觉得可行吗? :sweat_smile:

2周前 评论

功能的话其实和es差不多,甚至不如es,如果想商业化,建议对分词进行优化,对中文更友好,否则自建es几乎没有成本~

2周前 评论
rock365 (楼主) 2周前

我对于这种搜索功能用的是 tntsearch + jieba,但是查询速度和准确度只能说勉强够用,楼主做的插件在这两点上有优势吗?考虑到国内用户的白嫖习惯,可能面向海外市场或者做成 WordPress 插件会有点机会。

2周前 评论
rock365 (楼主) 2周前
  1. SQLite 不是并发性很差吗?
  2. simple 开源分词器比,优势如何?

之前我测试过,自带 fts5 + simple 分词器,全文搜索隔壁论坛 1300W 回帖(平均 102 字/帖),

  • 搜 10 词 + 回表,得到 140 结果,只需 0.04 秒?
  • 搜 5 词 + BM25 排序 + 回表,得到 76W 结果,需 1.1 秒?
2周前 评论
rock365 (楼主) 2周前
wxf666 (作者) 2周前
rock365 (楼主) 2周前

xunsearch 了解一下,现在已经凉了

2周前 评论
rock365 (楼主) 2周前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!