@tachybase/plugin-full-text-search 0.23.40 → 0.23.41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,9 @@
1
1
  module.exports = {
2
- "@tachybase/client": "0.23.40",
2
+ "@tachybase/client": "0.23.41",
3
3
  "antd": "5.22.5",
4
4
  "lodash": "4.17.21",
5
- "@tachybase/actions": "0.23.40",
6
- "@tachybase/database": "0.23.40",
7
- "@tachybase/server": "0.23.40"
5
+ "@tachybase/server": "0.23.41",
6
+ "@tachybase/database": "0.23.41",
7
+ "@tachybase/actions": "0.23.41",
8
+ "sequelize": "6.37.5"
8
9
  };
@@ -0,0 +1,23 @@
1
+ import { WhereOptions } from 'sequelize';
2
+ import { handleFieldParams } from '../types';
3
+ type NestedRecord<T, K extends string> = K extends `${infer Head}.${infer Tail}` ? {
4
+ [Key in Head]: NestedRecord<T, Tail>;
5
+ } : {
6
+ [Key in K]: T;
7
+ };
8
+ export declare class FieldBase {
9
+ type: string;
10
+ like: symbol;
11
+ likeOperator: string;
12
+ constructor();
13
+ getFormateDateStr(field: string, fieldInfo: any): string;
14
+ date(params: handleFieldParams): any;
15
+ string(params: handleFieldParams): WhereOptions<any> | null;
16
+ number(params: handleFieldParams): WhereOptions<any>;
17
+ json(params: handleFieldParams): any;
18
+ protected convertToObj<T, K extends string>(key: K, value: T): NestedRecord<T, K>;
19
+ getMultiSelectFilter(field: string, matchEnum: string[]): WhereOptions<any>;
20
+ array(params: handleFieldParams): WhereOptions<any>;
21
+ private getMatchEnum;
22
+ }
23
+ export {};
@@ -0,0 +1,116 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __export = (target, all) => {
6
+ for (var name in all)
7
+ __defProp(target, name, { get: all[name], enumerable: true });
8
+ };
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
+ var FieldBase_exports = {};
19
+ __export(FieldBase_exports, {
20
+ FieldBase: () => FieldBase
21
+ });
22
+ module.exports = __toCommonJS(FieldBase_exports);
23
+ var import_database = require("@tachybase/database");
24
+ var import_sequelize = require("sequelize");
25
+ var import_utils = require("../utils");
26
+ class FieldBase {
27
+ type = "";
28
+ like = import_database.Op.like;
29
+ likeOperator = "LIKE";
30
+ constructor() {
31
+ }
32
+ getFormateDateStr(field, fieldInfo) {
33
+ return "YYYY-MM-DD HH:mm:ss";
34
+ }
35
+ date(params) {
36
+ return null;
37
+ }
38
+ string(params) {
39
+ var _a, _b;
40
+ const { field, fields, keyword } = params;
41
+ const fieldInfo = fields.get(field);
42
+ if (((_b = (_a = fieldInfo == null ? void 0 : fieldInfo.options) == null ? void 0 : _a.uiSchema) == null ? void 0 : _b["x-component"]) === "Select") {
43
+ const matchEnum = this.getMatchEnum(fieldInfo, keyword);
44
+ if (!matchEnum.length) {
45
+ return null;
46
+ }
47
+ return this.convertToObj(field, { [import_database.Op.in]: matchEnum });
48
+ }
49
+ return this.convertToObj(field, { [this.like]: `%${(0, import_utils.escapeLike)(keyword)}%` });
50
+ }
51
+ number(params) {
52
+ const { field, keyword } = params;
53
+ return {
54
+ [import_database.Op.and]: [
55
+ (0, import_database.where)(
56
+ (0, import_database.literal)(`CAST(${(0, import_sequelize.col)(field).col} AS TEXT)`),
57
+ // 确保不加引号,直接插入 SQL 表达式
58
+ {
59
+ [import_database.Op.like]: `%${(0, import_utils.escapeLike)(keyword)}%`
60
+ }
61
+ )
62
+ ]
63
+ };
64
+ }
65
+ json(params) {
66
+ return null;
67
+ }
68
+ // a.b.c = xxx 转成 { a: { b: { c: 'xxx' } } }
69
+ convertToObj(key, value) {
70
+ const MAX_DEPTH = 3;
71
+ const parts = key.split(".");
72
+ if (parts.length > MAX_DEPTH) {
73
+ throw new Error(`Maximum nesting depth of ${MAX_DEPTH} exceeded`);
74
+ }
75
+ const newKey = parts.shift();
76
+ if (!newKey) {
77
+ throw new Error("Invalid key");
78
+ }
79
+ if (!parts.length) {
80
+ return { [newKey]: value };
81
+ }
82
+ return { [newKey]: this.convertToObj(parts.join("."), value) };
83
+ }
84
+ // 多选框如何生成filter
85
+ getMultiSelectFilter(field, matchEnum) {
86
+ return this.convertToObj(field, { [import_database.Op.contains]: matchEnum });
87
+ }
88
+ array(params) {
89
+ var _a, _b;
90
+ const { field, keyword, fields } = params;
91
+ const fieldInfo = fields.get(field);
92
+ if (((_b = (_a = fieldInfo == null ? void 0 : fieldInfo.options) == null ? void 0 : _a.uiSchema) == null ? void 0 : _b["x-component"]) === "Select") {
93
+ const matchEnum = this.getMatchEnum(fieldInfo, keyword);
94
+ if (!matchEnum.length) {
95
+ return null;
96
+ }
97
+ return this.getMultiSelectFilter(field, matchEnum);
98
+ }
99
+ return null;
100
+ }
101
+ getMatchEnum(fieldInfo, keyword) {
102
+ const matchEnum = [];
103
+ const enumList = (fieldInfo == null ? void 0 : fieldInfo.options.uiSchema.enum) || [];
104
+ const lowerKeyword = keyword.toLowerCase();
105
+ for (const item of enumList) {
106
+ if (typeof (item == null ? void 0 : item.label) === "string" && item.label.toLowerCase().includes(lowerKeyword)) {
107
+ matchEnum.push(item.value);
108
+ }
109
+ }
110
+ return matchEnum;
111
+ }
112
+ }
113
+ // Annotate the CommonJS export names for ESM import in node:
114
+ 0 && (module.exports = {
115
+ FieldBase
116
+ });
@@ -0,0 +1,11 @@
1
+ import { WhereOptions } from 'sequelize';
2
+ import { handleFieldParams } from '../types';
3
+ import { FieldBase } from './FieldBase';
4
+ export declare class FieldMariadb extends FieldBase {
5
+ type: string;
6
+ getFormateDateStr(field: string, fieldInfo: any): string;
7
+ number(params: handleFieldParams): WhereOptions<any>;
8
+ date(params: handleFieldParams): WhereOptions<any>;
9
+ json(params: handleFieldParams): WhereOptions<any>;
10
+ getMultiSelectFilter(field: string, matchEnum: string[]): WhereOptions<any>;
11
+ }
@@ -0,0 +1,88 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __export = (target, all) => {
6
+ for (var name in all)
7
+ __defProp(target, name, { get: all[name], enumerable: true });
8
+ };
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
+ var FieldMariadb_exports = {};
19
+ __export(FieldMariadb_exports, {
20
+ FieldMariadb: () => FieldMariadb
21
+ });
22
+ module.exports = __toCommonJS(FieldMariadb_exports);
23
+ var import_database = require("@tachybase/database");
24
+ var import_sequelize = require("sequelize");
25
+ var import_utils = require("../utils");
26
+ var import_FieldBase = require("./FieldBase");
27
+ class FieldMariadb extends import_FieldBase.FieldBase {
28
+ type = "mariadb";
29
+ getFormateDateStr(field, fieldInfo) {
30
+ var _a, _b, _c, _d;
31
+ let formatStr = "%Y-%m-%d";
32
+ if ((_d = (_c = (_b = (_a = fieldInfo == null ? void 0 : fieldInfo.get(field)) == null ? void 0 : _a.options) == null ? void 0 : _b.uiSchema) == null ? void 0 : _c["x-component-props"]) == null ? void 0 : _d.dateFormat) {
33
+ const props = fieldInfo.get(field).options.uiSchema["x-component-props"];
34
+ formatStr = props.dateFormat.replace("YYYY", "%Y").replace("MM", "%m").replace("DD", "%d");
35
+ if (props.showTime) {
36
+ if (props.timeFormat.endsWith(" a")) {
37
+ formatStr += " %I:%i:%s %p";
38
+ } else {
39
+ formatStr += " %H:%i:%s";
40
+ }
41
+ }
42
+ }
43
+ return formatStr;
44
+ }
45
+ number(params) {
46
+ const { field, keyword } = params;
47
+ return {
48
+ [import_database.Op.and]: [
49
+ (0, import_database.where)(
50
+ (0, import_database.literal)(`CAST(${(0, import_sequelize.col)(field).col} AS CHAR)`),
51
+ // 确保不加引号,直接插入 SQL 表达式
52
+ {
53
+ [import_database.Op.like]: `%${(0, import_utils.escapeLike)(keyword)}%`
54
+ }
55
+ )
56
+ ]
57
+ };
58
+ }
59
+ date(params) {
60
+ const { field, keyword, dateStr, timezone } = params;
61
+ return {
62
+ [import_database.Op.and]: [
63
+ (0, import_database.where)((0, import_database.fn)("DATE_FORMAT", (0, import_database.fn)("CONVERT_TZ", (0, import_sequelize.col)(field), "+00:00", timezone), dateStr), {
64
+ [this.like]: `%${(0, import_utils.escapeLike)(keyword)}%`
65
+ })
66
+ ]
67
+ };
68
+ }
69
+ json(params) {
70
+ const { field, keyword } = params;
71
+ return {
72
+ [import_database.Op.and]: [
73
+ (0, import_database.where)((0, import_database.literal)(`JSON_UNQUOTE(JSON_EXTRACT(${field}, '$'))`), {
74
+ [import_database.Op.like]: `%${(0, import_utils.escapeLike)(keyword)}%`
75
+ })
76
+ ]
77
+ };
78
+ }
79
+ getMultiSelectFilter(field, matchEnum) {
80
+ return {
81
+ [import_database.Op.and]: [(0, import_database.literal)(`JSON_CONTAINS(${(0, import_sequelize.col)(field).col}, '${JSON.stringify(matchEnum)}')`)]
82
+ };
83
+ }
84
+ }
85
+ // Annotate the CommonJS export names for ESM import in node:
86
+ 0 && (module.exports = {
87
+ FieldMariadb
88
+ });
@@ -0,0 +1,10 @@
1
+ import { handleFieldParams } from '../types';
2
+ import { FieldBase } from './FieldBase';
3
+ export declare class FieldPostgres extends FieldBase {
4
+ type: string;
5
+ like: symbol;
6
+ likeOperator: string;
7
+ getFormateDateStr(field: string, fieldInfo: any): string;
8
+ date(params: handleFieldParams): any;
9
+ json(params: handleFieldParams): any;
10
+ }
@@ -0,0 +1,79 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __export = (target, all) => {
6
+ for (var name in all)
7
+ __defProp(target, name, { get: all[name], enumerable: true });
8
+ };
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
+ var FieldPostgres_exports = {};
19
+ __export(FieldPostgres_exports, {
20
+ FieldPostgres: () => FieldPostgres
21
+ });
22
+ module.exports = __toCommonJS(FieldPostgres_exports);
23
+ var import_database = require("@tachybase/database");
24
+ var import_sequelize = require("sequelize");
25
+ var import_utils = require("../utils");
26
+ var import_FieldBase = require("./FieldBase");
27
+ class FieldPostgres extends import_FieldBase.FieldBase {
28
+ type = "postgres";
29
+ like = import_database.Op.iLike;
30
+ likeOperator = "ILIKE";
31
+ getFormateDateStr(field, fieldInfo) {
32
+ var _a, _b, _c, _d;
33
+ let formatStr = "YYYY-MM-DD HH:mm:ss";
34
+ if ((_d = (_c = (_b = (_a = fieldInfo == null ? void 0 : fieldInfo.get(field)) == null ? void 0 : _a.options) == null ? void 0 : _b.uiSchema) == null ? void 0 : _c["x-component-props"]) == null ? void 0 : _d.dateFormat) {
35
+ const props = fieldInfo.get(field).options.uiSchema["x-component-props"];
36
+ formatStr = props.dateFormat;
37
+ if (props.showTime) {
38
+ formatStr += props.timeFormat.endsWith(" a") ? " HH12:MI:SS" : " HH24:MI:SS";
39
+ }
40
+ }
41
+ return formatStr;
42
+ }
43
+ date(params) {
44
+ const { field, keyword, dateStr, timezone } = params;
45
+ return {
46
+ [import_database.Op.and]: [
47
+ (0, import_database.where)(
48
+ (0, import_database.fn)(
49
+ "TO_CHAR",
50
+ (0, import_database.fn)(
51
+ "TIMEZONE",
52
+ timezone,
53
+ // 参数1:目标时区
54
+ (0, import_database.fn)("TIMEZONE", "UTC", (0, import_sequelize.col)(field))
55
+ // 参数2:UTC 转换后的字段
56
+ ),
57
+ dateStr
58
+ // 参数3:格式化字符串
59
+ ),
60
+ {
61
+ [import_database.Op.like]: `%${(0, import_utils.escapeLike)(keyword)}%`
62
+ }
63
+ )
64
+ ]
65
+ };
66
+ }
67
+ json(params) {
68
+ const { field, keyword } = params;
69
+ return this.convertToObj(field, {
70
+ ["::text"]: {
71
+ [this.like]: `%${(0, import_utils.escapeLike)(keyword)}%`
72
+ }
73
+ });
74
+ }
75
+ }
76
+ // Annotate the CommonJS export names for ESM import in node:
77
+ 0 && (module.exports = {
78
+ FieldPostgres
79
+ });
@@ -0,0 +1,15 @@
1
+ import { Op } from '@tachybase/database';
2
+ import { WhereOptions } from 'sequelize';
3
+ import { handleFieldParams } from '../types';
4
+ import { FieldBase } from './FieldBase';
5
+ export declare class FieldSqlite extends FieldBase {
6
+ type: string;
7
+ getFormateDateStr(field: string, fieldInfo: any): string;
8
+ date(params: handleFieldParams): {
9
+ [Op.and]: any[];
10
+ };
11
+ json(params: handleFieldParams): {
12
+ [Op.and]: any[];
13
+ };
14
+ getMultiSelectFilter(field: string, matchEnum: string[]): WhereOptions<any>;
15
+ }
@@ -0,0 +1,83 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __export = (target, all) => {
6
+ for (var name in all)
7
+ __defProp(target, name, { get: all[name], enumerable: true });
8
+ };
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
+ var FieldSqlite_exports = {};
19
+ __export(FieldSqlite_exports, {
20
+ FieldSqlite: () => FieldSqlite
21
+ });
22
+ module.exports = __toCommonJS(FieldSqlite_exports);
23
+ var import_database = require("@tachybase/database");
24
+ var import_sequelize = require("sequelize");
25
+ var import_utils = require("../utils");
26
+ var import_FieldBase = require("./FieldBase");
27
+ class FieldSqlite extends import_FieldBase.FieldBase {
28
+ type = "sqlite";
29
+ getFormateDateStr(field, fieldInfo) {
30
+ var _a, _b, _c, _d;
31
+ let formatStr = "%Y-%m-%d";
32
+ if ((_d = (_c = (_b = (_a = fieldInfo == null ? void 0 : fieldInfo.get(field)) == null ? void 0 : _a.options) == null ? void 0 : _b.uiSchema) == null ? void 0 : _c["x-component-props"]) == null ? void 0 : _d.dateFormat) {
33
+ const props = fieldInfo.get(field).options.uiSchema["x-component-props"];
34
+ formatStr = props.dateFormat.replace("YYYY", "%Y").replace("MM", "%m").replace("DD", "%d");
35
+ if (props.showTime) {
36
+ if (props.timeFormat.endsWith(" a")) {
37
+ formatStr += " %I:%M:%S %p";
38
+ } else {
39
+ formatStr += " %H:%M:%S";
40
+ }
41
+ }
42
+ }
43
+ return formatStr;
44
+ }
45
+ date(params) {
46
+ const { field, keyword, dateStr, timezone } = params;
47
+ return {
48
+ [import_database.Op.and]: [
49
+ (0, import_database.where)((0, import_database.fn)("strftime", dateStr, (0, import_database.fn)("datetime", (0, import_sequelize.col)(field), (0, import_utils.convertTimezoneOffset)(timezone))), {
50
+ [this.like]: `%${(0, import_utils.escapeLike)(keyword)}%`
51
+ })
52
+ ]
53
+ };
54
+ }
55
+ json(params) {
56
+ const { field, keyword } = params;
57
+ return {
58
+ [import_database.Op.and]: [
59
+ (0, import_database.where)((0, import_database.literal)(`json_extract(${field}, '$')`), {
60
+ [this.like]: `%${(0, import_utils.escapeLike)(keyword)}%`
61
+ })
62
+ ]
63
+ };
64
+ }
65
+ getMultiSelectFilter(field, matchEnum) {
66
+ const matchList = matchEnum.map((value) => `'${value}'`).join(",");
67
+ return {
68
+ [import_database.Op.and]: [
69
+ (0, import_database.literal)(`
70
+ EXISTS (
71
+ SELECT 1
72
+ FROM json_each(${(0, import_sequelize.col)(field).col})
73
+ WHERE json_each.value IN (${matchList})
74
+ )
75
+ `)
76
+ ]
77
+ };
78
+ }
79
+ }
80
+ // Annotate the CommonJS export names for ESM import in node:
81
+ 0 && (module.exports = {
82
+ FieldSqlite
83
+ });
@@ -0,0 +1,2 @@
1
+ import { Context } from '@tachybase/actions';
2
+ export declare function searchMiddleware(ctx: Context, next: Function): Promise<any>;
@@ -0,0 +1,97 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __export = (target, all) => {
6
+ for (var name in all)
7
+ __defProp(target, name, { get: all[name], enumerable: true });
8
+ };
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
+ var search_exports = {};
19
+ __export(search_exports, {
20
+ searchMiddleware: () => searchMiddleware
21
+ });
22
+ module.exports = __toCommonJS(search_exports);
23
+ var import_constants = require("../../constants");
24
+ var import_FieldMariadb = require("../dialects/FieldMariadb");
25
+ var import_FieldPostgres = require("../dialects/FieldPostgres");
26
+ var import_FieldSqlite = require("../dialects/FieldSqlite");
27
+ var import_searchField = require("../searchField");
28
+ function getDialect(dbType) {
29
+ const handlers = {
30
+ postgres: () => new import_FieldPostgres.FieldPostgres(),
31
+ mysql: () => new import_FieldMariadb.FieldMariadb(),
32
+ // TODO: 考虑mysql5.7的兼容性
33
+ mariadb: () => new import_FieldMariadb.FieldMariadb(),
34
+ sqlite: () => new import_FieldSqlite.FieldSqlite()
35
+ };
36
+ const handler = handlers[dbType];
37
+ if (!handler) {
38
+ throw new Error(`Unsupported database type: ${dbType}`);
39
+ }
40
+ return handler();
41
+ }
42
+ async function searchMiddleware(ctx, next) {
43
+ var _a, _b, _c;
44
+ const params = ctx.action.params;
45
+ if ((_a = params.search) == null ? void 0 : _a.keywords) {
46
+ params.search.keywords = params.search.keywords.map((v) => v.trim()).filter((v) => v);
47
+ }
48
+ if (!((_c = (_b = params.search) == null ? void 0 : _b.keywords) == null ? void 0 : _c.length)) {
49
+ return next();
50
+ }
51
+ if (params.search.keywords.length > import_constants.SEARCH_KEYWORDS_MAX) {
52
+ ctx.throw(400, `keywords max length is ${import_constants.SEARCH_KEYWORDS_MAX}`);
53
+ }
54
+ let fields = [];
55
+ const collection = ctx.db.getCollection(ctx.action.resourceName);
56
+ if (params.search.fields && !params.search.isSearchAllFields) {
57
+ fields = params.search.fields;
58
+ } else {
59
+ fields = [...collection.fields.keys()];
60
+ }
61
+ const dbType = ctx.db.sequelize.getDialect();
62
+ const handler = getDialect(dbType);
63
+ const timezone = ctx.get("X-Timezone") || "+00:00";
64
+ const searchFilterList = [];
65
+ for (const field of fields) {
66
+ searchFilterList.push(
67
+ ...(0, import_searchField.processField)({
68
+ field,
69
+ handler,
70
+ collection,
71
+ ctx,
72
+ search: params.search,
73
+ timezone
74
+ })
75
+ );
76
+ }
77
+ if (!searchFilterList.length) {
78
+ return next();
79
+ }
80
+ const searchFilter = { $or: searchFilterList };
81
+ if (params.filter && Object.keys(params.filter).length) {
82
+ if (Array.isArray(params.filter.$and)) {
83
+ params.filter.$and.push(searchFilter);
84
+ } else {
85
+ params.filter = {
86
+ $and: [params.filter, searchFilter]
87
+ };
88
+ }
89
+ } else {
90
+ params.filter = searchFilter;
91
+ }
92
+ await next();
93
+ }
94
+ // Annotate the CommonJS export names for ESM import in node:
95
+ 0 && (module.exports = {
96
+ searchMiddleware
97
+ });
@@ -21,235 +21,19 @@ __export(plugin_exports, {
21
21
  default: () => plugin_default
22
22
  });
23
23
  module.exports = __toCommonJS(plugin_exports);
24
- var import_database = require("@tachybase/database");
25
24
  var import_server = require("@tachybase/server");
26
- var import_constants = require("../constants");
27
- function escapeLike(value) {
28
- return value.replace(/[_%]/g, "\\$&");
29
- }
30
- const stringFields = ["string", "text", "sequence", "uid", "integer", "float"];
31
- const numberFields = ["bigInt", "double"];
32
- const dateFields = ["date", "datetime", "timestamp"];
33
- const jsonFields = ["json", "jsonb"];
34
- function getCollectionField(collection, fieldStr, db) {
35
- if (!fieldStr.includes(".")) {
36
- return {
37
- collection,
38
- fieldStr
39
- };
40
- }
41
- const parts = fieldStr.split(".");
42
- const associationTable = parts.shift();
43
- const fields = collection.getFields();
44
- const foreignField = fields.find((v) => v.name === associationTable);
45
- const nextCollection = db.getCollection(foreignField.target);
46
- const nextField = parts.join(".");
47
- return getCollectionField(nextCollection, nextField, db);
48
- }
49
- function handleJsonQuery(field, dbType, keyword) {
50
- if (dbType === "postgres") {
51
- return (0, import_database.where)(
52
- (0, import_database.literal)(`${field}->>0`),
53
- // Assuming the key is '0', adjust for your actual key
54
- {
55
- [import_database.Op.iLike]: `%${escapeLike(keyword)}%`
56
- }
57
- );
58
- } else if (dbType === "mysql") {
59
- return (0, import_database.where)((0, import_database.literal)(`JSON_UNQUOTE(JSON_EXTRACT(${field}, '$'))`), {
60
- [import_database.Op.like]: `%${escapeLike(keyword)}%`
61
- });
62
- } else if (dbType === "sqlite") {
63
- return (0, import_database.where)((0, import_database.literal)(`json_extract(${field}, '$')`), {
64
- [import_database.Op.like]: `%${escapeLike(keyword)}%`
65
- });
66
- } else {
67
- return field;
68
- }
69
- }
70
- function convertTimezoneOffset(offset) {
71
- const regex = /^([+-])(\d{2}):(\d{2})$/;
72
- const match = offset.match(regex);
73
- if (match) {
74
- const sign = match[1];
75
- const hours = parseInt(match[2], 10);
76
- return `${sign}${hours} hours`;
77
- }
78
- return offset;
79
- }
80
- function getRealFieldFilter(field, value) {
81
- const parts = field.split(".");
82
- const key = parts.shift();
83
- if (!parts.length) {
84
- return { [key]: value };
85
- }
86
- return { [key]: getRealFieldFilter(parts.join("."), value) };
87
- }
25
+ var import_search = require("./middlewares/search");
88
26
  class PluginFullTextSearchServer extends import_server.Plugin {
89
27
  async afterAdd() {
90
28
  }
91
29
  async beforeLoad() {
92
30
  }
93
31
  async load() {
94
- this.app.resourcer.use(
95
- async (ctx, next) => {
96
- var _a, _b, _c;
97
- if (ctx.action.actionName !== "list") {
98
- return next();
99
- }
100
- const params = ctx.action.params;
101
- if ((_a = params.search) == null ? void 0 : _a.keywords) {
102
- params.search.keywords = params.search.keywords.map((v) => v.trim()).filter((v) => v);
103
- }
104
- if (!((_c = (_b = params.search) == null ? void 0 : _b.keywords) == null ? void 0 : _c.length)) {
105
- return next();
106
- }
107
- if (params.search.keywords.length > import_constants.SEARCH_KEYWORDS_MAX) {
108
- ctx.throw(500, `keywords max length is ${import_constants.SEARCH_KEYWORDS_MAX}`);
109
- }
110
- let fields = [];
111
- const collection = ctx.db.getCollection(ctx.action.resourceName);
112
- const fieldsAll = collection.fields;
113
- if (params.search.fields && !params.search.isSearchAllFields) {
114
- fields = params.search.fields;
115
- } else {
116
- fields = [...collection.fields.keys()];
117
- }
118
- const utcOffset = ctx.get("X-Timezone") || "+00:00";
119
- const dbType = ctx.db.sequelize.getDialect();
120
- const searchFilter = fields.reduce((acc, field) => {
121
- var _a2, _b2, _c2, _d, _e;
122
- let type;
123
- let fieldName;
124
- let fieldInfo;
125
- if (!field.includes(".")) {
126
- fieldName = dbType === "postgres" ? `"${ctx.action.resourceName}"."${field}"` : `\`${collection.name}\`.\`${field}\``;
127
- fieldInfo = fieldsAll;
128
- type = (_a2 = fieldInfo.get(field)) == null ? void 0 : _a2.type;
129
- } else {
130
- const { collection: targetCollection, fieldStr } = getCollectionField(collection, field, ctx.db);
131
- fieldInfo = targetCollection.fields;
132
- type = (_b2 = fieldInfo.get(fieldStr)) == null ? void 0 : _b2.type;
133
- fieldName = dbType === "postgres" ? `"${targetCollection.name}"."${fieldStr}"` : `\`${targetCollection.name}\`.\`${fieldStr}\``;
134
- fieldInfo = fieldsAll;
135
- }
136
- if (stringFields.includes(type)) {
137
- for (const keyword of params.search.keywords) {
138
- const filterCondition = getRealFieldFilter(field, {
139
- [dbType === "postgres" ? import_database.Op.iLike : import_database.Op.like]: `%${escapeLike(keyword)}%`
140
- });
141
- acc.push(filterCondition);
142
- }
143
- } else if (numberFields.includes(type)) {
144
- if (field.includes(".")) {
145
- return acc;
146
- }
147
- let castFunction = "";
148
- if (dbType === "mysql") {
149
- castFunction = `CAST(${fieldName} AS CHAR)`;
150
- } else {
151
- castFunction = `CAST(${fieldName} AS TEXT)`;
152
- }
153
- const searchList = [];
154
- for (const keyword of params.search.keywords) {
155
- searchList.push(
156
- (0, import_database.where)(
157
- (0, import_database.literal)(castFunction),
158
- // 将 BIGINT 转换为字符串
159
- {
160
- // 根据数据库类型选择 LIKE 或 ILIKE
161
- [dbType === "postgres" ? import_database.Op.iLike : import_database.Op.like]: `%${escapeLike(keyword)}%`
162
- }
163
- )
164
- );
165
- }
166
- acc.push({
167
- [import_database.Op.or]: searchList
168
- });
169
- } else if (dateFields.includes(type)) {
170
- if (field.includes(".")) {
171
- return acc;
172
- }
173
- let formatStr = "YYYY-MM-DD HH:mm:ss";
174
- const info = fieldInfo.get(field);
175
- const searchList = [];
176
- if ((_e = (_d = (_c2 = info == null ? void 0 : info.options) == null ? void 0 : _c2.uiSchema) == null ? void 0 : _d["x-component-props"]) == null ? void 0 : _e.dateFormat) {
177
- const props = info.options.uiSchema["x-component-props"];
178
- if (dbType === "postgres") {
179
- formatStr = props.dateFormat;
180
- if (props.showTime) {
181
- if (props.timeFormat.endsWith(" a")) {
182
- formatStr += " HH12:MI:SS";
183
- } else {
184
- formatStr += " HH24:MI:SS";
185
- }
186
- }
187
- } else if (dbType === "mysql") {
188
- formatStr = props.dateFormat.replace("YYYY", "%Y").replace("MM", "%m").replace("DD", "%d");
189
- if (props.showTime) {
190
- if (props.timeFormat.endsWith(" a")) {
191
- formatStr += " %I:%i:%s %p";
192
- } else {
193
- formatStr += " %H:%i:%s";
194
- }
195
- }
196
- } else if (dbType === "sqlite") {
197
- formatStr = props.dateFormat.replace("YYYY", "%Y").replace("MM", "%m").replace("DD", "%d");
198
- if (props.showTime) {
199
- if (props.timeFormat.endsWith(" a")) {
200
- formatStr += " %I:%M:%S %p";
201
- } else {
202
- formatStr += " %H:%M:%S";
203
- }
204
- }
205
- }
206
- }
207
- for (const keyword of params.search.keywords) {
208
- const condition = dbType === "postgres" ? (0, import_database.literal)(`TO_CHAR((${fieldName} AT TIME ZONE 'UTC') AT TIME ZONE '${utcOffset}', '${formatStr}')`) : dbType === "mysql" ? (0, import_database.fn)("DATE_FORMAT", (0, import_database.fn)("CONVERT_TZ", fieldName, "+00:00", utcOffset), formatStr) : dbType === "sqlite" ? (0, import_database.fn)("strftime", formatStr, (0, import_database.fn)("datetime", fieldName, convertTimezoneOffset(utcOffset))) : fieldName;
209
- searchList.push(
210
- (0, import_database.where)(condition, {
211
- [dbType === "postgres" ? import_database.Op.iLike : import_database.Op.like]: `%${escapeLike(keyword)}%`
212
- })
213
- );
214
- }
215
- acc.push({
216
- [import_database.Op.or]: searchList
217
- });
218
- } else if (jsonFields.includes(type)) {
219
- if (field.includes(".")) {
220
- return acc;
221
- }
222
- const searchList = [];
223
- for (const keyword of params.search.keywords) {
224
- searchList.push(handleJsonQuery(fieldName, dbType, keyword));
225
- }
226
- acc.push({
227
- [import_database.Op.or]: searchList
228
- });
229
- }
230
- return acc;
231
- }, []);
232
- if (searchFilter.length) {
233
- if (params.filter && Object.keys(params.filter).length) {
234
- if (params.filter.$and) {
235
- params.filter.$and.push({ $or: searchFilter });
236
- } else {
237
- params.filter = {
238
- $and: [params.filter, { $or: searchFilter }]
239
- };
240
- }
241
- } else {
242
- params.filter = { $or: searchFilter };
243
- }
244
- }
245
- await next();
246
- },
247
- {
248
- tag: "full-text-search",
249
- after: "acl",
250
- before: "parseVariables"
251
- }
252
- );
32
+ this.app.resourcer.use(import_search.searchMiddleware, {
33
+ tag: "full-text-search",
34
+ after: "acl",
35
+ before: "parseVariables"
36
+ });
253
37
  }
254
38
  async install() {
255
39
  }
@@ -0,0 +1,7 @@
1
+ import { FieldBase } from './dialects/FieldBase';
2
+ import { ProcessFieldParams } from './types';
3
+ export declare function handleField(handler: FieldBase, func: Function, field: string, fields: Map<string, any>, keywords: string[], extraParams?: {
4
+ timezone?: string;
5
+ dateStr?: string;
6
+ }): any[];
7
+ export declare function processField({ field, handler, collection, ctx, search, timezone }: ProcessFieldParams): any[];
@@ -0,0 +1,113 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __export = (target, all) => {
6
+ for (var name in all)
7
+ __defProp(target, name, { get: all[name], enumerable: true });
8
+ };
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
+ var searchField_exports = {};
19
+ __export(searchField_exports, {
20
+ handleField: () => handleField,
21
+ processField: () => processField
22
+ });
23
+ module.exports = __toCommonJS(searchField_exports);
24
+ const fieldTypes = {
25
+ string: ["string", "text", "sequence", "uid", "integer", "float"],
26
+ number: ["bigInt", "double"],
27
+ date: ["date", "datetime", "timestamp"],
28
+ json: ["json", "jsonb"],
29
+ array: ["array"]
30
+ };
31
+ function isFieldType(type, fieldType) {
32
+ return fieldTypes[fieldType].includes(type);
33
+ }
34
+ function getCollectionField(collection, fieldStr, db) {
35
+ if (!fieldStr.includes(".")) {
36
+ return {
37
+ collection,
38
+ fieldStr
39
+ };
40
+ }
41
+ const parts = fieldStr.split(".");
42
+ const associationTable = parts.shift();
43
+ const fields = collection.getFields();
44
+ const foreignField = fields.find((v) => v.name === associationTable);
45
+ if (!foreignField) {
46
+ db.logger.error(`Foreign field '${associationTable}' not found in collection '${collection.name}'`);
47
+ }
48
+ const newCollection = db.getCollection(foreignField.target);
49
+ const newField = parts.join(".");
50
+ return getCollectionField(newCollection, newField, db);
51
+ }
52
+ function handleField(handler, func, field, fields, keywords, extraParams = {}) {
53
+ const conditions = [];
54
+ for (const keyword of keywords) {
55
+ const params = {
56
+ field,
57
+ fields,
58
+ keyword,
59
+ timezone: extraParams.timezone,
60
+ dateStr: extraParams.dateStr
61
+ };
62
+ const condition = func.call(handler, params);
63
+ if (condition) {
64
+ conditions.push(condition);
65
+ }
66
+ }
67
+ return conditions;
68
+ }
69
+ function processField({ field, handler, collection, ctx, search, timezone }) {
70
+ var _a, _b, _c, _d;
71
+ let type;
72
+ let fields;
73
+ if (!field.includes(".")) {
74
+ fields = collection.fields;
75
+ type = (_a = fields.get(field)) == null ? void 0 : _a.type;
76
+ } else {
77
+ const { collection: targetCollection, fieldStr } = getCollectionField(collection, field, ctx.db);
78
+ fields = targetCollection.fields;
79
+ type = (_b = fields.get(fieldStr)) == null ? void 0 : _b.type;
80
+ }
81
+ if ((_d = (_c = fields.get(field)) == null ? void 0 : _c.options) == null ? void 0 : _d.isForeignKey) {
82
+ return [];
83
+ }
84
+ if (isFieldType(type, "string")) {
85
+ return handleField(handler, handler.string, field, fields, search.keywords);
86
+ } else if (isFieldType(type, "number")) {
87
+ if (!field.includes(".")) {
88
+ return handleField(handler, handler.number, field, fields, search.keywords);
89
+ }
90
+ } else if (isFieldType(type, "date")) {
91
+ if (!field.includes(".")) {
92
+ const dateStr = handler.getFormateDateStr(field, fields);
93
+ return handleField(handler, handler.date, field, fields, search.keywords, {
94
+ timezone,
95
+ dateStr
96
+ });
97
+ }
98
+ } else if (isFieldType(type, "json")) {
99
+ if (!field.includes(".")) {
100
+ return handleField(handler, handler.json, field, fields, search.keywords);
101
+ }
102
+ } else if (isFieldType(type, "array")) {
103
+ if (!field.includes(".")) {
104
+ return handleField(handler, handler.array, field, fields, search.keywords);
105
+ }
106
+ }
107
+ return [];
108
+ }
109
+ // Annotate the CommonJS export names for ESM import in node:
110
+ 0 && (module.exports = {
111
+ handleField,
112
+ processField
113
+ });
@@ -0,0 +1,24 @@
1
+ import { Context } from '@tachybase/actions';
2
+ import { Collection } from '@tachybase/database';
3
+ import { FieldBase } from './dialects/FieldBase';
4
+ export type FiledName = string;
5
+ export interface SearchParams {
6
+ keywords?: string[];
7
+ fields?: string[];
8
+ isSearchAllFields?: boolean;
9
+ }
10
+ export interface ProcessFieldParams {
11
+ ctx: Context;
12
+ field: string;
13
+ handler: FieldBase;
14
+ collection: Collection;
15
+ search: SearchParams;
16
+ timezone: string;
17
+ }
18
+ export type handleFieldParams = {
19
+ field: string;
20
+ keyword: string;
21
+ fields: Map<string, any>;
22
+ timezone?: string;
23
+ dateStr?: string;
24
+ };
@@ -0,0 +1,15 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __copyProps = (to, from, except, desc) => {
6
+ if (from && typeof from === "object" || typeof from === "function") {
7
+ for (let key of __getOwnPropNames(from))
8
+ if (!__hasOwnProp.call(to, key) && key !== except)
9
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
10
+ }
11
+ return to;
12
+ };
13
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
14
+ var types_exports = {};
15
+ module.exports = __toCommonJS(types_exports);
@@ -0,0 +1,2 @@
1
+ export declare function escapeLike(value: string): string;
2
+ export declare function convertTimezoneOffset(utcOffset: string): string;
@@ -0,0 +1,41 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __export = (target, all) => {
6
+ for (var name in all)
7
+ __defProp(target, name, { get: all[name], enumerable: true });
8
+ };
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
+ var utils_exports = {};
19
+ __export(utils_exports, {
20
+ convertTimezoneOffset: () => convertTimezoneOffset,
21
+ escapeLike: () => escapeLike
22
+ });
23
+ module.exports = __toCommonJS(utils_exports);
24
+ function escapeLike(value) {
25
+ return value.replace(/[_%]/g, "\\$&");
26
+ }
27
+ function convertTimezoneOffset(utcOffset) {
28
+ if (!/^[+-]?\d{2}:\d{2}$/.test(utcOffset)) {
29
+ throw new Error("Invalid UTC offset format. Expected format: (+/-)HH:MM");
30
+ }
31
+ const parts = utcOffset.split(":");
32
+ const hours = parseInt(parts[0].replace("+", ""), 10);
33
+ const minutes = parseInt(parts[1], 10);
34
+ const totalMinutes = hours * 60 + (hours >= 0 ? minutes : -minutes);
35
+ return `${totalMinutes} minutes`;
36
+ }
37
+ // Annotate the CommonJS export names for ESM import in node:
38
+ 0 && (module.exports = {
39
+ convertTimezoneOffset,
40
+ escapeLike
41
+ });
package/package.json CHANGED
@@ -1,18 +1,20 @@
1
1
  {
2
2
  "name": "@tachybase/plugin-full-text-search",
3
- "version": "0.23.40",
3
+ "version": "0.23.41",
4
4
  "description": "Provides full text search capability",
5
5
  "main": "dist/server/index.js",
6
6
  "devDependencies": {
7
+ "@types/sequelize": "^4.28.20",
7
8
  "antd": "5.22.5",
8
- "lodash": "4.17.21"
9
+ "lodash": "4.17.21",
10
+ "sequelize": "^6.37.5"
9
11
  },
10
12
  "peerDependencies": {
11
- "@tachybase/actions": "0.23.40",
12
- "@tachybase/server": "0.23.40",
13
- "@tachybase/test": "0.23.40",
14
- "@tachybase/client": "0.23.40",
15
- "@tachybase/database": "0.23.40"
13
+ "@tachybase/actions": "0.23.41",
14
+ "@tachybase/client": "0.23.41",
15
+ "@tachybase/database": "0.23.41",
16
+ "@tachybase/test": "0.23.41",
17
+ "@tachybase/server": "0.23.41"
16
18
  },
17
19
  "description.zh-CN": "提供全字段搜索能力",
18
20
  "displayName.zh-CN": "全文搜索"