mirror of
https://github.com/immich-app/immich.git
synced 2025-12-22 01:11:20 +03:00
feat(server): sort assets randomly from the API 'api/search/metadata' endpoint by including 'order': 'rand' in the API call. (#12741)
feat(server): search metadata random sort order Co-authored-by: Jason Rasmussen <jason@rasm.me>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Param, Post, Put, Query } from '@nestjs/common';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
import { EndpointLifecycle } from 'src/decorators';
|
||||
import { AssetResponseDto, MemoryLaneResponseDto } from 'src/dtos/asset-response.dto';
|
||||
import {
|
||||
AssetBulkDeleteDto,
|
||||
@@ -31,6 +32,7 @@ export class AssetController {
|
||||
|
||||
@Get('random')
|
||||
@Authenticated()
|
||||
@EndpointLifecycle({ deprecatedAt: 'v1.116.0' })
|
||||
getRandom(@Auth() auth: AuthDto, @Query() dto: RandomAssetsDto): Promise<AssetResponseDto[]> {
|
||||
return this.service.getRandom(auth, dto.count ?? 1);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { PersonResponseDto } from 'src/dtos/person.dto';
|
||||
import {
|
||||
MetadataSearchDto,
|
||||
PlacesResponseDto,
|
||||
RandomSearchDto,
|
||||
SearchExploreResponseDto,
|
||||
SearchPeopleDto,
|
||||
SearchPlacesDto,
|
||||
@@ -28,6 +29,13 @@ export class SearchController {
|
||||
return this.service.searchMetadata(auth, dto);
|
||||
}
|
||||
|
||||
@Post('random')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@Authenticated()
|
||||
searchRandom(@Auth() auth: AuthDto, @Body() dto: RandomSearchDto): Promise<SearchResponseDto> {
|
||||
return this.service.searchRandom(auth, dto);
|
||||
}
|
||||
|
||||
@Post('smart')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@Authenticated()
|
||||
|
||||
@@ -119,7 +119,15 @@ class BaseSearchDto {
|
||||
personIds?: string[];
|
||||
}
|
||||
|
||||
export class MetadataSearchDto extends BaseSearchDto {
|
||||
export class RandomSearchDto extends BaseSearchDto {
|
||||
@ValidateBoolean({ optional: true })
|
||||
withStacked?: boolean;
|
||||
|
||||
@ValidateBoolean({ optional: true })
|
||||
withPeople?: boolean;
|
||||
}
|
||||
|
||||
export class MetadataSearchDto extends RandomSearchDto {
|
||||
@ValidateUUID({ optional: true })
|
||||
id?: string;
|
||||
|
||||
@@ -133,12 +141,6 @@ export class MetadataSearchDto extends BaseSearchDto {
|
||||
@Optional()
|
||||
checksum?: string;
|
||||
|
||||
@ValidateBoolean({ optional: true })
|
||||
withStacked?: boolean;
|
||||
|
||||
@ValidateBoolean({ optional: true })
|
||||
withPeople?: boolean;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@Optional()
|
||||
|
||||
@@ -116,6 +116,7 @@ export interface SearchPeopleOptions {
|
||||
|
||||
export interface SearchOrderOptions {
|
||||
orderDirection?: 'ASC' | 'DESC';
|
||||
random?: boolean;
|
||||
}
|
||||
|
||||
export interface SearchPaginationOptions {
|
||||
|
||||
@@ -73,8 +73,13 @@ export class SearchRepository implements ISearchRepository {
|
||||
async searchMetadata(pagination: SearchPaginationOptions, options: AssetSearchOptions): Paginated<AssetEntity> {
|
||||
let builder = this.assetRepository.createQueryBuilder('asset');
|
||||
builder = searchAssetBuilder(builder, options);
|
||||
|
||||
builder.orderBy('asset.fileCreatedAt', options.orderDirection ?? 'DESC');
|
||||
|
||||
if (options.random) {
|
||||
// TODO replace with complicated SQL magic after kysely migration
|
||||
builder.addSelect('RANDOM() as r').orderBy('r');
|
||||
}
|
||||
|
||||
return paginatedBuilder<AssetEntity>(builder, {
|
||||
mode: PaginationMode.SKIP_TAKE,
|
||||
skip: (pagination.page - 1) * pagination.size,
|
||||
|
||||
@@ -6,6 +6,7 @@ import { PersonResponseDto } from 'src/dtos/person.dto';
|
||||
import {
|
||||
MetadataSearchDto,
|
||||
PlacesResponseDto,
|
||||
RandomSearchDto,
|
||||
SearchPeopleDto,
|
||||
SearchPlacesDto,
|
||||
SearchResponseDto,
|
||||
@@ -93,6 +94,22 @@ export class SearchService {
|
||||
return this.mapResponse(items, hasNextPage ? (page + 1).toString() : null, { auth });
|
||||
}
|
||||
|
||||
async searchRandom(auth: AuthDto, dto: RandomSearchDto): Promise<SearchResponseDto> {
|
||||
const userIds = await this.getUserIdsToSearch(auth);
|
||||
const page = dto.page ?? 1;
|
||||
const size = dto.size || 250;
|
||||
const { hasNextPage, items } = await this.searchRepository.searchMetadata(
|
||||
{ page, size },
|
||||
{
|
||||
...dto,
|
||||
userIds,
|
||||
random: true,
|
||||
},
|
||||
);
|
||||
|
||||
return this.mapResponse(items, hasNextPage ? (page + 1).toString() : null, { auth });
|
||||
}
|
||||
|
||||
async searchSmart(auth: AuthDto, dto: SmartSearchDto): Promise<SearchResponseDto> {
|
||||
const { machineLearning } = await this.configCore.getConfig({ withCache: false });
|
||||
if (!isSmartSearchEnabled(machineLearning)) {
|
||||
|
||||
Reference in New Issue
Block a user