nodebb-plugin-dbsearch 6.1.0 → 6.2.1

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/lib/dbsearch.js CHANGED
@@ -8,6 +8,7 @@ const nconf = require.main.require('nconf');
8
8
  const db = require.main.require('./src/database');
9
9
  const topics = require.main.require('./src/topics');
10
10
  const posts = require.main.require('./src/posts');
11
+ const messaging = require.main.require('./src/messaging');
11
12
  const utils = require.main.require('./src/utils');
12
13
  const socketAdmin = require.main.require('./src/socket.io/admin');
13
14
  const batch = require.main.require('./src/batch');
@@ -156,6 +157,22 @@ search.actionTopicChangeOwner = function (hookData) {
156
157
  topicsSave(hookData.topics);
157
158
  };
158
159
 
160
+ search.actionMessagingSave = async function (hookData) {
161
+ await messagesSave([hookData.message]);
162
+ };
163
+
164
+ search.actionMessagingDelete = async function (hookData) {
165
+ await searchRemove('chat', [hookData.message.mid]);
166
+ };
167
+
168
+ search.actionMessagingRestore = async function (hookData) {
169
+ await messagesSave([hookData.message]);
170
+ };
171
+
172
+ search.actionMessagingEdit = async function (hookData) {
173
+ await messagesSave([hookData.message]);
174
+ };
175
+
159
176
  search.filterSearchQuery = async function (data) {
160
177
  if (!data || !data.index) {
161
178
  return data;
@@ -199,15 +216,43 @@ search.filterSearchTopic = async function (hookData) {
199
216
  return hookData;
200
217
  };
201
218
 
219
+ search.filterMessagingSearchMessages = async function (data) {
220
+ if (!data || !data.content) {
221
+ return data;
222
+ }
223
+ const limit = 100;
224
+ const query = {};
225
+ if (data.hasOwnProperty('roomId') && data.roomId) {
226
+ query.roomId = data.roomId;
227
+ }
228
+ if (data.hasOwnProperty('uid') && data.uid) {
229
+ query.uid = data.uid;
230
+ }
231
+ if (data.hasOwnProperty('content') && data.content) {
232
+ query.content = data.content;
233
+ }
234
+ if (!Object.keys(query).length) {
235
+ return [];
236
+ }
237
+ if (data.hasOwnProperty('matchWords')) {
238
+ query.matchWords = data.matchWords;
239
+ }
240
+
241
+ data.ids = data.ids.concat(await searchModule.chat.search(query, limit));
242
+ return data;
243
+ };
244
+
202
245
  search.reindex = async function () {
203
246
  await db.setObject('nodebb-plugin-dbsearch', {
204
247
  topicsIndexed: 0,
205
248
  postsIndexed: 0,
249
+ messagesIndexed: 0,
206
250
  working: 1,
207
251
  });
208
252
  await Promise.all([
209
253
  reIndexTopics(),
210
254
  reIndexPosts(),
255
+ reIndexMessages(),
211
256
  ]);
212
257
  await db.setObject('nodebb-plugin-dbsearch', {
213
258
  working: 0,
@@ -308,12 +353,55 @@ async function postsSave(posts) {
308
353
  await db.incrObjectFieldBy('nodebb-plugin-dbsearch', 'postsIndexed', result.pids.length);
309
354
  }
310
355
 
356
+ async function reIndexMessages() {
357
+ await batch.processSortedSet(`messages:mid`, async (mids) => {
358
+ let messageData = await messaging.getMessagesFields(mids, ['mid', 'content', 'roomId', 'fromuid', 'deleted', 'system']);
359
+ messageData = messageData.filter(p => p && p.deleted !== 1 && p.system !== 1);
360
+ await messagesSave(messageData);
361
+ }, {
362
+ batch: batchSize,
363
+ });
364
+ }
365
+
366
+ async function messagesSave(msgs) {
367
+ msgs = msgs.filter(m => m && m.mid && parseInt(m.deleted, 10) !== 1 && parseInt(m.system, 10) !== 1);
368
+
369
+ let data = msgs.map((msgData) => {
370
+ const indexData = {};
371
+ if (msgData.content) {
372
+ indexData.content = msgData.content;
373
+ }
374
+ if (msgData.roomId) {
375
+ indexData.roomId = msgData.roomId;
376
+ }
377
+ if (msgData.fromuid) {
378
+ indexData.uid = msgData.fromuid;
379
+ }
380
+ if (!Object.keys(indexData).length) {
381
+ return null;
382
+ }
383
+ return indexData;
384
+ });
385
+
386
+ const mids = msgs.filter((msg, index) => !!data[index]).map(msg => msg.mid);
387
+ data = data.filter(Boolean);
388
+ if (!data.length) {
389
+ return;
390
+ }
391
+
392
+ const result = await plugins.hooks.fire('filter:search.indexMessages', { data: data, mids: mids, messages: msgs });
393
+ await searchModule.chat.index(result.data, result.mids);
394
+ await db.incrObjectFieldBy('nodebb-plugin-dbsearch', 'messagesIndexed', result.mids.length);
395
+ }
396
+
311
397
  async function searchRemove(key, ids) {
312
398
  await db.searchRemove(key, ids);
313
399
  if (key === 'topic') {
314
400
  await db.incrObjectFieldBy('nodebb-plugin-dbsearch', 'topicsIndexed', -ids.length);
315
401
  } else if (key === 'post') {
316
402
  await db.incrObjectFieldBy('nodebb-plugin-dbsearch', 'postsIndexed', -ids.length);
403
+ } else if (key === 'chat') {
404
+ await db.incrObjectFieldBy('nodebb-plugin-dbsearch', 'messagesIndexed', -ids.length);
317
405
  }
318
406
  }
319
407
 
@@ -398,6 +486,7 @@ async function getPluginData() {
398
486
  const data = await db.getObject('nodebb-plugin-dbsearch') || {};
399
487
  data.topicsIndexed = parseInt(data.topicsIndexed, 10) || 0;
400
488
  data.postsIndexed = parseInt(data.postsIndexed, 10) || 0;
489
+ data.messagesIndexed = parseInt(data.messagesIndexed, 10) || 0;
401
490
  data.excludeCategories = data.excludeCategories || '[]';
402
491
  data.postLimit = data.postLimit || defaultPostLimit;
403
492
  data.topicLimit = data.topicLimit || defaultTopicLimit;
@@ -415,7 +504,7 @@ async function getPluginData() {
415
504
 
416
505
  async function getGlobalAndPluginData() {
417
506
  const [global, plugin, allCategories] = await Promise.all([
418
- db.getObjectFields('global', ['topicCount', 'postCount']),
507
+ db.getObjectFields('global', ['topicCount', 'postCount', 'messageCount']),
419
508
  getPluginData(),
420
509
  categories.buildForSelectAll(['value', 'text']),
421
510
  ]);
@@ -431,10 +520,12 @@ async function getGlobalAndPluginData() {
431
520
  plugin.allCategories = allCategories;
432
521
  plugin.topicCount = parseInt(global.topicCount, 10);
433
522
  plugin.postCount = parseInt(global.postCount, 10);
523
+ plugin.messageCount = parseInt(global.messageCount, 10);
434
524
  plugin.topicLimit = plugin.topicLimit || defaultTopicLimit;
435
525
  plugin.postLimit = plugin.postLimit || defaultPostLimit;
436
526
  plugin.topicsIndexed = plugin.topicsIndexed > plugin.topicCount ? plugin.topicCount : plugin.topicsIndexed;
437
527
  plugin.postsIndexed = plugin.postsIndexed > plugin.postCount ? plugin.postCount : plugin.postsIndexed;
528
+ plugin.messagesIndexed = plugin.messagesIndexed > plugin.messageCount ? plugin.messageCount : plugin.messagesIndexed;
438
529
  plugin.languageSupported = languageSupported;
439
530
  plugin.languages = languages;
440
531
  plugin.indexLanguage = plugin.indexLanguage || 'en';
@@ -451,35 +542,40 @@ async function getGlobalAndPluginData() {
451
542
 
452
543
  async function getProgress() {
453
544
  const [global, pluginData] = await Promise.all([
454
- db.getObjectFields('global', ['topicCount', 'postCount']),
545
+ db.getObjectFields('global', ['topicCount', 'postCount', 'messageCount']),
455
546
  getPluginData(),
456
547
  ]);
457
548
  const topicCount = parseInt(global.topicCount, 10);
458
549
  const postCount = parseInt(global.postCount, 10);
550
+ const messageCount = parseInt(global.messageCount, 10);
459
551
  const topicsPercent = topicCount ? (pluginData.topicsIndexed / topicCount) * 100 : 0;
460
552
  const postsPercent = postCount ? (pluginData.postsIndexed / postCount) * 100 : 0;
553
+ const messagesPercent = messageCount ? (pluginData.messagesIndexed / messageCount) * 100 : 0;
461
554
  return {
462
555
  topicsPercent: Math.max(0, Math.min(100, topicsPercent.toFixed(2))),
463
556
  postsPercent: Math.max(0, Math.min(100, postsPercent.toFixed(2))),
557
+ messagesPercent: Math.max(0, Math.min(100, messagesPercent.toFixed(2))),
464
558
  topicsIndexed: topicsPercent >= 100 ? topicCount : Math.max(0, pluginData.topicsIndexed),
465
559
  postsIndexed: postsPercent >= 100 ? postCount : Math.max(0, pluginData.postsIndexed),
560
+ messagesIndexed: messagesPercent >= 100 ? messageCount : Math.max(0, pluginData.messagesIndexed),
466
561
  working: pluginData.working,
467
562
  };
468
563
  }
469
564
 
470
565
  socketAdmin.plugins.dbsearch.reindex = function (socket, data, callback) {
471
- try {
472
- search.reindex();
473
- } catch (err) {
474
- winston.error(err);
475
- }
566
+ setTimeout(async () => {
567
+ try {
568
+ await search.reindex();
569
+ } catch (err) {
570
+ winston.error(err);
571
+ }
572
+ }, 0);
476
573
  callback();
477
574
  };
478
575
 
479
576
  socketAdmin.plugins.dbsearch.clearIndex = async function () {
480
577
  setTimeout(async () => {
481
578
  try {
482
- console.log('clear called');
483
579
  await clearIndex();
484
580
  } catch (err) {
485
581
  winston.error(err.stack);
@@ -495,11 +591,13 @@ async function clearIndex() {
495
591
  await Promise.all([
496
592
  clearSet('topics:tid', 'topic'),
497
593
  clearSet('posts:pid', 'post'),
594
+ clearSet('messages:mid', 'chat'),
498
595
  ]);
499
596
 
500
597
  await db.setObject('nodebb-plugin-dbsearch', {
501
598
  postsIndexed: 0,
502
599
  topicsIndexed: 0,
600
+ messagesIndexed: 0,
503
601
  working: 0,
504
602
  });
505
603
  }
package/lib/mongo.js CHANGED
@@ -13,6 +13,7 @@ exports.createIndices = async function (language) {
13
13
  if (nconf.get('isPrimary') && !nconf.get('jobsDisabled')) {
14
14
  await db.client.collection('searchtopic').createIndex({ content: 'text', uid: 1, cid: 1 }, options);
15
15
  await db.client.collection('searchpost').createIndex({ content: 'text', uid: 1, cid: 1 }, options);
16
+ await db.client.collection('searchchat').createIndex({ content: 'text', roomId: 1, uid: 1 }, options);
16
17
  }
17
18
  };
18
19
 
@@ -26,6 +27,8 @@ exports.changeIndexLanguage = async function (language) {
26
27
  await db.client.collection('searchtopic').createIndex(indexSpec, options);
27
28
  await db.client.collection('searchpost').dropIndex('content_text_uid_1_cid_1');
28
29
  await db.client.collection('searchpost').createIndex(indexSpec, options);
30
+ await db.client.collection('searchchat').dropIndex('content_text_roomId_1_uid_1');
31
+ await db.client.collection('searchchat').createIndex({ content: 'text', roomId: 1, uid: 1 }, options);
29
32
  };
30
33
 
31
34
  exports.searchIndex = async function (key, data, ids) {
@@ -53,18 +56,7 @@ exports.searchIndex = async function (key, data, ids) {
53
56
  exports.search = async function (key, data, limit) {
54
57
  const searchQuery = {};
55
58
  if (data.content) {
56
- let words = data.content.split(' ');
57
- const allQuoted = data.content.startsWith('"') && data.content.endsWith('"');
58
- if (data.matchWords === 'all' && !allQuoted) {
59
- words = words.map((word) => {
60
- if (!word.startsWith('"') && !word.endsWith('"')) {
61
- return `"${word}"`;
62
- }
63
- return word;
64
- });
65
- }
66
-
67
- searchQuery.$text = { $search: words.join(' ') };
59
+ searchQuery.$text = buildTextQuery(data.content, data.matchWords);
68
60
  }
69
61
 
70
62
  if (Array.isArray(data.cid) && data.cid.length) {
@@ -112,3 +104,80 @@ exports.searchRemove = async function (key, ids) {
112
104
 
113
105
  await db.client.collection(`search${key}`).deleteMany({ _id: { $in: ids } });
114
106
  };
107
+
108
+ exports.chat = {};
109
+ exports.chat.index = async (data, ids) => {
110
+ if (!ids.length) {
111
+ return;
112
+ }
113
+
114
+ ids = ids.map(id => parseInt(id, 10));
115
+
116
+ const bulk = db.client.collection(`searchchat`).initializeUnorderedBulkOp();
117
+ ids.forEach((id, index) => {
118
+ const d = data[index];
119
+ if (d && d.content && d.uid && d.roomId) {
120
+ bulk.find({ _id: id }).upsert().updateOne({
121
+ $set: {
122
+ content: String(d.content),
123
+ roomId: String(d.roomId),
124
+ uid: String(d.uid),
125
+ },
126
+ });
127
+ }
128
+ });
129
+
130
+ await bulk.execute();
131
+ };
132
+
133
+ exports.chat.search = async (data, limit) => {
134
+ const searchQuery = {};
135
+ if (!data.content) {
136
+ return [];
137
+ }
138
+ searchQuery.$text = buildTextQuery(data.content, data.matchWords);
139
+
140
+ if (Array.isArray(data.roomId) && data.roomId.filter(Boolean).length) {
141
+ if (data.roomId.length > 1) {
142
+ searchQuery.roomId = { $in: data.roomId.filter(Boolean).map(String) };
143
+ } else {
144
+ searchQuery.roomId = String(data.roomId[0]);
145
+ }
146
+ }
147
+
148
+ if (Array.isArray(data.uid) && data.uid.filter(Boolean).length) {
149
+ if (data.uid.length > 1) {
150
+ searchQuery.uid = { $in: data.uid.filter(Boolean).map(String) };
151
+ } else {
152
+ searchQuery.uid = String(data.uid[0]);
153
+ }
154
+ }
155
+
156
+ const collection = db.client.collection(`searchchat`);
157
+ const results = await collection.aggregate([
158
+ { $match: searchQuery },
159
+ { $sort: { score: { $meta: 'textScore' } } },
160
+ { $limit: parseInt(limit, 10) },
161
+ { $project: { _id: 1 } },
162
+ ]).toArray();
163
+ if (!results || !results.length) {
164
+ return [];
165
+ }
166
+ return results.map(item => item._id);
167
+ };
168
+
169
+ function buildTextQuery(content, matchWords) {
170
+ let words = content.split(' ');
171
+ const allQuoted = content.startsWith('"') && content.endsWith('"');
172
+ if (matchWords === 'all' && !allQuoted) {
173
+ words = words.map((word) => {
174
+ if (!word.startsWith('"') && !word.endsWith('"')) {
175
+ return `"${word}"`;
176
+ }
177
+ return word;
178
+ });
179
+ }
180
+
181
+ return { $search: words.join(' ') };
182
+ }
183
+
package/lib/postgres.js CHANGED
@@ -17,10 +17,16 @@ async function initDB() {
17
17
  await db.pool.query(`CREATE INDEX IF NOT EXISTS "idx__searchtopic__content" ON "searchtopic" USING GIN (to_tsvector('${searchLanguage}', "content"))`);
18
18
  await db.pool.query('CREATE INDEX IF NOT EXISTS "idx__searchtopic__uid" ON "searchtopic"("uid")');
19
19
  await db.pool.query('CREATE INDEX IF NOT EXISTS "idx__searchtopic__cid" ON "searchtopic"("cid")');
20
+
20
21
  await db.pool.query('CREATE TABLE IF NOT EXISTS "searchpost" ( "id" BIGINT NOT NULL PRIMARY KEY, "content" TEXT, "uid" BIGINT, "cid" BIGINT )');
21
22
  await db.pool.query(`CREATE INDEX IF NOT EXISTS "idx__searchpost__content" ON "searchpost" USING GIN (to_tsvector('${searchLanguage}', "content"))`);
22
23
  await db.pool.query('CREATE INDEX IF NOT EXISTS "idx__searchpost__uid" ON "searchpost"("uid")');
23
24
  await db.pool.query('CREATE INDEX IF NOT EXISTS "idx__searchpost__cid" ON "searchpost"("cid")');
25
+
26
+ await db.pool.query('CREATE TABLE IF NOT EXISTS "searchchat" ( "id" BIGINT NOT NULL PRIMARY KEY, "content" TEXT, "rid" BIGINT, "uid" BIGINT )');
27
+ await db.pool.query(`CREATE INDEX IF NOT EXISTS "idx__searchchat__content" ON "searchchat" USING GIN (to_tsvector('${searchLanguage}', "content"))`);
28
+ await db.pool.query('CREATE INDEX IF NOT EXISTS "idx__searchchat__rid" ON "searchchat"("rid")');
29
+ await db.pool.query('CREATE INDEX IF NOT EXISTS "idx__searchchat__uid" ON "searchchat"("uid")');
24
30
  }
25
31
 
26
32
  async function handleError(err) {
@@ -45,8 +51,12 @@ exports.changeIndexLanguage = async function (language) {
45
51
  pubsub.publish('dbsearch-language-changed', language);
46
52
  await db.pool.query('DROP INDEX "idx__searchtopic__content"');
47
53
  await db.pool.query(`CREATE INDEX "idx__searchtopic__content" ON "searchtopic" USING GIN (to_tsvector('${language}', "content"))`);
54
+
48
55
  await db.pool.query('DROP INDEX "idx__searchpost__content"');
49
56
  await db.pool.query(`CREATE INDEX "idx__searchpost__content" ON "searchpost" USING GIN (to_tsvector('${language}', "content"))`);
57
+
58
+ await db.pool.query('DROP INDEX "idx__searchchat__content"');
59
+ await db.pool.query(`CREATE INDEX "idx__searchchat__content" ON "searchchat" USING GIN (to_tsvector('${language}', "content"))`);
50
60
  };
51
61
 
52
62
  exports.searchIndex = async function (key, data, ids) {
@@ -109,3 +119,48 @@ exports.searchRemove = async function (key, ids) {
109
119
  await handleError(err);
110
120
  }
111
121
  };
122
+
123
+ exports.chat = {};
124
+ exports.chat.index = async (data, ids) => {
125
+ if (!ids.length) {
126
+ return;
127
+ }
128
+
129
+ ids = ids.map(id => parseInt(id, 10));
130
+ try {
131
+ await db.pool.query({
132
+ name: `dbsearch-searchIndex-chat`,
133
+ text: `INSERT INTO "searchchat" SELECT d."id", d."data"->>'content' "content", (d."data"->>'uid')::bigint "uid", (d."data"->>'roomId')::bigint "roomId" FROM UNNEST($1::bigint[], $2::jsonb[]) d("id", "data") ON CONFLICT ("id") DO UPDATE SET "content" = COALESCE(EXCLUDED."content", "searchchat"."content"), "uid" = COALESCE(EXCLUDED."uid", "searchchat"."uid"), "roomId" = COALESCE(EXCLUDED."roomId", "searchchat"."roomId")`,
134
+ values: [ids, data],
135
+ });
136
+ } catch (err) {
137
+ winston.error(`Error indexing ${err.stack}`);
138
+ await handleError(err);
139
+ await exports.chat.index(data, ids);
140
+ }
141
+ };
142
+
143
+ exports.chat.search = async (data, limit) => {
144
+ if (Array.isArray(data.uid) && data.uid.filter(Boolean).length) {
145
+ data.uid = data.uid.filter(Boolean);
146
+ } else {
147
+ data.uid = null;
148
+ }
149
+
150
+ if (Array.isArray(data.roomId) && data.roomId.filter(Boolean).length) {
151
+ data.roomId = data.roomId.filter(Boolean);
152
+ } else {
153
+ data.roomId = null;
154
+ }
155
+ try {
156
+ const res = await db.pool.query({
157
+ name: `dbsearch-search-chat`,
158
+ text: `SELECT ARRAY(SELECT s."id" FROM "searchchat" s WHERE ($1::text IS NULL OR to_tsvector($5::regconfig, "content") @@ plainto_tsquery($5::regconfig, $1::text)) AND ($2::bigint[] IS NULL OR "uid" = ANY($2::bigint[])) AND ($3::bigint[] IS NULL OR "roomId" = ANY($3::bigint[])) ORDER BY ts_rank_cd(to_tsvector($5::regconfig, "content"), plainto_tsquery($5::regconfig, $1::text)) DESC, s."id" ASC LIMIT $4::integer) r`,
159
+ values: [data.content, data.uid, data.roomId, parseInt(limit, 10), searchLanguage],
160
+ });
161
+ return res.rows[0].r;
162
+ } catch (err) {
163
+ await handleError(err);
164
+ return [];
165
+ }
166
+ };
package/lib/redis.js CHANGED
@@ -9,6 +9,7 @@ const db = require.main.require('./src/database');
9
9
  exports.createIndices = async function () {
10
10
  db.postSearch = redisSearch.createSearch('nodebbpostsearch', db.client);
11
11
  db.topicSearch = redisSearch.createSearch('nodebbtopicsearch', db.client);
12
+ db.chatSearch = redisSearch.createSearch('nodebbchatsearch', db.client);
12
13
  };
13
14
 
14
15
  exports.changeIndexLanguage = async function () {
@@ -44,9 +45,39 @@ exports.searchRemove = async function (key, ids) {
44
45
  if (!key || !ids.length) {
45
46
  return;
46
47
  }
47
- const method = key === 'post' ? db.postSearch : db.topicSearch;
48
-
49
48
  await async.eachLimit(ids, 500, async (ids) => {
50
- await method.remove(ids);
49
+ await db[`${key}Search`].remove(ids);
50
+ });
51
+ };
52
+
53
+ exports.chat = {};
54
+ exports.chat.index = async (data, ids) => {
55
+ if (!ids.length) {
56
+ return;
57
+ }
58
+ const indexData = ids.map((id, index) => ({
59
+ id: id,
60
+ data: {
61
+ content: String(data[index].content),
62
+ roomId: String(data[index].roomId),
63
+ uid: String(data[index].uid),
64
+ },
65
+ }));
66
+
67
+ await async.eachLimit(indexData, 500, async (indexData) => {
68
+ await db.chatSearch.index(indexData.data, indexData.id);
51
69
  });
52
70
  };
71
+
72
+ exports.chat.search = async (data, limit) => {
73
+ const query = {
74
+ matchWords: data.matchWords,
75
+ query: {
76
+ roomId: data.roomId,
77
+ uid: data.uid,
78
+ content: data.content,
79
+ },
80
+ };
81
+
82
+ return await db.chatSearch.query(query, 0, limit - 1);
83
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-dbsearch",
3
- "version": "6.1.0",
3
+ "version": "6.2.1",
4
4
  "description": "A Plugin that lets users search posts and topics",
5
5
  "main": "index.js",
6
6
  "repository": {
@@ -19,7 +19,7 @@
19
19
  "license": "BSD-2-Clause",
20
20
  "dependencies": {
21
21
  "lodash": "4.17.21",
22
- "redisearch": "^1.0.1"
22
+ "redisearch": "^1.0.3"
23
23
  },
24
24
  "devDependencies": {
25
25
  "eslint": "7.32.0",
@@ -27,6 +27,6 @@
27
27
  "eslint-plugin-import": "2.25.2"
28
28
  },
29
29
  "nbbpm": {
30
- "compatibility": "^3.0.0"
30
+ "compatibility": "^3.3.0"
31
31
  }
32
32
  }
package/plugin.json CHANGED
@@ -27,8 +27,14 @@
27
27
  { "hook": "action:topic.move", "method": "actionTopicMove" },
28
28
  { "hook": "action:topic.changeOwner", "method": "actionTopicChangeOwner" },
29
29
 
30
+ { "hook": "action:messaging.save", "method": "actionMessagingSave" },
31
+ { "hook": "action:messaging.delete", "method": "actionMessagingDelete" },
32
+ { "hook": "action:messaging.restore", "method": "actionMessagingRestore" },
33
+ { "hook": "action:messaging.edit", "method": "actionMessagingEdit" },
34
+
30
35
  { "hook": "filter:search.query", "method": "filterSearchQuery" },
31
- { "hook": "filter:topic.search", "method": "filterSearchTopic" }
36
+ { "hook": "filter:topic.search", "method": "filterSearchTopic" },
37
+ { "hook": "filter:messaging.searchMessages", "method": "filterMessagingSearchMessages" }
32
38
  ],
33
39
  "modules": {
34
40
  "../admin/plugins/dbsearch.js": "public/admin.js"
package/public/admin.js CHANGED
@@ -113,8 +113,10 @@ define('admin/plugins/dbsearch', [
113
113
 
114
114
  $('#topics-indexed').text(progress.topicsIndexed);
115
115
  $('#posts-indexed').text(progress.postsIndexed);
116
+ $('#messages-indexed').text(progress.messagesIndexed);
116
117
  $('.topic-progress').css('width', progress.topicsPercent + '%').text(progress.topicsPercent + '%');
117
118
  $('.post-progress').css('width', progress.postsPercent + '%').text(progress.postsPercent + '%');
119
+ $('.message-progress').css('width', progress.messagesPercent + '%').text(progress.messagesPercent + '%');
118
120
  });
119
121
  }
120
122
 
@@ -25,6 +25,15 @@
25
25
  </div>
26
26
  </div>
27
27
 
28
+ <div class="mb-3">
29
+ <div class="alert alert-info">
30
+ Messages Indexed: <strong id="messages-indexed">{messagesIndexed}</strong> / <strong>{messageCount}</strong>
31
+ </div>
32
+ <div class="progress" style="height:24px;">
33
+ <div class="message-progress progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width:{progressData.messagesPercent}%;min-width: 2em;">{progressData.messagesPercent}%</div>
34
+ </div>
35
+ </div>
36
+
28
37
  <button class="btn btn-warning" id="reindex" <!-- IF working -->disabled<!-- ENDIF working -->>Re Index</button>
29
38
  <button class="btn btn-danger" id="clear-index">Clear Index</button>
30
39
  <span id="work-in-progress" class="<!-- IF !working -->hidden<!-- ENDIF !working -->">