aws-service-stack 0.18.319 → 0.18.321
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.
- package/dist/_examples/service/entity/entity-service.js +2 -1
- package/dist/_examples/service/entity/entity-service.js.map +1 -1
- package/dist/model/filter.model.d.ts +7 -0
- package/dist/model/filter.model.js +9 -1
- package/dist/model/filter.model.js.map +1 -1
- package/dist/repositories/base-es.repo.d.ts +4 -1
- package/dist/repositories/base-es.repo.interface.d.ts +4 -1
- package/dist/repositories/base-es.repo.interface.js.map +1 -1
- package/dist/repositories/base-es.repo.js +9 -4
- package/dist/repositories/base-es.repo.js.map +1 -1
- package/dist/service/base.service.js.map +1 -1
- package/dist/service/crud.service.d.ts +4 -1
- package/dist/service/crud.service.interface.d.ts +4 -1
- package/dist/service/crud.service.interface.js.map +1 -1
- package/dist/service/crud.service.js +23 -20
- package/dist/service/crud.service.js.map +1 -1
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.js +2 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/opensearch/opensearch.parser.aggs.d.ts +8 -0
- package/dist/utils/opensearch/opensearch.parser.aggs.js +66 -0
- package/dist/utils/opensearch/opensearch.parser.aggs.js.map +1 -0
- package/dist/utils/opensearch/opensearch.parser.d.ts +13 -0
- package/dist/utils/opensearch/opensearch.parser.js +62 -0
- package/dist/utils/opensearch/opensearch.parser.js.map +1 -0
- package/dist/utils/opensearch/opensearch.parser.keyword.d.ts +13 -0
- package/dist/utils/opensearch/opensearch.parser.keyword.js +61 -0
- package/dist/utils/opensearch/opensearch.parser.keyword.js.map +1 -0
- package/dist/utils/opensearch/opensearch.parser.query.map.d.ts +5 -0
- package/dist/utils/opensearch/opensearch.parser.query.map.js +39 -0
- package/dist/utils/opensearch/opensearch.parser.query.map.js.map +1 -0
- package/dist/utils/opensearch/opensearch.parser.sort.d.ts +1 -0
- package/dist/utils/opensearch/opensearch.parser.sort.js +22 -0
- package/dist/utils/opensearch/opensearch.parser.sort.js.map +1 -0
- package/dist/utils/opensearch/opensearch.parser.utils.d.ts +1 -0
- package/dist/utils/opensearch/opensearch.parser.utils.js +47 -0
- package/dist/utils/opensearch/opensearch.parser.utils.js.map +1 -0
- package/dist/utils/opensearch/opensearch.transform.d.ts +1 -0
- package/dist/utils/opensearch/opensearch.transform.js +48 -0
- package/dist/utils/opensearch/opensearch.transform.js.map +1 -0
- package/package.json +1 -1
- package/dist/utils/opensearch.parser.d.ts +0 -26
- package/dist/utils/opensearch.parser.js +0 -206
- package/dist/utils/opensearch.parser.js.map +0 -1
|
@@ -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,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":";;AAkBA,gDAoDC;AAED,gDAsCC;AAED,kCAqBC;AArID,2CAAyG;AACzG,yDAAyD;AAEzD,mDAAmD;AACnD,MAAM,YAAY,GAGd;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,kDAAkD;AAClD,SAAgB,kBAAkB,CAAC,WAAW,EAAE,eAAuB;IACrE,MAAM,OAAO,GAAG,gBAAgB,CAAC,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,YAAY,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,kBAAkB,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;IAE3C,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,IAAA,sCAAmB,EAAC,WAAW,CAAC,CAAC;IAExD,OAAO;QACL,KAAK,EAAE,eAAe;QACtB,IAAI,EAAE;YACJ,KAAK,EAAE;gBACL,IAAI,EAAE,SAAS;aAChB;YACD,IAAI,EAAE,IAAI,IAAI,EAAE;YAChB,IAAI,EAAE,IAAI,IAAI,CAAC;YACf,IAAI,EAAE,WAAW,CAAC,WAAW,CAAC;SAC/B;KACF,CAAC;AACJ,CAAC;AAED,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,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,KAAK,EAAE,cAAc;SAC5C,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,OAAO,IAAI,CAAC,CAAC,4BAA4B;AAC3C,CAAC;AAED,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;AAED,SAAS,gBAAgB,CAAC,WAAmC;IAC3D,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,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;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,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":["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"]}
|