Meilisearch是一个基于 rust 开发的,快速的、完全开源的轻量级搜索引擎。它的数据存储基于磁盘与内存映射,不受 RAM 限制。在一定数量级下,搜索速度不逊于 Elasticsearch(ES),所以对于中小型项目来说,MeiliSearch 是一个不错的选择
一,基于docker安装Meilisearch
1,拉取镜像
docker pull getmeili/meilisearch:latest
2,启动服务
// 执行下面命令, \ 后换行
docker run -itd --rm \
--name meilisearch \
-p 7700:7700 \
-v $(pwd)/data:/meili_data \
-e MEILI_MASTER_KEY='b1xxxxxxxxxxxxxxxxxxxxxxxxxcb' \
getmeili/meilisearch:latest
注意:
MEILI_MASTER_KEY:指定master key,至少16字节的UTF-8字符串,这是为了保护Meilisearch实例免遭未经授权的使用,后继js初始化时需要用到的主密钥
查看是否运行成功
也可以浏览器http://ipxxx:7700 查看
二,在node项目使用
1,安装依赖
npm install meilisearch
2,MeiliSearch实例初始化
const { MeiliSearch } = require('meilisearch');
const _env = process.env.EGG_SERVER_ENV;
const hosts = {
local: 'http://127.0.0.1:7700',
gray: 'http://10.x.x.x:7700',
prod: 'http://10.x.x.x:7700',
}
const meilisearchClient = new MeiliSearch({
host: hosts[_env],
apiKey: 'b1xxxxxxxxxxxxxxxxxxxxxxxxxcb' // 上面安装的 MEILI_MASTER_KEY
});
module.exports = meilisearchClient
3,service封装的常用方法
'use strict';
const Service = require('egg').Service;
const meilisearch = require('../utils/meilisearch');
class MeilisearchService extends Service {
// 添加数据方法,index是模块标识
async addDocuments(index, docs, config) {
let res;
try {
res = await meilisearch.index(index).addDocuments(docs, config || {});
} catch (error) {
res = null;
}
return res;
}
// 修改数据方法,index是模块标识
async updateDocuments(index, docs, config) {
let res;
try {
res = await meilisearch.index(index).updateDocuments(docs, config || {});
} catch (error) {
res = null;
}
return res;
}
// 删除数据方法,index是模块标识
async deleteDocument(index, id) {
let res;
try {
res = await meilisearch.index(index).deleteDocument(id);
} catch (error) {
res = null;
}
return res;
}
// 查询数据方法,index是模块标识
async search(index, query, options, config) {
let res;
try {
res = await meilisearch.index(index).search(query, options || {}, config || {});
} catch (error) {
res = null;
}
return res;
}
}
module.exports = MeilisearchService;
4,例子
在controller中新建或修改时,需要同步添加或修改meilisearch中的数据
// 新增
async add() {
const { ctx, app, service } = this;
const reqBody = ctx.request.body;
try {
const newData = {
...reqBody
};
// 数据入库
const res = await ctx.model.xxx.create(newData);
// 添加数据进meilisearch
await service.meilisearch.addDocuments('homeSearch', [res]);
ctx.success('保存成功');
} catch (error) {
ctx.error(4002, error);
}
}
// 修改
async update() {
const { ctx, app, service } = this;
const reqBody = ctx.request.body;
const updateData = {
...reqBody
}
try {
const existItem = await model.findByPk(reqBody.id);
if (!existItem) {
return ctx.error(4002, '该资源不存在');
}
// 更新数据
await existItem.update(updateData);
// 更新meilisearch数据
await service.meilisearch.updateDocuments('homeSearch', [existItem.dataValues]);
ctx.success(existItem);
} catch (error) {
ctx.error(4002, error);
}
}
// 搜索
async search() {
const { ctx, service } = this;
const { keyword, target, page, pageSize } = ctx.query;
const options = {
attributesToRetrieve: [],
attributesToCrop: ['content'],
cropMarker: '…',
attributesToHighlight: ['*'],
highlightPreTag: '<span class="highlight">', // 高亮标签包裹命中关键词
highlightPostTag: '</span>',
filter: ['status=1']
};
if (!isNaN(page) && !isNaN(pageSize)) {
options.limit = +pageSize;
options.offset = (+page - 1) * (+pageSize);
}
if (target) {
options.filter.push(`typeId=${target}`);
}
const resourceSearcher = meilisearchClient.index('homeSearch')
try {
// 更新可过滤filter的属性, 要和options的filter配合使用
await resourceSearcher.updateFilterableAttributes(['status', 'typeId']);
// 更新搜索设置
await resourceSearcher.updateSettings({
// 搜索内容的字段
searchableAttributes:['title', 'userName', 'content'],
// searchableAttributes:['*'],
// 返回字段
displayedAttributes:['*'],
// displayedAttributes:['title', 'authorName', 'content'],
});
// 搜索资源
const res = await resourceSearcher.search(keyword, options);
return ctx.success(res);
} catch (error) {
ctx.error(4002, error);
}
}
搜索效果:
meilisearch-js git地址:github.com/meilisearch…