新增 NOT LIKE、IS NULL 和 IS NOT NULL 操作符,并更新文档示例
This commit is contained in:
40
README.md
40
README.md
@@ -7,7 +7,7 @@
|
||||
- **WHERE**: 条件过滤
|
||||
- **ORDER BY**: 排序
|
||||
- **LIMIT**: 限制结果数量
|
||||
- **操作符**: `=`, `!=`, `>`, `<`, `>=`, `<=`, `IN`, `CONTAINS`, `LIKE`
|
||||
- **操作符**: `=`, `!=`, `>`, `<`, `>=`, `<=`, `IN`, `CONTAINS`, `LIKE`, `NOT LIKE`, `IS NULL`, `IS NOT NULL`
|
||||
- **逻辑**: `AND`, `OR`
|
||||
|
||||
## 语法示例
|
||||
@@ -32,6 +32,15 @@ filter(users, "WHERE metadata.region IN ['beijing', 'shanghai']");
|
||||
filter(products, "WHERE name LIKE '%Apple%'");
|
||||
filter(products, "WHERE name LIKE 'Apple%'");
|
||||
filter(products, "WHERE name LIKE '%Phone'");
|
||||
|
||||
// NOT LIKE 操作 (排除匹配模式的项)
|
||||
filter(products, "WHERE name NOT LIKE '%Apple%'");
|
||||
|
||||
// IS NULL 操作 (匹配 null 或 undefined 的值)
|
||||
filter(users, "WHERE email IS NULL");
|
||||
|
||||
// IS NOT NULL 操作 (匹配非 null 且非 undefined 的值)
|
||||
filter(users, "WHERE email IS NOT NULL");
|
||||
```
|
||||
|
||||
## 使用方法
|
||||
@@ -72,3 +81,32 @@ const premiumUsers = filter(users, "WHERE metadata.tags CONTAINS 'premium'");
|
||||
// 查找北京的用户,按创建时间倒序
|
||||
const beijingUsers = filter(users, "WHERE metadata.region = 'beijing' ORDER BY metadata.created_at DESC");
|
||||
```
|
||||
|
||||
## 新增操作符说明
|
||||
|
||||
### NOT LIKE
|
||||
排除匹配指定模式的数据项。
|
||||
|
||||
```javascript
|
||||
// 查找不包含 "Apple" 的产品
|
||||
const nonAppleProducts = filter(products, "WHERE name NOT LIKE '%Apple%'");
|
||||
```
|
||||
|
||||
### IS NULL / IS NOT NULL
|
||||
检查字段值是否为 `null` 或 `undefined`。
|
||||
|
||||
```javascript
|
||||
const usersWithNullEmail = [
|
||||
{ name: 'Alice', email: 'alice@example.com', age: 25 },
|
||||
{ name: 'Bob', email: null, age: 30 },
|
||||
{ name: 'Charlie', email: undefined, age: 35 },
|
||||
];
|
||||
|
||||
// 查找 email 为 null 或 undefined 的用户
|
||||
const usersWithoutEmail = filter(usersWithNullEmail, "WHERE email IS NULL");
|
||||
|
||||
// 查找 email 不为 null 且不为 undefined 的用户
|
||||
const usersWithEmail = filter(usersWithNullEmail, "WHERE email IS NOT NULL");
|
||||
```
|
||||
|
||||
**注意**: `IS NULL` 和 `IS NOT NULL` 会同时检查 JavaScript 的 `null` 和 `undefined` 值。
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@kevisual/js-filter",
|
||||
"version": "0.0.2",
|
||||
"description": "sql like filter for js arrays",
|
||||
"version": "0.0.3",
|
||||
"description": "用于 JavaScript 数组的 SQL LIKE 过滤器函数",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"scripts": {
|
||||
|
||||
40
src/index.ts
40
src/index.ts
@@ -1,7 +1,7 @@
|
||||
type Token = {
|
||||
type: 'WHERE' | 'AND' | 'OR' | 'ORDER' | 'BY' | 'ASC' | 'DESC' | 'LIMIT'
|
||||
| 'EQ' | 'NEQ' | 'GT' | 'LT' | 'GTE' | 'LTE' | 'IN' | 'CONTAINS' | 'LIKE'
|
||||
| 'IDENTIFIER' | 'STRING' | 'NUMBER' | 'COMMA' | 'LBRACKET' | 'RBRACKET'
|
||||
| 'EQ' | 'NEQ' | 'GT' | 'LT' | 'GTE' | 'LTE' | 'IN' | 'CONTAINS' | 'LIKE' | 'NOT'
|
||||
| 'IS' | 'NULL' | 'IDENTIFIER' | 'STRING' | 'NUMBER' | 'COMMA' | 'LBRACKET' | 'RBRACKET'
|
||||
| 'EOF';
|
||||
value: string;
|
||||
pos: number;
|
||||
@@ -63,7 +63,7 @@ class Lexer {
|
||||
}
|
||||
|
||||
private isKeyword(value: string): boolean {
|
||||
const keywords = ['WHERE', 'AND', 'OR', 'ORDER', 'BY', 'ASC', 'DESC', 'LIMIT', 'IN', 'CONTAINS', 'LIKE'];
|
||||
const keywords = ['WHERE', 'AND', 'OR', 'ORDER', 'BY', 'ASC', 'DESC', 'LIMIT', 'IN', 'CONTAINS', 'LIKE', 'NOT', 'IS', 'NULL'];
|
||||
return keywords.includes(value.toUpperCase());
|
||||
}
|
||||
|
||||
@@ -139,7 +139,10 @@ class Lexer {
|
||||
'LIMIT': 'LIMIT',
|
||||
'IN': 'IN',
|
||||
'CONTAINS': 'CONTAINS',
|
||||
'LIKE': 'LIKE'
|
||||
'LIKE': 'LIKE',
|
||||
'NOT': 'NOT',
|
||||
'IS': 'IS',
|
||||
'NULL': 'NULL'
|
||||
};
|
||||
|
||||
if (keywords[upperIdentifier]) {
|
||||
@@ -210,10 +213,26 @@ class Parser {
|
||||
let operator: string;
|
||||
let value: any;
|
||||
|
||||
if (this.currentToken.type === 'LIKE') {
|
||||
if (this.currentToken.type === 'NOT') {
|
||||
this.eat('NOT');
|
||||
operator = 'NOT LIKE';
|
||||
this.eat('LIKE');
|
||||
value = this.parseValue();
|
||||
} else if (this.currentToken.type === 'LIKE') {
|
||||
operator = 'LIKE';
|
||||
this.eat('LIKE');
|
||||
value = this.parseValue();
|
||||
} else if (this.currentToken.type === 'IS') {
|
||||
this.eat('IS');
|
||||
if ((this.currentToken.type as any) === 'NOT') {
|
||||
operator = 'IS NOT NULL';
|
||||
this.eat('NOT');
|
||||
this.eat('NULL');
|
||||
} else {
|
||||
operator = 'IS NULL';
|
||||
this.eat('NULL');
|
||||
}
|
||||
value = null;
|
||||
} else if (this.currentToken.type === 'CONTAINS') {
|
||||
operator = 'CONTAINS';
|
||||
this.eat('CONTAINS');
|
||||
@@ -232,6 +251,8 @@ class Parser {
|
||||
return { type: 'Condition', field, operator, value };
|
||||
}
|
||||
|
||||
|
||||
|
||||
private parseInList(): any[] {
|
||||
this.eat('LBRACKET');
|
||||
const values: any[] = [];
|
||||
@@ -334,6 +355,15 @@ class Executor {
|
||||
return false;
|
||||
}
|
||||
return this.likeToRegex(actualValue).test(fieldValue);
|
||||
case 'NOT LIKE':
|
||||
if (typeof fieldValue !== 'string' || typeof actualValue !== 'string') {
|
||||
return true;
|
||||
}
|
||||
return !this.likeToRegex(actualValue).test(fieldValue);
|
||||
case 'IS NULL':
|
||||
return fieldValue === null || fieldValue === undefined;
|
||||
case 'IS NOT NULL':
|
||||
return fieldValue !== null && fieldValue !== undefined;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -73,3 +73,24 @@ console.log(filter(products, "WHERE name LIKE '%Phone%'"));
|
||||
|
||||
console.log('\n=== 第二个字符任意的产品 (A_pple%) ===');
|
||||
console.log(filter(products, "WHERE name LIKE 'A_pple%'"));
|
||||
|
||||
console.log('\n=== NOT LIKE 测试 ===');
|
||||
console.log('\n=== 不包含 Apple 的产品 (NOT LIKE %Apple%) ===');
|
||||
console.log(filter(products, "WHERE name NOT LIKE '%Apple%'"));
|
||||
|
||||
console.log('\n=== IS NULL 和 IS NOT NULL 测试 ===');
|
||||
const usersWithNull = [
|
||||
{ name: 'Alice', email: 'alice@example.com', age: 25 },
|
||||
{ name: 'Bob', email: null, age: 30 },
|
||||
{ name: 'Charlie', email: undefined, age: 35 },
|
||||
{ name: 'Diana', email: 'diana@example.com', age: null }
|
||||
];
|
||||
|
||||
console.log('\n=== Email 为 NULL 或 undefined 的用户 ===');
|
||||
console.log(filter(usersWithNull, "WHERE email IS NULL"));
|
||||
|
||||
console.log('\n=== Email 不为 NULL 且不为 undefined 的用户 ===');
|
||||
console.log(filter(usersWithNull, "WHERE email IS NOT NULL"));
|
||||
|
||||
console.log('\n=== Age 不为 NULL 且不为 undefined 的用户 ===');
|
||||
console.log(filter(usersWithNull, "WHERE age IS NOT NULL"));
|
||||
|
||||
Reference in New Issue
Block a user