aws-service-stack 0.18.319 → 0.18.320

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.
Files changed (43) hide show
  1. package/dist/_examples/service/entity/entity-service.js +2 -1
  2. package/dist/_examples/service/entity/entity-service.js.map +1 -1
  3. package/dist/model/filter.model.d.ts +7 -0
  4. package/dist/model/filter.model.js +9 -1
  5. package/dist/model/filter.model.js.map +1 -1
  6. package/dist/repositories/base-es.repo.d.ts +4 -1
  7. package/dist/repositories/base-es.repo.interface.d.ts +4 -1
  8. package/dist/repositories/base-es.repo.interface.js.map +1 -1
  9. package/dist/repositories/base-es.repo.js +9 -4
  10. package/dist/repositories/base-es.repo.js.map +1 -1
  11. package/dist/service/crud.service.d.ts +4 -1
  12. package/dist/service/crud.service.interface.d.ts +4 -1
  13. package/dist/service/crud.service.interface.js.map +1 -1
  14. package/dist/service/crud.service.js +2 -2
  15. package/dist/service/crud.service.js.map +1 -1
  16. package/dist/utils/index.d.ts +2 -0
  17. package/dist/utils/index.js +2 -0
  18. package/dist/utils/index.js.map +1 -1
  19. package/dist/utils/opensearch/opensearch.parser.aggs.d.ts +8 -0
  20. package/dist/utils/opensearch/opensearch.parser.aggs.js +66 -0
  21. package/dist/utils/opensearch/opensearch.parser.aggs.js.map +1 -0
  22. package/dist/utils/opensearch/opensearch.parser.d.ts +13 -0
  23. package/dist/utils/opensearch/opensearch.parser.js +62 -0
  24. package/dist/utils/opensearch/opensearch.parser.js.map +1 -0
  25. package/dist/utils/opensearch/opensearch.parser.keyword.d.ts +13 -0
  26. package/dist/utils/opensearch/opensearch.parser.keyword.js +61 -0
  27. package/dist/utils/opensearch/opensearch.parser.keyword.js.map +1 -0
  28. package/dist/utils/opensearch/opensearch.parser.query.map.d.ts +5 -0
  29. package/dist/utils/opensearch/opensearch.parser.query.map.js +39 -0
  30. package/dist/utils/opensearch/opensearch.parser.query.map.js.map +1 -0
  31. package/dist/utils/opensearch/opensearch.parser.sort.d.ts +1 -0
  32. package/dist/utils/opensearch/opensearch.parser.sort.js +22 -0
  33. package/dist/utils/opensearch/opensearch.parser.sort.js.map +1 -0
  34. package/dist/utils/opensearch/opensearch.parser.utils.d.ts +1 -0
  35. package/dist/utils/opensearch/opensearch.parser.utils.js +47 -0
  36. package/dist/utils/opensearch/opensearch.parser.utils.js.map +1 -0
  37. package/dist/utils/opensearch/opensearch.transform.d.ts +1 -0
  38. package/dist/utils/opensearch/opensearch.transform.js +48 -0
  39. package/dist/utils/opensearch/opensearch.transform.js.map +1 -0
  40. package/package.json +1 -1
  41. package/dist/utils/opensearch.parser.d.ts +0 -26
  42. package/dist/utils/opensearch.parser.js +0 -206
  43. package/dist/utils/opensearch.parser.js.map +0 -1
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildSearchQueryOS = buildSearchQueryOS;
4
+ const types_1 = require("../../model/index.js");
5
+ const opensearch_parser_keyword_1 = require("./opensearch.parser.keyword");
6
+ const opensearch_parser_sort_1 = require("./opensearch.parser.sort");
7
+ const opensearch_parser_query_map_1 = require("./opensearch.parser.query.map");
8
+ const opensearch_parser_utils_1 = require("./opensearch.parser.utils");
9
+ const opensearch_utils_1 = require("../opensearch.utils");
10
+ const opensearch_parser_aggs_1 = require("./opensearch.parser.aggs");
11
+ // ---------------- Main Function ----------------
12
+ function buildSearchQueryOS(queryParams, opensearchIndex) {
13
+ const filters = (0, opensearch_parser_utils_1.parseQueryString)(queryParams);
14
+ const filterMap = {}; // merge range per field
15
+ const boolQuery = {
16
+ must: [],
17
+ must_not: [],
18
+ filter: [],
19
+ should: [],
20
+ minimum_should_match: undefined,
21
+ };
22
+ for (const [key, value] of Object.entries(filters)) {
23
+ if (!value)
24
+ continue;
25
+ const [field, boolQueryType = types_1.BoolQueryTypeOS.eq] = key.split("__");
26
+ const fn = opensearch_parser_query_map_1.boolQueryMap[boolQueryType];
27
+ if (!fn)
28
+ continue;
29
+ // Range filters
30
+ if (boolQueryType === types_1.RangeTypeOS.gte || boolQueryType === types_1.RangeTypeOS.lte) {
31
+ const existing = filterMap[field]?.range;
32
+ filterMap[field] = fn(field, value, existing);
33
+ continue;
34
+ }
35
+ // Other filters
36
+ const builtQuery = fn(field, value);
37
+ if (builtQuery.negation)
38
+ boolQuery.must_not.push(builtQuery.query);
39
+ else
40
+ boolQuery.must.push(builtQuery?.query ? builtQuery.query : builtQuery);
41
+ }
42
+ const filter = Object.values(filterMap);
43
+ if (filter.length)
44
+ boolQuery.filter = filter;
45
+ (0, opensearch_parser_keyword_1.buildSearchKeyword)(queryParams, boolQuery);
46
+ const { size, from } = (0, opensearch_utils_1.calculatePagination)(queryParams);
47
+ const aggs = (0, opensearch_parser_aggs_1.buildAggregations)(queryParams);
48
+ console.log("aggs", aggs);
49
+ return {
50
+ index: opensearchIndex,
51
+ body: {
52
+ query: {
53
+ bool: boolQuery,
54
+ },
55
+ aggs: aggs,
56
+ size: size ?? 25,
57
+ from: from ?? 0,
58
+ sort: (0, opensearch_parser_sort_1.buildSortOS)(queryParams),
59
+ },
60
+ };
61
+ }
62
+ //# sourceMappingURL=opensearch.parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opensearch.parser.js","sourceRoot":"","sources":["../../../src/utils/opensearch/opensearch.parser.ts"],"names":[],"mappings":";;AASA,gDAyDC;AAlED,2CAA4E;AAC5E,2EAAiE;AACjE,qEAAuD;AACvD,+EAA6D;AAC7D,uEAA6D;AAC7D,0DAA0D;AAC1D,qEAA6D;AAE7D,kDAAkD;AAClD,SAAgB,kBAAkB,CAAC,WAAW,EAAE,eAAuB;IACrE,MAAM,OAAO,GAAG,IAAA,0CAAgB,EAAC,WAAW,CAAC,CAAC;IAE9C,MAAM,SAAS,GAAwB,EAAE,CAAC,CAAC,wBAAwB;IAEnE,MAAM,SAAS,GAAgB;QAC7B,IAAI,EAAE,EAAE;QACR,QAAQ,EAAE,EAAE;QACZ,MAAM,EAAE,EAAE;QACV,MAAM,EAAE,EAAE;QACV,oBAAoB,EAAE,SAAS;KAChC,CAAC;IAEF,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACnD,IAAI,CAAC,KAAK;YAAE,SAAS;QAErB,MAAM,CAAC,KAAK,EAAE,aAAa,GAAG,uBAAe,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACpE,MAAM,EAAE,GAAG,0CAAY,CAAC,aAAa,CAAC,CAAC;QACvC,IAAI,CAAC,EAAE;YAAE,SAAS;QAElB,gBAAgB;QAChB,IAAI,aAAa,KAAK,mBAAW,CAAC,GAAG,IAAI,aAAa,KAAK,mBAAW,CAAC,GAAG,EAAE,CAAC;YAC3E,MAAM,QAAQ,GAAG,SAAS,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC;YACzC,SAAS,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;YAC9C,SAAS;QACX,CAAC;QAED,gBAAgB;QAChB,MAAM,UAAU,GAAG,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QACpC,IAAI,UAAU,CAAC,QAAQ;YAAE,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;;YAC9D,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;IAC9E,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAExC,IAAI,MAAM,CAAC,MAAM;QAAE,SAAS,CAAC,MAAM,GAAG,MAAM,CAAC;IAE7C,IAAA,8CAAkB,EAAC,WAAW,EAAE,SAAS,CAAC,CAAC;IAE3C,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,IAAA,sCAAmB,EAAC,WAAW,CAAC,CAAC;IAExD,MAAM,IAAI,GAAG,IAAA,0CAAiB,EAAC,WAAW,CAAC,CAAC;IAE5C,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAE1B,OAAO;QACL,KAAK,EAAE,eAAe;QACtB,IAAI,EAAE;YACJ,KAAK,EAAE;gBACL,IAAI,EAAE,SAAS;aAChB;YACD,IAAI,EAAE,IAAI;YACV,IAAI,EAAE,IAAI,IAAI,EAAE;YAChB,IAAI,EAAE,IAAI,IAAI,CAAC;YACf,IAAI,EAAE,IAAA,oCAAW,EAAC,WAAW,CAAC;SAC/B;KACF,CAAC;AACJ,CAAC","sourcesContent":["import { BoolQueryOS, BoolQueryTypeOS, RangeTypeOS } from \"@chinggis/types\";\nimport { buildSearchKeyword } from \"./opensearch.parser.keyword\";\nimport { buildSortOS } from \"./opensearch.parser.sort\";\nimport { boolQueryMap } from \"./opensearch.parser.query.map\";\nimport { parseQueryString } from \"./opensearch.parser.utils\";\nimport { calculatePagination } from \"../opensearch.utils\";\nimport { buildAggregations } from \"./opensearch.parser.aggs\";\n\n// ---------------- Main Function ----------------\nexport function buildSearchQueryOS(queryParams, opensearchIndex: string) {\n const filters = parseQueryString(queryParams);\n\n const filterMap: Record<string, any> = {}; // merge range per field\n\n const boolQuery: BoolQueryOS = {\n must: [],\n must_not: [],\n filter: [],\n should: [],\n minimum_should_match: undefined,\n };\n\n for (const [key, value] of Object.entries(filters)) {\n if (!value) continue;\n\n const [field, boolQueryType = BoolQueryTypeOS.eq] = key.split(\"__\");\n const fn = boolQueryMap[boolQueryType];\n if (!fn) continue;\n\n // Range filters\n if (boolQueryType === RangeTypeOS.gte || boolQueryType === RangeTypeOS.lte) {\n const existing = filterMap[field]?.range;\n filterMap[field] = fn(field, value, existing);\n continue;\n }\n\n // Other filters\n const builtQuery = fn(field, value);\n if (builtQuery.negation) boolQuery.must_not.push(builtQuery.query);\n else boolQuery.must.push(builtQuery?.query ? builtQuery.query : builtQuery);\n }\n\n const filter = Object.values(filterMap);\n\n if (filter.length) boolQuery.filter = filter;\n\n buildSearchKeyword(queryParams, boolQuery);\n\n const { size, from } = calculatePagination(queryParams);\n\n const aggs = buildAggregations(queryParams);\n\n console.log(\"aggs\", aggs);\n\n return {\n index: opensearchIndex,\n body: {\n query: {\n bool: boolQuery,\n },\n aggs: aggs,\n size: size ?? 25,\n from: from ?? 0,\n sort: buildSortOS(queryParams),\n },\n };\n}\n"]}
@@ -0,0 +1,13 @@
1
+ export declare function buildSearchKeyword(queryParams: string, boolQuery: {
2
+ must: Array<any>;
3
+ must_not: Array<any>;
4
+ filter: Array<any>;
5
+ should: Array<any>;
6
+ minimum_should_match?: number;
7
+ }): {
8
+ must: Array<any>;
9
+ must_not: Array<any>;
10
+ filter: Array<any>;
11
+ should: Array<any>;
12
+ minimum_should_match?: number;
13
+ };
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildSearchKeyword = buildSearchKeyword;
4
+ const types_1 = require("../../model/index.js");
5
+ function buildSearchKeyword(queryParams, boolQuery) {
6
+ const params = new URLSearchParams(queryParams);
7
+ const search = params.get("search");
8
+ const fieldsRaw = params.get("searchFields");
9
+ const operator = (params.get("searchOperator") || types_1.SearchOperatorOS.OR).toLowerCase();
10
+ if (!search || !fieldsRaw) {
11
+ return boolQuery;
12
+ }
13
+ const fields = stringSplitter(fieldsRaw);
14
+ for (const field of fields) {
15
+ const query = buildSearchKeywordQuery(field, search);
16
+ if (query) {
17
+ if (operator === types_1.SearchOperatorOS.OR) {
18
+ boolQuery.should.push(query);
19
+ boolQuery.minimum_should_match = 1;
20
+ }
21
+ if (operator === types_1.SearchOperatorOS.AND) {
22
+ boolQuery.must.push(query);
23
+ }
24
+ }
25
+ }
26
+ return boolQuery;
27
+ }
28
+ function buildSearchKeywordQuery(field, searchKeyword) {
29
+ if (!searchKeyword || searchKeyword.trim() === "")
30
+ return undefined;
31
+ // 1. Олон *-уудыг нэг * болгох
32
+ let cleanedKeyword = searchKeyword.replace(/\*+/g, "*").trim();
33
+ if (!cleanedKeyword.endsWith("*")) {
34
+ // 2. Хэрвээ keyword-ын төгсгөлд * байхгүй бол автоматаар * нэмэх
35
+ cleanedKeyword += "*";
36
+ }
37
+ // word-д *-оор эхэлсэн бол wildcard
38
+ if (cleanedKeyword.startsWith("*")) {
39
+ return {
40
+ wildcard: {
41
+ [`${field}.keyword`]: {
42
+ value: cleanedKeyword,
43
+ case_insensitive: true,
44
+ },
45
+ },
46
+ };
47
+ }
48
+ // энгийн keyword → phrase_prefix
49
+ return {
50
+ match_phrase_prefix: {
51
+ [field]: cleanedKeyword,
52
+ },
53
+ };
54
+ }
55
+ function stringSplitter(rawString, splitter = ",") {
56
+ return rawString
57
+ .split(splitter)
58
+ .map((f) => f.trim())
59
+ .filter(Boolean);
60
+ }
61
+ //# sourceMappingURL=opensearch.parser.keyword.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opensearch.parser.keyword.js","sourceRoot":"","sources":["../../../src/utils/opensearch/opensearch.parser.keyword.ts"],"names":[],"mappings":";;AAEA,gDAsCC;AAxCD,2CAAmD;AAEnD,SAAgB,kBAAkB,CAChC,WAAmB,EACnB,SAMC;IAED,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,WAAW,CAAC,CAAC;IAEhD,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACpC,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAC7C,MAAM,QAAQ,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,gBAAgB,CAAC,IAAI,wBAAgB,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;IAErF,IAAI,CAAC,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC;QAC1B,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,MAAM,GAAG,cAAc,CAAC,SAAS,CAAC,CAAC;IAEzC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,KAAK,GAAG,uBAAuB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QAErD,IAAI,KAAK,EAAE,CAAC;YACV,IAAI,QAAQ,KAAK,wBAAgB,CAAC,EAAE,EAAE,CAAC;gBACrC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBAC7B,SAAS,CAAC,oBAAoB,GAAG,CAAC,CAAC;YACrC,CAAC;YAED,IAAI,QAAQ,KAAK,wBAAgB,CAAC,GAAG,EAAE,CAAC;gBACtC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC7B,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,uBAAuB,CAAC,KAAa,EAAE,aAAqB;IACnE,IAAI,CAAC,aAAa,IAAI,aAAa,CAAC,IAAI,EAAE,KAAK,EAAE;QAAE,OAAO,SAAS,CAAC;IAEpE,+BAA+B;IAC/B,IAAI,cAAc,GAAQ,aAAa,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IAEpE,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAClC,iEAAiE;QACjE,cAAc,IAAI,GAAG,CAAC;IACxB,CAAC;IAED,oCAAoC;IACpC,IAAI,cAAc,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACnC,OAAO;YACL,QAAQ,EAAE;gBACR,CAAC,GAAG,KAAK,UAAU,CAAC,EAAE;oBACpB,KAAK,EAAE,cAAc;oBACrB,gBAAgB,EAAE,IAAI;iBACvB;aACF;SACF,CAAC;IACJ,CAAC;IACD,iCAAiC;IACjC,OAAO;QACL,mBAAmB,EAAE;YACnB,CAAC,KAAK,CAAC,EAAE,cAAc;SACxB;KACF,CAAC;AACJ,CAAC;AAED,SAAS,cAAc,CAAC,SAAiB,EAAE,QAAQ,GAAG,GAAG;IACvD,OAAO,SAAS;SACb,KAAK,CAAC,QAAQ,CAAC;SACf,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SACpB,MAAM,CAAC,OAAO,CAAC,CAAC;AACrB,CAAC","sourcesContent":["import { SearchOperatorOS } from \"@chinggis/types\";\n\nexport function buildSearchKeyword(\n queryParams: string,\n boolQuery: {\n must: Array<any>;\n must_not: Array<any>;\n filter: Array<any>;\n should: Array<any>;\n minimum_should_match?: number;\n },\n) {\n const params = new URLSearchParams(queryParams);\n\n const search = params.get(\"search\");\n const fieldsRaw = params.get(\"searchFields\");\n const operator = (params.get(\"searchOperator\") || SearchOperatorOS.OR).toLowerCase();\n\n if (!search || !fieldsRaw) {\n return boolQuery;\n }\n\n const fields = stringSplitter(fieldsRaw);\n\n for (const field of fields) {\n const query = buildSearchKeywordQuery(field, search);\n\n if (query) {\n if (operator === SearchOperatorOS.OR) {\n boolQuery.should.push(query);\n boolQuery.minimum_should_match = 1;\n }\n\n if (operator === SearchOperatorOS.AND) {\n boolQuery.must.push(query);\n }\n }\n }\n\n return boolQuery;\n}\n\nfunction buildSearchKeywordQuery(field: string, searchKeyword: string) {\n if (!searchKeyword || searchKeyword.trim() === \"\") return undefined;\n\n // 1. Олон *-уудыг нэг * болгох\n let cleanedKeyword: any = searchKeyword.replace(/\\*+/g, \"*\").trim();\n\n if (!cleanedKeyword.endsWith(\"*\")) {\n // 2. Хэрвээ keyword-ын төгсгөлд * байхгүй бол автоматаар * нэмэх\n cleanedKeyword += \"*\";\n }\n\n // word-д *-оор эхэлсэн бол wildcard\n if (cleanedKeyword.startsWith(\"*\")) {\n return {\n wildcard: {\n [`${field}.keyword`]: {\n value: cleanedKeyword,\n case_insensitive: true,\n },\n },\n };\n }\n // энгийн keyword → phrase_prefix\n return {\n match_phrase_prefix: {\n [field]: cleanedKeyword,\n },\n };\n}\n\nfunction stringSplitter(rawString: string, splitter = \",\") {\n return rawString\n .split(splitter)\n .map((f) => f.trim())\n .filter(Boolean);\n}\n"]}
@@ -0,0 +1,5 @@
1
+ import { BoolQueryTypeOS } from "../../model/index.js";
2
+ export declare const boolQueryMap: Record<BoolQueryTypeOS, (field: string, value: string, existing?: Record<string, any>) => {
3
+ query: any;
4
+ negation?: boolean;
5
+ } | any>;
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.boolQueryMap = void 0;
4
+ const types_1 = require("../../model/index.js");
5
+ // ---------------- Bool Query Map ----------------
6
+ exports.boolQueryMap = {
7
+ eq: (field, value) => buildTermFilter(field, value),
8
+ ne: (field, value) => ({ query: buildTermFilter(field, value), negation: true }),
9
+ in: (field, value) => buildTermsFilter(field, value),
10
+ gte: (field, value, existing) => buildRangeFilter(field, value, types_1.RangeTypeOS.gte, existing),
11
+ lte: (field, value, existing) => buildRangeFilter(field, value, types_1.RangeTypeOS.lte, existing),
12
+ exists: (field, value) => buildExistsFilter(field, value),
13
+ regex: (field, value) => buildRegexpFilter(field, value),
14
+ };
15
+ // ---------------- Operator Functions ----------------
16
+ function buildTermFilter(field, value) {
17
+ return { term: { [field]: value } };
18
+ }
19
+ function buildTermsFilter(field, value) {
20
+ return { terms: { [field]: value.split(",") } };
21
+ }
22
+ function buildRangeFilter(field, value, operator, existing) {
23
+ const val = isNaN(Number(value)) ? value : Number(value);
24
+ return {
25
+ range: {
26
+ [field]: {
27
+ ...(existing?.[field] || {}),
28
+ [operator]: val,
29
+ },
30
+ },
31
+ };
32
+ }
33
+ function buildExistsFilter(field, value) {
34
+ return { query: { exists: { field } }, negation: value === "false" || value === false };
35
+ }
36
+ function buildRegexpFilter(field, value) {
37
+ return { regexp: { [field]: value } };
38
+ }
39
+ //# sourceMappingURL=opensearch.parser.query.map.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opensearch.parser.query.map.js","sourceRoot":"","sources":["../../../src/utils/opensearch/opensearch.parser.query.map.ts"],"names":[],"mappings":";;;AAAA,2CAA+D;AAE/D,mDAAmD;AACtC,QAAA,YAAY,GAGrB;IACF,EAAE,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC;IACnD,EAAE,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAChF,EAAE,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,gBAAgB,CAAC,KAAK,EAAE,KAAK,CAAC;IACpD,GAAG,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,CAAC,gBAAgB,CAAC,KAAK,EAAE,KAAK,EAAE,mBAAW,CAAC,GAAG,EAAE,QAAQ,CAAC;IAC1F,GAAG,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,CAAC,gBAAgB,CAAC,KAAK,EAAE,KAAK,EAAE,mBAAW,CAAC,GAAG,EAAE,QAAQ,CAAC;IAC1F,MAAM,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,iBAAiB,CAAC,KAAK,EAAE,KAAK,CAAC;IACzD,KAAK,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,iBAAiB,CAAC,KAAK,EAAE,KAAK,CAAC;CACzD,CAAC;AAEF,uDAAuD;AACvD,SAAS,eAAe,CAAC,KAAa,EAAE,KAAa;IACnD,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC;AACtC,CAAC;AAED,SAAS,gBAAgB,CAAC,KAAa,EAAE,KAAa;IACpD,OAAO,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;AAClD,CAAC;AAED,SAAS,gBAAgB,CAAC,KAAa,EAAE,KAAa,EAAE,QAAqB,EAAE,QAA8B;IAC3G,MAAM,GAAG,GAAG,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACzD,OAAO;QACL,KAAK,EAAE;YACL,CAAC,KAAK,CAAC,EAAE;gBACP,GAAG,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;gBAC5B,CAAC,QAAQ,CAAC,EAAE,GAAG;aAChB;SACF;KACF,CAAC;AACJ,CAAC;AAED,SAAS,iBAAiB,CAAC,KAAa,EAAE,KAAuB;IAC/D,OAAO,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,QAAQ,EAAE,KAAK,KAAK,OAAO,IAAI,KAAK,KAAK,KAAK,EAAE,CAAC;AAC1F,CAAC;AAED,SAAS,iBAAiB,CAAC,KAAa,EAAE,KAAa;IACrD,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC;AACxC,CAAC","sourcesContent":["import { BoolQueryTypeOS, RangeTypeOS } from \"@chinggis/types\";\n\n// ---------------- Bool Query Map ----------------\nexport const boolQueryMap: Record<\n BoolQueryTypeOS,\n (field: string, value: string, existing?: Record<string, any>) => { query: any; negation?: boolean } | any\n> = {\n eq: (field, value) => buildTermFilter(field, value),\n ne: (field, value) => ({ query: buildTermFilter(field, value), negation: true }),\n in: (field, value) => buildTermsFilter(field, value),\n gte: (field, value, existing) => buildRangeFilter(field, value, RangeTypeOS.gte, existing),\n lte: (field, value, existing) => buildRangeFilter(field, value, RangeTypeOS.lte, existing),\n exists: (field, value) => buildExistsFilter(field, value),\n regex: (field, value) => buildRegexpFilter(field, value),\n};\n\n// ---------------- Operator Functions ----------------\nfunction buildTermFilter(field: string, value: string) {\n return { term: { [field]: value } };\n}\n\nfunction buildTermsFilter(field: string, value: string) {\n return { terms: { [field]: value.split(\",\") } };\n}\n\nfunction buildRangeFilter(field: string, value: string, operator: RangeTypeOS, existing?: Record<string, any>) {\n const val = isNaN(Number(value)) ? value : Number(value);\n return {\n range: {\n [field]: {\n ...(existing?.[field] || {}),\n [operator]: val,\n },\n },\n };\n}\n\nfunction buildExistsFilter(field: string, value: string | boolean) {\n return { query: { exists: { field } }, negation: value === \"false\" || value === false };\n}\n\nfunction buildRegexpFilter(field: string, value: string) {\n return { regexp: { [field]: value } };\n}\n"]}
@@ -0,0 +1 @@
1
+ export declare function buildSortOS(queryParams: string): any[];
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildSortOS = buildSortOS;
4
+ const types_1 = require("../../model/index.js");
5
+ function buildSortOS(queryParams) {
6
+ const params = new URLSearchParams(queryParams);
7
+ const fieldsRaw = params.get("sortField") || "";
8
+ const ordersRaw = params.get("sort") || "";
9
+ const fields = fieldsRaw
10
+ .split(",")
11
+ .map((f) => f.trim())
12
+ .filter(Boolean);
13
+ const orders = ordersRaw.split(",").map((o) => o.trim().toLowerCase());
14
+ const sort = [];
15
+ fields.forEach((field, i) => {
16
+ sort.push({
17
+ [field]: orders[i] || types_1.SortOrder.ASC, // default asc
18
+ });
19
+ });
20
+ return sort; // use directly in body.sort
21
+ }
22
+ //# sourceMappingURL=opensearch.parser.sort.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opensearch.parser.sort.js","sourceRoot":"","sources":["../../../src/utils/opensearch/opensearch.parser.sort.ts"],"names":[],"mappings":";;AAEA,kCAqBC;AAvBD,2CAA4C;AAE5C,SAAgB,WAAW,CAAC,WAAmB;IAC7C,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,WAAW,CAAC,CAAC;IAEhD,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC;IAChD,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;IAE3C,MAAM,MAAM,GAAG,SAAS;SACrB,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SACpB,MAAM,CAAC,OAAO,CAAC,CAAC;IACnB,MAAM,MAAM,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAe,CAAC,CAAC;IAEpF,MAAM,IAAI,GAAU,EAAE,CAAC;IAEvB,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE;QAC1B,IAAI,CAAC,IAAI,CAAC;YACR,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,iBAAS,CAAC,GAAG,EAAE,cAAc;SACpD,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,OAAO,IAAI,CAAC,CAAC,4BAA4B;AAC3C,CAAC","sourcesContent":["import { SortOrder } from \"@chinggis/types\";\n\nexport function buildSortOS(queryParams: string) {\n const params = new URLSearchParams(queryParams);\n\n const fieldsRaw = params.get(\"sortField\") || \"\";\n const ordersRaw = params.get(\"sort\") || \"\";\n\n const fields = fieldsRaw\n .split(\",\")\n .map((f) => f.trim())\n .filter(Boolean);\n const orders = ordersRaw.split(\",\").map((o) => o.trim().toLowerCase() as SortOrder);\n\n const sort: any[] = [];\n\n fields.forEach((field, i) => {\n sort.push({\n [field]: orders[i] || SortOrder.ASC, // default asc\n });\n });\n\n return sort; // use directly in body.sort\n}\n"]}
@@ -0,0 +1 @@
1
+ export declare function parseQueryString(queryParams: Record<string, string>): Record<string, string>;
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseQueryString = parseQueryString;
4
+ function parseQueryString(queryParams) {
5
+ const filtered = {};
6
+ for (const [key, value] of Object.entries(queryParams)) {
7
+ if (!value)
8
+ continue;
9
+ // --- ONLY field__operator format ---
10
+ if (!key.includes("__"))
11
+ continue;
12
+ const parsedValue = smartParse(value);
13
+ filtered[key] = parsedValue;
14
+ }
15
+ return filtered;
16
+ }
17
+ function smartParse(value) {
18
+ if (value?.includes("*"))
19
+ return value;
20
+ // Boolean
21
+ if (value === "true")
22
+ return true;
23
+ if (value === "false")
24
+ return false;
25
+ // Integer
26
+ if (/^-?\d+$/.test(value))
27
+ return parseInt(value, 10);
28
+ // Float / Double
29
+ if (/^-?\d*\.\d+$/.test(value))
30
+ return parseFloat(value);
31
+ // ISO Date (YYYY-MM-DD) or Dot Date (YYYY.MM.DD)
32
+ if (/^\d{4}-\d{2}-\d{2}/.test(value)) {
33
+ const date = new Date(value);
34
+ if (!isNaN(date.getTime()))
35
+ return date;
36
+ }
37
+ if (/^\d{4}\.\d{2}\.\d{2}/.test(value)) {
38
+ // Convert to ISO format for Date constructor
39
+ const isoValue = value.replace(/\./g, "-");
40
+ const date = new Date(isoValue);
41
+ if (!isNaN(date.getTime()))
42
+ return date;
43
+ }
44
+ // Default: string
45
+ return value;
46
+ }
47
+ //# sourceMappingURL=opensearch.parser.utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opensearch.parser.utils.js","sourceRoot":"","sources":["../../../src/utils/opensearch/opensearch.parser.utils.ts"],"names":[],"mappings":";;AAAA,4CAeC;AAfD,SAAgB,gBAAgB,CAAC,WAAmC;IAClE,MAAM,QAAQ,GAA2B,EAAE,CAAC;IAE5C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC;QACvD,IAAI,CAAC,KAAK;YAAE,SAAS;QAErB,sCAAsC;QACtC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC;YAAE,SAAS;QAElC,MAAM,WAAW,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;QAEtC,QAAQ,CAAC,GAAG,CAAC,GAAG,WAAW,CAAC;IAC9B,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAS,UAAU,CAAC,KAAa;IAC/B,IAAI,KAAK,EAAE,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IAEvC,UAAU;IACV,IAAI,KAAK,KAAK,MAAM;QAAE,OAAO,IAAI,CAAC;IAClC,IAAI,KAAK,KAAK,OAAO;QAAE,OAAO,KAAK,CAAC;IAEpC,UAAU;IACV,IAAI,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAEtD,iBAAiB;IACjB,IAAI,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,UAAU,CAAC,KAAK,CAAC,CAAC;IAEzD,iDAAiD;IACjD,IAAI,oBAAoB,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACrC,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC;QAC7B,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;YAAE,OAAO,IAAI,CAAC;IAC1C,CAAC;IACD,IAAI,sBAAsB,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACvC,6CAA6C;QAC7C,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QAC3C,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,QAAQ,CAAC,CAAC;QAChC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;YAAE,OAAO,IAAI,CAAC;IAC1C,CAAC;IAED,kBAAkB;IAClB,OAAO,KAAK,CAAC;AACf,CAAC","sourcesContent":["export function parseQueryString(queryParams: Record<string, string>): Record<string, string> {\n const filtered: Record<string, string> = {};\n\n for (const [key, value] of Object.entries(queryParams)) {\n if (!value) continue;\n\n // --- ONLY field__operator format ---\n if (!key.includes(\"__\")) continue;\n\n const parsedValue = smartParse(value);\n\n filtered[key] = parsedValue;\n }\n\n return filtered;\n}\n\nfunction smartParse(value: string): any {\n if (value?.includes(\"*\")) return value;\n\n // Boolean\n if (value === \"true\") return true;\n if (value === \"false\") return false;\n\n // Integer\n if (/^-?\\d+$/.test(value)) return parseInt(value, 10);\n\n // Float / Double\n if (/^-?\\d*\\.\\d+$/.test(value)) return parseFloat(value);\n\n // ISO Date (YYYY-MM-DD) or Dot Date (YYYY.MM.DD)\n if (/^\\d{4}-\\d{2}-\\d{2}/.test(value)) {\n const date = new Date(value);\n if (!isNaN(date.getTime())) return date;\n }\n if (/^\\d{4}\\.\\d{2}\\.\\d{2}/.test(value)) {\n // Convert to ISO format for Date constructor\n const isoValue = value.replace(/\\./g, \"-\");\n const date = new Date(isoValue);\n if (!isNaN(date.getTime())) return date;\n }\n\n // Default: string\n return value;\n}\n"]}
@@ -0,0 +1 @@
1
+ export declare function transformAggregationsResponse(aggs: any): any;
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.transformAggregationsResponse = transformAggregationsResponse;
4
+ function transformAgg(bucket) {
5
+ if (!bucket)
6
+ return;
7
+ const result = {};
8
+ // Füge doc_count und key hinzu, falls vorhanden
9
+ if ("key" in bucket)
10
+ result.key = bucket.key;
11
+ if ("doc_count" in bucket)
12
+ result.doc_count = bucket.doc_count;
13
+ // Gehe alle Keys durch
14
+ for (const k of Object.keys(bucket)) {
15
+ if (k === "key" || k === "doc_count")
16
+ continue;
17
+ if (k === "value") {
18
+ result["value"] = bucket[k];
19
+ }
20
+ const v = bucket[k];
21
+ if (v && typeof v === "object") {
22
+ // Bucket mit Unterbuckets
23
+ if ("buckets" in v) {
24
+ result[k] = v.buckets?.map((b) => {
25
+ return transformAgg(b);
26
+ });
27
+ }
28
+ // Metric Aggregation (sum, avg, etc.)
29
+ else if ("value" in v) {
30
+ result[k] = v.value;
31
+ }
32
+ // Nested Objekt → rekursiv
33
+ else {
34
+ result[k] = transformAgg(v);
35
+ }
36
+ }
37
+ }
38
+ return result;
39
+ }
40
+ // Transform für alle Top-Level Aggregationen
41
+ function transformAggregationsResponse(aggs) {
42
+ const result = {};
43
+ for (const key of Object.keys(aggs)) {
44
+ result[key] = aggs[key].buckets?.map(transformAgg) ?? transformAgg(aggs[key]);
45
+ }
46
+ return result;
47
+ }
48
+ //# sourceMappingURL=opensearch.transform.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opensearch.transform.js","sourceRoot":"","sources":["../../../src/utils/opensearch/opensearch.transform.ts"],"names":[],"mappings":";;AAwCA,sEAMC;AA9CD,SAAS,YAAY,CAAC,MAAW;IAC/B,IAAI,CAAC,MAAM;QAAE,OAAO;IACpB,MAAM,MAAM,GAAQ,EAAE,CAAC;IAEvB,gDAAgD;IAChD,IAAI,KAAK,IAAI,MAAM;QAAE,MAAM,CAAC,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC;IAC7C,IAAI,WAAW,IAAI,MAAM;QAAE,MAAM,CAAC,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC;IAE/D,uBAAuB;IACvB,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QACpC,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC,KAAK,WAAW;YAAE,SAAS;QAE/C,IAAI,CAAC,KAAK,OAAO,EAAE,CAAC;YAClB,MAAM,CAAC,OAAO,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QAC9B,CAAC;QAED,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QAEpB,IAAI,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;YAC/B,0BAA0B;YAC1B,IAAI,SAAS,IAAI,CAAC,EAAE,CAAC;gBACnB,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;oBAC/B,OAAO,YAAY,CAAC,CAAC,CAAC,CAAC;gBACzB,CAAC,CAAC,CAAC;YACL,CAAC;YACD,sCAAsC;iBACjC,IAAI,OAAO,IAAI,CAAC,EAAE,CAAC;gBACtB,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC;YACtB,CAAC;YACD,2BAA2B;iBACtB,CAAC;gBACJ,MAAM,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC;YAC9B,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,6CAA6C;AAC7C,SAAgB,6BAA6B,CAAC,IAAS;IACrD,MAAM,MAAM,GAAQ,EAAE,CAAC;IACvB,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACpC,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,GAAG,CAAC,YAAY,CAAC,IAAI,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAChF,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC","sourcesContent":["function transformAgg(bucket: any): any {\n if (!bucket) return;\n const result: any = {};\n\n // Füge doc_count und key hinzu, falls vorhanden\n if (\"key\" in bucket) result.key = bucket.key;\n if (\"doc_count\" in bucket) result.doc_count = bucket.doc_count;\n\n // Gehe alle Keys durch\n for (const k of Object.keys(bucket)) {\n if (k === \"key\" || k === \"doc_count\") continue;\n\n if (k === \"value\") {\n result[\"value\"] = bucket[k];\n }\n\n const v = bucket[k];\n\n if (v && typeof v === \"object\") {\n // Bucket mit Unterbuckets\n if (\"buckets\" in v) {\n result[k] = v.buckets?.map((b) => {\n return transformAgg(b);\n });\n }\n // Metric Aggregation (sum, avg, etc.)\n else if (\"value\" in v) {\n result[k] = v.value;\n }\n // Nested Objekt → rekursiv\n else {\n result[k] = transformAgg(v);\n }\n }\n }\n\n return result;\n}\n\n// Transform für alle Top-Level Aggregationen\nexport function transformAggregationsResponse(aggs: any): any {\n const result: any = {};\n for (const key of Object.keys(aggs)) {\n result[key] = aggs[key].buckets?.map(transformAgg) ?? transformAgg(aggs[key]);\n }\n return result;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aws-service-stack",
3
- "version": "0.18.319",
3
+ "version": "0.18.320",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "author": "chinggis.systems",
@@ -1,26 +0,0 @@
1
- import { BoolQueryOS } from "../model/index.js";
2
- export declare function buildSearchQueryOS(queryParams: any, opensearchIndex: string): {
3
- index: string;
4
- body: {
5
- query: {
6
- bool: BoolQueryOS;
7
- };
8
- size: number;
9
- from: number;
10
- sort: any[];
11
- };
12
- };
13
- export declare function buildSearchKeyword(queryParams: string, boolQuery: {
14
- must: Array<any>;
15
- must_not: Array<any>;
16
- filter: Array<any>;
17
- should: Array<any>;
18
- minimum_should_match?: number;
19
- }): {
20
- must: Array<any>;
21
- must_not: Array<any>;
22
- filter: Array<any>;
23
- should: Array<any>;
24
- minimum_should_match?: number;
25
- };
26
- export declare function buildSortOS(queryParams: string): any[];
@@ -1,206 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.buildSearchQueryOS = buildSearchQueryOS;
4
- exports.buildSearchKeyword = buildSearchKeyword;
5
- exports.buildSortOS = buildSortOS;
6
- const types_1 = require("../model/index.js");
7
- const opensearch_utils_1 = require("./opensearch.utils");
8
- // ---------------- Bool Query Map ----------------
9
- const boolQueryMap = {
10
- eq: (field, value) => buildTermFilter(field, value),
11
- ne: (field, value) => ({ query: buildTermFilter(field, value), negation: true }),
12
- in: (field, value) => buildTermsFilter(field, value),
13
- gte: (field, value, existing) => buildRangeFilter(field, value, types_1.RangeTypeOS.gte, existing),
14
- lte: (field, value, existing) => buildRangeFilter(field, value, types_1.RangeTypeOS.lte, existing),
15
- exists: (field, value) => buildExistsFilter(field, value),
16
- regex: (field, value) => buildRegexpFilter(field, value),
17
- };
18
- // ---------------- Main Function ----------------
19
- function buildSearchQueryOS(queryParams, opensearchIndex) {
20
- const filters = parseQueryString(queryParams);
21
- const filterMap = {}; // merge range per field
22
- const boolQuery = {
23
- must: [],
24
- must_not: [],
25
- filter: [],
26
- should: [],
27
- minimum_should_match: undefined,
28
- };
29
- for (const [key, value] of Object.entries(filters)) {
30
- if (!value)
31
- continue;
32
- const [field, boolQueryType = types_1.BoolQueryTypeOS.eq] = key.split("__");
33
- const fn = boolQueryMap[boolQueryType];
34
- if (!fn)
35
- continue;
36
- // Range filters
37
- if (boolQueryType === types_1.RangeTypeOS.gte || boolQueryType === types_1.RangeTypeOS.lte) {
38
- const existing = filterMap[field]?.range;
39
- filterMap[field] = fn(field, value, existing);
40
- continue;
41
- }
42
- // Other filters
43
- const builtQuery = fn(field, value);
44
- if (builtQuery.negation)
45
- boolQuery.must_not.push(builtQuery.query);
46
- else
47
- boolQuery.must.push(builtQuery?.query ? builtQuery.query : builtQuery);
48
- }
49
- const filter = Object.values(filterMap);
50
- if (filter.length)
51
- boolQuery.filter = filter;
52
- buildSearchKeyword(queryParams, boolQuery);
53
- const { size, from } = (0, opensearch_utils_1.calculatePagination)(queryParams);
54
- return {
55
- index: opensearchIndex,
56
- body: {
57
- query: {
58
- bool: boolQuery,
59
- },
60
- size: size ?? 25,
61
- from: from ?? 0,
62
- sort: buildSortOS(queryParams),
63
- },
64
- };
65
- }
66
- function buildSearchKeyword(queryParams, boolQuery) {
67
- const params = new URLSearchParams(queryParams);
68
- const search = params.get("search");
69
- const fieldsRaw = params.get("searchFields");
70
- const operator = (params.get("searchOperator") || types_1.SearchOperatorOS.OR).toLowerCase();
71
- if (!search || !fieldsRaw) {
72
- return boolQuery;
73
- }
74
- const fields = stringSplitter(fieldsRaw);
75
- for (const field of fields) {
76
- const query = buildSearchKeywordQuery(field, search);
77
- if (query) {
78
- if (operator === types_1.SearchOperatorOS.OR) {
79
- boolQuery.should.push(query);
80
- boolQuery.minimum_should_match = 1;
81
- }
82
- if (operator === types_1.SearchOperatorOS.AND) {
83
- boolQuery.must.push(query);
84
- }
85
- }
86
- }
87
- return boolQuery;
88
- }
89
- function buildSortOS(queryParams) {
90
- const params = new URLSearchParams(queryParams);
91
- const fieldsRaw = params.get("sortField") || "";
92
- const ordersRaw = params.get("sort") || "";
93
- const fields = fieldsRaw
94
- .split(",")
95
- .map((f) => f.trim())
96
- .filter(Boolean);
97
- const orders = ordersRaw.split(",").map((o) => o.trim().toLowerCase());
98
- const sort = [];
99
- fields.forEach((field, i) => {
100
- sort.push({
101
- [field]: orders[i] || "asc", // default asc
102
- });
103
- });
104
- return sort; // use directly in body.sort
105
- }
106
- // ---------------- Operator Functions ----------------
107
- function buildTermFilter(field, value) {
108
- return { term: { [field]: value } };
109
- }
110
- function buildTermsFilter(field, value) {
111
- return { terms: { [field]: value.split(",") } };
112
- }
113
- function buildRangeFilter(field, value, operator, existing) {
114
- const val = isNaN(Number(value)) ? value : Number(value);
115
- return {
116
- range: {
117
- [field]: {
118
- ...(existing?.[field] || {}),
119
- [operator]: val,
120
- },
121
- },
122
- };
123
- }
124
- function buildExistsFilter(field, value) {
125
- return { query: { exists: { field } }, negation: value === "false" || value === false };
126
- }
127
- function buildRegexpFilter(field, value) {
128
- return { regexp: { [field]: value } };
129
- }
130
- function parseQueryString(queryParams) {
131
- const filtered = {};
132
- for (const [key, value] of Object.entries(queryParams)) {
133
- if (!value)
134
- continue;
135
- // --- ONLY field__operator format ---
136
- if (!key.includes("__"))
137
- continue;
138
- const parsedValue = smartParse(value);
139
- filtered[key] = parsedValue;
140
- }
141
- return filtered;
142
- }
143
- function stringSplitter(rawString, splitter = ",") {
144
- return rawString
145
- .split(splitter)
146
- .map((f) => f.trim())
147
- .filter(Boolean);
148
- }
149
- function buildSearchKeywordQuery(field, searchKeyword) {
150
- if (!searchKeyword || searchKeyword.trim() === "")
151
- return undefined;
152
- // 1. Олон *-уудыг нэг * болгох
153
- let cleanedKeyword = searchKeyword.replace(/\*+/g, "*").trim();
154
- if (!cleanedKeyword.endsWith("*")) {
155
- // 2. Хэрвээ keyword-ын төгсгөлд * байхгүй бол автоматаар * нэмэх
156
- cleanedKeyword += "*";
157
- }
158
- // word-д *-оор эхэлсэн бол wildcard
159
- if (cleanedKeyword.startsWith("*")) {
160
- return {
161
- wildcard: {
162
- [`${field}.keyword`]: {
163
- value: cleanedKeyword,
164
- case_insensitive: true,
165
- },
166
- },
167
- };
168
- }
169
- // энгийн keyword → phrase_prefix
170
- return {
171
- match_phrase_prefix: {
172
- [field]: cleanedKeyword,
173
- },
174
- };
175
- }
176
- function smartParse(value) {
177
- if (value?.includes("*"))
178
- return value;
179
- // Boolean
180
- if (value === "true")
181
- return true;
182
- if (value === "false")
183
- return false;
184
- // Integer
185
- if (/^-?\d+$/.test(value))
186
- return parseInt(value, 10);
187
- // Float / Double
188
- if (/^-?\d*\.\d+$/.test(value))
189
- return parseFloat(value);
190
- // ISO Date (YYYY-MM-DD) or Dot Date (YYYY.MM.DD)
191
- if (/^\d{4}-\d{2}-\d{2}/.test(value)) {
192
- const date = new Date(value);
193
- if (!isNaN(date.getTime()))
194
- return date;
195
- }
196
- if (/^\d{4}\.\d{2}\.\d{2}/.test(value)) {
197
- // Convert to ISO format for Date constructor
198
- const isoValue = value.replace(/\./g, "-");
199
- const date = new Date(isoValue);
200
- if (!isNaN(date.getTime()))
201
- return date;
202
- }
203
- // Default: string
204
- return value;
205
- }
206
- //# sourceMappingURL=opensearch.parser.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"opensearch.parser.js","sourceRoot":"","sources":["../../src/utils/opensearch.parser.ts"],"names":[],"mappings":"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","sourcesContent":["import { BoolQueryOS, BoolQueryTypeOS, SortOrder, RangeTypeOS, SearchOperatorOS } from \"@chinggis/types\";\nimport { calculatePagination } from \"./opensearch.utils\";\n\n// ---------------- Bool Query Map ----------------\nconst boolQueryMap: Record<\n BoolQueryTypeOS,\n (field: string, value: string, existing?: Record<string, any>) => { query: any; negation?: boolean } | any\n> = {\n eq: (field, value) => buildTermFilter(field, value),\n ne: (field, value) => ({ query: buildTermFilter(field, value), negation: true }),\n in: (field, value) => buildTermsFilter(field, value),\n gte: (field, value, existing) => buildRangeFilter(field, value, RangeTypeOS.gte, existing),\n lte: (field, value, existing) => buildRangeFilter(field, value, RangeTypeOS.lte, existing),\n exists: (field, value) => buildExistsFilter(field, value),\n regex: (field, value) => buildRegexpFilter(field, value),\n};\n\n// ---------------- Main Function ----------------\nexport function buildSearchQueryOS(queryParams, opensearchIndex: string) {\n const filters = parseQueryString(queryParams);\n\n const filterMap: Record<string, any> = {}; // merge range per field\n\n const boolQuery: BoolQueryOS = {\n must: [],\n must_not: [],\n filter: [],\n should: [],\n minimum_should_match: undefined,\n };\n\n for (const [key, value] of Object.entries(filters)) {\n if (!value) continue;\n\n const [field, boolQueryType = BoolQueryTypeOS.eq] = key.split(\"__\");\n const fn = boolQueryMap[boolQueryType];\n if (!fn) continue;\n\n // Range filters\n if (boolQueryType === RangeTypeOS.gte || boolQueryType === RangeTypeOS.lte) {\n const existing = filterMap[field]?.range;\n filterMap[field] = fn(field, value, existing);\n continue;\n }\n\n // Other filters\n const builtQuery = fn(field, value);\n if (builtQuery.negation) boolQuery.must_not.push(builtQuery.query);\n else boolQuery.must.push(builtQuery?.query ? builtQuery.query : builtQuery);\n }\n\n const filter = Object.values(filterMap);\n\n if (filter.length) boolQuery.filter = filter;\n\n buildSearchKeyword(queryParams, boolQuery);\n\n const { size, from } = calculatePagination(queryParams);\n\n return {\n index: opensearchIndex,\n body: {\n query: {\n bool: boolQuery,\n },\n size: size ?? 25,\n from: from ?? 0,\n sort: buildSortOS(queryParams),\n },\n };\n}\n\nexport function buildSearchKeyword(\n queryParams: string,\n boolQuery: {\n must: Array<any>;\n must_not: Array<any>;\n filter: Array<any>;\n should: Array<any>;\n minimum_should_match?: number;\n },\n) {\n const params = new URLSearchParams(queryParams);\n\n const search = params.get(\"search\");\n const fieldsRaw = params.get(\"searchFields\");\n const operator = (params.get(\"searchOperator\") || SearchOperatorOS.OR).toLowerCase();\n\n if (!search || !fieldsRaw) {\n return boolQuery;\n }\n\n const fields = stringSplitter(fieldsRaw);\n\n for (const field of fields) {\n const query = buildSearchKeywordQuery(field, search);\n\n if (query) {\n if (operator === SearchOperatorOS.OR) {\n boolQuery.should.push(query);\n boolQuery.minimum_should_match = 1;\n }\n\n if (operator === SearchOperatorOS.AND) {\n boolQuery.must.push(query);\n }\n }\n }\n\n return boolQuery;\n}\n\nexport function buildSortOS(queryParams: string) {\n const params = new URLSearchParams(queryParams);\n\n const fieldsRaw = params.get(\"sortField\") || \"\";\n const ordersRaw = params.get(\"sort\") || \"\";\n\n const fields = fieldsRaw\n .split(\",\")\n .map((f) => f.trim())\n .filter(Boolean);\n const orders = ordersRaw.split(\",\").map((o) => o.trim().toLowerCase() as SortOrder);\n\n const sort: any[] = [];\n\n fields.forEach((field, i) => {\n sort.push({\n [field]: orders[i] || \"asc\", // default asc\n });\n });\n\n return sort; // use directly in body.sort\n}\n\n// ---------------- Operator Functions ----------------\nfunction buildTermFilter(field: string, value: string) {\n return { term: { [field]: value } };\n}\n\nfunction buildTermsFilter(field: string, value: string) {\n return { terms: { [field]: value.split(\",\") } };\n}\n\nfunction buildRangeFilter(field: string, value: string, operator: RangeTypeOS, existing?: Record<string, any>) {\n const val = isNaN(Number(value)) ? value : Number(value);\n return {\n range: {\n [field]: {\n ...(existing?.[field] || {}),\n [operator]: val,\n },\n },\n };\n}\n\nfunction buildExistsFilter(field: string, value: string | boolean) {\n return { query: { exists: { field } }, negation: value === \"false\" || value === false };\n}\n\nfunction buildRegexpFilter(field: string, value: string) {\n return { regexp: { [field]: value } };\n}\n\nfunction parseQueryString(queryParams: Record<string, string>): Record<string, string> {\n const filtered: Record<string, string> = {};\n\n for (const [key, value] of Object.entries(queryParams)) {\n if (!value) continue;\n\n // --- ONLY field__operator format ---\n if (!key.includes(\"__\")) continue;\n\n const parsedValue = smartParse(value);\n\n filtered[key] = parsedValue;\n }\n\n return filtered;\n}\n\nfunction stringSplitter(rawString: string, splitter = \",\") {\n return rawString\n .split(splitter)\n .map((f) => f.trim())\n .filter(Boolean);\n}\n\nfunction buildSearchKeywordQuery(field: string, searchKeyword: string) {\n if (!searchKeyword || searchKeyword.trim() === \"\") return undefined;\n\n // 1. Олон *-уудыг нэг * болгох\n let cleanedKeyword: any = searchKeyword.replace(/\\*+/g, \"*\").trim();\n\n if (!cleanedKeyword.endsWith(\"*\")) {\n // 2. Хэрвээ keyword-ын төгсгөлд * байхгүй бол автоматаар * нэмэх\n cleanedKeyword += \"*\";\n }\n\n // word-д *-оор эхэлсэн бол wildcard\n if (cleanedKeyword.startsWith(\"*\")) {\n return {\n wildcard: {\n [`${field}.keyword`]: {\n value: cleanedKeyword,\n case_insensitive: true,\n },\n },\n };\n }\n // энгийн keyword → phrase_prefix\n return {\n match_phrase_prefix: {\n [field]: cleanedKeyword,\n },\n };\n}\n\nfunction smartParse(value: string): any {\n if (value?.includes(\"*\")) return value;\n\n // Boolean\n if (value === \"true\") return true;\n if (value === \"false\") return false;\n\n // Integer\n if (/^-?\\d+$/.test(value)) return parseInt(value, 10);\n\n // Float / Double\n if (/^-?\\d*\\.\\d+$/.test(value)) return parseFloat(value);\n\n // ISO Date (YYYY-MM-DD) or Dot Date (YYYY.MM.DD)\n if (/^\\d{4}-\\d{2}-\\d{2}/.test(value)) {\n const date = new Date(value);\n if (!isNaN(date.getTime())) return date;\n }\n if (/^\\d{4}\\.\\d{2}\\.\\d{2}/.test(value)) {\n // Convert to ISO format for Date constructor\n const isoValue = value.replace(/\\./g, \"-\");\n const date = new Date(isoValue);\n if (!isNaN(date.getTime())) return date;\n }\n\n // Default: string\n return value;\n}\n"]}