@newcms/query-engine 0.1.0 → 0.2.0
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/index.cjs +351 -0
- package/dist/index.cjs.map +1 -0
- package/dist/{types.d.ts → index.d.cts} +41 -10
- package/dist/index.d.ts +150 -3
- package/dist/index.js +329 -1
- package/dist/index.js.map +1 -1
- package/package.json +11 -7
- package/dist/index.d.ts.map +0 -1
- package/dist/query-engine.d.ts +0 -30
- package/dist/query-engine.d.ts.map +0 -1
- package/dist/query-engine.js +0 -346
- package/dist/query-engine.js.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -2
- package/dist/types.js.map +0 -1
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc2) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc2 = __getOwnPropDesc(from, key)) || desc2.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
QueryEngine: () => QueryEngine
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(index_exports);
|
|
26
|
+
|
|
27
|
+
// src/query-engine.ts
|
|
28
|
+
var import_drizzle_orm = require("drizzle-orm");
|
|
29
|
+
var import_database = require("@newcms/database");
|
|
30
|
+
var DEFAULT_PER_PAGE = 10;
|
|
31
|
+
var QueryEngine = class {
|
|
32
|
+
constructor(db) {
|
|
33
|
+
this.db = db;
|
|
34
|
+
}
|
|
35
|
+
db;
|
|
36
|
+
/**
|
|
37
|
+
* Execute a content query.
|
|
38
|
+
*/
|
|
39
|
+
async query(params = {}) {
|
|
40
|
+
const perPage = params.perPage === -1 ? 0 : params.perPage ?? DEFAULT_PER_PAGE;
|
|
41
|
+
const page = params.page ?? 1;
|
|
42
|
+
const offset = perPage > 0 ? (page - 1) * perPage : 0;
|
|
43
|
+
const conditions = this.buildConditions(params);
|
|
44
|
+
const orderClause = this.buildOrderBy(params);
|
|
45
|
+
const [countResult] = await this.db.select({ count: import_drizzle_orm.sql`count(DISTINCT ${import_database.posts.id})::int` }).from(import_database.posts).leftJoin(import_database.postmeta, this.needsMetaJoin(params) ? (0, import_drizzle_orm.eq)(import_database.posts.id, import_database.postmeta.postId) : import_drizzle_orm.sql`false`).leftJoin(
|
|
46
|
+
import_database.termRelationships,
|
|
47
|
+
this.needsTaxJoin(params) ? (0, import_drizzle_orm.eq)(import_database.posts.id, import_database.termRelationships.objectId) : import_drizzle_orm.sql`false`
|
|
48
|
+
).where(conditions.length > 0 ? (0, import_drizzle_orm.and)(...conditions) : void 0);
|
|
49
|
+
const total = countResult.count;
|
|
50
|
+
const totalPages = perPage > 0 ? Math.ceil(total / perPage) : total > 0 ? 1 : 0;
|
|
51
|
+
let query = this.db.selectDistinct({
|
|
52
|
+
id: import_database.posts.id,
|
|
53
|
+
postAuthor: import_database.posts.postAuthor,
|
|
54
|
+
postDate: import_database.posts.postDate,
|
|
55
|
+
postDateGmt: import_database.posts.postDateGmt,
|
|
56
|
+
postContent: import_database.posts.postContent,
|
|
57
|
+
postTitle: import_database.posts.postTitle,
|
|
58
|
+
postExcerpt: import_database.posts.postExcerpt,
|
|
59
|
+
postStatus: import_database.posts.postStatus,
|
|
60
|
+
commentStatus: import_database.posts.commentStatus,
|
|
61
|
+
pingStatus: import_database.posts.pingStatus,
|
|
62
|
+
postPassword: import_database.posts.postPassword,
|
|
63
|
+
postName: import_database.posts.postName,
|
|
64
|
+
toPing: import_database.posts.toPing,
|
|
65
|
+
pinged: import_database.posts.pinged,
|
|
66
|
+
postModified: import_database.posts.postModified,
|
|
67
|
+
postModifiedGmt: import_database.posts.postModifiedGmt,
|
|
68
|
+
postContentFiltered: import_database.posts.postContentFiltered,
|
|
69
|
+
postParent: import_database.posts.postParent,
|
|
70
|
+
guid: import_database.posts.guid,
|
|
71
|
+
menuOrder: import_database.posts.menuOrder,
|
|
72
|
+
postType: import_database.posts.postType,
|
|
73
|
+
postMimeType: import_database.posts.postMimeType,
|
|
74
|
+
commentCount: import_database.posts.commentCount
|
|
75
|
+
}).from(import_database.posts).leftJoin(import_database.postmeta, this.needsMetaJoin(params) ? (0, import_drizzle_orm.eq)(import_database.posts.id, import_database.postmeta.postId) : import_drizzle_orm.sql`false`).leftJoin(
|
|
76
|
+
import_database.termRelationships,
|
|
77
|
+
this.needsTaxJoin(params) ? (0, import_drizzle_orm.eq)(import_database.posts.id, import_database.termRelationships.objectId) : import_drizzle_orm.sql`false`
|
|
78
|
+
).where(conditions.length > 0 ? (0, import_drizzle_orm.and)(...conditions) : void 0).orderBy(orderClause).$dynamic();
|
|
79
|
+
if (perPage > 0) {
|
|
80
|
+
query = query.limit(perPage).offset(offset);
|
|
81
|
+
}
|
|
82
|
+
const rows = await query;
|
|
83
|
+
const flags = this.buildFlags(params, rows.length, total);
|
|
84
|
+
return {
|
|
85
|
+
posts: rows,
|
|
86
|
+
total,
|
|
87
|
+
totalPages,
|
|
88
|
+
page,
|
|
89
|
+
perPage: perPage || total,
|
|
90
|
+
flags
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Shorthand: query a single post by ID.
|
|
95
|
+
*/
|
|
96
|
+
async querySingle(id, postType) {
|
|
97
|
+
return this.query({
|
|
98
|
+
include: [id],
|
|
99
|
+
postType: postType ?? "post",
|
|
100
|
+
postStatus: ["publish", "private", "draft"],
|
|
101
|
+
perPage: 1
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
// ─── Private: condition builders ─────────────────────────────
|
|
105
|
+
buildConditions(params) {
|
|
106
|
+
const conditions = [];
|
|
107
|
+
const postType = params.postType ?? "post";
|
|
108
|
+
if (Array.isArray(postType)) {
|
|
109
|
+
conditions.push((0, import_drizzle_orm.inArray)(import_database.posts.postType, postType));
|
|
110
|
+
} else {
|
|
111
|
+
conditions.push((0, import_drizzle_orm.eq)(import_database.posts.postType, postType));
|
|
112
|
+
}
|
|
113
|
+
const status = params.postStatus ?? "publish";
|
|
114
|
+
if (Array.isArray(status)) {
|
|
115
|
+
conditions.push((0, import_drizzle_orm.inArray)(import_database.posts.postStatus, status));
|
|
116
|
+
} else {
|
|
117
|
+
conditions.push((0, import_drizzle_orm.eq)(import_database.posts.postStatus, status));
|
|
118
|
+
}
|
|
119
|
+
if (params.author !== void 0) {
|
|
120
|
+
if (Array.isArray(params.author)) {
|
|
121
|
+
conditions.push((0, import_drizzle_orm.inArray)(import_database.posts.postAuthor, params.author));
|
|
122
|
+
} else {
|
|
123
|
+
conditions.push((0, import_drizzle_orm.eq)(import_database.posts.postAuthor, params.author));
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (params.include && params.include.length > 0) {
|
|
127
|
+
conditions.push((0, import_drizzle_orm.inArray)(import_database.posts.id, params.include));
|
|
128
|
+
}
|
|
129
|
+
if (params.exclude && params.exclude.length > 0) {
|
|
130
|
+
conditions.push((0, import_drizzle_orm.notInArray)(import_database.posts.id, params.exclude));
|
|
131
|
+
}
|
|
132
|
+
if (params.parent !== void 0) {
|
|
133
|
+
conditions.push((0, import_drizzle_orm.eq)(import_database.posts.postParent, params.parent));
|
|
134
|
+
}
|
|
135
|
+
if (params.slug !== void 0) {
|
|
136
|
+
if (Array.isArray(params.slug)) {
|
|
137
|
+
conditions.push((0, import_drizzle_orm.inArray)(import_database.posts.postName, params.slug));
|
|
138
|
+
} else {
|
|
139
|
+
conditions.push((0, import_drizzle_orm.eq)(import_database.posts.postName, params.slug));
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
if (params.search) {
|
|
143
|
+
const searchTerm = params.search.replace(/[^\w\s]/g, "").trim();
|
|
144
|
+
if (searchTerm) {
|
|
145
|
+
conditions.push(
|
|
146
|
+
(0, import_drizzle_orm.or)(
|
|
147
|
+
(0, import_drizzle_orm.like)(import_database.posts.postTitle, `%${searchTerm}%`),
|
|
148
|
+
(0, import_drizzle_orm.like)(import_database.posts.postContent, `%${searchTerm}%`)
|
|
149
|
+
)
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
if (params.tax) {
|
|
154
|
+
const taxConditions = this.buildTaxConditions(params.tax);
|
|
155
|
+
if (taxConditions) {
|
|
156
|
+
conditions.push(taxConditions);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (params.meta) {
|
|
160
|
+
const metaConditions = this.buildMetaConditions(params.meta);
|
|
161
|
+
if (metaConditions) {
|
|
162
|
+
conditions.push(metaConditions);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
if (params.date) {
|
|
166
|
+
const dateConditions = this.buildDateConditions(params.date);
|
|
167
|
+
if (dateConditions) {
|
|
168
|
+
conditions.push(dateConditions);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return conditions;
|
|
172
|
+
}
|
|
173
|
+
buildTaxConditions(tax) {
|
|
174
|
+
const clauseConditions = tax.clauses.map((clause) => {
|
|
175
|
+
const subConditions = [];
|
|
176
|
+
if (clause.termIds && clause.termIds.length > 0) {
|
|
177
|
+
const ttIdSubquery = import_drizzle_orm.sql`${import_database.termRelationships.termTaxonomyId} IN (
|
|
178
|
+
SELECT ${import_database.termTaxonomy.termTaxonomyId} FROM ${import_database.termTaxonomy}
|
|
179
|
+
WHERE ${import_database.termTaxonomy.taxonomy} = ${clause.taxonomy}
|
|
180
|
+
AND ${import_database.termTaxonomy.termId} IN (${import_drizzle_orm.sql.join(clause.termIds.map((id) => import_drizzle_orm.sql`${id}`), import_drizzle_orm.sql`, `)})
|
|
181
|
+
)`;
|
|
182
|
+
if (clause.operator === "NOT IN") {
|
|
183
|
+
subConditions.push(import_drizzle_orm.sql`${import_database.posts.id} NOT IN (
|
|
184
|
+
SELECT ${import_database.termRelationships.objectId} FROM ${import_database.termRelationships}
|
|
185
|
+
WHERE ${ttIdSubquery}
|
|
186
|
+
)`);
|
|
187
|
+
} else {
|
|
188
|
+
subConditions.push(ttIdSubquery);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
if (clause.termSlugs && clause.termSlugs.length > 0) {
|
|
192
|
+
const ttIdSubquery = import_drizzle_orm.sql`${import_database.termRelationships.termTaxonomyId} IN (
|
|
193
|
+
SELECT ${import_database.termTaxonomy.termTaxonomyId} FROM ${import_database.termTaxonomy}
|
|
194
|
+
INNER JOIN ${import_database.terms} ON ${import_database.terms.termId} = ${import_database.termTaxonomy.termId}
|
|
195
|
+
WHERE ${import_database.termTaxonomy.taxonomy} = ${clause.taxonomy}
|
|
196
|
+
AND ${import_database.terms.slug} IN (${import_drizzle_orm.sql.join(clause.termSlugs.map((s) => import_drizzle_orm.sql`${s}`), import_drizzle_orm.sql`, `)})
|
|
197
|
+
)`;
|
|
198
|
+
if (clause.operator === "NOT IN") {
|
|
199
|
+
subConditions.push(import_drizzle_orm.sql`${import_database.posts.id} NOT IN (
|
|
200
|
+
SELECT ${import_database.termRelationships.objectId} FROM ${import_database.termRelationships}
|
|
201
|
+
WHERE ${ttIdSubquery}
|
|
202
|
+
)`);
|
|
203
|
+
} else {
|
|
204
|
+
subConditions.push(ttIdSubquery);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return subConditions.length > 0 ? (0, import_drizzle_orm.and)(...subConditions) : void 0;
|
|
208
|
+
}).filter(Boolean);
|
|
209
|
+
if (clauseConditions.length === 0) return void 0;
|
|
210
|
+
const relation = tax.relation ?? "AND";
|
|
211
|
+
if (relation === "OR") {
|
|
212
|
+
return (0, import_drizzle_orm.or)(...clauseConditions);
|
|
213
|
+
}
|
|
214
|
+
return (0, import_drizzle_orm.and)(...clauseConditions);
|
|
215
|
+
}
|
|
216
|
+
buildMetaConditions(meta) {
|
|
217
|
+
const clauseConditions = meta.clauses.map((clause) => {
|
|
218
|
+
if (clause.compare === "EXISTS") {
|
|
219
|
+
return import_drizzle_orm.sql`${import_database.posts.id} IN (
|
|
220
|
+
SELECT ${import_database.postmeta.postId} FROM ${import_database.postmeta}
|
|
221
|
+
WHERE ${import_database.postmeta.metaKey} = ${clause.key}
|
|
222
|
+
)`;
|
|
223
|
+
}
|
|
224
|
+
if (clause.compare === "NOT EXISTS") {
|
|
225
|
+
return import_drizzle_orm.sql`${import_database.posts.id} NOT IN (
|
|
226
|
+
SELECT ${import_database.postmeta.postId} FROM ${import_database.postmeta}
|
|
227
|
+
WHERE ${import_database.postmeta.metaKey} = ${clause.key}
|
|
228
|
+
)`;
|
|
229
|
+
}
|
|
230
|
+
const valueStr = String(clause.value ?? "");
|
|
231
|
+
const compare = clause.compare ?? "=";
|
|
232
|
+
const castCol = clause.type === "NUMERIC" ? import_drizzle_orm.sql`CAST(${import_database.postmeta.metaValue} AS NUMERIC)` : import_database.postmeta.metaValue;
|
|
233
|
+
const compareValue = clause.type === "NUMERIC" ? import_drizzle_orm.sql`CAST(${valueStr} AS NUMERIC)` : import_drizzle_orm.sql`${valueStr}`;
|
|
234
|
+
let condition;
|
|
235
|
+
switch (compare) {
|
|
236
|
+
case "=":
|
|
237
|
+
condition = import_drizzle_orm.sql`${castCol} = ${compareValue}`;
|
|
238
|
+
break;
|
|
239
|
+
case "!=":
|
|
240
|
+
condition = import_drizzle_orm.sql`${castCol} != ${compareValue}`;
|
|
241
|
+
break;
|
|
242
|
+
case ">":
|
|
243
|
+
condition = import_drizzle_orm.sql`${castCol} > ${compareValue}`;
|
|
244
|
+
break;
|
|
245
|
+
case "<":
|
|
246
|
+
condition = import_drizzle_orm.sql`${castCol} < ${compareValue}`;
|
|
247
|
+
break;
|
|
248
|
+
case ">=":
|
|
249
|
+
condition = import_drizzle_orm.sql`${castCol} >= ${compareValue}`;
|
|
250
|
+
break;
|
|
251
|
+
case "<=":
|
|
252
|
+
condition = import_drizzle_orm.sql`${castCol} <= ${compareValue}`;
|
|
253
|
+
break;
|
|
254
|
+
case "LIKE":
|
|
255
|
+
condition = import_drizzle_orm.sql`${import_database.postmeta.metaValue} LIKE ${valueStr}`;
|
|
256
|
+
break;
|
|
257
|
+
case "NOT LIKE":
|
|
258
|
+
condition = import_drizzle_orm.sql`${import_database.postmeta.metaValue} NOT LIKE ${valueStr}`;
|
|
259
|
+
break;
|
|
260
|
+
default:
|
|
261
|
+
condition = import_drizzle_orm.sql`${castCol} = ${compareValue}`;
|
|
262
|
+
}
|
|
263
|
+
return import_drizzle_orm.sql`${import_database.posts.id} IN (
|
|
264
|
+
SELECT ${import_database.postmeta.postId} FROM ${import_database.postmeta}
|
|
265
|
+
WHERE ${import_database.postmeta.metaKey} = ${clause.key} AND ${condition}
|
|
266
|
+
)`;
|
|
267
|
+
}).filter(Boolean);
|
|
268
|
+
if (clauseConditions.length === 0) return void 0;
|
|
269
|
+
const relation = meta.relation ?? "AND";
|
|
270
|
+
if (relation === "OR") {
|
|
271
|
+
return (0, import_drizzle_orm.or)(...clauseConditions);
|
|
272
|
+
}
|
|
273
|
+
return (0, import_drizzle_orm.and)(...clauseConditions);
|
|
274
|
+
}
|
|
275
|
+
buildDateConditions(date) {
|
|
276
|
+
const clauseConditions = date.clauses.map((clause) => {
|
|
277
|
+
const col = clause.column === "post_modified" ? import_database.posts.postModified : import_database.posts.postDate;
|
|
278
|
+
const conditions = [];
|
|
279
|
+
if (clause.year !== void 0) {
|
|
280
|
+
conditions.push(import_drizzle_orm.sql`EXTRACT(YEAR FROM ${col}) = ${clause.year}`);
|
|
281
|
+
}
|
|
282
|
+
if (clause.month !== void 0) {
|
|
283
|
+
conditions.push(import_drizzle_orm.sql`EXTRACT(MONTH FROM ${col}) = ${clause.month}`);
|
|
284
|
+
}
|
|
285
|
+
if (clause.day !== void 0) {
|
|
286
|
+
conditions.push(import_drizzle_orm.sql`EXTRACT(DAY FROM ${col}) = ${clause.day}`);
|
|
287
|
+
}
|
|
288
|
+
if (clause.after !== void 0) {
|
|
289
|
+
const afterDate = clause.after instanceof Date ? clause.after : new Date(clause.after);
|
|
290
|
+
conditions.push((0, import_drizzle_orm.gt)(col, afterDate));
|
|
291
|
+
}
|
|
292
|
+
if (clause.before !== void 0) {
|
|
293
|
+
const beforeDate = clause.before instanceof Date ? clause.before : new Date(clause.before);
|
|
294
|
+
conditions.push((0, import_drizzle_orm.lt)(col, beforeDate));
|
|
295
|
+
}
|
|
296
|
+
return conditions.length > 0 ? (0, import_drizzle_orm.and)(...conditions) : void 0;
|
|
297
|
+
}).filter(Boolean);
|
|
298
|
+
if (clauseConditions.length === 0) return void 0;
|
|
299
|
+
const relation = date.relation ?? "AND";
|
|
300
|
+
if (relation === "OR") {
|
|
301
|
+
return (0, import_drizzle_orm.or)(...clauseConditions);
|
|
302
|
+
}
|
|
303
|
+
return (0, import_drizzle_orm.and)(...clauseConditions);
|
|
304
|
+
}
|
|
305
|
+
// ─── Private: order by ───────────────────────────────────────
|
|
306
|
+
buildOrderBy(params) {
|
|
307
|
+
const direction = params.order === "asc" ? import_drizzle_orm.asc : import_drizzle_orm.desc;
|
|
308
|
+
switch (params.orderBy) {
|
|
309
|
+
case "title":
|
|
310
|
+
return direction(import_database.posts.postTitle);
|
|
311
|
+
case "name":
|
|
312
|
+
return direction(import_database.posts.postName);
|
|
313
|
+
case "modified":
|
|
314
|
+
return direction(import_database.posts.postModified);
|
|
315
|
+
case "id":
|
|
316
|
+
return direction(import_database.posts.id);
|
|
317
|
+
case "author":
|
|
318
|
+
return direction(import_database.posts.postAuthor);
|
|
319
|
+
case "menu_order":
|
|
320
|
+
return direction(import_database.posts.menuOrder);
|
|
321
|
+
case "date":
|
|
322
|
+
default:
|
|
323
|
+
return direction(import_database.posts.postDate);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
// ─── Private: join detection ─────────────────────────────────
|
|
327
|
+
needsMetaJoin(params) {
|
|
328
|
+
return params.meta !== void 0 && params.meta.clauses.length > 0;
|
|
329
|
+
}
|
|
330
|
+
needsTaxJoin(params) {
|
|
331
|
+
return params.tax !== void 0 && params.tax.clauses.length > 0;
|
|
332
|
+
}
|
|
333
|
+
// ─── Private: query flags ────────────────────────────────────
|
|
334
|
+
buildFlags(params, resultCount, _total) {
|
|
335
|
+
const isSearch = Boolean(params.search);
|
|
336
|
+
const isSingle = (params.include?.length === 1 || params.slug !== void 0) && !Array.isArray(params.slug);
|
|
337
|
+
const isAuthor = params.author !== void 0;
|
|
338
|
+
const isTaxonomy = params.tax !== void 0;
|
|
339
|
+
const isDate = params.date !== void 0;
|
|
340
|
+
const isPage = params.postType === "page";
|
|
341
|
+
const is404 = resultCount === 0;
|
|
342
|
+
const isHome = !isSearch && !isSingle && !isAuthor && !isTaxonomy && !isDate && params.postType === "post";
|
|
343
|
+
const isArchive = !isSingle && (isAuthor || isTaxonomy || isDate);
|
|
344
|
+
return { isSingle, isArchive, isSearch, is404, isHome, isPage, isAuthor, isTaxonomy, isDate };
|
|
345
|
+
}
|
|
346
|
+
};
|
|
347
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
348
|
+
0 && (module.exports = {
|
|
349
|
+
QueryEngine
|
|
350
|
+
});
|
|
351
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/query-engine.ts"],"sourcesContent":["export { QueryEngine } from './query-engine';\nexport type {\n\tQueryParams,\n\tQueryResult,\n\tQueryFlags,\n\tTaxQuery,\n\tTaxQueryClause,\n\tMetaQuery,\n\tMetaQueryClause,\n\tDateQuery,\n\tDateQueryClause,\n} from './types';\n","import { eq, and, or, desc, asc, sql, inArray, like, gt, lt, notInArray } from 'drizzle-orm';\nimport {\n\tposts,\n\tpostmeta,\n\ttermRelationships,\n\ttermTaxonomy,\n\tterms,\n} from '@newcms/database';\nimport type { Database } from '@newcms/database';\nimport type {\n\tQueryParams,\n\tQueryResult,\n\tQueryFlags,\n\tTaxQuery,\n\tMetaQuery,\n\tDateQuery,\n} from './types';\n\nconst DEFAULT_PER_PAGE = 10;\n\n/**\n * Declarative query engine for content.\n *\n * Accepts a `QueryParams` object and builds a type-safe SQL query\n * via Drizzle ORM. Supports taxonomy sub-queries, meta sub-queries,\n * date filters, full-text search, and pagination.\n */\nexport class QueryEngine {\n\tconstructor(private db: Database) {}\n\n\t/**\n\t * Execute a content query.\n\t */\n\tasync query(params: QueryParams = {}): Promise<QueryResult> {\n\t\tconst perPage = params.perPage === -1 ? 0 : (params.perPage ?? DEFAULT_PER_PAGE);\n\t\tconst page = params.page ?? 1;\n\t\tconst offset = perPage > 0 ? (page - 1) * perPage : 0;\n\n\t\tconst conditions = this.buildConditions(params);\n\t\tconst orderClause = this.buildOrderBy(params);\n\n\t\t// Count total\n\t\tconst [countResult] = await this.db\n\t\t\t.select({ count: sql<number>`count(DISTINCT ${posts.id})::int` })\n\t\t\t.from(posts)\n\t\t\t.leftJoin(postmeta, this.needsMetaJoin(params) ? eq(posts.id, postmeta.postId) : sql`false`)\n\t\t\t.leftJoin(\n\t\t\t\ttermRelationships,\n\t\t\t\tthis.needsTaxJoin(params) ? eq(posts.id, termRelationships.objectId) : sql`false`,\n\t\t\t)\n\t\t\t.where(conditions.length > 0 ? and(...conditions) : undefined);\n\n\t\tconst total = countResult.count;\n\t\tconst totalPages = perPage > 0 ? Math.ceil(total / perPage) : (total > 0 ? 1 : 0);\n\n\t\t// Fetch posts\n\t\tlet query = this.db\n\t\t\t.selectDistinct({\n\t\t\t\tid: posts.id,\n\t\t\t\tpostAuthor: posts.postAuthor,\n\t\t\t\tpostDate: posts.postDate,\n\t\t\t\tpostDateGmt: posts.postDateGmt,\n\t\t\t\tpostContent: posts.postContent,\n\t\t\t\tpostTitle: posts.postTitle,\n\t\t\t\tpostExcerpt: posts.postExcerpt,\n\t\t\t\tpostStatus: posts.postStatus,\n\t\t\t\tcommentStatus: posts.commentStatus,\n\t\t\t\tpingStatus: posts.pingStatus,\n\t\t\t\tpostPassword: posts.postPassword,\n\t\t\t\tpostName: posts.postName,\n\t\t\t\ttoPing: posts.toPing,\n\t\t\t\tpinged: posts.pinged,\n\t\t\t\tpostModified: posts.postModified,\n\t\t\t\tpostModifiedGmt: posts.postModifiedGmt,\n\t\t\t\tpostContentFiltered: posts.postContentFiltered,\n\t\t\t\tpostParent: posts.postParent,\n\t\t\t\tguid: posts.guid,\n\t\t\t\tmenuOrder: posts.menuOrder,\n\t\t\t\tpostType: posts.postType,\n\t\t\t\tpostMimeType: posts.postMimeType,\n\t\t\t\tcommentCount: posts.commentCount,\n\t\t\t})\n\t\t\t.from(posts)\n\t\t\t.leftJoin(postmeta, this.needsMetaJoin(params) ? eq(posts.id, postmeta.postId) : sql`false`)\n\t\t\t.leftJoin(\n\t\t\t\ttermRelationships,\n\t\t\t\tthis.needsTaxJoin(params) ? eq(posts.id, termRelationships.objectId) : sql`false`,\n\t\t\t)\n\t\t\t.where(conditions.length > 0 ? and(...conditions) : undefined)\n\t\t\t.orderBy(orderClause)\n\t\t\t.$dynamic();\n\n\t\tif (perPage > 0) {\n\t\t\tquery = query.limit(perPage).offset(offset);\n\t\t}\n\n\t\tconst rows = await query;\n\n\t\tconst flags = this.buildFlags(params, rows.length, total);\n\n\t\treturn {\n\t\t\tposts: rows,\n\t\t\ttotal,\n\t\t\ttotalPages,\n\t\t\tpage,\n\t\t\tperPage: perPage || total,\n\t\t\tflags,\n\t\t};\n\t}\n\n\t/**\n\t * Shorthand: query a single post by ID.\n\t */\n\tasync querySingle(id: number, postType?: string): Promise<QueryResult> {\n\t\treturn this.query({\n\t\t\tinclude: [id],\n\t\t\tpostType: postType ?? 'post',\n\t\t\tpostStatus: ['publish', 'private', 'draft'],\n\t\t\tperPage: 1,\n\t\t});\n\t}\n\n\t// ─── Private: condition builders ─────────────────────────────\n\n\tprivate buildConditions(params: QueryParams): ReturnType<typeof eq>[] {\n\t\tconst conditions: ReturnType<typeof eq>[] = [];\n\n\t\t// Post type\n\t\tconst postType = params.postType ?? 'post';\n\t\tif (Array.isArray(postType)) {\n\t\t\tconditions.push(inArray(posts.postType, postType));\n\t\t} else {\n\t\t\tconditions.push(eq(posts.postType, postType));\n\t\t}\n\n\t\t// Post status\n\t\tconst status = params.postStatus ?? 'publish';\n\t\tif (Array.isArray(status)) {\n\t\t\tconditions.push(inArray(posts.postStatus, status));\n\t\t} else {\n\t\t\tconditions.push(eq(posts.postStatus, status));\n\t\t}\n\n\t\t// Author\n\t\tif (params.author !== undefined) {\n\t\t\tif (Array.isArray(params.author)) {\n\t\t\t\tconditions.push(inArray(posts.postAuthor, params.author));\n\t\t\t} else {\n\t\t\t\tconditions.push(eq(posts.postAuthor, params.author));\n\t\t\t}\n\t\t}\n\n\t\t// Include/exclude IDs\n\t\tif (params.include && params.include.length > 0) {\n\t\t\tconditions.push(inArray(posts.id, params.include));\n\t\t}\n\t\tif (params.exclude && params.exclude.length > 0) {\n\t\t\tconditions.push(notInArray(posts.id, params.exclude));\n\t\t}\n\n\t\t// Parent\n\t\tif (params.parent !== undefined) {\n\t\t\tconditions.push(eq(posts.postParent, params.parent));\n\t\t}\n\n\t\t// Slug\n\t\tif (params.slug !== undefined) {\n\t\t\tif (Array.isArray(params.slug)) {\n\t\t\t\tconditions.push(inArray(posts.postName, params.slug));\n\t\t\t} else {\n\t\t\t\tconditions.push(eq(posts.postName, params.slug));\n\t\t\t}\n\t\t}\n\n\t\t// Search (full-text)\n\t\tif (params.search) {\n\t\t\tconst searchTerm = params.search.replace(/[^\\w\\s]/g, '').trim();\n\t\t\tif (searchTerm) {\n\t\t\t\tconditions.push(\n\t\t\t\t\tor(\n\t\t\t\t\t\tlike(posts.postTitle, `%${searchTerm}%`),\n\t\t\t\t\t\tlike(posts.postContent, `%${searchTerm}%`),\n\t\t\t\t\t)!,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// Taxonomy sub-query\n\t\tif (params.tax) {\n\t\t\tconst taxConditions = this.buildTaxConditions(params.tax);\n\t\t\tif (taxConditions) {\n\t\t\t\tconditions.push(taxConditions);\n\t\t\t}\n\t\t}\n\n\t\t// Meta sub-query\n\t\tif (params.meta) {\n\t\t\tconst metaConditions = this.buildMetaConditions(params.meta);\n\t\t\tif (metaConditions) {\n\t\t\t\tconditions.push(metaConditions);\n\t\t\t}\n\t\t}\n\n\t\t// Date sub-query\n\t\tif (params.date) {\n\t\t\tconst dateConditions = this.buildDateConditions(params.date);\n\t\t\tif (dateConditions) {\n\t\t\t\tconditions.push(dateConditions);\n\t\t\t}\n\t\t}\n\n\t\treturn conditions;\n\t}\n\n\tprivate buildTaxConditions(tax: TaxQuery): ReturnType<typeof eq> | undefined {\n\t\tconst clauseConditions = tax.clauses.map((clause) => {\n\t\t\tconst subConditions: ReturnType<typeof eq>[] = [];\n\n\t\t\tif (clause.termIds && clause.termIds.length > 0) {\n\t\t\t\t// Sub-select to find term_taxonomy_ids for these term IDs\n\t\t\t\tconst ttIdSubquery = sql`${termRelationships.termTaxonomyId} IN (\n\t\t\t\t\tSELECT ${termTaxonomy.termTaxonomyId} FROM ${termTaxonomy}\n\t\t\t\t\tWHERE ${termTaxonomy.taxonomy} = ${clause.taxonomy}\n\t\t\t\t\tAND ${termTaxonomy.termId} IN (${sql.join(clause.termIds.map((id) => sql`${id}`), sql`, `)})\n\t\t\t\t)`;\n\n\t\t\t\tif (clause.operator === 'NOT IN') {\n\t\t\t\t\tsubConditions.push(sql`${posts.id} NOT IN (\n\t\t\t\t\t\tSELECT ${termRelationships.objectId} FROM ${termRelationships}\n\t\t\t\t\t\tWHERE ${ttIdSubquery}\n\t\t\t\t\t)`);\n\t\t\t\t} else {\n\t\t\t\t\tsubConditions.push(ttIdSubquery);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (clause.termSlugs && clause.termSlugs.length > 0) {\n\t\t\t\tconst ttIdSubquery = sql`${termRelationships.termTaxonomyId} IN (\n\t\t\t\t\tSELECT ${termTaxonomy.termTaxonomyId} FROM ${termTaxonomy}\n\t\t\t\t\tINNER JOIN ${terms} ON ${terms.termId} = ${termTaxonomy.termId}\n\t\t\t\t\tWHERE ${termTaxonomy.taxonomy} = ${clause.taxonomy}\n\t\t\t\t\tAND ${terms.slug} IN (${sql.join(clause.termSlugs.map((s) => sql`${s}`), sql`, `)})\n\t\t\t\t)`;\n\n\t\t\t\tif (clause.operator === 'NOT IN') {\n\t\t\t\t\tsubConditions.push(sql`${posts.id} NOT IN (\n\t\t\t\t\t\tSELECT ${termRelationships.objectId} FROM ${termRelationships}\n\t\t\t\t\t\tWHERE ${ttIdSubquery}\n\t\t\t\t\t)`);\n\t\t\t\t} else {\n\t\t\t\t\tsubConditions.push(ttIdSubquery);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn subConditions.length > 0 ? and(...subConditions) : undefined;\n\t\t}).filter(Boolean);\n\n\t\tif (clauseConditions.length === 0) return undefined;\n\n\t\tconst relation = tax.relation ?? 'AND';\n\t\tif (relation === 'OR') {\n\t\t\treturn or(...clauseConditions)!;\n\t\t}\n\t\treturn and(...clauseConditions)!;\n\t}\n\n\tprivate buildMetaConditions(meta: MetaQuery): ReturnType<typeof eq> | undefined {\n\t\tconst clauseConditions = meta.clauses.map((clause) => {\n\t\t\tif (clause.compare === 'EXISTS') {\n\t\t\t\treturn sql`${posts.id} IN (\n\t\t\t\t\tSELECT ${postmeta.postId} FROM ${postmeta}\n\t\t\t\t\tWHERE ${postmeta.metaKey} = ${clause.key}\n\t\t\t\t)`;\n\t\t\t}\n\t\t\tif (clause.compare === 'NOT EXISTS') {\n\t\t\t\treturn sql`${posts.id} NOT IN (\n\t\t\t\t\tSELECT ${postmeta.postId} FROM ${postmeta}\n\t\t\t\t\tWHERE ${postmeta.metaKey} = ${clause.key}\n\t\t\t\t)`;\n\t\t\t}\n\n\t\t\tconst valueStr = String(clause.value ?? '');\n\t\t\tconst compare = clause.compare ?? '=';\n\t\t\tconst castCol = clause.type === 'NUMERIC'\n\t\t\t\t? sql`CAST(${postmeta.metaValue} AS NUMERIC)`\n\t\t\t\t: postmeta.metaValue;\n\n\t\t\tconst compareValue = clause.type === 'NUMERIC'\n\t\t\t\t? sql`CAST(${valueStr} AS NUMERIC)`\n\t\t\t\t: sql`${valueStr}`;\n\n\t\t\tlet condition;\n\t\t\tswitch (compare) {\n\t\t\t\tcase '=': condition = sql`${castCol} = ${compareValue}`; break;\n\t\t\t\tcase '!=': condition = sql`${castCol} != ${compareValue}`; break;\n\t\t\t\tcase '>': condition = sql`${castCol} > ${compareValue}`; break;\n\t\t\t\tcase '<': condition = sql`${castCol} < ${compareValue}`; break;\n\t\t\t\tcase '>=': condition = sql`${castCol} >= ${compareValue}`; break;\n\t\t\t\tcase '<=': condition = sql`${castCol} <= ${compareValue}`; break;\n\t\t\t\tcase 'LIKE': condition = sql`${postmeta.metaValue} LIKE ${valueStr}`; break;\n\t\t\t\tcase 'NOT LIKE': condition = sql`${postmeta.metaValue} NOT LIKE ${valueStr}`; break;\n\t\t\t\tdefault: condition = sql`${castCol} = ${compareValue}`;\n\t\t\t}\n\n\t\t\treturn sql`${posts.id} IN (\n\t\t\t\tSELECT ${postmeta.postId} FROM ${postmeta}\n\t\t\t\tWHERE ${postmeta.metaKey} = ${clause.key} AND ${condition}\n\t\t\t)`;\n\t\t}).filter(Boolean);\n\n\t\tif (clauseConditions.length === 0) return undefined;\n\n\t\tconst relation = meta.relation ?? 'AND';\n\t\tif (relation === 'OR') {\n\t\t\treturn or(...clauseConditions)!;\n\t\t}\n\t\treturn and(...clauseConditions)!;\n\t}\n\n\tprivate buildDateConditions(date: DateQuery): ReturnType<typeof eq> | undefined {\n\t\tconst clauseConditions = date.clauses.map((clause) => {\n\t\t\tconst col = clause.column === 'post_modified' ? posts.postModified : posts.postDate;\n\t\t\tconst conditions: ReturnType<typeof eq>[] = [];\n\n\t\t\tif (clause.year !== undefined) {\n\t\t\t\tconditions.push(sql`EXTRACT(YEAR FROM ${col}) = ${clause.year}`);\n\t\t\t}\n\t\t\tif (clause.month !== undefined) {\n\t\t\t\tconditions.push(sql`EXTRACT(MONTH FROM ${col}) = ${clause.month}`);\n\t\t\t}\n\t\t\tif (clause.day !== undefined) {\n\t\t\t\tconditions.push(sql`EXTRACT(DAY FROM ${col}) = ${clause.day}`);\n\t\t\t}\n\t\t\tif (clause.after !== undefined) {\n\t\t\t\tconst afterDate = clause.after instanceof Date ? clause.after : new Date(clause.after);\n\t\t\t\tconditions.push(gt(col, afterDate));\n\t\t\t}\n\t\t\tif (clause.before !== undefined) {\n\t\t\t\tconst beforeDate = clause.before instanceof Date ? clause.before : new Date(clause.before);\n\t\t\t\tconditions.push(lt(col, beforeDate));\n\t\t\t}\n\n\t\t\treturn conditions.length > 0 ? and(...conditions) : undefined;\n\t\t}).filter(Boolean);\n\n\t\tif (clauseConditions.length === 0) return undefined;\n\n\t\tconst relation = date.relation ?? 'AND';\n\t\tif (relation === 'OR') {\n\t\t\treturn or(...clauseConditions)!;\n\t\t}\n\t\treturn and(...clauseConditions)!;\n\t}\n\n\t// ─── Private: order by ───────────────────────────────────────\n\n\tprivate buildOrderBy(params: QueryParams) {\n\t\tconst direction = params.order === 'asc' ? asc : desc;\n\t\tswitch (params.orderBy) {\n\t\t\tcase 'title': return direction(posts.postTitle);\n\t\t\tcase 'name': return direction(posts.postName);\n\t\t\tcase 'modified': return direction(posts.postModified);\n\t\t\tcase 'id': return direction(posts.id);\n\t\t\tcase 'author': return direction(posts.postAuthor);\n\t\t\tcase 'menu_order': return direction(posts.menuOrder);\n\t\t\tcase 'date':\n\t\t\tdefault: return direction(posts.postDate);\n\t\t}\n\t}\n\n\t// ─── Private: join detection ─────────────────────────────────\n\n\tprivate needsMetaJoin(params: QueryParams): boolean {\n\t\treturn params.meta !== undefined && params.meta.clauses.length > 0;\n\t}\n\n\tprivate needsTaxJoin(params: QueryParams): boolean {\n\t\treturn params.tax !== undefined && params.tax.clauses.length > 0;\n\t}\n\n\t// ─── Private: query flags ────────────────────────────────────\n\n\tprivate buildFlags(params: QueryParams, resultCount: number, _total: number): QueryFlags {\n\t\tconst isSearch = Boolean(params.search);\n\t\tconst isSingle = (params.include?.length === 1 || params.slug !== undefined) && !Array.isArray(params.slug);\n\t\tconst isAuthor = params.author !== undefined;\n\t\tconst isTaxonomy = params.tax !== undefined;\n\t\tconst isDate = params.date !== undefined;\n\t\tconst isPage = params.postType === 'page';\n\t\tconst is404 = resultCount === 0;\n\t\tconst isHome = !isSearch && !isSingle && !isAuthor && !isTaxonomy && !isDate && params.postType === 'post';\n\t\tconst isArchive = !isSingle && (isAuthor || isTaxonomy || isDate);\n\n\t\treturn { isSingle, isArchive, isSearch, is404, isHome, isPage, isAuthor, isTaxonomy, isDate };\n\t}\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { Database } from '@newcms/database';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Parameters for a content query.
|
|
3
5
|
*/
|
|
4
|
-
|
|
6
|
+
interface QueryParams {
|
|
5
7
|
/** Post type(s) to query. Default: 'post' */
|
|
6
8
|
postType?: string | string[];
|
|
7
9
|
/** Post status(es). Default: 'publish' */
|
|
@@ -40,12 +42,12 @@ export interface QueryParams {
|
|
|
40
42
|
/**
|
|
41
43
|
* Taxonomy sub-query — filter posts by term assignments.
|
|
42
44
|
*/
|
|
43
|
-
|
|
45
|
+
interface TaxQuery {
|
|
44
46
|
/** How to combine multiple clauses: AND = all must match, OR = any can match */
|
|
45
47
|
relation?: 'AND' | 'OR';
|
|
46
48
|
clauses: TaxQueryClause[];
|
|
47
49
|
}
|
|
48
|
-
|
|
50
|
+
interface TaxQueryClause {
|
|
49
51
|
taxonomy: string;
|
|
50
52
|
/** Term IDs to match */
|
|
51
53
|
termIds?: number[];
|
|
@@ -59,11 +61,11 @@ export interface TaxQueryClause {
|
|
|
59
61
|
/**
|
|
60
62
|
* Meta sub-query — filter posts by metadata values.
|
|
61
63
|
*/
|
|
62
|
-
|
|
64
|
+
interface MetaQuery {
|
|
63
65
|
relation?: 'AND' | 'OR';
|
|
64
66
|
clauses: MetaQueryClause[];
|
|
65
67
|
}
|
|
66
|
-
|
|
68
|
+
interface MetaQueryClause {
|
|
67
69
|
key: string;
|
|
68
70
|
value?: string | number | boolean;
|
|
69
71
|
compare?: '=' | '!=' | '>' | '<' | '>=' | '<=' | 'LIKE' | 'NOT LIKE' | 'IN' | 'NOT IN' | 'EXISTS' | 'NOT EXISTS';
|
|
@@ -73,11 +75,11 @@ export interface MetaQueryClause {
|
|
|
73
75
|
/**
|
|
74
76
|
* Date sub-query — filter posts by date ranges.
|
|
75
77
|
*/
|
|
76
|
-
|
|
78
|
+
interface DateQuery {
|
|
77
79
|
relation?: 'AND' | 'OR';
|
|
78
80
|
clauses: DateQueryClause[];
|
|
79
81
|
}
|
|
80
|
-
|
|
82
|
+
interface DateQueryClause {
|
|
81
83
|
year?: number;
|
|
82
84
|
month?: number;
|
|
83
85
|
day?: number;
|
|
@@ -88,7 +90,7 @@ export interface DateQueryClause {
|
|
|
88
90
|
/**
|
|
89
91
|
* Result of a content query.
|
|
90
92
|
*/
|
|
91
|
-
|
|
93
|
+
interface QueryResult<T = unknown> {
|
|
92
94
|
/** The matched posts */
|
|
93
95
|
posts: T[];
|
|
94
96
|
/** Total number of matching posts (ignoring pagination) */
|
|
@@ -105,7 +107,7 @@ export interface QueryResult<T = unknown> {
|
|
|
105
107
|
/**
|
|
106
108
|
* Flags indicating the type of query result.
|
|
107
109
|
*/
|
|
108
|
-
|
|
110
|
+
interface QueryFlags {
|
|
109
111
|
isSingle: boolean;
|
|
110
112
|
isArchive: boolean;
|
|
111
113
|
isSearch: boolean;
|
|
@@ -116,4 +118,33 @@ export interface QueryFlags {
|
|
|
116
118
|
isTaxonomy: boolean;
|
|
117
119
|
isDate: boolean;
|
|
118
120
|
}
|
|
119
|
-
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Declarative query engine for content.
|
|
124
|
+
*
|
|
125
|
+
* Accepts a `QueryParams` object and builds a type-safe SQL query
|
|
126
|
+
* via Drizzle ORM. Supports taxonomy sub-queries, meta sub-queries,
|
|
127
|
+
* date filters, full-text search, and pagination.
|
|
128
|
+
*/
|
|
129
|
+
declare class QueryEngine {
|
|
130
|
+
private db;
|
|
131
|
+
constructor(db: Database);
|
|
132
|
+
/**
|
|
133
|
+
* Execute a content query.
|
|
134
|
+
*/
|
|
135
|
+
query(params?: QueryParams): Promise<QueryResult>;
|
|
136
|
+
/**
|
|
137
|
+
* Shorthand: query a single post by ID.
|
|
138
|
+
*/
|
|
139
|
+
querySingle(id: number, postType?: string): Promise<QueryResult>;
|
|
140
|
+
private buildConditions;
|
|
141
|
+
private buildTaxConditions;
|
|
142
|
+
private buildMetaConditions;
|
|
143
|
+
private buildDateConditions;
|
|
144
|
+
private buildOrderBy;
|
|
145
|
+
private needsMetaJoin;
|
|
146
|
+
private needsTaxJoin;
|
|
147
|
+
private buildFlags;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export { type DateQuery, type DateQueryClause, type MetaQuery, type MetaQueryClause, QueryEngine, type QueryFlags, type QueryParams, type QueryResult, type TaxQuery, type TaxQueryClause };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,150 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { Database } from '@newcms/database';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Parameters for a content query.
|
|
5
|
+
*/
|
|
6
|
+
interface QueryParams {
|
|
7
|
+
/** Post type(s) to query. Default: 'post' */
|
|
8
|
+
postType?: string | string[];
|
|
9
|
+
/** Post status(es). Default: 'publish' */
|
|
10
|
+
postStatus?: string | string[];
|
|
11
|
+
/** Filter by author ID(s) */
|
|
12
|
+
author?: number | number[];
|
|
13
|
+
/** Search term (full-text search) */
|
|
14
|
+
search?: string;
|
|
15
|
+
/** Number of results per page. Default: 10. Use -1 for all. */
|
|
16
|
+
perPage?: number;
|
|
17
|
+
/** Page number (1-based). Default: 1 */
|
|
18
|
+
page?: number;
|
|
19
|
+
/** Order by field. Default: 'date' */
|
|
20
|
+
orderBy?: 'date' | 'title' | 'name' | 'modified' | 'id' | 'author' | 'menu_order' | 'relevance';
|
|
21
|
+
/** Order direction. Default: 'desc' */
|
|
22
|
+
order?: 'asc' | 'desc';
|
|
23
|
+
/** Include only these post IDs */
|
|
24
|
+
include?: number[];
|
|
25
|
+
/** Exclude these post IDs */
|
|
26
|
+
exclude?: number[];
|
|
27
|
+
/** Filter by parent ID (for hierarchical types) */
|
|
28
|
+
parent?: number;
|
|
29
|
+
/** Filter by slug(s) */
|
|
30
|
+
slug?: string | string[];
|
|
31
|
+
/** Only sticky posts */
|
|
32
|
+
stickyOnly?: boolean;
|
|
33
|
+
/** Taxonomy sub-query */
|
|
34
|
+
tax?: TaxQuery;
|
|
35
|
+
/** Meta sub-query */
|
|
36
|
+
meta?: MetaQuery;
|
|
37
|
+
/** Date sub-query */
|
|
38
|
+
date?: DateQuery;
|
|
39
|
+
/** Specific fields to return (optimization) */
|
|
40
|
+
fields?: 'all' | 'ids';
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Taxonomy sub-query — filter posts by term assignments.
|
|
44
|
+
*/
|
|
45
|
+
interface TaxQuery {
|
|
46
|
+
/** How to combine multiple clauses: AND = all must match, OR = any can match */
|
|
47
|
+
relation?: 'AND' | 'OR';
|
|
48
|
+
clauses: TaxQueryClause[];
|
|
49
|
+
}
|
|
50
|
+
interface TaxQueryClause {
|
|
51
|
+
taxonomy: string;
|
|
52
|
+
/** Term IDs to match */
|
|
53
|
+
termIds?: number[];
|
|
54
|
+
/** Term slugs to match */
|
|
55
|
+
termSlugs?: string[];
|
|
56
|
+
/** Operator: IN = has any of these terms, NOT IN = doesn't have these */
|
|
57
|
+
operator?: 'IN' | 'NOT IN' | 'AND';
|
|
58
|
+
/** Include children of hierarchical terms */
|
|
59
|
+
includeChildren?: boolean;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Meta sub-query — filter posts by metadata values.
|
|
63
|
+
*/
|
|
64
|
+
interface MetaQuery {
|
|
65
|
+
relation?: 'AND' | 'OR';
|
|
66
|
+
clauses: MetaQueryClause[];
|
|
67
|
+
}
|
|
68
|
+
interface MetaQueryClause {
|
|
69
|
+
key: string;
|
|
70
|
+
value?: string | number | boolean;
|
|
71
|
+
compare?: '=' | '!=' | '>' | '<' | '>=' | '<=' | 'LIKE' | 'NOT LIKE' | 'IN' | 'NOT IN' | 'EXISTS' | 'NOT EXISTS';
|
|
72
|
+
/** Cast type for comparison */
|
|
73
|
+
type?: 'CHAR' | 'NUMERIC' | 'DATE' | 'DATETIME';
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Date sub-query — filter posts by date ranges.
|
|
77
|
+
*/
|
|
78
|
+
interface DateQuery {
|
|
79
|
+
relation?: 'AND' | 'OR';
|
|
80
|
+
clauses: DateQueryClause[];
|
|
81
|
+
}
|
|
82
|
+
interface DateQueryClause {
|
|
83
|
+
year?: number;
|
|
84
|
+
month?: number;
|
|
85
|
+
day?: number;
|
|
86
|
+
after?: string | Date;
|
|
87
|
+
before?: string | Date;
|
|
88
|
+
column?: 'post_date' | 'post_modified';
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Result of a content query.
|
|
92
|
+
*/
|
|
93
|
+
interface QueryResult<T = unknown> {
|
|
94
|
+
/** The matched posts */
|
|
95
|
+
posts: T[];
|
|
96
|
+
/** Total number of matching posts (ignoring pagination) */
|
|
97
|
+
total: number;
|
|
98
|
+
/** Total number of pages */
|
|
99
|
+
totalPages: number;
|
|
100
|
+
/** Current page */
|
|
101
|
+
page: number;
|
|
102
|
+
/** Posts per page */
|
|
103
|
+
perPage: number;
|
|
104
|
+
/** Query type flags */
|
|
105
|
+
flags: QueryFlags;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Flags indicating the type of query result.
|
|
109
|
+
*/
|
|
110
|
+
interface QueryFlags {
|
|
111
|
+
isSingle: boolean;
|
|
112
|
+
isArchive: boolean;
|
|
113
|
+
isSearch: boolean;
|
|
114
|
+
is404: boolean;
|
|
115
|
+
isHome: boolean;
|
|
116
|
+
isPage: boolean;
|
|
117
|
+
isAuthor: boolean;
|
|
118
|
+
isTaxonomy: boolean;
|
|
119
|
+
isDate: boolean;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Declarative query engine for content.
|
|
124
|
+
*
|
|
125
|
+
* Accepts a `QueryParams` object and builds a type-safe SQL query
|
|
126
|
+
* via Drizzle ORM. Supports taxonomy sub-queries, meta sub-queries,
|
|
127
|
+
* date filters, full-text search, and pagination.
|
|
128
|
+
*/
|
|
129
|
+
declare class QueryEngine {
|
|
130
|
+
private db;
|
|
131
|
+
constructor(db: Database);
|
|
132
|
+
/**
|
|
133
|
+
* Execute a content query.
|
|
134
|
+
*/
|
|
135
|
+
query(params?: QueryParams): Promise<QueryResult>;
|
|
136
|
+
/**
|
|
137
|
+
* Shorthand: query a single post by ID.
|
|
138
|
+
*/
|
|
139
|
+
querySingle(id: number, postType?: string): Promise<QueryResult>;
|
|
140
|
+
private buildConditions;
|
|
141
|
+
private buildTaxConditions;
|
|
142
|
+
private buildMetaConditions;
|
|
143
|
+
private buildDateConditions;
|
|
144
|
+
private buildOrderBy;
|
|
145
|
+
private needsMetaJoin;
|
|
146
|
+
private needsTaxJoin;
|
|
147
|
+
private buildFlags;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export { type DateQuery, type DateQueryClause, type MetaQuery, type MetaQueryClause, QueryEngine, type QueryFlags, type QueryParams, type QueryResult, type TaxQuery, type TaxQueryClause };
|