@drodil/backstage-plugin-qeta-backend 3.47.2 → 3.48.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.
Files changed (45) hide show
  1. package/dist/database/DatabaseQetaStore.cjs.js +317 -1896
  2. package/dist/database/DatabaseQetaStore.cjs.js.map +1 -1
  3. package/dist/database/QetaStore.cjs.js.map +1 -1
  4. package/dist/database/stores/AnswersStore.cjs.js +253 -0
  5. package/dist/database/stores/AnswersStore.cjs.js.map +1 -0
  6. package/dist/database/stores/AttachmentsStore.cjs.js +72 -0
  7. package/dist/database/stores/AttachmentsStore.cjs.js.map +1 -0
  8. package/dist/database/stores/BaseStore.cjs.js +117 -0
  9. package/dist/database/stores/BaseStore.cjs.js.map +1 -0
  10. package/dist/database/stores/CollectionsStore.cjs.js +268 -0
  11. package/dist/database/stores/CollectionsStore.cjs.js.map +1 -0
  12. package/dist/database/stores/CommentsStore.cjs.js +136 -0
  13. package/dist/database/stores/CommentsStore.cjs.js.map +1 -0
  14. package/dist/database/stores/EntitiesStore.cjs.js +148 -0
  15. package/dist/database/stores/EntitiesStore.cjs.js.map +1 -0
  16. package/dist/database/stores/PostsStore.cjs.js +606 -0
  17. package/dist/database/stores/PostsStore.cjs.js.map +1 -0
  18. package/dist/database/stores/StatsStore.cjs.js +222 -0
  19. package/dist/database/stores/StatsStore.cjs.js.map +1 -0
  20. package/dist/database/stores/TagsStore.cjs.js +214 -0
  21. package/dist/database/stores/TagsStore.cjs.js.map +1 -0
  22. package/dist/database/stores/TemplatesStore.cjs.js +134 -0
  23. package/dist/database/stores/TemplatesStore.cjs.js.map +1 -0
  24. package/dist/database/stores/UsersStore.cjs.js +144 -0
  25. package/dist/database/stores/UsersStore.cjs.js.map +1 -0
  26. package/dist/service/PermissionManager.cjs.js +39 -28
  27. package/dist/service/PermissionManager.cjs.js.map +1 -1
  28. package/dist/service/routes/answers.cjs.js +47 -47
  29. package/dist/service/routes/answers.cjs.js.map +1 -1
  30. package/dist/service/routes/collections.cjs.js +24 -32
  31. package/dist/service/routes/collections.cjs.js.map +1 -1
  32. package/dist/service/routes/helpers.cjs.js +9 -13
  33. package/dist/service/routes/helpers.cjs.js.map +1 -1
  34. package/dist/service/routes/posts.cjs.js +33 -45
  35. package/dist/service/routes/posts.cjs.js.map +1 -1
  36. package/dist/service/routes/routeUtil.cjs.js +2 -1
  37. package/dist/service/routes/routeUtil.cjs.js.map +1 -1
  38. package/dist/service/types.cjs.js +1 -0
  39. package/dist/service/types.cjs.js.map +1 -1
  40. package/dist/service/util.cjs.js +174 -115
  41. package/dist/service/util.cjs.js.map +1 -1
  42. package/migrations/20251217_votes_count.js +48 -0
  43. package/migrations/20251218_posts_counts.js +47 -0
  44. package/migrations/20251218_x_collections_posts_count.js +26 -0
  45. package/package.json +26 -26
@@ -1,84 +1,33 @@
1
1
  'use strict';
2
2
 
3
3
  var backendPluginApi = require('@backstage/backend-plugin-api');
4
- var backstagePluginQetaCommon = require('@drodil/backstage-plugin-qeta-common');
5
- var pluginPermissionNode = require('@backstage/plugin-permission-node');
6
- var lodash = require('lodash');
7
- var tagDb = require('../tagDb.cjs.js');
4
+ var PostsStore = require('./stores/PostsStore.cjs.js');
5
+ var AnswersStore = require('./stores/AnswersStore.cjs.js');
6
+ var CommentsStore = require('./stores/CommentsStore.cjs.js');
7
+ var CollectionsStore = require('./stores/CollectionsStore.cjs.js');
8
+ var StatsStore = require('./stores/StatsStore.cjs.js');
9
+ var TagsStore = require('./stores/TagsStore.cjs.js');
10
+ var UsersStore = require('./stores/UsersStore.cjs.js');
11
+ var EntitiesStore = require('./stores/EntitiesStore.cjs.js');
12
+ var TemplatesStore = require('./stores/TemplatesStore.cjs.js');
13
+ var AttachmentsStore = require('./stores/AttachmentsStore.cjs.js');
8
14
 
9
15
  const migrationsDir = backendPluginApi.resolvePackagePath(
10
16
  "@drodil/backstage-plugin-qeta-backend",
11
17
  "migrations"
12
18
  );
13
- function isQetaFilter(filter) {
14
- return filter.hasOwnProperty("property");
15
- }
16
- function parseFilter(filter, query, db, type = "post", negate = false) {
17
- if (pluginPermissionNode.isNotCriteria(filter)) {
18
- return parseFilter(filter.not, query, db, type, !negate);
19
- }
20
- if (isQetaFilter(filter)) {
21
- const values = lodash.compact(filter.values) ?? [];
22
- let fk = "posts.id";
23
- if (type === "answer") {
24
- fk = "answers.postId";
25
- } else if (type === "collection") {
26
- fk = "collection_posts.postId";
27
- }
28
- if (filter.property === "tags") {
29
- const postIds = db("tags").leftJoin("post_tags", "tags.id", "post_tags.tagId").where("tags.tag", "in", values).select("post_tags.postId");
30
- query.whereIn(fk, postIds);
31
- return query;
32
- }
33
- if (filter.property === "tag.experts") {
34
- if (type === "post") {
35
- const postIds = db("tags").leftJoin("post_tags", "tags.id", "post_tags.tagId").leftJoin("tag_experts", "tag_experts.tagId", "tags.id").where("tag_experts.entityRef", "in", values).select("post_tags.postId");
36
- query.whereIn(fk, postIds);
37
- return query;
38
- } else if (type === "answer") {
39
- const answerIds = db("answers").leftJoin("posts", "answers.postId", "posts.id").leftJoin("post_tags", "post_tags.postId", "posts.id").leftJoin("post_tags", "tags.id", "post_tags.tagId").leftJoin("tag_experts", "tag_experts.tagId", "tags.id").where("tag_experts.entityRef", "in", values).select("answers.id");
40
- query.whereIn(fk, answerIds);
41
- return query;
42
- } else if (type === "tags") {
43
- const tagIds = db("tag_experts").leftJoin("tags", "tag_experts.tagId", "tags.id").where("tag_experts.entityRef", "in", values).select("tag_experts.tagId");
44
- query.whereIn(fk, tagIds);
45
- return query;
46
- } else if (type === "collection") {
47
- const collectionIds = db("collection_posts").leftJoin("posts", "collection_posts.postId", "posts.id").leftJoin("post_tags", "post_tags.postId", "posts.id").leftJoin("post_tags", "tags.id", "post_tags.tagId").leftJoin("tag_experts", "tag_experts.tagId", "tags.id").where("tag_experts.entityRef", "in", values).select("collection_posts.collectionId");
48
- query.whereIn(fk, collectionIds);
49
- return query;
50
- }
51
- }
52
- if (filter.property === "entityRefs") {
53
- const postIds = db("entities").leftJoin("post_entities", "entities.id", "post_entities.entityId").where("entities.entity_ref", "in", values).select("post_entities.postId");
54
- query.whereIn(fk, postIds);
55
- return query;
56
- }
57
- if (values.length === 0) {
58
- return query.whereNull(filter.property);
59
- }
60
- return query.whereIn(filter.property, values);
61
- }
62
- return query[negate ? "andWhereNot" : "andWhere"](function filterFunction() {
63
- if (pluginPermissionNode.isOrCriteria(filter)) {
64
- for (const subFilter of filter.anyOf ?? []) {
65
- this.orWhere(
66
- (subQuery) => parseFilter(subFilter, subQuery, db, type, false)
67
- );
68
- }
69
- } else if (pluginPermissionNode.isAndCriteria(filter)) {
70
- for (const subFilter of filter.allOf ?? []) {
71
- this.andWhere(
72
- (subQuery) => parseFilter(subFilter, subQuery, db, type, false)
73
- );
74
- }
75
- }
76
- });
77
- }
78
19
  class DatabaseQetaStore {
79
- constructor(db, tagDatabase) {
80
- this.db = db;
81
- this.tagDatabase = tagDatabase;
20
+ constructor(postsStore, answersStore, commentsStore, collectionsStore, statsStore, tagsStore, usersStore, entitiesStore, templatesStore, attachmentsStore) {
21
+ this.postsStore = postsStore;
22
+ this.answersStore = answersStore;
23
+ this.commentsStore = commentsStore;
24
+ this.collectionsStore = collectionsStore;
25
+ this.statsStore = statsStore;
26
+ this.tagsStore = tagsStore;
27
+ this.usersStore = usersStore;
28
+ this.entitiesStore = entitiesStore;
29
+ this.templatesStore = templatesStore;
30
+ this.attachmentsStore = attachmentsStore;
82
31
  }
83
32
  static async create({
84
33
  database,
@@ -88,1946 +37,418 @@ class DatabaseQetaStore {
88
37
  const client = await database.getClient();
89
38
  if (!database.migrations?.skip && !skipMigrations) {
90
39
  await client.migrate.latest({
91
- // nosonar
92
40
  directory: migrationsDir
93
41
  });
94
42
  }
95
- return new DatabaseQetaStore(client, tagDatabase);
43
+ const commentsStore = new CommentsStore.CommentsStore(client);
44
+ const tagsStore = new TagsStore.TagsStore(client, tagDatabase);
45
+ const entitiesStore = new EntitiesStore.EntitiesStore(client);
46
+ const usersStore = new UsersStore.UsersStore(client);
47
+ const templatesStore = new TemplatesStore.TemplatesStore(client, tagsStore, entitiesStore);
48
+ const attachmentsStore = new AttachmentsStore.AttachmentsStore(client);
49
+ const postsStore = new PostsStore.PostsStore(
50
+ client,
51
+ commentsStore,
52
+ tagsStore,
53
+ entitiesStore,
54
+ attachmentsStore,
55
+ tagDatabase
56
+ );
57
+ const answersStore = new AnswersStore.AnswersStore(
58
+ client,
59
+ commentsStore,
60
+ postsStore,
61
+ attachmentsStore
62
+ );
63
+ const collectionsStore = new CollectionsStore.CollectionsStore(
64
+ client,
65
+ postsStore,
66
+ attachmentsStore
67
+ );
68
+ const statsStore = new StatsStore.StatsStore(client);
69
+ postsStore.setAnswersStore(answersStore);
70
+ return new DatabaseQetaStore(
71
+ postsStore,
72
+ answersStore,
73
+ commentsStore,
74
+ collectionsStore,
75
+ statsStore,
76
+ tagsStore,
77
+ usersStore,
78
+ entitiesStore,
79
+ templatesStore,
80
+ attachmentsStore
81
+ );
96
82
  }
97
83
  async getPosts(user_ref, options, filters, opts) {
98
- const { includeTotal = true, includeDraftFilter = true } = opts ?? {};
99
- const query = this.getPostsBaseQuery(user_ref);
100
- if (options.type) {
101
- query.where("posts.type", options.type);
102
- }
103
- if (options.fromDate && options.toDate) {
104
- query.whereBetween("posts.created", [
105
- `${options.fromDate} 00:00:00.000+00`,
106
- `${options.toDate} 23:59:59.999+00`
107
- ]);
108
- } else if (options.fromDate) {
109
- query.where("posts.created", ">=", `${options.fromDate} 00:00:00.000+00`);
110
- } else if (options.toDate) {
111
- query.where("posts.created", "<=", `${options.toDate} 23:59:59.999+00`);
112
- }
113
- if (options.author) {
114
- if (Array.isArray(options.author)) {
115
- query.whereIn("posts.author", options.author);
116
- } else {
117
- query.where("posts.author", "=", options.author);
118
- }
119
- }
120
- if (options.excludeAuthors) {
121
- query.whereNotIn("posts.author", options.excludeAuthors);
122
- }
123
- if (options.status) {
124
- if (options.status === "draft") {
125
- query.where("posts.author", "=", user_ref);
126
- }
127
- query.where("posts.status", "=", options.status);
128
- } else if (includeDraftFilter) {
129
- query.where((q) => {
130
- q.where("posts.status", "active").orWhere((q2) => {
131
- q2.where("posts.status", "draft").where(
132
- "posts.author",
133
- "=",
134
- user_ref
135
- );
136
- });
137
- });
138
- }
139
- if (filters) {
140
- parseFilter(filters, query, this.db);
141
- }
142
- if (options.searchQuery) {
143
- this.applySearchQuery(
144
- query,
145
- ["posts.title", "posts.content"],
146
- options.searchQuery
147
- );
148
- }
149
- if (options.tags) {
150
- const tags = backstagePluginQetaCommon.filterTags(options.tags);
151
- if (options.tagsRelation === "or") {
152
- query.innerJoin("post_tags", "posts.id", "post_tags.postId");
153
- query.innerJoin("tags", "post_tags.tagId", "tags.id");
154
- query.whereIn("tags.tag", tags);
155
- } else {
156
- tags.forEach((t, i) => {
157
- query.innerJoin(`post_tags AS qt${i}`, "posts.id", `qt${i}.postId`);
158
- query.innerJoin(`tags AS t${i}`, `qt${i}.tagId`, `t${i}.id`);
159
- query.where(`t${i}.tag`, "=", t);
160
- });
161
- }
162
- }
163
- if (options.entities) {
164
- const entityValues = Array.isArray(options.entities) ? options.entities : [options.entities];
165
- if (options.entitiesRelation === "or") {
166
- query.innerJoin("post_entities", "posts.id", "post_entities.postId").innerJoin("entities", "post_entities.entityId", "entities.id").whereIn("entities.entity_ref", entityValues);
167
- } else {
168
- entityValues.forEach((t, i) => {
169
- query.innerJoin(
170
- `post_entities AS pe${i}`,
171
- "posts.id",
172
- `pe${i}.postId`
173
- );
174
- query.innerJoin(`entities AS e${i}`, `pe${i}.entityId`, `e${i}.id`);
175
- query.where(`e${i}.entity_ref`, "=", t);
176
- });
177
- }
178
- }
179
- if (options.collectionId) {
180
- query.innerJoin(
181
- "collection_posts",
182
- "posts.id",
183
- "collection_posts.postId"
184
- );
185
- query.where("collection_posts.collectionId", options.collectionId);
186
- } else if (options.orderBy === "rank") {
187
- query.innerJoin(
188
- "collection_posts",
189
- "posts.id",
190
- "collection_posts.postId"
191
- );
192
- }
193
- if (options.orderBy === "rank") {
194
- query.groupBy("rank");
195
- }
196
- if (options.noAnswers) {
197
- query.whereNull("answers.postId");
198
- }
199
- if (options.hasAnswers) {
200
- query.whereNotNull("answers.postId");
201
- }
202
- if (options.noCorrectAnswer) {
203
- query.leftJoin("answers as correct_answer", (builder) => {
204
- builder.on("posts.id", "correct_answer.postId").on("correct_answer.correct", this.db.raw("?", [true]));
205
- });
206
- query.whereNull("correct_answer.postId");
207
- }
208
- if (options.noVotes) {
209
- query.whereNull("post_votes.postId");
210
- }
211
- if (options.favorite) {
212
- query.where("user_favorite.user", "=", user_ref);
213
- query.whereNotNull("user_favorite.postId");
214
- }
215
- if (options.includeTrend || options.orderBy === "trend") {
216
- query.select(
217
- this.db.raw(
218
- `(
219
- (SELECT COALESCE(SUM(score), 0) FROM post_votes WHERE "postId" = posts.id) * 200 +
220
- (SELECT COALESCE(COUNT(*), 0) FROM answers WHERE "postId" = posts.id) * 100 +
221
- (SELECT COALESCE(COUNT(*), 0) FROM user_favorite WHERE "postId" = posts.id) * 50 +
222
- (SELECT COALESCE(COUNT(*), 0) FROM post_views WHERE "postId" = posts.id) * 10 +
223
- (SELECT COALESCE(COUNT(*), 0) FROM comments WHERE "postId" = posts.id) * 30
224
- ) /
225
- POWER(
226
- EXTRACT(EPOCH FROM (now() - posts.created)) / 172800 + 1,
227
- 1.5
228
- ) as trend`
229
- )
230
- );
231
- }
232
- if (options.ids) {
233
- query.whereIn("posts.id", options.ids);
234
- }
235
- const totalQuery = query.clone();
236
- if (options.random) {
237
- query.orderByRaw("RANDOM()");
238
- } else if (options.orderBy) {
239
- query.orderBy(options.orderBy, options.order ? options.order : "desc");
240
- } else {
241
- query.orderBy("created", "desc");
242
- }
243
- if (options.limit) {
244
- query.limit(options.limit);
245
- }
246
- if (options.offset) {
247
- query.offset(options.offset);
248
- }
249
- const results = await Promise.all([
250
- query,
251
- includeTotal ? this.db(totalQuery.as("totalQuery")).count("* as CNT").first() : void 0
252
- ]);
253
- const rows = results[0];
254
- const total = this.mapToInteger(results[1]?.CNT);
255
- return {
256
- posts: await Promise.all(
257
- rows.map(async (val) => {
258
- return this.mapPostEntity(val, user_ref, {
259
- ...opts,
260
- includeAnswers: options.includeAnswers ?? opts?.includeAnswers,
261
- includeVotes: options.includeVotes ?? opts?.includeVotes,
262
- includeEntities: options.includeEntities ?? opts?.includeEntities,
263
- includeAttachments: options.includeAttachments ?? opts?.includeAttachments,
264
- includeExperts: options.includeExperts ?? opts?.includeExperts
265
- });
266
- })
267
- ),
268
- total
269
- };
84
+ return this.postsStore.getPosts(user_ref, options, filters, opts);
270
85
  }
271
86
  async getPost(user_ref, id, recordView, options) {
272
- const rows = await this.getPostsBaseQuery(user_ref).where(
273
- "posts.id",
274
- "=",
275
- id
276
- );
277
- if (!rows || rows.length === 0) {
278
- return null;
279
- }
280
- const post = rows[0];
281
- if ((recordView === void 0 || recordView) && post.status === "active") {
282
- this.recordPostView(id, user_ref);
283
- }
284
- return await this.mapPostEntity(post, user_ref, options);
87
+ return this.postsStore.getPost(user_ref, id, recordView, options);
285
88
  }
286
89
  async getPostByAnswerId(user_ref, answerId, recordView, options) {
287
- const rows = await this.getPostsBaseQuery(user_ref).where("answers.id", "=", answerId).select("posts.*");
288
- if (!rows || rows.length === 0) {
289
- return null;
290
- }
291
- if (recordView === void 0 || recordView) {
292
- this.recordPostView(rows[0].id, user_ref);
293
- }
294
- return await this.mapPostEntity(
295
- rows[0],
90
+ return this.postsStore.getPostByAnswerId(
296
91
  user_ref,
92
+ answerId,
93
+ recordView,
297
94
  options
298
95
  );
299
96
  }
300
97
  async createPost(options) {
301
- const {
98
+ return this.postsStore.createPost(options);
99
+ }
100
+ async updatePost(options) {
101
+ return this.postsStore.updatePost(options);
102
+ }
103
+ async deletePost(id, permanently) {
104
+ return this.postsStore.deletePost(id, permanently);
105
+ }
106
+ async votePost(user_ref, postId, score) {
107
+ return this.postsStore.votePost(user_ref, postId, score);
108
+ }
109
+ async deletePostVote(user_ref, postId) {
110
+ return this.postsStore.deletePostVote(user_ref, postId);
111
+ }
112
+ async favoritePost(user_ref, postId) {
113
+ return this.postsStore.favoritePost(user_ref, postId);
114
+ }
115
+ async unfavoritePost(user_ref, postId) {
116
+ return this.postsStore.unfavoritePost(user_ref, postId);
117
+ }
118
+ async getUsersWhoFavoritedPost(postId) {
119
+ return this.postsStore.getUsersWhoFavoritedPost(postId);
120
+ }
121
+ async getAnswers(user_ref, options, filters, opts) {
122
+ return this.answersStore.getAnswers(user_ref, options, filters, opts);
123
+ }
124
+ async getAnswer(answerId, user_ref, options) {
125
+ return this.answersStore.getAnswer(answerId, user_ref, options);
126
+ }
127
+ async answerPost(user_ref, questionId, answer, created, images, anonymous, options) {
128
+ return this.answersStore.answerPost(
302
129
  user_ref,
303
- title,
304
- content,
305
- author,
130
+ questionId,
131
+ answer,
306
132
  created,
307
- tags,
308
- entities,
309
133
  images,
310
134
  anonymous,
311
- type = "question",
312
- headerImage,
313
- url,
314
- opts,
315
- status = "active"
316
- } = options;
317
- const posts = await this.db.insert(
318
- {
319
- author: author ?? user_ref,
320
- title,
321
- content,
322
- created,
323
- anonymous: anonymous ?? false,
324
- type: type ?? "question",
325
- headerImage,
326
- url,
327
- status,
328
- published: status === "active" ? created : null
329
- },
330
- ["id"]
331
- ).into("posts").returning([
332
- "id",
333
- "author",
334
- "title",
335
- "content",
336
- "created",
337
- "anonymous",
338
- "type",
339
- "status",
340
- "url"
341
- ]);
342
- await Promise.all([
343
- this.addTags(posts[0].id, tags),
344
- this.addEntities(posts[0].id, entities)
345
- ]);
346
- await this.updateAttachments(
347
- "postId",
348
- content ?? "",
349
- images ?? [],
350
- posts[0].id,
351
- headerImage
135
+ options
352
136
  );
353
- return this.mapPostEntity(posts[0], user_ref, opts);
354
- }
355
- async commentPost(post_id, user_ref, content, created, options) {
356
- await this.db.insert({
357
- author: user_ref,
358
- content,
359
- created,
360
- postId: post_id
361
- }).into("comments");
362
- return await this.getPost(user_ref, post_id, false, options);
363
- }
364
- async updatePostComment(post_id, id, user_ref, content, options) {
365
- const query = this.db("comments").where("id", "=", id).where("postId", "=", post_id);
366
- await query.update({ content, updatedBy: user_ref, updated: /* @__PURE__ */ new Date() });
367
- return this.getPost(user_ref, post_id, false, options);
368
- }
369
- async deletePostComment(post_id, id, user_ref, permanently, options) {
370
- const query = this.db("comments").where("id", "=", id).where("postId", "=", post_id);
371
- if (permanently) {
372
- await query.delete();
373
- } else {
374
- await query.update({ status: "deleted" });
375
- }
376
- return this.getPost(user_ref, post_id, false, options);
377
137
  }
378
- async commentAnswer(answer_id, user_ref, content, created, options) {
379
- await this.db.insert({
380
- author: user_ref,
381
- content,
382
- created,
383
- answerId: answer_id
384
- }).into("comments");
385
- return this.getAnswer(answer_id, user_ref, options);
386
- }
387
- async updateAnswerComment(answer_id, id, user_ref, content, options) {
388
- const query = this.db("comments").where("id", "=", id).where("answerId", "=", answer_id);
389
- await query.update({ content, updatedBy: user_ref, updated: /* @__PURE__ */ new Date() });
390
- return this.getAnswer(answer_id, user_ref, options);
391
- }
392
- async deleteAnswerComment(answer_id, id, user_ref, permanently, options) {
393
- const query = this.db("comments").where("id", "=", id).where("answerId", "=", answer_id);
394
- if (permanently) {
395
- await query.delete();
396
- } else {
397
- await query.update({ status: "deleted" });
398
- }
399
- return this.getAnswer(answer_id, user_ref, options);
400
- }
401
- async updatePost(options) {
402
- const {
403
- id,
138
+ async updateAnswer(user_ref, questionId, answerId, answer, author, images, options) {
139
+ return this.answersStore.updateAnswer(
404
140
  user_ref,
405
- title,
406
- content,
141
+ questionId,
142
+ answerId,
143
+ answer,
407
144
  author,
408
- tags,
409
- entities,
410
145
  images,
411
- headerImage,
412
- url,
413
- setUpdatedBy = true,
414
- opts,
415
- status = "active"
416
- } = options;
417
- const currentPost = await this.db("posts").select("status", "published").where("id", "=", id).first();
418
- const shouldSetPublished = currentPost && currentPost.status === "draft" && status === "active" && !currentPost.published;
419
- const query = this.db("posts").where("posts.id", "=", id);
420
- const rows = await query.update({
421
- title,
146
+ options
147
+ );
148
+ }
149
+ async deleteAnswer(id) {
150
+ return this.answersStore.deleteAnswer(id);
151
+ }
152
+ async voteAnswer(user_ref, answerId, score) {
153
+ return this.answersStore.voteAnswer(user_ref, answerId, score);
154
+ }
155
+ async deleteAnswerVote(user_ref, answerId) {
156
+ return this.answersStore.deleteAnswerVote(user_ref, answerId);
157
+ }
158
+ async markAnswerCorrect(postId, answerId) {
159
+ return this.answersStore.markAnswerCorrect(postId, answerId);
160
+ }
161
+ async markAnswerIncorrect(postId, answerId) {
162
+ return this.answersStore.markAnswerIncorrect(postId, answerId);
163
+ }
164
+ async clickPost(user_ref, postId) {
165
+ const vote = await this.postsStore.getPostVote(user_ref, postId);
166
+ const score = (vote?.score || 0) + 1;
167
+ await this.postsStore.votePost(user_ref, postId, score);
168
+ }
169
+ async commentPost(post_id, user_ref, content, created) {
170
+ await this.commentsStore.commentPost(post_id, user_ref, content, created);
171
+ return this.getPost(user_ref, post_id);
172
+ }
173
+ async commentAnswer(answer_id, user_ref, content, created) {
174
+ await this.commentsStore.commentAnswer(
175
+ answer_id,
176
+ user_ref,
422
177
  content,
423
- headerImage,
424
- author,
425
- url,
426
- updatedBy: setUpdatedBy ? user_ref : void 0,
427
- updated: setUpdatedBy ? /* @__PURE__ */ new Date() : void 0,
428
- status,
429
- published: shouldSetPublished ? /* @__PURE__ */ new Date() : void 0
430
- });
431
- if (!rows) {
432
- return null;
433
- }
434
- await Promise.all([
435
- this.addTags(id, tags, true),
436
- this.addEntities(id, entities, true)
437
- ]);
438
- await this.updateAttachments(
439
- "postId",
440
- content ?? "",
441
- images ?? [],
442
- id,
443
- headerImage
178
+ created
444
179
  );
445
- return await this.getPost(user_ref, id, false, opts);
180
+ return this.getAnswer(answer_id, user_ref);
446
181
  }
447
- async deletePost(id, permanently) {
448
- if (permanently) {
449
- const rows2 = await this.db("posts").where("id", "=", id).delete();
450
- return rows2 > 0;
451
- }
452
- const rows = await this.db("posts").where("id", "=", id).update({
453
- status: "deleted"
454
- });
455
- return rows > 0;
182
+ async updatePostComment(post_id, id, user_ref, content) {
183
+ await this.commentsStore.updatePostComment(post_id, id, user_ref, content);
184
+ return this.getPost(user_ref, post_id);
456
185
  }
457
- async answerPost(user_ref, postId, answer, created, images, anonymous, options) {
458
- const answers = await this.db.insert({
459
- postId,
460
- author: user_ref,
461
- content: answer,
462
- correct: false,
463
- created,
464
- anonymous: anonymous ?? false
465
- }).into("answers").returning("id");
466
- await this.updateAttachments(
467
- "answerId",
468
- answer,
469
- images ?? [],
470
- answers[0].id
186
+ async updateAnswerComment(answer_id, id, user_ref, content) {
187
+ await this.commentsStore.updateAnswerComment(
188
+ answer_id,
189
+ id,
190
+ user_ref,
191
+ content
471
192
  );
472
- return this.getAnswer(answers[0].id, user_ref, options);
193
+ return this.getAnswer(answer_id, user_ref);
473
194
  }
474
- async updateAnswer(user_ref, postId, answerId, answer, author, images, options) {
475
- const query = this.db("answers").where("answers.id", "=", answerId).where("answers.postId", "=", postId);
476
- const rows = await query.update({
477
- content: answer,
478
- author,
479
- updatedBy: user_ref,
480
- updated: /* @__PURE__ */ new Date()
481
- });
482
- if (!rows) {
483
- return null;
484
- }
485
- await this.updateAttachments("answerId", answer, images ?? [], answerId);
486
- return this.getAnswer(answerId, user_ref, options);
195
+ async deletePostComment(post_id, id, user_ref) {
196
+ await this.commentsStore.deletePostComment(post_id, id);
197
+ return this.getPost(user_ref, post_id);
487
198
  }
488
- async getAnswers(user_ref, options, filters, opts) {
489
- const { includeStatusFilter = true } = opts ?? {};
490
- const query = this.getAnswerBaseQuery();
491
- if (includeStatusFilter) {
492
- query.where("answers.status", "=", "active");
493
- }
494
- if (options.fromDate && options.toDate) {
495
- query.whereBetween("answers.created", [
496
- `${options.fromDate} 00:00:00.000+00`,
497
- `${options.toDate} 23:59:59.999+00`
498
- ]);
499
- }
500
- if (options.author) {
501
- query.where("answers.author", "=", options.author);
502
- }
503
- if (filters) {
504
- parseFilter(filters, query, this.db, "answer");
505
- }
506
- if (options.tags) {
507
- const tags = backstagePluginQetaCommon.filterTags(options.tags);
508
- if (options.tagsRelation === "or") {
509
- query.innerJoin("post_tags", "answers.postId", "post_tags.postId");
510
- query.innerJoin("tags", "post_tags.tagId", "tags.id");
511
- query.whereIn("tags.tag", tags);
512
- } else {
513
- tags.forEach((t, i) => {
514
- query.innerJoin(
515
- `post_tags AS at${i}`,
516
- "answers.postId",
517
- `at${i}.postId`
518
- );
519
- query.innerJoin(`tags AS t${i}`, `at${i}.tagId`, `t${i}.id`);
520
- query.where(`t${i}.tag`, "=", t);
521
- });
522
- }
523
- }
524
- if (options.entities) {
525
- if (options.entitiesRelation === "or") {
526
- query.innerJoin(
527
- "post_entities",
528
- "answers.postId",
529
- "post_entities.postId"
530
- );
531
- query.innerJoin("entities", "post_entities.entityId", "entities.id");
532
- query.whereIn("entities.entity_ref", options.entities);
533
- } else {
534
- options.entities.forEach((t, i) => {
535
- query.innerJoin(
536
- `post_entities AS pe${i}`,
537
- "answers.postId",
538
- `pe${i}.postId`
539
- );
540
- query.innerJoin(`entities AS e${i}`, `pe${i}.entityId`, `e${i}.id`);
541
- query.where(`e${i}.entity_ref`, "=", t);
542
- });
543
- }
544
- }
545
- if (options.noCorrectAnswer) {
546
- query.where("correct", "=", false);
547
- }
548
- if (options.noVotes) {
549
- query.whereNull("answer_votes.answerId");
550
- }
551
- if (options.searchQuery) {
552
- this.applySearchQuery(query, ["answers.content"], options.searchQuery);
553
- }
554
- const totalQuery = query.clone();
555
- if (options.orderBy) {
556
- query.orderBy(options.orderBy, options.order ? options.order : "desc");
557
- } else {
558
- query.orderBy("created", "desc");
559
- }
560
- if (options.limit) {
561
- query.limit(options.limit);
562
- }
563
- if (options.offset) {
564
- query.offset(options.offset);
565
- }
566
- if (options.ids) {
567
- query.whereIn("answers.id", options.ids);
568
- }
569
- const results = await Promise.all([
570
- query,
571
- this.db(totalQuery.as("totalQuery")).count("* as CNT").first()
572
- ]);
573
- const rows = results[0];
574
- const total = this.mapToInteger(results[1]?.CNT);
575
- return {
576
- answers: await Promise.all(
577
- rows.map(async (val) => {
578
- return this.mapAnswer(val, user_ref, opts);
579
- })
580
- ),
581
- total
582
- };
199
+ async deleteAnswerComment(answer_id, id, user_ref) {
200
+ await this.commentsStore.deleteAnswerComment(answer_id, id);
201
+ return this.getAnswer(answer_id, user_ref);
583
202
  }
584
- async getAnswer(answerId, user_ref, options) {
585
- const { includeStatusFilter = true } = options ?? {};
586
- const query = this.getAnswerBaseQuery().where("id", "=", answerId);
587
- if (includeStatusFilter) {
588
- query.where("answers.status", "=", "active");
589
- }
590
- const answers = await query.select();
591
- return this.mapAnswer(answers[0], user_ref, options);
203
+ async getCollections(user_ref, options, opts) {
204
+ return this.collectionsStore.getCollections(user_ref, options, opts);
592
205
  }
593
- async getComments(options, opts) {
594
- const { includeStatusFilter = true } = opts ?? {};
595
- const query = this.db("comments");
596
- if (options?.ids) {
597
- query.whereIn("id", options.ids);
598
- }
599
- if (includeStatusFilter) {
600
- query.where("status", "=", "active");
601
- }
602
- const comments = await query.select();
603
- return await Promise.all(comments.map(this.mapComment));
206
+ async getCollection(user_ref, id, options) {
207
+ return this.collectionsStore.getCollection(user_ref, id, options);
604
208
  }
605
- async getComment(commentId, opts) {
606
- const { includeStatusFilter = true } = opts ?? {};
607
- const query = this.db("comments").where("comments.id", "=", commentId);
608
- if (includeStatusFilter) {
609
- query.where("status", "=", "active");
610
- }
611
- if (opts?.postId) {
612
- query.andWhere("comments.postId", "=", opts.postId);
613
- }
614
- if (opts?.answerId) {
615
- query.andWhere("comments.answerId", "=", opts.answerId);
616
- }
617
- const comments = await query.select();
618
- if (comments.length === 0) {
619
- return null;
620
- }
621
- return await this.mapComment(comments[0]);
209
+ async createCollection(options) {
210
+ return this.collectionsStore.createCollection(
211
+ options
212
+ );
622
213
  }
623
- async deleteAnswer(id, permanently) {
624
- const query = this.db("answers").where("id", "=", id);
625
- if (permanently) {
626
- return !!await query.delete();
627
- }
628
- const rows = await query.update({ status: "deleted" });
629
- return rows > 0;
214
+ async updateCollection(options) {
215
+ return this.collectionsStore.updateCollection(options);
630
216
  }
631
- async clickPost(user_ref, postId) {
632
- const existingRows = await this.db("post_votes").where("author", "=", user_ref).where("postId", "=", postId);
633
- if (existingRows.length) {
634
- await this.db("post_votes").where("author", "=", user_ref).where("postId", "=", postId).increment("score", 1);
635
- } else {
636
- await this.db.insert({
637
- author: user_ref,
638
- postId,
639
- score: 1,
640
- timestamp: /* @__PURE__ */ new Date()
641
- }).onConflict().ignore().into("post_votes");
642
- }
217
+ async deleteCollection(id) {
218
+ return this.collectionsStore.deleteCollection(id);
643
219
  }
644
- async deletePostVote(user_ref, postId) {
645
- return !!await this.db("post_votes").where("author", "=", user_ref).where("postId", "=", postId).delete();
220
+ async addPostToCollection(user_ref, id, postId, options) {
221
+ return this.collectionsStore.addPostToCollection(
222
+ user_ref,
223
+ id,
224
+ postId,
225
+ options
226
+ );
646
227
  }
647
- async votePost(user_ref, postId, score) {
648
- await this.db("post_votes").where("author", "=", user_ref).where("postId", "=", postId).delete();
649
- const id = await this.db.insert(
650
- {
651
- author: user_ref,
652
- postId,
653
- score,
654
- timestamp: /* @__PURE__ */ new Date()
655
- },
656
- ["postId"]
657
- ).onConflict().ignore().into("post_votes");
658
- return id && id.length > 0;
228
+ async removePostFromCollection(user_ref, id, postId, options) {
229
+ return this.collectionsStore.removePostFromCollection(
230
+ user_ref,
231
+ id,
232
+ postId,
233
+ options
234
+ );
659
235
  }
660
- async favoritePost(user_ref, postId) {
661
- const id = await this.db.insert(
662
- {
663
- user: user_ref,
664
- postId
665
- },
666
- ["postId"]
667
- ).onConflict().ignore().into("user_favorite");
668
- return id && id.length > 0;
236
+ async getUsersForCollection(collectionId) {
237
+ return this.collectionsStore.getUsersForCollection(collectionId);
669
238
  }
670
- async unfavoritePost(user_ref, postId) {
671
- return !!await this.db("user_favorite").where("user", "=", user_ref).where("postId", "=", postId).delete();
239
+ async followCollection(user_ref, collectionId) {
240
+ return this.collectionsStore.followCollection(user_ref, collectionId);
672
241
  }
673
- async getUsersWhoFavoritedPost(postId) {
674
- const query = this.db("user_favorite").where("postId", "=", postId);
675
- const users = await query.select("user");
676
- return users.map((u) => u.user);
242
+ async unfollowCollection(user_ref, collectionId) {
243
+ return this.collectionsStore.unfollowCollection(user_ref, collectionId);
677
244
  }
678
- async deleteAnswerVote(user_ref, answerId) {
679
- return !!await this.db("answer_votes").where("author", "=", user_ref).where("answerId", "=", answerId).delete();
245
+ async getUserCollections(user_ref, options) {
246
+ return this.collectionsStore.getUserCollections(user_ref, options);
680
247
  }
681
- async voteAnswer(user_ref, answerId, score) {
682
- await this.db("answer_votes").where("author", "=", user_ref).where("answerId", "=", answerId).delete();
683
- const id = await this.db.insert(
684
- {
685
- author: user_ref,
686
- answerId,
687
- score,
688
- timestamp: /* @__PURE__ */ new Date()
689
- },
690
- ["answerId"]
691
- ).onConflict().ignore().into("answer_votes");
692
- return id && id.length > 0;
248
+ async getPostRank(collectionId, postId) {
249
+ return this.collectionsStore.getPostRank(collectionId, postId);
693
250
  }
694
- async markAnswerCorrect(postId, answerId) {
695
- return await this.markAnswer(postId, answerId, true);
251
+ async getTopRankedPostId(collectionId) {
252
+ return this.collectionsStore.getTopRankedPostId(collectionId);
696
253
  }
697
- async markAnswerIncorrect(postId, answerId) {
698
- return await this.markAnswer(postId, answerId, false);
254
+ async getBottomRankedPostId(collectionId) {
255
+ return this.collectionsStore.getBottomRankedPostId(collectionId);
699
256
  }
700
- async getTags(options, filters) {
701
- const { includeExperts = true } = options ?? {};
702
- const query = this.getTagBaseQuery();
703
- if (options?.noDescription) {
704
- query.whereNull("tags.description");
705
- }
706
- if (options?.searchQuery) {
707
- this.applySearchQuery(
708
- query,
709
- ["tags.tag", "tags.description"],
710
- options.searchQuery
711
- );
712
- }
713
- if (options?.ids) {
714
- query.whereIn("tags.id", options.ids);
715
- }
716
- if (filters) {
717
- parseFilter(filters, query, this.db);
718
- }
719
- const totalQuery = query.clone();
720
- if (options?.orderBy) {
721
- query.orderBy(options?.orderBy, options?.order || "desc");
722
- }
723
- if (options?.limit) {
724
- query.limit(options.limit);
725
- }
726
- if (options?.offset) {
727
- query.offset(options.offset);
728
- }
729
- const results = await Promise.all([
730
- query,
731
- this.db(totalQuery.as("totalQuery")).count("* as CNT").first()
732
- ]);
733
- const rows = results[0];
734
- const total = this.mapToInteger(results[1]?.CNT);
735
- return {
736
- total,
737
- tags: await Promise.all(
738
- rows.map(async (tag) => {
739
- return {
740
- id: tag.id,
741
- tag: tag.tag,
742
- description: tag.description,
743
- postsCount: this.mapToInteger(tag.postsCount),
744
- followerCount: this.mapToInteger(tag.followerCount),
745
- experts: includeExperts ? await this.getTagExpertsById(tag.id) : void 0
746
- };
747
- })
748
- )
749
- };
257
+ async getNextRankedPostId(collectionId, postId) {
258
+ return this.collectionsStore.getNextRankedPostId(collectionId, postId);
750
259
  }
751
- async getTagById(id) {
752
- const query = this.getTagBaseQuery();
753
- const tags = await query.where("tags.id", "=", id);
754
- if (tags.length === 0) {
755
- return null;
756
- }
757
- return {
758
- id: tags[0].id,
759
- tag: tags[0].tag,
760
- description: tags[0].description,
761
- postsCount: this.mapToInteger(tags[0].postsCount),
762
- followerCount: this.mapToInteger(tags[0].followerCount),
763
- experts: await this.getTagExpertsById(tags[0].id)
764
- };
260
+ async getPreviousRankedPostId(collectionId, postId) {
261
+ return this.collectionsStore.getPreviousRankedPostId(collectionId, postId);
765
262
  }
766
- async getTag(tag) {
767
- const query = this.getTagBaseQuery();
768
- const tags = await query.where("tags.tag", "=", tag);
769
- if (tags.length === 0) {
770
- return null;
771
- }
772
- return {
773
- id: tags[0].id,
774
- tag: tags[0].tag,
775
- description: tags[0].description,
776
- postsCount: this.mapToInteger(tags[0].postsCount),
777
- followerCount: this.mapToInteger(tags[0].followerCount),
778
- experts: await this.getTagExpertsById(tags[0].id)
779
- };
263
+ async updatePostRank(collectionId, postId, rank) {
264
+ return this.collectionsStore.updatePostRank(collectionId, postId, rank);
780
265
  }
781
- async deleteTag(id) {
782
- const query = this.db("tags").where("id", "=", id);
783
- return !!await query.delete();
266
+ async getTags(options, filters) {
267
+ return this.tagsStore.getTags(options, filters);
784
268
  }
785
269
  async getTagExperts(tags) {
786
- if (tags.length === 0) {
787
- return [];
788
- }
789
- const query = this.db("tag_experts").leftJoin("tags", "tag_experts.tagId", "tags.id").whereIn("tags.tag", tags);
790
- const resp = await query.select("entityRef");
791
- return [...new Set(resp.map((r) => r.entityRef))];
270
+ return this.tagsStore.getTagExperts(tags);
271
+ }
272
+ async getTag(tag) {
273
+ return this.tagsStore.getTag(tag);
274
+ }
275
+ async getTagById(id) {
276
+ return this.tagsStore.getTagById(id);
792
277
  }
793
278
  async createTag(tag, description, experts) {
794
- const trimmed = tag.trim();
795
- const ret = await this.db.insert({ tag: trimmed, description }).into("tags").returning(["id"]).onConflict("tag").ignore();
796
- if (ret && experts && experts.length > 0) {
797
- await this.updateTagExperts(ret[0].id, experts);
798
- }
799
- return this.getTag(trimmed);
279
+ return this.tagsStore.createTag(tag, description, experts);
800
280
  }
801
281
  async updateTag(id, description, experts) {
802
- await this.db("tags").where("tags.id", "=", id).update({ description: description ?? null });
803
- if (experts && experts.length > 0) {
804
- await this.updateTagExperts(id, experts);
805
- }
806
- return this.getTagById(id);
282
+ return this.tagsStore.updateTag(id, description, experts);
807
283
  }
808
- async getUsersCount() {
809
- const query = await this.db("unique_authors").count("author as CNT");
810
- return Number(query[0].CNT);
284
+ async deleteTag(id) {
285
+ return this.tagsStore.deleteTag(id);
286
+ }
287
+ async getEntities(options) {
288
+ return this.entitiesStore.getEntities(options);
289
+ }
290
+ async getEntity(entity_ref) {
291
+ return this.entitiesStore.getEntity(entity_ref);
811
292
  }
812
293
  async getUsers(options) {
813
- const query = this.getUserBaseQuery();
814
- if (options?.entityRefs) {
815
- query.whereIn("unique_authors.author", options.entityRefs);
816
- }
817
- const totalQuery = query.clone();
818
- if (options?.orderBy) {
819
- query.orderBy(options?.orderBy, options?.order || "desc");
820
- }
821
- if (options?.limit) {
822
- query.limit(options.limit);
823
- }
824
- if (options?.offset) {
825
- query.offset(options.offset);
826
- }
827
- const results = await Promise.all([
828
- query,
829
- this.db(totalQuery.as("totalQuery")).count("* as CNT").first()
830
- ]);
831
- const rows = results[0];
832
- const total = this.mapToInteger(results[1]?.CNT);
833
- return {
834
- total,
835
- users: rows.map((r) => ({
836
- userRef: r.author,
837
- totalViews: this.mapToInteger(r.totalViews),
838
- totalQuestions: this.mapToInteger(r.totalQuestions),
839
- totalAnswers: this.mapToInteger(r.totalAnswers),
840
- totalComments: this.mapToInteger(r.postComments) + this.mapToInteger(r.answerComments),
841
- totalVotes: this.mapToInteger(r.postVotes) + this.mapToInteger(r.answerVotes),
842
- totalArticles: this.mapToInteger(r.totalArticles),
843
- totalFollowers: this.mapToInteger(r.totalFollowers),
844
- totalLinks: this.mapToInteger(r.totalLinks)
845
- }))
846
- };
294
+ return this.usersStore.getUsers(options);
847
295
  }
848
296
  async getUser(user_ref) {
849
- const q = this.getUserBaseQuery();
850
- const rows = await q.where("author", user_ref);
851
- if (rows.length === 0) {
852
- return null;
853
- }
854
- return {
855
- userRef: rows[0].author,
856
- totalViews: this.mapToInteger(rows[0].totalViews),
857
- totalQuestions: this.mapToInteger(rows[0].totalQuestions),
858
- totalAnswers: this.mapToInteger(rows[0].totalAnswers),
859
- totalComments: this.mapToInteger(rows[0].postComments) + this.mapToInteger(rows[0].answerComments),
860
- totalVotes: this.mapToInteger(rows[0].postVotes) + this.mapToInteger(rows[0].answerVotes),
861
- totalArticles: this.mapToInteger(rows[0].totalArticles),
862
- totalFollowers: this.mapToInteger(rows[0].totalFollowers),
863
- totalLinks: this.mapToInteger(rows[0].totalLinks)
864
- };
297
+ return this.usersStore.getUser(user_ref);
865
298
  }
866
- async getUserCollections(user_ref, options) {
867
- const rows = await this.db("user_collections").where("userRef", user_ref).leftJoin(
868
- "collections",
869
- "user_collections.collectionId",
870
- "collections.id"
871
- ).select("*");
872
- return {
873
- collections: await Promise.all(
874
- rows.map(async (val) => {
875
- return this.mapCollectionEntity(val, user_ref, options);
876
- })
877
- ),
878
- count: rows.length
879
- };
299
+ async getUserTags(user_ref, filters) {
300
+ return this.tagsStore.getUserTags(user_ref, filters);
880
301
  }
881
- async getUsersForCollection(collectionId) {
882
- const users = await this.db("user_collections").where("collectionId", collectionId).select("userRef");
883
- return users.map((user) => user.userRef);
302
+ async followTag(user_ref, tag) {
303
+ return this.tagsStore.followTag(user_ref, tag);
884
304
  }
885
- async followCollection(user_ref, collectionId) {
886
- await this.db.insert(
887
- {
888
- userRef: user_ref,
889
- collectionId
890
- },
891
- ["collectionId"]
892
- ).into("user_collections");
893
- return true;
305
+ async unfollowTag(user_ref, tag) {
306
+ return this.tagsStore.unfollowTag(user_ref, tag);
894
307
  }
895
- async unfollowCollection(user_ref, collectionId) {
896
- await this.db("user_collections").where("userRef", user_ref).where("collectionId", collectionId).delete();
897
- return true;
308
+ async getUserEntities(user_ref) {
309
+ return this.entitiesStore.getUserEntities(user_ref);
898
310
  }
899
- async getUserTags(user_ref, filters) {
900
- const query = this.db("user_tags").where("userRef", user_ref).leftJoin("tags", "user_tags.tagId", "tags.id");
901
- if (filters) {
902
- parseFilter(filters, query, this.db);
903
- }
904
- const tags = await query.select("tags.tag");
905
- return {
906
- tags: tags.map((tag) => tag.tag),
907
- count: tags.length
908
- };
311
+ async followEntity(user_ref, entityRef) {
312
+ return this.entitiesStore.followEntity(user_ref, entityRef);
313
+ }
314
+ async unfollowEntity(user_ref, entityRef) {
315
+ return this.entitiesStore.unfollowEntity(user_ref, entityRef);
909
316
  }
910
317
  async getUsersForTags(tags) {
911
- if (!tags || tags.length === 0) {
912
- return [];
913
- }
914
- const followingUsersQuery = this.db("user_tags").leftJoin("tags", "user_tags.tagId", "tags.id").whereIn("tags.tag", tags).select("userRef");
915
- const [followingUsers, experts] = await Promise.all([
916
- followingUsersQuery,
917
- this.getTagExperts(tags)
918
- ]);
919
- return [
920
- .../* @__PURE__ */ new Set([...followingUsers.map((user) => user.userRef), ...experts])
921
- ];
318
+ return this.tagsStore.getUsersForTags(tags);
922
319
  }
923
- async followTag(user_ref, tag) {
924
- const tagId = await this.db("tags").where("tag", tag).select("id").first();
925
- if (!tagId) {
926
- return false;
927
- }
928
- await this.db.insert(
929
- {
930
- userRef: user_ref,
931
- tagId: tagId.id
932
- },
933
- ["tagId"]
934
- ).into("user_tags");
935
- return true;
320
+ async getUsersForEntities(entityRefs) {
321
+ return this.entitiesStore.getUsersForEntities(entityRefs);
936
322
  }
937
- async unfollowTag(user_ref, tag) {
938
- const tagId = await this.db("tags").where("tag", tag).select("id").first();
939
- if (!tagId) {
940
- return false;
941
- }
942
- await this.db("user_tags").where("userRef", user_ref).where("tagId", tagId.id).delete();
943
- return true;
323
+ async getFollowedUsers(user_ref) {
324
+ return this.usersStore.getFollowedUsers(user_ref);
944
325
  }
945
- async getEntities(options) {
946
- const query = this.getEntitiesBaseQuery();
947
- if (options?.entityRefs) {
948
- query.whereIn("entities.entity_ref", options.entityRefs);
949
- }
950
- const totalQuery = query.clone();
951
- if (options?.orderBy) {
952
- query.orderBy(options?.orderBy, options?.order || "desc");
953
- }
954
- if (options?.limit) {
955
- query.limit(options.limit);
956
- }
957
- if (options?.offset) {
958
- query.offset(options.offset);
959
- }
960
- const results = await Promise.all([
961
- query,
962
- this.db(totalQuery.as("totalQuery")).count("* as CNT").first()
963
- ]);
964
- const rows = results[0];
965
- const total = this.mapToInteger(results[1]?.CNT);
966
- return {
967
- total,
968
- entities: rows.map((entity) => {
969
- return {
970
- id: entity.id,
971
- entityRef: entity.entity_ref,
972
- postsCount: this.mapToInteger(entity.postsCount),
973
- followerCount: this.mapToInteger(entity.followerCount)
974
- };
975
- })
976
- };
326
+ async followUser(user_ref, followedUserRef) {
327
+ return this.usersStore.followUser(user_ref, followedUserRef);
977
328
  }
978
- async getEntity(entity_ref) {
979
- const query = this.getEntitiesBaseQuery();
980
- const rows = await query.where("entity_ref", "=", entity_ref);
981
- if (rows.length === 0) {
982
- return null;
983
- }
984
- return {
985
- id: rows[0].id,
986
- entityRef: rows[0].entity_ref,
987
- postsCount: this.mapToInteger(rows[0].postsCount),
988
- followerCount: this.mapToInteger(rows[0].followerCount)
989
- };
329
+ async unfollowUser(user_ref, followedUserRef) {
330
+ return this.usersStore.unfollowUser(user_ref, followedUserRef);
990
331
  }
991
- async getUserEntities(user_ref) {
992
- const entities = await this.db("user_entities").where("userRef", user_ref).select("entityRef");
993
- return {
994
- entityRefs: entities.map((e) => e.entityRef),
995
- count: entities.length
996
- };
332
+ async getGlobalStats() {
333
+ return this.statsStore.getGlobalStats();
997
334
  }
998
- async getUsersForEntities(entityRefs) {
999
- if (!entityRefs || entityRefs.length === 0) {
1000
- return [];
1001
- }
1002
- const users = await this.db("user_entities").whereIn("entityRef", entityRefs).select("userRef");
1003
- return users.map((user) => user.userRef);
335
+ async getUserStats(user_ref) {
336
+ return this.statsStore.getUserStats(user_ref);
1004
337
  }
1005
- async followEntity(user_ref, entityRef) {
1006
- await this.db.insert({
1007
- userRef: user_ref,
1008
- entityRef
1009
- }).into("user_entities");
1010
- return true;
338
+ async getTemplates() {
339
+ return this.templatesStore.getTemplates();
1011
340
  }
1012
- async unfollowEntity(user_ref, entityRef) {
1013
- await this.db("user_entities").where("userRef", user_ref).where("entityRef", entityRef).delete();
1014
- return true;
341
+ async getTemplate(id) {
342
+ return this.templatesStore.getTemplate(id);
1015
343
  }
1016
- async getFollowedUsers(user_ref) {
1017
- const entities = await this.db("user_users").where("userRef", user_ref).select("followedUserRef");
1018
- return {
1019
- followedUserRefs: entities.map((e) => e.followedUserRef),
1020
- count: entities.length
1021
- };
344
+ async createTemplate(options) {
345
+ return this.templatesStore.createTemplate(options);
1022
346
  }
1023
- async getFollowingUsers(user_ref) {
1024
- const users = await this.db("user_users").where("followedUserRef", user_ref).select("userRef");
1025
- return users.map((user) => user.userRef);
347
+ async deleteTemplate(id) {
348
+ return this.templatesStore.deleteTemplate(id);
1026
349
  }
1027
- async followUser(user_ref, followedUserRef) {
1028
- await this.db.insert({
1029
- userRef: user_ref,
1030
- followedUserRef
1031
- }).into("user_users");
1032
- return true;
350
+ async updateTemplate(options) {
351
+ return this.templatesStore.updateTemplate(options);
1033
352
  }
1034
- async unfollowUser(user_ref, followedUserRef) {
1035
- await this.db("user_users").where("userRef", user_ref).where("followedUserRef", followedUserRef).delete();
1036
- return true;
353
+ async getAIAnswer(postId) {
354
+ return this.postsStore.getAIAnswer(postId);
355
+ }
356
+ async saveAIAnswer(postId, response) {
357
+ return this.postsStore.saveAIAnswer(postId, response);
358
+ }
359
+ async deleteAIAnswer(postId) {
360
+ return this.postsStore.deleteAIAnswer(postId);
361
+ }
362
+ async postAttachment({
363
+ uuid,
364
+ locationType,
365
+ locationUri,
366
+ extension,
367
+ mimeType,
368
+ binaryImage,
369
+ path,
370
+ creator
371
+ }) {
372
+ return this.attachmentsStore.postAttachment({
373
+ uuid,
374
+ locationType,
375
+ locationUri,
376
+ extension,
377
+ mimeType,
378
+ binaryImage,
379
+ path,
380
+ creator
381
+ });
382
+ }
383
+ async getAttachment(uuid) {
384
+ return this.attachmentsStore.getAttachment(uuid);
385
+ }
386
+ async deleteAttachment(uuid) {
387
+ return this.attachmentsStore.deleteAttachment(uuid);
1037
388
  }
389
+ async getDeletableAttachments(dayLimit) {
390
+ return this.attachmentsStore.getDeletableAttachments(dayLimit);
391
+ }
392
+ // Stats
1038
393
  async getMostUpvotedPosts({
1039
394
  author,
1040
395
  options
1041
396
  }) {
1042
- const query = this.db("posts as q").sum("qv.score as total").select("q.author").join("post_votes as qv", "q.id", "qv.postId").groupBy("q.author").orderBy("total", "desc").where("anonymous", "!=", true);
1043
- if (author) {
1044
- query.where("q.author", "=", author);
1045
- }
1046
- if (options?.period) {
1047
- query.where("q.created", ">", options.period);
1048
- }
1049
- if (options?.type) {
1050
- query.where("q.type", "=", options.type);
1051
- }
1052
- if (options?.limit) {
1053
- query.limit(options.limit);
1054
- }
1055
- const rows = await query;
1056
- rows.map((row, index) => {
1057
- row.position = index + 1;
1058
- });
1059
- return rows;
397
+ return this.statsStore.getMostUpvotedPosts({ author, options });
1060
398
  }
1061
399
  async getTotalPosts({
1062
400
  author,
1063
401
  options
1064
402
  }) {
1065
- const query = this.db("posts as q").count("q.id as total").select("q.author").groupBy("author").orderBy("total", "desc").where("q.anonymous", "!=", true);
1066
- if (author) {
1067
- query.where("q.author", "=", author);
1068
- }
1069
- if (options?.period) {
1070
- query.where("q.created", ">", options.period);
1071
- }
1072
- if (options?.type) {
1073
- query.where("q.type", "=", options.type);
1074
- }
1075
- if (options?.limit) {
1076
- query.limit(options.limit);
1077
- }
1078
- const rows = await query;
1079
- if (!author) {
1080
- rows.map((row, index) => {
1081
- row.position = index + 1;
1082
- });
1083
- }
1084
- return rows;
403
+ return this.statsStore.getTotalPosts({ author, options });
1085
404
  }
1086
405
  async getMostUpvotedAnswers({
1087
406
  author,
1088
407
  options
1089
408
  }) {
1090
- const query = this.db("answers as a").sum("av.score as total").select("a.author").join("answer_votes as av", "a.id", "av.answerId").groupBy("a.author").orderBy("total", "desc").where("a.anonymous", "!=", true);
1091
- if (author) {
1092
- query.where("a.author", "=", author);
1093
- }
1094
- if (options?.period) {
1095
- query.where("a.created", ">", options.period);
1096
- }
1097
- if (options?.limit) {
1098
- query.limit(options.limit);
1099
- }
1100
- const rows = await query;
1101
- rows.map((row, index) => {
1102
- row.position = index + 1;
1103
- });
1104
- return rows;
409
+ return this.statsStore.getMostUpvotedAnswers({ author, options });
1105
410
  }
1106
411
  async getMostUpvotedCorrectAnswers({
1107
412
  author,
1108
413
  options
1109
414
  }) {
1110
- const query = this.db("answers as a").sum("av.score as total").select("a.author").join("answer_votes as av", "a.id", "av.answerId").groupBy("a.author").orderBy("total", "desc").where("a.correct", "=", true).where("a.anonymous", "!=", true);
1111
- if (author) {
1112
- query.where("a.author", "=", author);
1113
- }
1114
- if (options?.period) {
1115
- query.where("a.created", ">", options.period);
1116
- }
1117
- if (options?.limit) {
1118
- query.limit(options.limit);
1119
- }
1120
- const rows = await query;
1121
- rows.map((row, index) => {
1122
- row.position = index + 1;
1123
- });
1124
- return rows;
415
+ return this.statsStore.getMostUpvotedCorrectAnswers({ author, options });
1125
416
  }
1126
417
  async getTotalAnswers({
1127
418
  author,
1128
419
  options
1129
420
  }) {
1130
- const query = this.db("answers as a").count("a.id as total").select("a.author").groupBy("author").orderBy("total", "desc").where("a.anonymous", "!=", true);
1131
- if (author) {
1132
- query.where("a.author", "=", author);
1133
- }
1134
- if (options?.period) {
1135
- query.where("a.created", ">", options.period);
1136
- }
1137
- if (options?.limit) {
1138
- query.limit(options.limit);
1139
- }
1140
- const rows = await query;
1141
- if (!author) {
1142
- rows.map((row, index) => {
1143
- row.position = index + 1;
1144
- });
1145
- }
1146
- return rows;
1147
- }
1148
- async postAttachment({
1149
- uuid,
1150
- locationType,
1151
- locationUri,
1152
- extension,
1153
- mimeType,
1154
- binaryImage,
1155
- path,
1156
- creator,
1157
- postId,
1158
- answerId,
1159
- collectionId
1160
- }) {
1161
- const attachments = await this.db.insert(
1162
- {
1163
- uuid,
1164
- locationType,
1165
- locationUri,
1166
- extension,
1167
- mimeType,
1168
- path,
1169
- binaryImage,
1170
- creator,
1171
- created: /* @__PURE__ */ new Date(),
1172
- postId,
1173
- answerId,
1174
- collectionId
1175
- },
1176
- ["id", "uuid", "locationUri", "locationType"]
1177
- ).into("attachments");
1178
- return attachments[0];
1179
- }
1180
- async deleteAttachment(uuid) {
1181
- const query = this.db("attachments").where("uuid", "=", uuid);
1182
- return !!await query.delete();
1183
- }
1184
- async getAttachment(uuid) {
1185
- const rawAttachment = await this.db("attachments").where("uuid", "=", uuid).first();
1186
- if (!rawAttachment) {
1187
- return void 0;
1188
- }
1189
- return this.mapAttachment(rawAttachment);
1190
- }
1191
- async getDeletableAttachments(dayLimit) {
1192
- const now = /* @__PURE__ */ new Date();
1193
- now.setDate(now.getDate() - dayLimit);
1194
- return this.db("attachments").whereNull("postId").whereNull("answerId").whereNull("collectionId").where(`created`, "<=", now).select();
1195
- }
1196
- async getTotalViews(user_ref, lastDays, excludeUser) {
1197
- const now = /* @__PURE__ */ new Date();
1198
- if (lastDays) {
1199
- now.setDate(now.getDate() - lastDays);
1200
- }
1201
- const postViewsQuery = this.db("post_views").innerJoin("posts", "post_views.postId", "posts.id").where("posts.author", user_ref);
1202
- if (lastDays) {
1203
- postViewsQuery.where("posts.created", ">", now);
1204
- }
1205
- if (excludeUser) {
1206
- postViewsQuery.where("post_views.author", "!=", user_ref);
1207
- }
1208
- const answerViewsQuery = this.db("post_views").innerJoin("answers", "post_views.postId", "answers.postId").innerJoin("posts", "post_views.postId", "posts.id").where("answers.author", user_ref).whereNot("posts.author", user_ref);
1209
- if (lastDays) {
1210
- answerViewsQuery.where("answers.created", ">", now);
1211
- }
1212
- if (excludeUser) {
1213
- answerViewsQuery.where("post_views.author", "!=", user_ref);
1214
- }
1215
- const postViews = await postViewsQuery.count("* as total");
1216
- const answerViews = await answerViewsQuery.count("* as total");
1217
- return Number(postViews[0].total) + Number(answerViews[0].total);
421
+ return this.statsStore.getTotalAnswers({ author, options });
1218
422
  }
1219
- async saveUserStats(user, date) {
1220
- await this.db("user_stats").insert({
1221
- ...user,
1222
- date
1223
- }).onConflict(["userRef", "date"]).merge();
423
+ async getCount(table, filters) {
424
+ return this.statsStore.getCount(table, filters);
1224
425
  }
1225
426
  async saveGlobalStats(date) {
1226
- await this.db("global_stats").insert({
1227
- totalQuestions: await this.getCount("posts", { type: "question" }),
1228
- totalAnswers: await this.getCount("answers"),
1229
- totalUsers: await this.getUsersCount(),
1230
- totalTags: await this.getCount("tags"),
1231
- totalViews: await this.getCount("post_views"),
1232
- totalComments: await this.getCount("comments"),
1233
- totalVotes: await this.getCount("post_votes") + await this.getCount("answer_votes"),
1234
- totalArticles: await this.getCount("posts", { type: "article" }),
1235
- totalLinks: await this.getCount("posts", { type: "link" }),
1236
- date
1237
- }).onConflict(["date"]).merge();
427
+ return this.statsStore.saveGlobalStats(date);
1238
428
  }
1239
- async getGlobalStats() {
1240
- return this.db("global_stats").select("*").orderBy("date", "desc");
429
+ async saveUserStats(user, date) {
430
+ return this.statsStore.saveUserStats(user, date);
1241
431
  }
1242
- async getUserStats(user_ref) {
1243
- return this.db("user_stats").where("userRef", user_ref).select("*").orderBy("date", "desc");
432
+ async getTotalViews(user_ref, lastDays, excludeUser) {
433
+ return this.statsStore.getTotalViews(user_ref, lastDays, excludeUser);
1244
434
  }
1245
435
  async cleanStats(days, date) {
1246
- const now = new Date(date);
1247
- now.setDate(now.getDate() - days);
1248
- await this.db("user_stats").where("date", "<=", now).delete();
1249
- await this.db("global_stats").where("date", "<=", now).delete();
1250
- }
1251
- async getCount(table, filters) {
1252
- const query = this.db(table);
1253
- if (filters?.author) {
1254
- query.where("author", filters.author);
1255
- }
1256
- if (filters?.type) {
1257
- query.where("type", filters.type);
1258
- }
1259
- const result = await query.count("* as total").first();
1260
- return this.mapToInteger(result?.total);
1261
- }
1262
- async getCollections(user_ref, options, opts) {
1263
- const query = this.getCollectionsBaseQuery();
1264
- if (options.owner) {
1265
- query.where("owner", options.owner);
1266
- }
1267
- if (options.fromDate && options.toDate) {
1268
- query.whereBetween("collections.created", [
1269
- `${options.fromDate} 00:00:00.000+00`,
1270
- `${options.toDate} 23:59:59.999+00`
1271
- ]);
1272
- }
1273
- if (options.searchQuery) {
1274
- this.applySearchQuery(
1275
- query,
1276
- ["collections.title", "collections.description"],
1277
- options.searchQuery
1278
- );
1279
- }
1280
- if (options.tags) {
1281
- const tags = backstagePluginQetaCommon.filterTags(options.tags);
1282
- if (options.tagsRelation === "or") {
1283
- query.leftJoin(
1284
- "post_tags",
1285
- "collection_posts.postId",
1286
- "post_tags.postId"
1287
- );
1288
- query.leftJoin("tags", "post_tags.tagId", "tags.id");
1289
- query.where((qb) => {
1290
- qb.whereIn("tags.tag", tags).orWhereNull("collection_posts.postId");
1291
- });
1292
- } else {
1293
- tags.forEach((t, i) => {
1294
- query.innerJoin(
1295
- `post_tags AS qt${i}`,
1296
- "collection_posts.postId",
1297
- `qt${i}.postId`
1298
- );
1299
- query.innerJoin(`tags AS t${i}`, `qt${i}.tagId`, `t${i}.id`);
1300
- query.where(`t${i}.tag`, "=", t);
1301
- });
1302
- }
1303
- }
1304
- if (options.entities) {
1305
- const entityValues = Array.isArray(options.entities) ? options.entities : [options.entities];
1306
- if (options.entitiesRelation === "or") {
1307
- query.leftJoin(
1308
- "post_entities",
1309
- "collection_posts.postId",
1310
- "post_entities.postId"
1311
- );
1312
- query.leftJoin("entities", "post_entities.entityId", "entities.id");
1313
- query.where((qb) => {
1314
- qb.whereIn("entities.entity_ref", entityValues).orWhereNull(
1315
- "collection_posts.postId"
1316
- );
1317
- });
1318
- } else {
1319
- entityValues.forEach((t, i) => {
1320
- query.innerJoin(
1321
- `post_entities AS pe${i}`,
1322
- "collection_posts.postId",
1323
- `pe${i}.postId`
1324
- );
1325
- query.innerJoin(`entities AS e${i}`, `pe${i}.entityId`, `e${i}.id`);
1326
- query.where(`e${i}.entity_ref`, "=", t);
1327
- });
1328
- }
1329
- }
1330
- if (options.ids) {
1331
- query.whereIn("collections.id", options.ids);
1332
- }
1333
- if (opts?.filters) {
1334
- parseFilter(opts.filters, query, this.db);
1335
- }
1336
- const totalQuery = query.clone();
1337
- if (options.orderBy) {
1338
- query.orderBy(options.orderBy, options.order || "desc");
1339
- } else {
1340
- query.orderBy("created", "desc");
1341
- }
1342
- if (options.limit) {
1343
- query.limit(options.limit);
1344
- }
1345
- if (options.offset) {
1346
- query.offset(options.offset);
1347
- }
1348
- const results = await Promise.all([
1349
- query,
1350
- this.db(totalQuery.as("totalQuery")).count("* as CNT").first()
1351
- ]);
1352
- const rows = results[0];
1353
- const total = this.mapToInteger(results[1]?.CNT);
1354
- return {
1355
- collections: await Promise.all(
1356
- rows.map(async (val) => {
1357
- return this.mapCollectionEntity(val, user_ref, {
1358
- ...opts,
1359
- includePosts: options.includePosts ?? opts?.includePosts,
1360
- includeExperts: options.includeExperts ?? opts?.includeExperts
1361
- });
1362
- })
1363
- ),
1364
- total
1365
- };
1366
- }
1367
- async getCollection(user_ref, id, options) {
1368
- const collections = await this.db("collections").where("id", "=", id);
1369
- if (collections.length === 0) {
1370
- return null;
1371
- }
1372
- return this.mapCollectionEntity(collections[0], user_ref, options);
1373
- }
1374
- async createCollection(options) {
1375
- const { user_ref, title, description, created, images, headerImage, opts } = options;
1376
- const collections = await this.db.insert(
1377
- {
1378
- owner: user_ref,
1379
- title,
1380
- description,
1381
- created,
1382
- headerImage
1383
- },
1384
- ["id"]
1385
- ).into("collections").returning(["id", "title", "description", "created", "headerImage"]);
1386
- await this.updateAttachments(
1387
- "collectionId",
1388
- description ?? "",
1389
- images ?? [],
1390
- collections[0].id,
1391
- headerImage
1392
- );
1393
- return this.mapCollectionEntity(collections[0], user_ref, opts);
1394
- }
1395
- async updateCollection(options) {
1396
- const { id, user_ref, title, description, images, headerImage, opts } = options;
1397
- const query = this.db("collections").where("collections.id", "=", id);
1398
- const rows = await query.update({
1399
- title,
1400
- description,
1401
- headerImage
1402
- });
1403
- if (!rows) {
1404
- return null;
1405
- }
1406
- await this.updateAttachments(
1407
- "collectionId",
1408
- description ?? "",
1409
- images ?? [],
1410
- id,
1411
- headerImage
1412
- );
1413
- return await this.getCollection(user_ref, id, opts);
1414
- }
1415
- async deleteCollection(id) {
1416
- const query = this.db("collections").where("id", "=", id);
1417
- return !!await query.delete();
1418
- }
1419
- async addPostToCollection(user_ref, id, postId, options) {
1420
- await this.db.insert({
1421
- collectionId: id,
1422
- postId
1423
- }).into("collection_posts").onConflict().ignore();
1424
- return await this.getCollection(user_ref, id, options);
1425
- }
1426
- async removePostFromCollection(user_ref, id, postId, options) {
1427
- await this.db("collection_posts").where("collectionId", id).where("postId", postId).delete();
1428
- return await this.getCollection(user_ref, id, options);
1429
- }
1430
- async getTemplates() {
1431
- const templates = await this.db("templates").select("*");
1432
- return {
1433
- templates: await Promise.all(
1434
- templates.map((t) => this.mapTemplateEntity(t))
1435
- ),
1436
- total: templates.length
1437
- };
1438
- }
1439
- async createTemplate(options) {
1440
- const {
1441
- title,
1442
- questionTitle,
1443
- questionContent,
1444
- description,
1445
- tags,
1446
- entities
1447
- } = options;
1448
- const templates = await this.db.insert(
1449
- {
1450
- title,
1451
- description,
1452
- questionTitle,
1453
- questionContent
1454
- },
1455
- ["id"]
1456
- ).into("templates").returning([
1457
- "id",
1458
- "title",
1459
- "description",
1460
- "questionTitle",
1461
- "questionContent"
1462
- ]);
1463
- await Promise.all([
1464
- this.addTags(templates[0].id, tags, true, "template_tags", "templateId"),
1465
- this.addEntities(
1466
- templates[0].id,
1467
- entities,
1468
- true,
1469
- "template_entities",
1470
- "templateId"
1471
- )
1472
- ]);
1473
- return this.mapTemplateEntity(templates[0]);
1474
- }
1475
- async getTemplate(id) {
1476
- const templates = await this.db("templates").where("id", "=", id);
1477
- if (templates.length === 0) {
1478
- return null;
1479
- }
1480
- return this.mapTemplateEntity(templates[0]);
1481
- }
1482
- async deleteTemplate(id) {
1483
- const query = this.db("templates").where("id", "=", id);
1484
- return !!await query.delete();
1485
- }
1486
- async updateTemplate(options) {
1487
- const {
1488
- id,
1489
- title,
1490
- description,
1491
- questionTitle,
1492
- questionContent,
1493
- tags,
1494
- entities
1495
- } = options;
1496
- const query = this.db("templates").where("templates.id", "=", id);
1497
- const rows = await query.update({
1498
- title,
1499
- description,
1500
- questionTitle,
1501
- questionContent
1502
- });
1503
- if (!rows) {
1504
- return null;
1505
- }
1506
- await Promise.all([
1507
- this.addTags(id, tags, true, "template_tags", "templateId"),
1508
- this.addEntities(id, entities, true, "template_entities", "templateId")
1509
- ]);
1510
- return await this.getTemplate(id);
1511
- }
1512
- async getAIAnswer(postId) {
1513
- const row = await this.db("post_ai_answers").where("postId", postId).select().first();
1514
- if (!row) {
1515
- return null;
1516
- }
1517
- return {
1518
- answer: row.answer,
1519
- created: row.created
1520
- };
1521
- }
1522
- async saveAIAnswer(postId, response) {
1523
- await this.db.insert({
1524
- postId,
1525
- answer: response.answer,
1526
- created: /* @__PURE__ */ new Date()
1527
- }).into("post_ai_answers").onConflict().ignore();
436
+ return this.statsStore.cleanStats(days, date);
1528
437
  }
1529
- async deleteAIAnswer(postId) {
1530
- return !!await this.db("post_ai_answers").where("postId", postId).delete();
1531
- }
1532
- async getPostRank(collectionId, postId) {
1533
- const rank = await this.db("collection_posts").where("collectionId", collectionId).where("postId", postId).select("rank").first();
1534
- return rank?.rank ?? null;
1535
- }
1536
- async getTopRankedPostId(collectionId) {
1537
- const post = await this.db("collection_posts").where("collectionId", collectionId).orderBy("rank", "desc").limit(1).select(["postId", "rank"]);
1538
- return post[0] ? { postId: post[0].postId, rank: post[0].rank } : null;
1539
- }
1540
- async getBottomRankedPostId(collectionId) {
1541
- const post = await this.db("collection_posts").where("collectionId", collectionId).orderBy("rank", "asc").limit(1).select(["postId", "rank"]);
1542
- return post[0] ? { postId: post[0].postId, rank: post[0].rank } : null;
1543
- }
1544
- async getNextRankedPostId(collectionId, postId) {
1545
- const rank = await this.getPostRank(collectionId, postId);
1546
- if (rank === null) {
1547
- return null;
1548
- }
1549
- const post = await this.db("collection_posts").where("collectionId", collectionId).where("rank", ">", rank).orderBy("rank", "asc").select(["postId", "rank"]).first();
1550
- return post ? { postId: post.postId, rank: post.rank } : null;
1551
- }
1552
- async getPreviousRankedPostId(collectionId, postId) {
1553
- const rank = await this.getPostRank(collectionId, postId);
1554
- if (rank === null) {
1555
- return null;
1556
- }
1557
- const post = await this.db("collection_posts").where("collectionId", collectionId).where("rank", "<", rank).orderBy("rank", "desc").select(["postId", "rank"]).first();
1558
- return post ? { postId: post.postId, rank: post.rank } : null;
438
+ async getUsersCount() {
439
+ return this.usersStore.getUsersCount();
1559
440
  }
1560
- async updatePostRank(collectionId, postId, rank) {
1561
- await this.db("collection_posts").where("collectionId", collectionId).where("postId", postId).update({ rank });
441
+ async getFollowingUsers(user_ref) {
442
+ return this.usersStore.getFollowingUsers(user_ref);
1562
443
  }
1563
444
  async getEntityLinks() {
1564
- const rows = await this.db("post_entities").leftJoin("entities", "post_entities.entityId", "entities.id").leftJoin("posts", "post_entities.postId", "posts.id").whereNotNull("posts.url").where("posts.status", "active").where("posts.type", "link").whereNotNull("entities.entity_ref").select("entities.entity_ref as entityRef", "posts.title", "posts.url");
1565
- const entityLinksMap = /* @__PURE__ */ new Map();
1566
- for (const row of rows) {
1567
- const entityRef = row.entityRef;
1568
- const link = {
1569
- title: row.title,
1570
- url: row.url,
1571
- type: "qeta"
1572
- };
1573
- if (!entityLinksMap.has(entityRef)) {
1574
- entityLinksMap.set(entityRef, []);
1575
- }
1576
- entityLinksMap.get(entityRef).push(link);
1577
- }
1578
- return Array.from(entityLinksMap.entries()).map(([entityRef, links]) => ({
1579
- entityRef,
1580
- links
1581
- }));
1582
- }
1583
- async getTagExpertsById(id) {
1584
- const rows = await this.db("tag_experts").where("tagId", id).select("entityRef");
1585
- return rows.map((r) => r.entityRef);
1586
- }
1587
- async updateTagExperts(id, experts) {
1588
- await this.db("tag_experts").where("tagId", id).delete();
1589
- await this.db.insert(experts.map((e) => ({ tagId: id, entityRef: e }))).into("tag_experts").onConflict(["tagId", "entityRef"]).merge();
1590
- }
1591
- getEntitiesBaseQuery() {
1592
- const entityId = this.db.ref("entities.id");
1593
- const entityRef = this.db.ref("entities.entity_ref");
1594
- const postsCount = this.db("post_entities").where("post_entities.entityId", entityId).count("*").as("postsCount");
1595
- const followerCount = this.db("user_entities").where("user_entities.entityRef", entityRef).count("*").as("followerCount");
1596
- return this.db("entities").rightJoin("post_entities", "entities.id", "post_entities.entityId").orderBy("postsCount", "desc").select("id", "entity_ref", postsCount, followerCount).groupBy("entities.id");
1597
- }
1598
- getTagBaseQuery() {
1599
- const tagRef = this.db.ref("tags.id");
1600
- const postsCount = this.db("post_tags").where("post_tags.tagId", tagRef).count("*").as("postsCount");
1601
- const followerCount = this.db("user_tags").where("user_tags.tagId", tagRef).count("*").as("followerCount");
1602
- return this.db("tags").leftJoin("post_tags", "tags.id", "post_tags.tagId").orderBy("postsCount", "desc").select("id", "tag", "description", postsCount, followerCount).groupBy("tags.id");
1603
- }
1604
- getUserBaseQuery() {
1605
- if (this.db.client.config.client !== "pg") {
1606
- return this.db("posts").select([
1607
- "author",
1608
- this.db.raw("0 as totalViews"),
1609
- this.db.raw("0 as totalQuestions"),
1610
- this.db.raw("0 as totalArticles"),
1611
- this.db.raw("0 as totalLinks"),
1612
- this.db.raw("0 as totalAnswers"),
1613
- this.db.raw("0 as answerComments"),
1614
- this.db.raw("0 as postComments"),
1615
- this.db.raw("0 as answerVotes"),
1616
- this.db.raw("0 as postVotes"),
1617
- this.db.raw("0 as totalFollowers")
1618
- ]).distinct();
1619
- }
1620
- const authorRef = this.db.ref("unique_authors.author");
1621
- const views = this.db("post_views").where("post_views.author", authorRef).count("*").as("totalViews");
1622
- const questions = this.db("posts").where("posts.author", authorRef).where("posts.type", "question").count("*").as("totalQuestions");
1623
- const articles = this.db("posts").where("posts.author", authorRef).where("posts.type", "article").count("*").as("totalArticles");
1624
- const links = this.db("posts").where("posts.author", authorRef).where("posts.type", "link").count("*").as("totalLinks");
1625
- const answers = this.db("answers").where("answers.author", authorRef).count("*").as("totalAnswers");
1626
- const comments = this.db("comments").where("comments.author", authorRef).count("*").as("comments");
1627
- const aVotes = this.db("answer_votes").where("answer_votes.author", authorRef).count("*").as("answerVotes");
1628
- const pVotes = this.db("post_votes").where("post_votes.author", authorRef).count("*").as("postVotes");
1629
- const followers = this.db("user_users").where("user_users.followedUserRef", authorRef).count("*").as("totalFollowers");
1630
- return this.db("unique_authors").select(
1631
- "author",
1632
- views,
1633
- questions,
1634
- answers,
1635
- articles,
1636
- links,
1637
- comments,
1638
- pVotes,
1639
- aVotes,
1640
- followers
1641
- );
445
+ return this.entitiesStore.getEntityLinks();
1642
446
  }
1643
- /**
1644
- * Maps string or number value to integer. This is due to fact that postgres returns
1645
- * strings instead numbers for count and sum functions.
1646
- * @param val
1647
- */
1648
- mapToInteger = (val) => {
1649
- return typeof val === "string" ? Number.parseInt(val, 10) : val ?? 0;
1650
- };
1651
- async mapCollectionEntity(val, user_ref, options) {
1652
- const { postFilters, includePosts = true } = options ?? {};
1653
- const results = await Promise.all([
1654
- includePosts ? this.getPosts(
1655
- user_ref,
1656
- { collectionId: val.id, includeEntities: true },
1657
- postFilters,
1658
- {
1659
- tagsFilter: options?.tagFilters,
1660
- includeComments: false,
1661
- includeAnswers: false,
1662
- includeAttachments: false,
1663
- includeVotes: false,
1664
- includeTotal: false,
1665
- includeExperts: options?.includeExperts ?? false
1666
- }
1667
- ) : { posts: [] },
1668
- this.db("attachments").where("collectionId", val.id).select("id"),
1669
- this.db("user_collections").count("*").as("followers").where("collectionId", val.id).first(),
1670
- options?.includeExperts ?? true ? this.getCollectionExperts(val.id) : void 0
1671
- ]);
1672
- const entities = lodash.compact([
1673
- ...new Set(results[0].posts.map((p) => p.entities).flat())
1674
- ]);
1675
- const tags = lodash.compact([
1676
- ...new Set(results[0].posts.map((p) => p.tags).flat())
1677
- ]);
1678
- return {
1679
- id: val.id,
1680
- title: val.title,
1681
- owner: val.owner,
1682
- description: val.description,
1683
- created: val.created,
1684
- posts: results[0].posts,
1685
- headerImage: val.headerImage,
1686
- postsCount: this.mapToInteger(val.postsCount),
1687
- entities,
1688
- tags,
1689
- images: results[1].map((r) => r.id),
1690
- followers: this.mapToInteger(results[2].count),
1691
- experts: results[3]
1692
- };
1693
- }
1694
- async getCollectionExperts(collectionId) {
1695
- const rows = await this.db("tag_experts").leftJoin("tags", "tag_experts.tagId", "tags.id").leftJoin("post_tags", "post_tags.tagId", "tags.id").leftJoin(
1696
- "collection_posts",
1697
- "collection_posts.postId",
1698
- "post_tags.postId"
1699
- ).where("collection_posts.collectionId", collectionId).select("tag_experts.entityRef");
1700
- return [...new Set(rows.map((r) => r.entityRef))];
1701
- }
1702
- async mapTemplateEntity(val) {
1703
- const additionalInfo = await Promise.all([
1704
- this.getRelatedTags(val.id, "template_tags", "templateId"),
1705
- this.getRelatedEntities(val.id, "template_entities", "templateId")
1706
- ]);
1707
- return {
1708
- id: val.id,
1709
- title: val.title,
1710
- description: val.description,
1711
- questionTitle: val.questionTitle ?? void 0,
1712
- questionContent: val.questionContent ?? void 0,
1713
- tags: additionalInfo[0],
1714
- entities: additionalInfo[1]
1715
- };
1716
- }
1717
- async mapPostEntity(val, user_ref, options) {
1718
- const {
1719
- includeTags = true,
1720
- includeAnswers = true,
1721
- includeVotes = true,
1722
- includeEntities = true,
1723
- includeComments = true,
1724
- includeAttachments = true,
1725
- includeExperts = true,
1726
- tagsFilter
1727
- } = options ?? {};
1728
- const additionalInfo = await Promise.all([
1729
- includeTags ? this.getRelatedTags(val.id, "post_tags", "postId", tagsFilter) : void 0,
1730
- includeAnswers ? this.getPostAnswers(val.id, user_ref, {
1731
- ...options,
1732
- includePost: false,
1733
- filter: options?.answersFilter
1734
- }) : void 0,
1735
- includeVotes ? this.getPostVotes(val.id) : void 0,
1736
- includeEntities ? this.getRelatedEntities(val.id) : void 0,
1737
- includeComments ? this.getPostComments(val.id, options?.commentsFilter) : void 0,
1738
- includeAttachments ? this.db("attachments").select("id").where("postId", val.id) : void 0,
1739
- includeExperts ? this.getPostExperts(val.id) : void 0
1740
- ]);
1741
- return {
1742
- id: val.id,
1743
- author: val.anonymous && val.author !== user_ref ? "anonymous" : val.author,
1744
- own: val.author === user_ref,
1745
- title: val.title,
1746
- content: val.content,
1747
- created: val.created,
1748
- updated: val.updated,
1749
- updatedBy: val.updatedBy,
1750
- status: val.status,
1751
- score: this.mapToInteger(val.score),
1752
- views: this.mapToInteger(val.views),
1753
- answersCount: this.mapToInteger(val.answersCount),
1754
- correctAnswer: this.mapToInteger(val.correctAnswers) > 0,
1755
- commentsCount: this.mapToInteger(val.commentsCount),
1756
- favorite: this.mapToInteger(val.favorite) > 0,
1757
- tags: additionalInfo[0],
1758
- answers: additionalInfo[1],
1759
- votes: additionalInfo[2],
1760
- entities: additionalInfo[3],
1761
- trend: this.mapToInteger(val.trend),
1762
- comments: additionalInfo[4],
1763
- ownVote: additionalInfo[2]?.find((v) => v.author === user_ref)?.score,
1764
- anonymous: val.anonymous,
1765
- type: val.type,
1766
- headerImage: val.headerImage,
1767
- url: val.url ?? void 0,
1768
- images: additionalInfo[5]?.map((r) => r.id),
1769
- experts: additionalInfo[6],
1770
- published: val.published ? val.published : void 0
1771
- };
1772
- }
1773
- mapComment(val) {
1774
- return {
1775
- id: val.id,
1776
- author: val.author,
1777
- content: val.content,
1778
- created: val.created,
1779
- updated: val.updated,
1780
- updatedBy: val.updatedBy,
1781
- status: val.status
1782
- };
1783
- }
1784
- async mapAnswer(val, user_ref, options) {
1785
- const {
1786
- includeVotes = true,
1787
- includeComments = true,
1788
- includePost = true,
1789
- includeExperts = true
1790
- } = options ?? {};
1791
- const additionalInfo = await Promise.all([
1792
- includeVotes ? this.getAnswerVotes(val.id) : void 0,
1793
- includeComments ? this.getAnswerComments(val.id, options?.commentsFilter) : void 0,
1794
- includePost ? this.getPost(user_ref, val.postId, false) : void 0,
1795
- this.db("attachments").select("id").where("answerId", val.id),
1796
- includeExperts ? this.getAnswerExperts(val.id) : void 0
1797
- ]);
1798
- return {
1799
- id: val.id,
1800
- postId: val.postId,
1801
- own: val.author === user_ref,
1802
- author: val.anonymous && val.author !== user_ref ? "anonymous" : val.author,
1803
- content: val.content,
1804
- correct: val.correct,
1805
- created: val.created,
1806
- updated: val.updated,
1807
- updatedBy: val.updatedBy,
1808
- score: this.mapToInteger(val.score),
1809
- status: val.status,
1810
- votes: additionalInfo[0],
1811
- comments: additionalInfo[1],
1812
- anonymous: val.anonymous,
1813
- post: additionalInfo[2] ?? void 0,
1814
- images: additionalInfo[3].map((r) => r.id),
1815
- experts: additionalInfo[4] ?? void 0
1816
- };
1817
- }
1818
- mapVote(val) {
1819
- return {
1820
- author: val.author,
1821
- score: val.score,
1822
- timestamp: val.timestamp
1823
- };
1824
- }
1825
- mapAttachment(val) {
1826
- return {
1827
- ...val,
1828
- created: val.created instanceof Date ? val.created : new Date(val.created)
1829
- };
1830
- }
1831
- async getRelatedTags(id, tableName = "post_tags", columnName = "postId", tagsFilter) {
1832
- const query = this.db("tags").leftJoin(tableName, "tags.id", `${tableName}.tagId`).where(`${tableName}.${columnName}`, "=", id);
1833
- if (tagsFilter) {
1834
- parseFilter(tagsFilter, query, this.db, "tags");
1835
- }
1836
- const rows = await query.select();
1837
- return rows.map((val) => val.tag);
1838
- }
1839
- async getPostComments(postId, commentsFilter, opts) {
1840
- const { includeStatusFilter = true } = opts ?? {};
1841
- const query = this.db("comments").where("comments.postId", "=", postId).orderBy("created");
1842
- if (includeStatusFilter) {
1843
- query.where("comments.status", "=", "active");
1844
- }
1845
- if (commentsFilter) {
1846
- parseFilter(commentsFilter, query, this.db, "comments");
1847
- }
1848
- const rows = await query.select();
1849
- return rows.map((val) => this.mapComment(val));
1850
- }
1851
- async getPostExperts(postId) {
1852
- const rows = await this.db("tag_experts").leftJoin("post_tags", "tag_experts.tagId", "post_tags.tagId").where("post_tags.postId", postId).select("tag_experts.entityRef");
1853
- return [...new Set(rows.map((r) => r.entityRef))];
1854
- }
1855
- async getAnswerComments(answerId, commentsFilter, opts) {
1856
- const { includeStatusFilter = true } = opts ?? {};
1857
- const query = this.db("comments").where("comments.answerId", "=", answerId).orderBy("created");
1858
- if (includeStatusFilter) {
1859
- query.where("comments.status", "=", "active");
1860
- }
1861
- if (commentsFilter) {
1862
- parseFilter(commentsFilter, query, this.db, "comments");
1863
- }
1864
- const rows = await query.select();
1865
- return rows.map((val) => this.mapComment(val));
1866
- }
1867
- async getRelatedEntities(id, tableName = "post_entities", columnName = "postId") {
1868
- const rows = await this.db("entities").leftJoin(tableName, "entities.id", `${tableName}.entityId`).where(`${tableName}.${columnName}`, "=", id).select();
1869
- return rows.map((val) => val.entity_ref);
1870
- }
1871
- async getPostVotes(postId) {
1872
- const rows = await this.db("post_votes").where("postId", "=", postId).select();
1873
- return rows.map((val) => this.mapVote(val));
1874
- }
1875
- async getAnswerVotes(answerId) {
1876
- const rows = await this.db("answer_votes").where("answerId", "=", answerId).select();
1877
- return rows.map((val) => this.mapVote(val));
1878
- }
1879
- async getAnswerExperts(answerId) {
1880
- const rows = await this.db("tag_experts").leftJoin("tags", "tag_experts.tagId", "tags.id").leftJoin("post_tags", "post_tags.tagId", "tags.id").leftJoin("answers", "answers.postId", "post_tags.postId").where("answers.id", answerId).select("tag_experts.entityRef");
1881
- return [...new Set(rows.map((r) => r.entityRef))];
1882
- }
1883
- getAnswerBaseQuery() {
1884
- const postRef = this.db.ref("answers.id");
1885
- const score = this.db("answer_votes").where("answer_votes.answerId", postRef).sum("score").as("score");
1886
- return this.db("answers").leftJoin("answer_votes", "answers.id", "answer_votes.answerId").select("answers.*", score).groupBy("answers.id");
1887
- }
1888
- async getPostAnswers(postId, user_ref, options) {
1889
- const { includeStatusFilter = true } = options ?? {};
1890
- const query = this.getAnswerBaseQuery().where("postId", "=", postId).orderBy("answers.correct", "desc").orderBy("answers.created");
1891
- if (includeStatusFilter) {
1892
- query.where("answers.status", "=", "active");
1893
- }
1894
- if (options?.filter) {
1895
- parseFilter(options.filter, query, this.db, "answer");
1896
- }
1897
- const rows = await query.select();
1898
- return await Promise.all(
1899
- rows.map(async (val) => {
1900
- return this.mapAnswer(val, user_ref, options);
1901
- })
1902
- );
1903
- }
1904
- async recordPostView(postId, user_ref) {
1905
- await this.db.insert({
1906
- author: user_ref,
1907
- postId,
1908
- timestamp: /* @__PURE__ */ new Date()
1909
- }).into("post_views");
1910
- }
1911
- getPostsBaseQuery(user, opts) {
1912
- const { includeStatusFilter = true } = opts ?? {};
1913
- const postRef = this.db.ref("posts.id");
1914
- const score = this.db("post_votes").where("post_votes.postId", postRef).sum("score").as("score");
1915
- const views = this.db("post_views").where("post_views.postId", postRef).count("*").as("views");
1916
- const answersCount = this.db("answers").where("answers.postId", postRef).count("*").as("answersCount");
1917
- if (includeStatusFilter) {
1918
- answersCount.where("answers.status", "=", "active");
1919
- }
1920
- const correctAnswers = this.db("answers").where("answers.postId", postRef).where("answers.correct", "=", true).count("*").as("correctAnswers");
1921
- if (includeStatusFilter) {
1922
- correctAnswers.where("answers.status", "=", "active");
1923
- }
1924
- const commentsCount = this.db("comments").where("comments.postId", postRef).count("*").as("commentsCount");
1925
- const favorite = this.db("user_favorite").where("user_favorite.user", "=", user).where("user_favorite.postId", postRef).count("*").as("favorite");
1926
- return this.db("posts").select(
1927
- "posts.*",
1928
- score,
1929
- views,
1930
- answersCount,
1931
- correctAnswers,
1932
- commentsCount,
1933
- favorite
1934
- ).leftJoin("post_votes", "posts.id", "post_votes.postId").leftJoin("post_views", "posts.id", "post_views.postId").leftJoin("answers", "posts.id", "answers.postId").leftJoin("comments", "posts.id", "comments.postId").leftJoin("user_favorite", "posts.id", "user_favorite.postId").groupBy("posts.id");
1935
- }
1936
- getCollectionsBaseQuery() {
1937
- const collectionRef = this.db.ref("collections.id");
1938
- const postsCount = this.db("collection_posts").leftJoin("posts", "collection_posts.postId", "posts.id").where("collection_posts.collectionId", collectionRef).where("posts.status", "=", "active").count("*").as("postsCount");
1939
- return this.db("collections").select("collections.*", postsCount).leftJoin(
1940
- "collection_posts",
1941
- "collections.id",
1942
- "collection_posts.collectionId"
1943
- ).leftJoin("posts", "collection_posts.postId", "posts.id").groupBy("collections.id");
1944
- }
1945
- async addTags(id, tagsInput, removeOld, tableName = "post_tags", columnName = "postId") {
1946
- const tags = backstagePluginQetaCommon.filterTags(tagsInput);
1947
- if (removeOld) {
1948
- await this.db(tableName).where(columnName, "=", id).delete();
1949
- }
1950
- if (!tags || tags.length === 0) {
1951
- return;
1952
- }
1953
- const existingTags = await this.db("tags").whereIn("tag", tags).returning("id").select();
1954
- const newTags = tags.filter((t) => !existingTags.some((e) => e.tag === t));
1955
- const allTags = {
1956
- ...tagDb.TAGS,
1957
- ...await this.tagDatabase?.getTags()
1958
- };
1959
- const tagIds = (await Promise.all(
1960
- [...new Set(newTags)].map(async (tag) => {
1961
- const trimmed = tag.trim();
1962
- const description = trimmed in allTags ? allTags[trimmed] : void 0;
1963
- return this.db.insert({ tag: trimmed, description }).into("tags").returning("id").onConflict("tag").ignore();
1964
- })
1965
- )).flat().map((tag) => tag.id).concat(existingTags.map((t) => t.id));
1966
- await Promise.all(
1967
- tagIds.map(async (tagId) => {
1968
- await this.db.insert({ [columnName]: id, tagId }).into(tableName).onConflict().ignore();
1969
- })
1970
- );
1971
- }
1972
- async addEntities(id, entitiesInput, removeOld, tableName = "post_entities", columnName = "postId") {
1973
- if (removeOld) {
1974
- await this.db(tableName).where(columnName, "=", id).delete();
1975
- }
1976
- const regex = /\w+:\w+\/\w+/g;
1977
- const entities = entitiesInput?.filter((input) => input.match(regex));
1978
- if (!entities || entities.length === 0) {
1979
- return;
1980
- }
1981
- const existingEntities = await this.db("entities").whereIn("entity_ref", entities).returning("id").select();
1982
- const newEntities = entities.filter(
1983
- (t) => !existingEntities.some((e) => e.entity_ref === t)
1984
- );
1985
- const entityIds = (await Promise.all(
1986
- [...new Set(newEntities)].map(
1987
- async (entity) => await this.db.insert({ entity_ref: entity }).into("entities").returning("id").onConflict("entity_ref").ignore()
1988
- )
1989
- )).flat().map((entity) => entity.id).concat(existingEntities.map((c) => c.id));
1990
- await Promise.all(
1991
- entityIds.map(async (entityId) => {
1992
- await this.db.insert({ [columnName]: id, entityId }).into(tableName).onConflict().ignore();
1993
- })
1994
- );
1995
- }
1996
- async markAnswer(postId, answerId, correct) {
1997
- if (correct) {
1998
- const exists = await this.db("answers").select("id").where("correct", "=", true).where("postId", "=", postId);
1999
- if (exists && exists.length > 0) {
2000
- return false;
2001
- }
2002
- }
2003
- const query = this.db("answers").onConflict().ignore().where("answers.id", "=", answerId).where("postId", "=", postId);
2004
- const ret = await query.update({ correct }, ["id"]);
2005
- return ret !== void 0 && ret?.length > 0;
447
+ async getComments(options, opts) {
448
+ return this.commentsStore.getComments(options, opts);
2006
449
  }
2007
- async updateAttachments(key, content, images, id, headerImage) {
2008
- if (images.length > 0) {
2009
- await this.db("attachments").whereIn("id", images).update({ [key]: id });
2010
- }
2011
- const attachments = await this.db("attachments").where(key, id).select("uuid");
2012
- const uuids = attachments.map((a) => a.uuid);
2013
- const toRemove = uuids.filter((uuid) => {
2014
- return !(content.includes(uuid) || headerImage?.includes(uuid));
2015
- });
2016
- await this.db("attachments").whereIn("uuid", toRemove).update({ [key]: null });
2017
- }
2018
- applySearchQuery(query, columns, searchQuery) {
2019
- if (this.db.client.config.client === "pg") {
2020
- query.whereRaw(
2021
- `((to_tsvector(CONCAT(${columns.join(
2022
- ","
2023
- )})) @@ to_tsquery(quote_literal(?) || ':*')))`,
2024
- [`${searchQuery}`]
2025
- );
2026
- } else {
2027
- query.whereRaw(`LOWER(CONCAT(${columns.join(",")})) LIKE LOWER(?)`, [
2028
- `%${searchQuery}%`
2029
- ]);
2030
- }
450
+ async getComment(commentId, opts) {
451
+ return this.commentsStore.getComment(commentId, opts);
2031
452
  }
2032
453
  }
2033
454