likes
comments
collection
share

一种更好的构建 MeiliSearch Filter 的方法

作者站长头像
站长
· 阅读数 34

MeiliSearch 是一个强大轻量的搜索引擎。在使用过程中我发现构建 filter 的过程有点麻烦。并且 meilisearch-js 没有提供任何辅助工具来构建 filter,你需要手动构建 filter 字符串。经常出现拼写错误,且需要经常翻阅文档,写出来的代码也不好维护。因此,我写了个辅助工具 (meilisearch-helper)来帮助构建 filter。这个库提供了良好的 TypeScript 支持,并且易于使用。

这个工具提供了两种方法来构建 filter:

  1. 使用 buildMeiliSearchFilter 函数从 MongoDB 风格的 filter object 对象构建 filter。
  2. 使用 MeiliSearchFilterBuilder 类通过链式 API 构建 filter。

buildMeiliSearchFilter

首先,导入 buildMeiliSearchFilter 函数:

import { buildMeiliSearchFilter } from 'meilisearch-helper';

定义可过滤属性的类型:

type FilterableAttributes = 'name' | 'age' | 'isStudent';

现在,你可以使用 buildMeiliSearchFilter 函数来构建 filter:

const filter = buildMeiliSearchFilter<FilterableAttributes>({
  $or: [
    { $or: [{ name: 'John' }, { name: 'Doe' }] },
    { $and: [{ age: { $gt: 18 } }, { isStudent: true }] },
  ],
  age: { $lt: 30 },
  $geoRadius: {
    lat: 45.472735,
    lng: 9.184019,
    distanceInMeters: 2000,
  },
});

console.log(filter);
// => ((name = "John" OR name = "Doe") OR age > 18 AND isStudent = true) AND age < 30 AND _geoRadius(45.472735, 9.184019, 2000)

或者,你可以在一个单独的变量中构建 MongoDB 风格的 filter object:

import { buildMeiliSearchFilter, FilterQuery } from 'meilisearch-helper';

type FilterableAttributes = 'name' | 'age' | 'isStudent';

const query: FilterQuery<FilterableAttributes> = {};

query.$or = [
  { $or: [{ name: 'John' }, { name: 'Doe' }] },
  { $and: [{ age: { $gt: 18 } }, { isStudent: true }] },
];

query.age = { $lt: 30 };

query.$geoRadius = {
  lat: 45.472735,
  lng: 9.184019,
  distanceInMeters: 2000,
};

const filter = buildMeiliSearchFilter(query);
console.log(filter);
// => ((name = "John" OR name = "Doe") OR age > 18 AND isStudent = true) AND age < 30 AND _geoRadius(45.472735, 9.184019, 2000)

MeiliSearchFilterBuilder

如果你不喜欢 MongoDB 风格的 filter object,你可以使用 MeiliSearchFilterBuilder 类通过链式 API 来构建 filter。

import { MeiliSearchFilterBuilder } from 'meilisearch-helper';

type FilterableAttributes = 'name' | 'age' | 'isStudent';

const filter = new MeiliSearchFilterBuilder<FilterableAttributes>()
  .where((eb) =>
    eb.or([
      eb.or([eb('name', '=', 'John'), eb('name', '=', 'Doe')]),
      eb.and([eb('age', '>', 18), eb('isStudent', '=', true)]),
    ]),
  )
  .where('age', '<', 30)
  .where((eb) => eb.geoRadius(45.472735, 9.184019, 2000))
  .build();

console.log(filter);
// => ((name = "John" OR name = "Doe") OR age > 18 AND isStudent = true) AND age < 30 AND _geoRadius(45.472735, 9.184019, 2000)

where 方法可以传递一个函数,该函数接受一个 ExpressionBuilder 对象作为参数。ExpressionBuilder 对象有以下方法:

  • eb(lhs: FilterableAttributes, operator: FilterBuilderOperator, rhs: BaseValueTypes): string:构建一个简单的表达式。

    eb('age', '>', 18); // => age > 18
    
  • eb.or(expressions: string[]): string:用 OR 组合多个表达式。

    eb.or([eb('age', '>', 18), 'isStudent = true']); // => (age > 18 OR isStudent = true)
    
  • eb.and(expressions: string[]): string:用 AND 组合多个表达式。

    eb.and([eb('age', '>', 18), 'isStudent = true']); // => age > 18 AND isStudent = true
    
  • eb.geoRadius(lat: number, lng: number, distanceInMeters: number): string:构建一个地理半径表达式。

    eb.geoRadius(45.472735, 9.184019, 2000); // => _geoRadius(45.472735, 9.184019, 2000)
    
  • eb.geoBoundingBox(topRight: { lat: number; lng: number }, bottomLeft: { lat: number; lng: number }): string:构建一个地理边界框表达式。

    eb.geoBoundingBox(
      { lat: 45.472735, lng: 19.184019 },
      { lat: 25.472735, lng: 9.184019 },
    ); // => _geoBoundingBox([45.472735, 19.184019], [25.472735, 9.184019])
    

MeiliSearchFilterBuilder 是不可变的,每次修改必须重新赋值!

let builder = new MeiliSearchFilterBuilder<FilterableAttributes>();

builder = builder.where('name', '=', 'John');
builder = builder.where('age', '=', 20);
builder = builder.where('isStudent', '=', true);

console.log(builder.build()); // => name = "John" AND age = 20 AND isStudent = true

希望你在项目中找到这个库的用处。如果你有任何反馈或建议,请告诉我。感谢阅读!

更多细节可以查看项目源代码:github.com/CodyTseng/m…

转载自:https://juejin.cn/post/7380179109291032614
评论
请登录