Skip to content

Commit 2af37e7

Browse files
committed
feat: optimize search functionality in song and user services with aggregation and indexing
1 parent 7aa68c6 commit 2af37e7

File tree

5 files changed

+157
-42
lines changed

5 files changed

+157
-42
lines changed

server/src/search/search.service.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ export class SearchService {
2222
public async search(queryBody: SearchQueryDTO) {
2323
this.logger.debug(`Searching for ${JSON.stringify(queryBody)}`);
2424

25+
queryBody.query = (queryBody.query || '').trim().toLowerCase();
26+
2527
const {
2628
query,
2729
page = 1,

server/src/song/entity/song.entity.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ export class Song {
5757
@Prop({ type: ThumbnailData, required: true })
5858
thumbnailData: ThumbnailData;
5959

60-
@Prop({ type: String, required: true })
60+
@Prop({ type: String, required: true, index: true })
6161
category: CategoryType;
6262

6363
@Prop({ type: String, required: true })
@@ -72,13 +72,13 @@ export class Song {
7272
@Prop({ type: Boolean, required: true, default: true })
7373
allowDownload: boolean;
7474

75-
@Prop({ type: String, required: true })
75+
@Prop({ type: String, required: true, index: true })
7676
title: string;
7777

78-
@Prop({ type: String, required: false })
78+
@Prop({ type: String, required: false, index: true })
7979
originalAuthor: string;
8080

81-
@Prop({ type: String, required: false })
81+
@Prop({ type: String, required: false, index: true })
8282
description: string;
8383

8484
// SONG FILE ATTRIBUTES (Populated from NBS file - immutable)

server/src/song/song.service.ts

Lines changed: 86 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -477,26 +477,93 @@ export class SongService {
477477
const skip = (page - 1) * limit;
478478
const sortOrder = order ? 1 : -1;
479479

480-
const songs = await this.songModel
481-
.find({
482-
$or: [
483-
{ originalAuthor: { $regex: query, $options: 'i' } },
484-
{ title: { $regex: query, $options: 'i' } },
485-
{ description: { $regex: query, $options: 'i' } },
486-
],
487-
category: category,
488-
})
489-
.select('title category thumbnailUrl likeCount')
490-
.sort({ [sort]: sortOrder })
491-
.skip(skip)
492-
.limit(limit);
480+
const songs: SongViewDto[] = await this.songModel.aggregate([
481+
{
482+
/**
483+
$search: {
484+
index: 'song_search_index',
485+
text: {
486+
query: query,
487+
},
488+
},
489+
*/
490+
$match: {
491+
$or: [
492+
{ originalAuthor: { $regex: query, $options: 'i' } },
493+
{ title: { $regex: query, $options: 'i' } },
494+
{ description: { $regex: query, $options: 'i' } },
495+
],
496+
//category: category,
497+
},
498+
},
499+
{
500+
$sort: { [sort]: sortOrder },
501+
},
502+
{
503+
$skip: skip,
504+
},
505+
{
506+
$limit: limit,
507+
},
508+
{
509+
$lookup: {
510+
from: 'users', // The collection to join
511+
localField: 'uploader', // The field from the input documents (username)
512+
foreignField: 'username', // The field from the documents of the "from" collection (username)
513+
as: 'uploader', // The name of the new array field to add to the input documents
514+
},
515+
},
516+
{
517+
$unwind: '$uploader', // Unwind the array to include the user document directly
518+
},
519+
{
520+
$project: {
521+
publicId: 1,
522+
createdAt: 1,
523+
thumbnailUrl: 1,
524+
playCount: 1,
525+
downloadCount: 1,
526+
likeCount: 1,
527+
allowDownload: 1,
528+
title: 1,
529+
originalAuthor: 1,
530+
description: 1,
531+
category: 1,
532+
license: 1,
533+
customInstruments: 1,
534+
fileSize: 1,
535+
stats: 1,
536+
'uploader.username': 1,
537+
'uploader.profileImage': 1,
538+
},
539+
},
540+
]);
493541

494-
const total = await this.songModel.countDocuments({
495-
originalAuthor: { $regex: query, $options: 'i' },
496-
title: { $regex: query, $options: 'i' },
497-
description: { $regex: query, $options: 'i' },
498-
category: category,
499-
});
542+
const totalResult = await this.songModel.aggregate([
543+
{
544+
/**
545+
$search: {
546+
index: 'song_search_index',
547+
text: {
548+
query: query,
549+
},
550+
},
551+
*/
552+
$match: {
553+
$or: [
554+
{ originalAuthor: { $regex: query, $options: 'i' } },
555+
{ title: { $regex: query, $options: 'i' } },
556+
{ description: { $regex: query, $options: 'i' } },
557+
],
558+
category: category,
559+
},
560+
},
561+
{
562+
$count: 'total',
563+
},
564+
]);
565+
566+
const total = totalResult.length > 0 ? totalResult[0].total : 0;
500567

501568
this.logger.debug(
502569
`Retrieved songs: ${songs.length} documents, with total: ${total}`,

server/src/user/entity/user.entity.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,10 @@ export class User {
3030
@Prop({ type: Number, required: true, default: 0 })
3131
playCount: number;
3232

33-
@Prop({ type: String, required: true })
33+
@Prop({ type: String, required: true, index: true })
3434
username: string;
3535

36-
@Prop({ type: String, required: true, default: '#' })
36+
@Prop({ type: String, required: true, default: '#', index: true })
3737
publicName: string;
3838

3939
@Prop({ type: String, required: true, unique: true })

server/src/user/user.service.ts

Lines changed: 63 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -72,24 +72,70 @@ export class UserService {
7272
const skip = (page - 1) * limit;
7373
const sortOrder = order ? 1 : -1;
7474

75-
const users = await this.userModel
76-
.find({
77-
$or: [
78-
{ username: { $regex: query, $options: 'i' } },
79-
{ publicName: { $regex: query, $options: 'i' } },
80-
{ description: { $regex: query, $options: 'i' } },
81-
],
82-
})
83-
.select('username publicName email profileImage')
84-
.sort({ [sort]: sortOrder })
85-
.skip(skip)
86-
.limit(limit);
75+
const users: {
76+
username: string;
77+
profileImage: string;
78+
}[] = await this.userModel.aggregate([
79+
{
80+
/*
81+
$search: {
82+
index: 'user_search_index',
83+
text: {
84+
query: query,
85+
path: {
86+
wildcard: '*',
87+
},
88+
},
89+
},
90+
*/
91+
$match: {
92+
$or: [
93+
{ username: { $regex: query, $options: 'i' } },
94+
{ publicName: { $regex: query, $options: 'i' } },
95+
// { description: { $regex: query, $options: 'i' } },
96+
],
97+
},
98+
},
99+
{
100+
$project: {
101+
username: 1,
102+
profileImage: 1,
103+
},
104+
},
105+
{
106+
$sort: { [sort]: sortOrder }, // Sort the results
107+
},
108+
{
109+
$skip: skip, // Skip the first 'skip' results
110+
},
111+
{
112+
$limit: limit, // Limit the results to 'limit'
113+
},
114+
]);
87115

88-
const total = await this.userModel.countDocuments({
89-
username: { $regex: query, $options: 'i' },
90-
publicName: { $regex: query, $options: 'i' },
91-
description: { $regex: query, $options: 'i' },
92-
});
116+
const totalResult = await this.userModel.aggregate([
117+
{
118+
/*
119+
$search: {
120+
index: 'user_search_index',
121+
text: {
122+
query: query,
123+
},
124+
},*/
125+
$match: {
126+
$or: [
127+
{ username: { $regex: query, $options: 'i' } },
128+
{ publicName: { $regex: query, $options: 'i' } },
129+
// { description: { $regex: query, $options: 'i' } },
130+
],
131+
},
132+
},
133+
{
134+
$count: 'total',
135+
},
136+
]);
137+
138+
const total = totalResult.length > 0 ? totalResult[0].total : 0;
93139

94140
this.logger.debug(
95141
`Retrived users: ${users.length} documents, with total: ${total}`,

0 commit comments

Comments
 (0)