• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

CMU-17313Q / fall23-nodebb-inshallah-a / 6239420468

19 Sep 2023 06:18PM UTC coverage: 77.199% (+0.01%) from 77.188%
6239420468

push

github

web-flow
Merge pull request #59 from CMU-17313Q/Adding-isAnonymous-if-conditions

Adding is anonymous if conditions

9091 of 13356 branches covered (0.0%)

Branch coverage included in aggregate %.

20876 of 25462 relevant lines covered (81.99%)

2463.76 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

4.01
/src/search.js
1
'use strict';
2

3
const _ = require('lodash');
20✔
4

5
const db = require('./database');
20✔
6
const posts = require('./posts');
20✔
7
const topics = require('./topics');
20✔
8
const categories = require('./categories');
20✔
9
const user = require('./user');
20✔
10
const plugins = require('./plugins');
20✔
11
const privileges = require('./privileges');
20✔
12
const utils = require('./utils');
20✔
13

14
const search = module.exports;
20✔
15

16
search.search = async function (data) {
20✔
17
    const start = process.hrtime();
×
18
    data.sortBy = data.sortBy || 'relevance';
×
19

20
    let result;
21
    if (data.searchIn === 'posts' || data.searchIn === 'titles' || data.searchIn === 'titlesposts') {
×
22
        result = await searchInContent(data);
×
23
    } else if (data.searchIn === 'users') {
×
24
        result = await user.search(data);
×
25
    } else if (data.searchIn === 'categories') {
×
26
        result = await categories.search(data);
×
27
    } else if (data.searchIn === 'tags') {
×
28
        result = await topics.searchAndLoadTags(data);
×
29
    } else if (data.searchIn) {
×
30
        result = await plugins.hooks.fire('filter:search.searchIn', {
×
31
            data,
32
        });
33
    } else {
34
        throw new Error('[[error:unknown-search-filter]]');
×
35
    }
36

37
    result.time = (process.elapsedTimeSince(start) / 1000).toFixed(2);
×
38
    return result;
×
39
};
40

41
async function searchInContent(data) {
42
    data.uid = data.uid || 0;
×
43

44
    const [searchCids, searchUids] = await Promise.all([
×
45
        getSearchCids(data),
46
        getSearchUids(data),
47
    ]);
48

49
    async function doSearch(type, searchIn) {
50
        if (searchIn.includes(data.searchIn)) {
×
51
            const result = await plugins.hooks.fire('filter:search.query', {
×
52
                index: type,
53
                content: data.query,
54
                matchWords: data.matchWords || 'all',
×
55
                cid: searchCids,
56
                uid: searchUids,
57
                searchData: data,
58
                ids: [],
59
            });
60
            return Array.isArray(result) ? result : result.ids;
×
61
        }
62
        return [];
×
63
    }
64
    let pids = [];
×
65
    let tids = [];
×
66
    const inTopic = String(data.query || '').match(/^in:topic-([\d]+) /);
×
67
    if (inTopic) {
×
68
        const tid = inTopic[1];
×
69
        const cleanedTerm = data.query.replace(inTopic[0], '');
×
70
        pids = await topics.search(tid, cleanedTerm);
×
71
    } else {
72
        [pids, tids] = await Promise.all([
×
73
            doSearch('post', ['posts', 'titlesposts']),
74
            doSearch('topic', ['titles', 'titlesposts']),
75
        ]);
76
    }
77

78
    const mainPids = await topics.getMainPids(tids);
×
79

80
    let allPids = mainPids.concat(pids).filter(Boolean);
×
81

82
    allPids = await privileges.posts.filter('topics:read', allPids, data.uid);
×
83
    allPids = await filterAndSort(allPids, data);
×
84

85
    const metadata = await plugins.hooks.fire('filter:search.inContent', {
×
86
        pids: allPids,
87
        data: data,
88
    });
89

90
    if (data.returnIds) {
×
91
        const mainPidsSet = new Set(mainPids);
×
92
        const mainPidToTid = _.zipObject(mainPids, tids);
×
93
        const pidsSet = new Set(pids);
×
94
        const returnPids = allPids.filter(pid => pidsSet.has(pid));
×
95
        const returnTids = allPids.filter(pid => mainPidsSet.has(pid)).map(pid => mainPidToTid[pid]);
×
96
        return { pids: returnPids, tids: returnTids };
×
97
    }
98

99
    const itemsPerPage = Math.min(data.itemsPerPage || 10, 100);
×
100
    const returnData = {
×
101
        posts: [],
102
        matchCount: metadata.pids.length,
103
        pageCount: Math.max(1, Math.ceil(parseInt(metadata.pids.length, 10) / itemsPerPage)),
104
    };
105

106
    if (data.page) {
×
107
        const start = Math.max(0, (data.page - 1)) * itemsPerPage;
×
108
        metadata.pids = metadata.pids.slice(start, start + itemsPerPage);
×
109
    }
110

111
    returnData.posts = await posts.getPostSummaryByPids(metadata.pids, data.uid, {});
×
112
    await plugins.hooks.fire('filter:search.contentGetResult', { result: returnData, data: data });
×
113
    delete metadata.pids;
×
114
    delete metadata.data;
×
115
    return Object.assign(returnData, metadata);
×
116
}
117

118
async function filterAndSort(pids, data) {
119
    if (data.sortBy === 'relevance' && !data.replies && !data.timeRange && !data.hasTags && !plugins.hooks.hasListeners('filter:search.filterAndSort')) {
×
120
        return pids;
×
121
    }
122
    let postsData = await getMatchedPosts(pids, data);
×
123
    if (!postsData.length) {
×
124
        return pids;
×
125
    }
126
    postsData = postsData.filter(Boolean);
×
127

128
    postsData = filterByPostcount(postsData, data.replies, data.repliesFilter);
×
129
    postsData = filterByTimerange(postsData, data.timeRange, data.timeFilter);
×
130
    postsData = filterByTags(postsData, data.hasTags);
×
131

132
    sortPosts(postsData, data);
×
133

134
    const result = await plugins.hooks.fire('filter:search.filterAndSort', { pids: pids, posts: postsData, data: data });
×
135
    return result.posts.map(post => post && post.pid);
×
136
}
137

138
async function getMatchedPosts(pids, data) {
139
    const postFields = ['pid', 'uid', 'tid', 'timestamp', 'deleted', 'upvotes', 'downvotes'];
×
140

141
    let postsData = await posts.getPostsFields(pids, postFields);
×
142
    postsData = postsData.filter(post => post && !post.deleted);
×
143
    const uids = _.uniq(postsData.map(post => post.uid));
×
144
    const tids = _.uniq(postsData.map(post => post.tid));
×
145

146
    const [users, topics] = await Promise.all([
×
147
        getUsers(uids, data),
148
        getTopics(tids, data),
149
    ]);
150

151
    const tidToTopic = _.zipObject(tids, topics);
×
152
    const uidToUser = _.zipObject(uids, users);
×
153
    postsData.forEach((post) => {
×
154
        if (topics && tidToTopic[post.tid]) {
×
155
            post.topic = tidToTopic[post.tid];
×
156
            if (post.topic && post.topic.category) {
×
157
                post.category = post.topic.category;
×
158
            }
159
        }
160

161
        if (uidToUser[post.uid]) {
×
162
            post.user = uidToUser[post.uid];
×
163
        }
164
    });
165

166
    return postsData.filter(post => post && post.topic && !post.topic.deleted);
×
167
}
168

169
async function getUsers(uids, data) {
170
    if (data.sortBy.startsWith('user')) {
×
171
        return user.getUsersFields(uids, ['username']);
×
172
    }
173
    return [];
×
174
}
175

176
async function getTopics(tids, data) {
177
    const topicsData = await topics.getTopicsData(tids);
×
178
    const cids = _.uniq(topicsData.map(topic => topic && topic.cid));
×
179
    const categories = await getCategories(cids, data);
×
180

181
    const cidToCategory = _.zipObject(cids, categories);
×
182
    topicsData.forEach((topic) => {
×
183
        if (topic && categories && cidToCategory[topic.cid]) {
×
184
            topic.category = cidToCategory[topic.cid];
×
185
        }
186
        if (topic && topic.tags) {
×
187
            topic.tags = topic.tags.map(tag => tag.value);
×
188
        }
189
    });
190

191
    return topicsData;
×
192
}
193

194
async function getCategories(cids, data) {
195
    const categoryFields = [];
×
196

197
    if (data.sortBy.startsWith('category.')) {
×
198
        categoryFields.push(data.sortBy.split('.')[1]);
×
199
    }
200
    if (!categoryFields.length) {
×
201
        return null;
×
202
    }
203

204
    return await db.getObjectsFields(cids.map(cid => `category:${cid}`), categoryFields);
×
205
}
206

207
function filterByPostcount(posts, postCount, repliesFilter) {
208
    postCount = parseInt(postCount, 10);
×
209
    if (postCount) {
×
210
        if (repliesFilter === 'atleast') {
×
211
            posts = posts.filter(post => post.topic && post.topic.postcount >= postCount);
×
212
        } else {
213
            posts = posts.filter(post => post.topic && post.topic.postcount <= postCount);
×
214
        }
215
    }
216
    return posts;
×
217
}
218

219
function filterByTimerange(posts, timeRange, timeFilter) {
220
    timeRange = parseInt(timeRange, 10) * 1000;
×
221
    if (timeRange) {
×
222
        const time = Date.now() - timeRange;
×
223
        if (timeFilter === 'newer') {
×
224
            posts = posts.filter(post => post.timestamp >= time);
×
225
        } else {
226
            posts = posts.filter(post => post.timestamp <= time);
×
227
        }
228
    }
229
    return posts;
×
230
}
231

232
function filterByTags(posts, hasTags) {
233
    if (Array.isArray(hasTags) && hasTags.length) {
×
234
        posts = posts.filter((post) => {
×
235
            let hasAllTags = false;
×
236
            if (post && post.topic && Array.isArray(post.topic.tags) && post.topic.tags.length) {
×
237
                hasAllTags = hasTags.every(tag => post.topic.tags.includes(tag));
×
238
            }
239
            return hasAllTags;
×
240
        });
241
    }
242
    return posts;
×
243
}
244

245
function sortPosts(posts, data) {
246
    if (!posts.length || data.sortBy === 'relevance') {
×
247
        return;
×
248
    }
249

250
    data.sortDirection = data.sortDirection || 'desc';
×
251
    const direction = data.sortDirection === 'desc' ? 1 : -1;
×
252
    const fields = data.sortBy.split('.');
×
253
    if (fields.length === 1) {
×
254
        return posts.sort((p1, p2) => direction * (p2[fields[0]] - p1[fields[0]]));
×
255
    }
256

257
    const firstPost = posts[0];
×
258
    if (!fields || fields.length !== 2 || !firstPost[fields[0]] || !firstPost[fields[0]][fields[1]]) {
×
259
        return;
×
260
    }
261

262
    const isNumeric = utils.isNumber(firstPost[fields[0]][fields[1]]);
×
263

264
    if (isNumeric) {
×
265
        posts.sort((p1, p2) => direction * (p2[fields[0]][fields[1]] - p1[fields[0]][fields[1]]));
×
266
    } else {
267
        posts.sort((p1, p2) => {
×
268
            if (p1[fields[0]][fields[1]] > p2[fields[0]][fields[1]]) {
×
269
                return direction;
×
270
            } else if (p1[fields[0]][fields[1]] < p2[fields[0]][fields[1]]) {
×
271
                return -direction;
×
272
            }
273
            return 0;
×
274
        });
275
    }
276
}
277

278
async function getSearchCids(data) {
279
    if (!Array.isArray(data.categories) || !data.categories.length) {
×
280
        return [];
×
281
    }
282

283
    if (data.categories.includes('all')) {
×
284
        return await categories.getCidsByPrivilege('categories:cid', data.uid, 'read');
×
285
    }
286

287
    const [watchedCids, childrenCids] = await Promise.all([
×
288
        getWatchedCids(data),
289
        getChildrenCids(data),
290
    ]);
291
    return _.uniq(watchedCids.concat(childrenCids).concat(data.categories).filter(Boolean));
×
292
}
293

294
async function getWatchedCids(data) {
295
    if (!data.categories.includes('watched')) {
×
296
        return [];
×
297
    }
298
    return await user.getWatchedCategories(data.uid);
×
299
}
300

301
async function getChildrenCids(data) {
302
    if (!data.searchChildren) {
×
303
        return [];
×
304
    }
305
    const childrenCids = await Promise.all(data.categories.map(cid => categories.getChildrenCids(cid)));
×
306
    return await privileges.categories.filterCids('find', _.uniq(_.flatten(childrenCids)), data.uid);
×
307
}
308

309
async function getSearchUids(data) {
310
    if (!data.postedBy) {
×
311
        return [];
×
312
    }
313
    return await user.getUidsByUsernames(Array.isArray(data.postedBy) ? data.postedBy : [data.postedBy]);
×
314
}
315

316
require('./promisify')(search);
20✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc