前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >MeiliSearch轻量级搜索引擎-食用指南

MeiliSearch轻量级搜索引擎-食用指南

作者头像
爱吃大橘
发布2022-12-27 14:44:27
2.4K0
发布2022-12-27 14:44:27
举报
文章被收录于专栏:前端笔记薄前端笔记薄

MeiliSearch 是一个开源、速度极快且高度相关的搜索引擎。但它不仅仅是通用搜索引擎:MeiliSearch 高度可定制的搜索引擎 API 为您提供了极大的灵活性。例如,您可以修改排名规则、添加自定义排名规则、配置同义词、过滤掉停用词等等。为了提高您的搜索能力,MeiliSearch 允许您设置作为索引的分面过滤器。

背景

因为最近开发文档搜索功能,所以开始接触搜索引擎,目的主要是对比和选型。

搜索引擎有很多,最为常见的是ES,是搜索引擎的主流,但ES太重了,对于小项目而言增加的硬件成本,运维成本。对于小项目来说不太合适。想要分词很好,要么费人,要么费机器。

至于利用关系型数据库(MySQL和PostgreSQL等)自带的全文检索功能,虽然有对应的功能,但性能不佳,很费机器。

综合研究下来,开箱即用的 MeiliSearch, 对于中小型项目来说,MeiliSearch 是一个不错的选择。

使用教程
安装Meilisearch服务
代码语言:javascript
复制
# Install Meilisearch

curl -L https://install.meilisearch.com | sh

# Launch Meilisearch

./meilisearch
验证

查看索引列表

代码语言:javascript
复制
curl http://127.0.0.1:7700/indexes

// {"results":[],"offset":0,"limit":20,"total":0}

// 因为是从零开始的,所以索引列表应该是空数组
设置访问密码

可选,为了安全还是要设置的

代码语言:javascript
复制
./meilisearch --master-key="MASTER_KEY"

// 访问的时候要带上密码

curl http://127.0.0.1:7700/indexes / --header 'Authorization: Bearer xxxx'

// 如果是0.24以下老版本,--header 'X-Meili-API-Key: xxxx'

现在我们可以愉快的尝试meiliSearch的强大功能了

初始化客户端

首先,创建用npm创建新项目:

代码语言:javascript
复制
mkdir myMeiliSearch

cd ./myMeiliSearch

npm init -y

然后,安装meilisearch-js 依赖:

代码语言:javascript
复制
npm install meilisearch

最终, 创建一个index.js 文件在根目录,在这里输入我们的代码。

代码语言:javascript
复制
touch index.js
填充一些demo数据

数据字段如下

代码语言:javascript
复制
{

id: 'number',

title: 'string',

genres: 'Array'

}

index.js如下

代码语言:javascript
复制
const {MeiliSearch} = require('meilisearch')

const main = async () => {

const client = new MeiliSearch({

host: 'http://127.0.0.1:7700',

apiKey: 'xxxx',

})

// An index is where the documents are stored.

const index = client.index('movies')

const documents = [

{ id: 1, title: 'Carol', genres: ['Romance', 'Drama'] },

{ id: 2, title: 'Wonder Woman', genres: ['Action', 'Adventure'] },

{ id: 3, title: 'Life of Pi', genres: ['Adventure', 'Drama'] },

{ id: 4, title: 'Mad Max: Fury Road', genres: ['Adventure', 'Science Fiction'] },

{ id: 5, title: 'Moana', genres: ['Fantasy', 'Action']},

{ id: 6, title: 'Philadelphia', genres: ['Drama'] },

]

// If the index 'movies' does not exist, Meilisearch creates it when you first add the documents.

let response = await index.addDocuments(documents)

console.log(response) // => { "uid": 0 }

// 打印效果如下

/*EnqueuedTask {

taskUid: 0,

indexUid: 'movies',

status: 'enqueued',

type: 'documentAdditionOrUpdate',

enqueuedAt: 2022-11-02T07:20:47.332Z

}*/

}

main()

诸如文档添加之类的任务总是返回一个唯一的标识符。您可以使用此标识符 taskUid 来检查任务的状态(入队、处理、成功或失败)。

查询

index.js 代码如下

代码语言:javascript
复制
const { MeiliSearch } = require('meilisearch')

const main = async () => {

const client = new MeiliSearch({

host: 'http://127.0.0.1:7700',

apiKey: 'xxxx',

})

// An index is where the documents are stored.

const index = client.index('movies')

// Meilisearch is typo-tolerant:

const search = await index.search('philoudelphia')

console.log(search)

// 查询结果如下

/*

{

hits: [ { id: 6, title: 'Philadelphia', genres: [Array] } ],

estimatedTotalHits: 1,

query: 'philoudelphia',

limit: 20,

offset: 0,

processingTimeMs: 4

}

*/

}

同时尝试,文本前缀触发

代码语言:javascript
复制
const search = await index.search('phi')

// 查询结果如下

/*

{

hits: [ { id: 6, title: 'Philadelphia', genres: [Array] } ],

estimatedTotalHits: 1,

query: 'phi',

limit: 20,

offset: 0,

processingTimeMs: 0

}

*/

发现依然有效,当如如果是中间的单词。比如Philadelphia的中间部分ladel,这时候有query:ladel,是搜出不来的。

那么 Road 这种明显是分隔的单词(分词)怎么样,我尝试了一些query:Maxquery:Road的时候,都可以。

重点来了,中文支持的如何呢?

我们继续往索引中添加数据,

代码语言:javascript
复制
[

{ id: 7, title: '一只狐狸', genres: ['狐狸', '老爷爷'] },

{ id: 8, title: '帅气的猫', genres: ['蓝色的猫', '淘气的老鼠'] },

{ id: 9, title: '喜欢盆子的狗', genres: ['盆子', '狗'] },

{ id: 10, title: '无敌的超人', genres: ['超人', '可路菲菲'] },

{ id: 11, title: '斗破天下', genres: ['萧快乐', '范无敌'] },

{ id: 12, title: '闺蜜之主', genres: ['赵猥琐', '美丽女神'] },

]

然后,我们搜索一下“猫”,不负众望,

代码语言:javascript
复制
{

hits: [ { id: 8, title: '帅气的猫', genres: [Array] } ],

estimatedTotalHits: 1,

query: '猫',

limit: 20,

offset: 0,

processingTimeMs: 0

}

无论是搜单个字还是全部,它都可以匹配出来,简直完美。仅这些,使用es实现就要好几天,甚至不能轻易实现。

条件判断

如果你需要条件判断,那么你需要先配置一下

代码语言:javascript
复制
// 官网上的ReadMe.md index.updateAttributesForFaceting 是过期的写法

//详情 https://github.com/meilisearch/meilisearch-js/pull/941

await index.updateFilterableAttributes([

'id',

'genres'

])

如果你还想搜索genres字段,你需要

代码语言:javascript
复制
await index.updateSearchableAttributes([

'id',

'title',

'genres'

])

查询一下id大于3且genres中包含Adventure的项

代码语言:javascript
复制
const search = await index.search(

'Adventure',

{

filter: ['id > 3']

}

)

console.log(search)

/*

{

hits: [ { id: 4, title: 'Mad Max: Fury Road', genres: [Array] } ],

estimatedTotalHits: 1,

query: 'Adventure',

limit: 20,

offset: 0,

processingTimeMs: 0

}

*/

是不是很容易?

附上完整代码
代码语言:javascript
复制
const { MeiliSearch } = require('meilisearch')

const main = async () => {

const client = new MeiliSearch({

host: 'http://127.0.0.1:7700',

apiKey: 'xxxx',

})

// An index is where the documents are stored.

const index = client.index('movies')

await index.updateFilterableAttributes([

'id',

'genres'

])

await index.updateSearchableAttributes([

'id',

'title',

'genres'

])

// addDocument(index)

// addDocumentZh(index)

// addDocumentEnZh(index)

// updateDocumentEnZh(index)

// Meilisearch is typo-tolerant:

// const search = await index.search('ww')

const search = await index.search(

'Adventure',

{

filter: ['id > 3']

}

)

console.log(search)

}

async function addDocument(index) {

const documents = [

{ id: 1, title: 'Carol', genres: ['Romance', 'Drama'] },

{ id: 2, title: 'Wonder Woman', genres: ['Action', 'Adventure'] },

{ id: 3, title: 'Life of Pi', genres: ['Adventure', 'Drama'] },

{ id: 4, title: 'Mad Max: Fury Road', genres: ['Adventure', 'Science Fiction'] },

{ id: 5, title: 'Moana', genres: ['Fantasy', 'Action'] },

{ id: 6, title: 'Philadelphia', genres: ['Drama'] },

]

// If the index 'movies' does not exist, Meilisearch creates it when you first add the documents.

let response = await index.addDocuments(documents)

console.log(response) // => { "uid": 0 }

}

async function addDocumentZh(index) {

const documents = [

{ id: 7, title: '一只狐狸', genres: ['狐狸', '老爷爷'] },

{ id: 8, title: '帅气的猫', genres: ['蓝色的猫', '淘气的老鼠'] },

{ id: 9, title: '喜欢盆子的狗', genres: ['盆子', '狗'] },

{ id: 10, title: '无敌的超人', genres: ['超人', '可路菲菲'] },

{ id: 11, title: '斗破天下', genres: ['萧快乐', '范无敌'] },

{ id: 12, title: '闺蜜之主', genres: ['赵猥琐', '美丽女神'] },

]

// If the index 'movies' does not exist, Meilisearch creates it when you first add the documents.

let response = await index.addDocuments(documents)

console.log(response) // => { "uid": 0 }

}

async function addDocumentEnZh(index) {

const documents = [

{ id: 13, title: '一只App狐狸', genres: ['狐狸', '老爷爷'] },

{ id: 14, title: 'Niu帅气的猫', genres: ['蓝色的猫', '淘气的老鼠'] },

{ id: 15, title: '喜欢盆子A的狗', genres: ['盆子', '狗'] },

{ id: 16, title: '无敌的超人', genres: ['超人', '可路菲菲'] },

{ id: 17, title: '斗Kankan破K天下', genres: ['萧快乐', '范无敌'] },

{ id: 18, title: '闺蜜Www之主', genres: ['赵猥琐', '美丽女神'] },

]

// If the index 'movies' does not exist, Meilisearch creates it when you first add the documents.

let response = await index.addDocuments(documents)

console.log(response) // => { "uid": 0 }

}

async function updateDocumentEnZh(index) {

const documents = [

{ id: 13, title: '一只App狐狸', genres: ['狐狸', '老爷爷'] },

{ id: 14, title: 'Niu帅气的猫', genres: ['蓝色的猫', '淘气的老鼠'] },

{ id: 15, title: '喜欢盆子A的狗', genres: ['盆子', '狗'] },

{ id: 16, title: '无敌的超人', genres: ['超人', '可路菲菲'] },

{ id: 17, title: '斗Kankan破K天下', genres: ['萧快乐', '范无敌'] },

{ id: 18, title: '闺蜜Www之主1', genres: ['赵猥琐', '美丽女神'] },

]

// If the index 'movies' does not exist, Meilisearch creates it when you first add the documents.

let response = await index.updateDocuments(documents)

console.log(response) // => { "uid": 0 }

}

main()
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-11-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景
  • 使用教程
    • 安装Meilisearch服务
      • 验证
        • 设置访问密码
          • 初始化客户端
            • 填充一些demo数据
              • 查询
                • 条件判断
                  • 附上完整代码
                  相关产品与服务
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档