一种更好的构建 MeiliSearch Filter 的方法
MeiliSearch 是一个强大轻量的搜索引擎。在使用过程中我发现构建 filter 的过程有点麻烦。并且 meilisearch-js 没有提供任何辅助工具来构建 filter,你需要手动构建 filter 字符串。经常出现拼写错误,且需要经常翻阅文档,写出来的代码也不好维护。因此,我写了个辅助工具 (meilisearch-helper)来帮助构建 filter。这个库提供了良好的 TypeScript 支持,并且易于使用。
这个工具提供了两种方法来构建 filter:
- 使用
buildMeiliSearchFilter
函数从 MongoDB 风格的 filter object 对象构建 filter。 - 使用
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