loupe/loupe

一个基于PHP和SQLite的全文搜索引擎,具有分词、词干提取、拼写容错、过滤和地理支持功能

资助包维护!
Toflar

0.7.0 2024-09-18 12:25 UTC

README

Loupe
一个基于SQLite,仅使用PHP的全文搜索引擎。

Loupe…

  • …只需要PHP和SQLite,你不需要其他任何东西 - 没有容器,什么都没有
  • …具有拼写容错能力(基于State Set Index算法和Levenshtein)
  • …支持使用引号 " 进行短语搜索
  • …支持对任何属性进行过滤(和排序),使用任何基于SQL的过滤器语句
  • …支持基于地理距离进行过滤(和排序)
  • …根据典型的TF-IDF余弦相似度算法排序相关性
  • …自动检测语言
  • …支持词干提取
  • …非常易于使用
  • …总之,这是替换您老式的SQL LIKE %...%查询的最简单方法,提供更好的搜索体验,而不需要额外的服务来管理。SQLite无处不在,只需要您的文件系统。

介绍性博客文章

如果您第一次遇到Loupe,您可能想阅读Medium上的博客文章Markdown文件,以了解更多关于原因以及您可以使用它的信息。请注意,该博客文章中引用的一些实现细节(例如使用的库)可能已经不再是最新的。

性能

性能取决于许多因素,但以下是一些基于对~32k电影固定数据和该存储库中bin目录中的测试文件进行索引的大致数字

  • 索引(php bin/index_performance_test.php)将在5分钟以内完成(约每秒110个文档)
  • 查询(php bin/search_performance_test.php)对于启用拼写容错并按相关性排序的“Amakin Dkywalker”,大约需要80 ms

请注意,任何超过50k文档的东西可能都不是Loupe的使用案例。请阅读文档中的性能章节。您可以在相关讨论中报告您自己的性能测量结果和更多详细信息。

致谢

如果您熟悉MeiliSearch,您会注意到API受到了很大的启发。这样做的原因很简单

  1. 首先和最重要的是:我认为,他们在从开发者的角度保持配置简单易懂方面做得非常出色。基本的搜索工具不应该复杂。
  2. 如果Loupe不再满足您的使用案例(您需要高级功能、更好的性能等),切换到MeiliSearch应该易如反掌。

我甚至冒昧地复制了一些测试数据,以用于Loupe的功能测试。

安装

  1. 请确保您已安装 pdo_sqlite,并且您的SQLite版本至少为3.16.0。这是添加PRAGMA函数的时候,没有这些函数,无法进行模式比较。建议您至少使用版本3.35.0,这是数学函数被引入SQLite的时候。否则,Loupe必须使用polyfill,这将导致轻微的性能损失。
  2. 运行 composer require loupe/loupe

使用方法

<?php

namespace App;

require_once 'vendor/autoload.php';

use Loupe\Loupe\Config\TypoTolerance;
use Loupe\Loupe\Configuration;
use Loupe\Loupe\LoupeFactory;
use Loupe\Loupe\SearchParameters;

$configuration = Configuration::create()
    ->withPrimaryKey('uuid') // optional, by default it's 'id'
    ->withSearchableAttributes(['firstname', 'lastname']) // optional, by default it's ['*'] - everything is indexed
    ->withFilterableAttributes(['departments', 'age'])
    ->withSortableAttributes(['lastname'])
    ->withTypoTolerance(TypoTolerance::create()->withFirstCharTypoCountsDouble(false)) // can be further fine-tuned but is enabled by default
;

$loupeFactory = new LoupeFactory();

$loupe = $loupeFactory->create('path/to/my_loupe_data_dir', $configuration);

// or create in-memory search:
$loupe = $loupeFactory->createInMemory($configuration);

$loupe->addDocuments([
    [
        'uuid' => 2,
        'firstname' => 'Uta',
        'lastname' => 'Koertig',
        'departments' => [
            'Development',
            'Backoffice',
        ],
        'age' => 29,
    ],
    [
        'uuid' => 6,
        'firstname' => 'Huckleberry',
        'lastname' => 'Finn',
        'departments' => [
            'Backoffice',
        ],
        'age' => 18,
    ],
]);


$searchParameters = SearchParameters::create()
    ->withQuery('Gucleberry')
    ->withAttributesToRetrieve(['uuid', 'firstname'])
    ->withFilter("(departments = 'Backoffice' OR departments = 'Project Management') AND age > 17")
    ->withSort(['lastname:asc'])
;

$results = $loupe->search($searchParameters);

print_r($results->toArray());

/*
Array
(
    [hits] => Array
        (
            [0] => Array
                (
                    [uuid] => 6
                    [firstname] => Huckleberry
                )

        )

    [query] => Gucleberry
    [processingTimeMs] => 4
    [hitsPerPage] => 20
    [page] => 1
    [totalPages] => 1
    [totalHits] => 1
)
*/

文档

你可能会问:“为什么是Loupe?”在法语中,“Loupe”意为“放大镜”,我觉得这个名字非常适合这个库。在给我的PHP爬虫库起一个法语名字之后,我有了这样的感觉 :-)