backend: update field selection to use arrays instead of comma-separated strings across repositories and services
This commit is contained in:
parent
66978509b1
commit
491c3392ce
@ -1,4 +1,4 @@
|
||||
import { SortAndPagination } from '@openvidu-meet/typings';
|
||||
import { SortAndPagination, SortOrder } from '@openvidu-meet/typings';
|
||||
import { inject, injectable, unmanaged } from 'inversify';
|
||||
import { Document, FilterQuery, Model, UpdateQuery } from 'mongoose';
|
||||
import { PaginatedResult, PaginationCursor } from '../models/db-pagination.model.js';
|
||||
@ -30,22 +30,17 @@ export abstract class BaseRepository<TDomain, TDocument extends Document> {
|
||||
/**
|
||||
* Finds a single document matching the given filter.
|
||||
* @param filter - MongoDB query filter
|
||||
* @param fields - Optional comma-separated list of fields to select from database
|
||||
* @param fields - Optional array of field names to select from database
|
||||
* @returns The document or null if not found
|
||||
*/
|
||||
protected async findOne(filter: FilterQuery<TDocument>, fields?: string): Promise<TDocument | null> {
|
||||
protected async findOne(filter: FilterQuery<TDocument>, fields?: string[]): Promise<TDocument | null> {
|
||||
try {
|
||||
let query = this.model.findOne(filter);
|
||||
|
||||
if (fields) {
|
||||
//!FIXME: This transform should be optimized to avoid unnecessary string manipulation
|
||||
|
||||
const fieldSelection = fields
|
||||
.split(',')
|
||||
.map((field) => field.trim())
|
||||
.filter((field) => field !== '')
|
||||
.join(' ');
|
||||
query = query.select(fieldSelection);
|
||||
// Apply field selection if specified
|
||||
if (fields && fields.length > 0) {
|
||||
// Convert array of fields to space-separated string for Mongoose select()
|
||||
query = query.select(fields.join(' '));
|
||||
}
|
||||
|
||||
return await query.exec();
|
||||
@ -62,20 +57,17 @@ export abstract class BaseRepository<TDomain, TDocument extends Document> {
|
||||
* WARNING: Use with caution on large collections. Consider using findMany() with pagination instead.
|
||||
*
|
||||
* @param filter - Base MongoDB query filter
|
||||
* @param fields - Optional comma-separated list of fields to select from database
|
||||
* @param fields - Optional array of field names to select from database
|
||||
* @returns Array of domain objects matching the filter
|
||||
*/
|
||||
protected async findAll(filter: FilterQuery<TDocument> = {}, fields?: string): Promise<TDomain[]> {
|
||||
protected async findAll(filter: FilterQuery<TDocument> = {}, fields?: string[]): Promise<TDomain[]> {
|
||||
try {
|
||||
let query = this.model.find(filter);
|
||||
|
||||
if (fields) {
|
||||
const fieldSelection = fields
|
||||
.split(',')
|
||||
.map((field) => field.trim())
|
||||
.filter((field) => field !== '')
|
||||
.join(' ');
|
||||
query = query.select(fieldSelection);
|
||||
// Apply field selection if specified
|
||||
if (fields && fields.length > 0) {
|
||||
// Convert array of fields to space-separated string for Mongoose select()
|
||||
query = query.select(fields.join(' '));
|
||||
}
|
||||
|
||||
// Transform documents to domain objects
|
||||
@ -96,15 +88,15 @@ export abstract class BaseRepository<TDomain, TDocument extends Document> {
|
||||
* @param options.nextPageToken - Token for pagination (encoded cursor)
|
||||
* @param options.sortField - Field to sort by (default: 'createdAt')
|
||||
* @param options.sortOrder - Sort order: 'asc' or 'desc' (default: 'desc')
|
||||
* @param fields - Optional comma-separated list of fields to select from database
|
||||
* @param fields - Optional array of field names to select from database
|
||||
* @returns Paginated result with items, truncation flag, and optional next token
|
||||
*/
|
||||
protected async findMany(
|
||||
filter: FilterQuery<TDocument> = {},
|
||||
options: SortAndPagination = {},
|
||||
fields?: string
|
||||
fields?: string[]
|
||||
): Promise<PaginatedResult<TDomain>> {
|
||||
const { maxItems = 100, nextPageToken, sortField = '_id', sortOrder = 'desc' } = options;
|
||||
const { maxItems = 100, nextPageToken, sortField = '_id', sortOrder = SortOrder.DESC } = options;
|
||||
|
||||
// Parse and apply pagination cursor if provided
|
||||
if (nextPageToken) {
|
||||
@ -113,7 +105,7 @@ export abstract class BaseRepository<TDomain, TDocument extends Document> {
|
||||
}
|
||||
|
||||
// Convert sort order to MongoDB format
|
||||
const mongoSortOrder: 1 | -1 = sortOrder === 'asc' ? 1 : -1;
|
||||
const mongoSortOrder: 1 | -1 = sortOrder === SortOrder.ASC ? 1 : -1;
|
||||
|
||||
// Build compound sort: primary field + _id
|
||||
const sort: Record<string, 1 | -1> = {
|
||||
@ -128,17 +120,9 @@ export abstract class BaseRepository<TDomain, TDocument extends Document> {
|
||||
let query = this.model.find(filter).sort(sort).limit(limit);
|
||||
|
||||
// Apply field selection if specified
|
||||
if (fields) {
|
||||
// !FIXME: This transform should be optimized to avoid unnecessary string manipulation.
|
||||
// !The argument method should ideally accept an array of fields instead of a comma-separated string to avoid this overhead.
|
||||
// Convert comma-separated string to space-separated format for MongoDB select()
|
||||
const fieldSelection = fields
|
||||
.split(',')
|
||||
.map((field) => field.trim())
|
||||
.filter((field) => field !== '')
|
||||
.join(' ');
|
||||
|
||||
query = query.select(fieldSelection);
|
||||
if (fields && fields.length > 0) {
|
||||
// Convert array of fields to space-separated string for Mongoose select()
|
||||
query = query.select(fields.join(' '));
|
||||
}
|
||||
|
||||
const documents = await query.exec();
|
||||
@ -350,10 +334,10 @@ export abstract class BaseRepository<TDomain, TDocument extends Document> {
|
||||
filter: FilterQuery<TDocument>,
|
||||
cursor: PaginationCursor,
|
||||
sortField: string,
|
||||
sortOrder: 'asc' | 'desc'
|
||||
sortOrder: SortOrder
|
||||
): void {
|
||||
const comparison = sortOrder === 'asc' ? '$gt' : '$lt';
|
||||
const equalComparison = sortOrder === 'asc' ? '$gt' : '$lt';
|
||||
const comparison = sortOrder === SortOrder.ASC ? '$gt' : '$lt';
|
||||
const equalComparison = sortOrder === SortOrder.ASC ? '$gt' : '$lt';
|
||||
|
||||
// Build compound filter for pagination
|
||||
// This ensures correct ordering even when sortField values are not unique
|
||||
@ -368,7 +352,7 @@ export abstract class BaseRepository<TDomain, TDocument extends Document> {
|
||||
} as FilterQuery<TDocument>);
|
||||
|
||||
// In ascending order, also include documents where the field exists (they come after missing fields)
|
||||
if (sortOrder === 'asc') {
|
||||
if (sortOrder === SortOrder.ASC) {
|
||||
orConditions.push({
|
||||
[sortField]: { $exists: true }
|
||||
} as FilterQuery<TDocument>);
|
||||
@ -386,7 +370,7 @@ export abstract class BaseRepository<TDomain, TDocument extends Document> {
|
||||
);
|
||||
|
||||
// In descending order, also include documents where the field doesn't exist (they come after all values)
|
||||
if (sortOrder === 'desc') {
|
||||
if (sortOrder === SortOrder.DESC) {
|
||||
orConditions.push({
|
||||
[sortField]: { $exists: false }
|
||||
} as FilterQuery<TDocument>);
|
||||
|
||||
@ -2,7 +2,8 @@ import {
|
||||
MeetRecordingField,
|
||||
MeetRecordingFilters,
|
||||
MeetRecordingInfo,
|
||||
MeetRecordingStatus
|
||||
MeetRecordingStatus,
|
||||
SortOrder
|
||||
} from '@openvidu-meet/typings';
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { uid as secureUid } from 'uid/secure';
|
||||
@ -78,13 +79,11 @@ export class RecordingRepository<TRecording extends MeetRecordingInfo = MeetReco
|
||||
* Finds a recording by its recordingId.
|
||||
*
|
||||
* @param recordingId - The ID of the recording to find
|
||||
* @param fields - Comma-separated list of fields to include in the result
|
||||
* @param fields - Array of field names to include in the result
|
||||
* @returns The recording (without access secrets), or null if not found
|
||||
*/
|
||||
async findByRecordingId(recordingId: string, fields?: MeetRecordingField[]): Promise<TRecording | null> {
|
||||
//!FIXME: This transform should be removed because the findOne method should accept an array of fields instead of a comma-separated string, to avoid unnecessary string manipulation
|
||||
const fieldsString = fields ? fields.join(',') : undefined;
|
||||
const document = await this.findOne({ recordingId }, fieldsString);
|
||||
const document = await this.findOne({ recordingId }, fields);
|
||||
return document ? this.toDomain(document) : null;
|
||||
}
|
||||
|
||||
@ -100,7 +99,7 @@ export class RecordingRepository<TRecording extends MeetRecordingInfo = MeetReco
|
||||
* @param options.roomId - Optional room ID for exact match filtering
|
||||
* @param options.roomName - Optional room name for regex match filtering (case-insensitive)
|
||||
* @param options.status - Optional recording status to filter by
|
||||
* @param options.fields - Comma-separated list of fields to include in the result
|
||||
* @param options.fields - Array of field names to include in the result
|
||||
* @param options.maxItems - Maximum number of results to return (default: 10)
|
||||
* @param options.nextPageToken - Token for pagination (encoded cursor with last sortField value and _id)
|
||||
* @param options.sortField - Field to sort by (default: 'startDate')
|
||||
@ -121,7 +120,7 @@ export class RecordingRepository<TRecording extends MeetRecordingInfo = MeetReco
|
||||
maxItems = 10,
|
||||
nextPageToken,
|
||||
sortField = 'startDate',
|
||||
sortOrder = 'desc'
|
||||
sortOrder = SortOrder.DESC
|
||||
} = options;
|
||||
|
||||
// Build base filter
|
||||
@ -156,8 +155,7 @@ export class RecordingRepository<TRecording extends MeetRecordingInfo = MeetReco
|
||||
sortField,
|
||||
sortOrder
|
||||
},
|
||||
//! FIXME: This transform should be removed because the findMany method should accept an array of fields instead of a comma-separated string, to avoid unnecessary string manipulation
|
||||
fields?.join(',')
|
||||
fields
|
||||
);
|
||||
|
||||
return {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { MeetRoomMember, MeetRoomMemberFilters, MeetRoomMemberPermissions } from '@openvidu-meet/typings';
|
||||
import { MeetRoomMember, MeetRoomMemberFilters, MeetRoomMemberPermissions, SortOrder } from '@openvidu-meet/typings';
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { MeetRoomMemberDocument, MeetRoomMemberModel } from '../models/mongoose-schemas/room-member.schema.js';
|
||||
import { LoggerService } from '../services/logger.service.js';
|
||||
@ -67,10 +67,10 @@ export class RoomMemberRepository<TRoomMember extends MeetRoomMember = MeetRoomM
|
||||
*
|
||||
* @param roomId - The ID of the room
|
||||
* @param memberIds - Array of member identifiers
|
||||
* @param fields - Comma-separated list of fields to include in the result
|
||||
* @param fields - Array of field names to include in the result
|
||||
* @returns Array of found room members
|
||||
*/
|
||||
async findByRoomAndMemberIds(roomId: string, memberIds: string[], fields?: string): Promise<TRoomMember[]> {
|
||||
async findByRoomAndMemberIds(roomId: string, memberIds: string[], fields?: string[]): Promise<TRoomMember[]> {
|
||||
return await this.findAll({ roomId, memberId: { $in: memberIds } }, fields);
|
||||
}
|
||||
|
||||
@ -81,7 +81,7 @@ export class RoomMemberRepository<TRoomMember extends MeetRoomMember = MeetRoomM
|
||||
* @returns Array of room IDs where the user is a member
|
||||
*/
|
||||
async getRoomIdsByMemberId(memberId: string): Promise<string[]> {
|
||||
const members = await this.findAll({ memberId }, 'roomId');
|
||||
const members = await this.findAll({ memberId }, ['roomId']);
|
||||
return members.map((m) => m.roomId);
|
||||
}
|
||||
|
||||
@ -101,7 +101,7 @@ export class RoomMemberRepository<TRoomMember extends MeetRoomMember = MeetRoomM
|
||||
memberId,
|
||||
[`effectivePermissions.${permission}`]: true
|
||||
},
|
||||
'roomId'
|
||||
['roomId']
|
||||
);
|
||||
return members.map((member) => member.roomId);
|
||||
}
|
||||
@ -112,7 +112,7 @@ export class RoomMemberRepository<TRoomMember extends MeetRoomMember = MeetRoomM
|
||||
* @param roomId - The ID of the room
|
||||
* @param options - Query options
|
||||
* @param options.name - Optional member name to filter by (case-insensitive partial match)
|
||||
* @param options.fields - Comma-separated list of fields to include in the result
|
||||
* @param options.fields - Array of field names to include in the result
|
||||
* @param options.maxItems - Maximum number of results to return (default: 100)
|
||||
* @param options.nextPageToken - Token for pagination
|
||||
* @param options.sortField - Field to sort by (default: 'membershipDate')
|
||||
@ -133,7 +133,7 @@ export class RoomMemberRepository<TRoomMember extends MeetRoomMember = MeetRoomM
|
||||
maxItems = 100,
|
||||
nextPageToken,
|
||||
sortField = 'membershipDate',
|
||||
sortOrder = 'desc'
|
||||
sortOrder = SortOrder.DESC
|
||||
} = options;
|
||||
|
||||
// Build base filter
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { MeetRoom, MeetRoomField, MeetRoomFilters, MeetRoomStatus } from '@openvidu-meet/typings';
|
||||
import { MeetRoom, MeetRoomField, MeetRoomFilters, MeetRoomStatus, SortOrder } from '@openvidu-meet/typings';
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { MeetRoomDocument, MeetRoomModel } from '../models/mongoose-schemas/room.schema.js';
|
||||
import { LoggerService } from '../services/logger.service.js';
|
||||
@ -73,13 +73,11 @@ export class RoomRepository<TRoom extends MeetRoom = MeetRoom> extends BaseRepos
|
||||
* Returns the room with enriched URLs (including base URL).
|
||||
*
|
||||
* @param roomId - The unique room identifier
|
||||
* @param fields - Comma-separated list of fields to include in the result
|
||||
* @param fields - Array of field names to include in the result
|
||||
* @returns The room or null if not found
|
||||
*/
|
||||
async findByRoomId(roomId: string, fields?: MeetRoomField[]): Promise<TRoom | null> {
|
||||
//!FIXME: This transform should be removed once the controller is updated to pass the fields as an array of MeetRoomField instead of a comma-separated string.
|
||||
const fieldsString = fields ? fields.join(',') : undefined;
|
||||
const document = await this.findOne({ roomId }, fieldsString);
|
||||
const document = await this.findOne({ roomId }, fields);
|
||||
return document ? this.enrichRoomWithBaseUrls(document) : null;
|
||||
}
|
||||
|
||||
@ -88,10 +86,10 @@ export class RoomRepository<TRoom extends MeetRoom = MeetRoom> extends BaseRepos
|
||||
* Returns rooms with enriched URLs (including base URL).
|
||||
*
|
||||
* @param owner - The userId of the room owner
|
||||
* @param fields - Comma-separated list of fields to include in the result
|
||||
* @param fields - Array of field names to include in the result
|
||||
* @returns Array of rooms owned by the user
|
||||
*/
|
||||
async findByOwner(owner: string, fields?: string): Promise<TRoom[]> {
|
||||
async findByOwner(owner: string, fields?: MeetRoomField[]): Promise<TRoom[]> {
|
||||
return await this.findAll({ owner }, fields);
|
||||
}
|
||||
|
||||
@ -107,7 +105,7 @@ export class RoomRepository<TRoom extends MeetRoom = MeetRoom> extends BaseRepos
|
||||
* @param options.status - Optional room status to filter by
|
||||
* @param options.owner - Optional owner userId to filter by
|
||||
* @param options.roomIds - Optional array of room IDs to filter by, representing rooms the user is a member of
|
||||
* @param options.fields - Comma-separated list of fields to include in the result
|
||||
* @param options.fields - Array of field names to include in the result
|
||||
* @param options.maxItems - Maximum number of results to return (default: 100)
|
||||
* @param options.nextPageToken - Token for pagination (encoded cursor with last sortField value and _id)
|
||||
* @param options.sortField - Field to sort by (default: 'creationDate')
|
||||
@ -128,7 +126,7 @@ export class RoomRepository<TRoom extends MeetRoom = MeetRoom> extends BaseRepos
|
||||
maxItems = 100,
|
||||
nextPageToken,
|
||||
sortField = 'creationDate',
|
||||
sortOrder = 'desc'
|
||||
sortOrder = SortOrder.DESC
|
||||
} = options;
|
||||
|
||||
// Build base filter
|
||||
@ -160,8 +158,7 @@ export class RoomRepository<TRoom extends MeetRoom = MeetRoom> extends BaseRepos
|
||||
sortField,
|
||||
sortOrder
|
||||
},
|
||||
//! FIXME: This transform should be removed because the findMany method should accept an array of fields instead of a comma-separated string, to avoid unnecessary string manipulation
|
||||
fields?.join(',')
|
||||
fields
|
||||
);
|
||||
|
||||
return {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { MeetUser, MeetUserFilters } from '@openvidu-meet/typings';
|
||||
import { MeetUser, MeetUserFilters, SortOrder } from '@openvidu-meet/typings';
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { MeetUserDocument, MeetUserModel } from '../models/mongoose-schemas/user.schema.js';
|
||||
import { LoggerService } from '../services/logger.service.js';
|
||||
@ -95,7 +95,7 @@ export class UserRepository<TUser extends MeetUser = MeetUser> extends BaseRepos
|
||||
maxItems = 100,
|
||||
nextPageToken,
|
||||
sortField = 'registrationDate',
|
||||
sortOrder = 'desc'
|
||||
sortOrder = SortOrder.DESC
|
||||
} = options;
|
||||
|
||||
// Build base filter
|
||||
|
||||
@ -361,11 +361,10 @@ export class RoomMemberService {
|
||||
deleted: string[];
|
||||
failed: { memberId: string; error: string }[];
|
||||
}> {
|
||||
const membersToDelete = await this.roomMemberRepository.findByRoomAndMemberIds(
|
||||
roomId,
|
||||
memberIds,
|
||||
'memberId,currentParticipantIdentity'
|
||||
);
|
||||
const membersToDelete = await this.roomMemberRepository.findByRoomAndMemberIds(roomId, memberIds, [
|
||||
'memberId',
|
||||
'currentParticipantIdentity'
|
||||
]);
|
||||
const foundMemberIds = membersToDelete.map((m) => m.memberId);
|
||||
|
||||
const failed = memberIds
|
||||
|
||||
@ -396,7 +396,7 @@ export class RoomService {
|
||||
|
||||
// If USER role, also get owned room IDs
|
||||
if (user.role === MeetUserRole.USER) {
|
||||
const ownedRooms = await this.roomRepository.findByOwner(user.userId, 'roomId');
|
||||
const ownedRooms = await this.roomRepository.findByOwner(user.userId, ['roomId']);
|
||||
ownedRoomIds = ownedRooms.map((r) => r.roomId);
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user