mirror of
https://github.com/immich-app/immich.git
synced 2025-01-16 00:36:47 +01:00
chore(server): remove old asset search (#9104)
* chore(server): remove old asset search * chore: remove more unused search code
This commit is contained in:
parent
0c60aaf557
commit
5a49de5592
20 changed files with 30 additions and 1095 deletions
BIN
mobile/openapi/README.md
generated
BIN
mobile/openapi/README.md
generated
Binary file not shown.
BIN
mobile/openapi/doc/AssetApi.md
generated
BIN
mobile/openapi/doc/AssetApi.md
generated
Binary file not shown.
BIN
mobile/openapi/doc/SearchApi.md
generated
BIN
mobile/openapi/doc/SearchApi.md
generated
Binary file not shown.
BIN
mobile/openapi/lib/api/asset_api.dart
generated
BIN
mobile/openapi/lib/api/asset_api.dart
generated
Binary file not shown.
BIN
mobile/openapi/lib/api/search_api.dart
generated
BIN
mobile/openapi/lib/api/search_api.dart
generated
Binary file not shown.
BIN
mobile/openapi/test/asset_api_test.dart
generated
BIN
mobile/openapi/test/asset_api_test.dart
generated
Binary file not shown.
BIN
mobile/openapi/test/search_api_test.dart
generated
BIN
mobile/openapi/test/search_api_test.dart
generated
Binary file not shown.
|
@ -1823,423 +1823,6 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
"/assets": {
|
||||
"get": {
|
||||
"deprecated": true,
|
||||
"operationId": "searchAssets",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "checksum",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "city",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "country",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "createdAfter",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"format": "date-time",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "createdBefore",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"format": "date-time",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "deviceAssetId",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "deviceId",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "encodedVideoPath",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "id",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"format": "uuid",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "isArchived",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "isEncoded",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "isExternal",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "isFavorite",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "isMotion",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "isNotInAlbum",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "isOffline",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "isReadOnly",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "isVisible",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "lensModel",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "libraryId",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"format": "uuid",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "make",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "model",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "order",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/AssetOrder"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "originalFileName",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "originalPath",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "page",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"minimum": 1,
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "personIds",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"format": "uuid",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "previewPath",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "resizePath",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"deprecated": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "size",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"minimum": 1,
|
||||
"maximum": 1000,
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "state",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "takenAfter",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"format": "date-time",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "takenBefore",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"format": "date-time",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "thumbnailPath",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "trashedAfter",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"format": "date-time",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "trashedBefore",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"format": "date-time",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "type",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/AssetTypeEnum"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "updatedAfter",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"format": "date-time",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "updatedBefore",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"format": "date-time",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "webpPath",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"deprecated": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "withArchived",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"default": false,
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "withDeleted",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "withExif",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "withPeople",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "withStacked",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/AssetResponseDto"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"bearer": []
|
||||
},
|
||||
{
|
||||
"cookie": []
|
||||
},
|
||||
{
|
||||
"api_key": []
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"Asset"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/audit/deletes": {
|
||||
"get": {
|
||||
"operationId": "getAuditDeletes",
|
||||
|
@ -4383,130 +3966,6 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
"/search": {
|
||||
"get": {
|
||||
"deprecated": true,
|
||||
"operationId": "search",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "clip",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"deprecated": true,
|
||||
"schema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "motion",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "page",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"minimum": 1,
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "q",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "query",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "recent",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "size",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"minimum": 1,
|
||||
"maximum": 1000,
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "smart",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "type",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"enum": [
|
||||
"IMAGE",
|
||||
"VIDEO",
|
||||
"AUDIO",
|
||||
"OTHER"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "withArchived",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/SearchResponseDto"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"bearer": []
|
||||
},
|
||||
{
|
||||
"cookie": []
|
||||
},
|
||||
{
|
||||
"api_key": []
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"Search"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/search/cities": {
|
||||
"get": {
|
||||
"operationId": "getAssetsByCity",
|
||||
|
|
|
@ -600,31 +600,6 @@ export type FileChecksumResponseDto = {
|
|||
export type FileReportFixDto = {
|
||||
items: FileReportItemDto[];
|
||||
};
|
||||
export type SearchFacetCountResponseDto = {
|
||||
count: number;
|
||||
value: string;
|
||||
};
|
||||
export type SearchFacetResponseDto = {
|
||||
counts: SearchFacetCountResponseDto[];
|
||||
fieldName: string;
|
||||
};
|
||||
export type SearchAlbumResponseDto = {
|
||||
count: number;
|
||||
facets: SearchFacetResponseDto[];
|
||||
items: AlbumResponseDto[];
|
||||
total: number;
|
||||
};
|
||||
export type SearchAssetResponseDto = {
|
||||
count: number;
|
||||
facets: SearchFacetResponseDto[];
|
||||
items: AssetResponseDto[];
|
||||
nextPage: string | null;
|
||||
total: number;
|
||||
};
|
||||
export type SearchResponseDto = {
|
||||
albums: SearchAlbumResponseDto;
|
||||
assets: SearchAssetResponseDto;
|
||||
};
|
||||
export type SearchExploreItem = {
|
||||
data: AssetResponseDto;
|
||||
value: string;
|
||||
|
@ -680,6 +655,31 @@ export type MetadataSearchDto = {
|
|||
withPeople?: boolean;
|
||||
withStacked?: boolean;
|
||||
};
|
||||
export type SearchFacetCountResponseDto = {
|
||||
count: number;
|
||||
value: string;
|
||||
};
|
||||
export type SearchFacetResponseDto = {
|
||||
counts: SearchFacetCountResponseDto[];
|
||||
fieldName: string;
|
||||
};
|
||||
export type SearchAlbumResponseDto = {
|
||||
count: number;
|
||||
facets: SearchFacetResponseDto[];
|
||||
items: AlbumResponseDto[];
|
||||
total: number;
|
||||
};
|
||||
export type SearchAssetResponseDto = {
|
||||
count: number;
|
||||
facets: SearchFacetResponseDto[];
|
||||
items: AssetResponseDto[];
|
||||
nextPage: string | null;
|
||||
total: number;
|
||||
};
|
||||
export type SearchResponseDto = {
|
||||
albums: SearchAlbumResponseDto;
|
||||
assets: SearchAssetResponseDto;
|
||||
};
|
||||
export type PlacesResponseDto = {
|
||||
admin1name?: string;
|
||||
admin2name?: string;
|
||||
|
@ -1530,106 +1530,6 @@ export function updateAsset({ id, updateAssetDto }: {
|
|||
body: updateAssetDto
|
||||
})));
|
||||
}
|
||||
export function searchAssets({ checksum, city, country, createdAfter, createdBefore, deviceAssetId, deviceId, encodedVideoPath, id, isArchived, isEncoded, isExternal, isFavorite, isMotion, isNotInAlbum, isOffline, isReadOnly, isVisible, lensModel, libraryId, make, model, order, originalFileName, originalPath, page, personIds, previewPath, resizePath, size, state, takenAfter, takenBefore, thumbnailPath, trashedAfter, trashedBefore, $type, updatedAfter, updatedBefore, webpPath, withArchived, withDeleted, withExif, withPeople, withStacked }: {
|
||||
checksum?: string;
|
||||
city?: string;
|
||||
country?: string;
|
||||
createdAfter?: string;
|
||||
createdBefore?: string;
|
||||
deviceAssetId?: string;
|
||||
deviceId?: string;
|
||||
encodedVideoPath?: string;
|
||||
id?: string;
|
||||
isArchived?: boolean;
|
||||
isEncoded?: boolean;
|
||||
isExternal?: boolean;
|
||||
isFavorite?: boolean;
|
||||
isMotion?: boolean;
|
||||
isNotInAlbum?: boolean;
|
||||
isOffline?: boolean;
|
||||
isReadOnly?: boolean;
|
||||
isVisible?: boolean;
|
||||
lensModel?: string;
|
||||
libraryId?: string;
|
||||
make?: string;
|
||||
model?: string;
|
||||
order?: AssetOrder;
|
||||
originalFileName?: string;
|
||||
originalPath?: string;
|
||||
page?: number;
|
||||
personIds?: string[];
|
||||
previewPath?: string;
|
||||
resizePath?: string;
|
||||
size?: number;
|
||||
state?: string;
|
||||
takenAfter?: string;
|
||||
takenBefore?: string;
|
||||
thumbnailPath?: string;
|
||||
trashedAfter?: string;
|
||||
trashedBefore?: string;
|
||||
$type?: AssetTypeEnum;
|
||||
updatedAfter?: string;
|
||||
updatedBefore?: string;
|
||||
webpPath?: string;
|
||||
withArchived?: boolean;
|
||||
withDeleted?: boolean;
|
||||
withExif?: boolean;
|
||||
withPeople?: boolean;
|
||||
withStacked?: boolean;
|
||||
}, opts?: Oazapfts.RequestOpts) {
|
||||
return oazapfts.ok(oazapfts.fetchJson<{
|
||||
status: 200;
|
||||
data: AssetResponseDto[];
|
||||
}>(`/assets${QS.query(QS.explode({
|
||||
checksum,
|
||||
city,
|
||||
country,
|
||||
createdAfter,
|
||||
createdBefore,
|
||||
deviceAssetId,
|
||||
deviceId,
|
||||
encodedVideoPath,
|
||||
id,
|
||||
isArchived,
|
||||
isEncoded,
|
||||
isExternal,
|
||||
isFavorite,
|
||||
isMotion,
|
||||
isNotInAlbum,
|
||||
isOffline,
|
||||
isReadOnly,
|
||||
isVisible,
|
||||
lensModel,
|
||||
libraryId,
|
||||
make,
|
||||
model,
|
||||
order,
|
||||
originalFileName,
|
||||
originalPath,
|
||||
page,
|
||||
personIds,
|
||||
previewPath,
|
||||
resizePath,
|
||||
size,
|
||||
state,
|
||||
takenAfter,
|
||||
takenBefore,
|
||||
thumbnailPath,
|
||||
trashedAfter,
|
||||
trashedBefore,
|
||||
"type": $type,
|
||||
updatedAfter,
|
||||
updatedBefore,
|
||||
webpPath,
|
||||
withArchived,
|
||||
withDeleted,
|
||||
withExif,
|
||||
withPeople,
|
||||
withStacked
|
||||
}))}`, {
|
||||
...opts
|
||||
}));
|
||||
}
|
||||
export function getAuditDeletes({ after, entityType, userId }: {
|
||||
after: string;
|
||||
entityType: EntityType;
|
||||
|
@ -2201,36 +2101,6 @@ export function fixAuditFiles({ fileReportFixDto }: {
|
|||
body: fileReportFixDto
|
||||
})));
|
||||
}
|
||||
export function search({ clip, motion, page, q, query, recent, size, smart, $type, withArchived }: {
|
||||
clip?: boolean;
|
||||
motion?: boolean;
|
||||
page?: number;
|
||||
q?: string;
|
||||
query?: string;
|
||||
recent?: boolean;
|
||||
size?: number;
|
||||
smart?: boolean;
|
||||
$type?: "IMAGE" | "VIDEO" | "AUDIO" | "OTHER";
|
||||
withArchived?: boolean;
|
||||
}, opts?: Oazapfts.RequestOpts) {
|
||||
return oazapfts.ok(oazapfts.fetchJson<{
|
||||
status: 200;
|
||||
data: SearchResponseDto;
|
||||
}>(`/search${QS.query(QS.explode({
|
||||
clip,
|
||||
motion,
|
||||
page,
|
||||
q,
|
||||
query,
|
||||
recent,
|
||||
size,
|
||||
smart,
|
||||
"type": $type,
|
||||
withArchived
|
||||
}))}`, {
|
||||
...opts
|
||||
}));
|
||||
}
|
||||
export function getAssetsByCity(opts?: Oazapfts.RequestOpts) {
|
||||
return oazapfts.ok(oazapfts.fetchJson<{
|
||||
status: 200;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Param, Post, Put, Query } from '@nestjs/common';
|
||||
import { ApiOperation, ApiTags } from '@nestjs/swagger';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
import { AssetResponseDto, MemoryLaneResponseDto } from 'src/dtos/asset-response.dto';
|
||||
import {
|
||||
AssetBulkDeleteDto,
|
||||
|
@ -12,30 +12,13 @@ import {
|
|||
UpdateAssetDto,
|
||||
} from 'src/dtos/asset.dto';
|
||||
import { AuthDto } from 'src/dtos/auth.dto';
|
||||
import { MapMarkerDto, MapMarkerResponseDto, MemoryLaneDto, MetadataSearchDto } from 'src/dtos/search.dto';
|
||||
import { MapMarkerDto, MapMarkerResponseDto, MemoryLaneDto } from 'src/dtos/search.dto';
|
||||
import { UpdateStackParentDto } from 'src/dtos/stack.dto';
|
||||
import { Auth, Authenticated, SharedLinkRoute } from 'src/middleware/auth.guard';
|
||||
import { Route } from 'src/middleware/file-upload.interceptor';
|
||||
import { AssetService } from 'src/services/asset.service';
|
||||
import { SearchService } from 'src/services/search.service';
|
||||
import { UUIDParamDto } from 'src/validation';
|
||||
|
||||
@ApiTags('Asset')
|
||||
@Controller('assets')
|
||||
@Authenticated()
|
||||
export class AssetsController {
|
||||
constructor(private searchService: SearchService) {}
|
||||
|
||||
@Get()
|
||||
@ApiOperation({ deprecated: true })
|
||||
async searchAssets(@Auth() auth: AuthDto, @Query() dto: MetadataSearchDto): Promise<AssetResponseDto[]> {
|
||||
const {
|
||||
assets: { items },
|
||||
} = await this.searchService.searchMetadata(auth, dto);
|
||||
return items;
|
||||
}
|
||||
}
|
||||
|
||||
@ApiTags('Asset')
|
||||
@Controller(Route.ASSET)
|
||||
@Authenticated()
|
||||
|
|
|
@ -3,7 +3,7 @@ import { AlbumController } from 'src/controllers/album.controller';
|
|||
import { APIKeyController } from 'src/controllers/api-key.controller';
|
||||
import { AppController } from 'src/controllers/app.controller';
|
||||
import { AssetControllerV1 } from 'src/controllers/asset-v1.controller';
|
||||
import { AssetController, AssetsController } from 'src/controllers/asset.controller';
|
||||
import { AssetController } from 'src/controllers/asset.controller';
|
||||
import { AuditController } from 'src/controllers/audit.controller';
|
||||
import { AuthController } from 'src/controllers/auth.controller';
|
||||
import { DownloadController } from 'src/controllers/download.controller';
|
||||
|
@ -34,7 +34,6 @@ export const controllers = [
|
|||
AppController,
|
||||
AssetController,
|
||||
AssetControllerV1,
|
||||
AssetsController,
|
||||
AuditController,
|
||||
AuthController,
|
||||
DownloadController,
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
import { Body, Controller, Get, HttpCode, HttpStatus, Post, Query } from '@nestjs/common';
|
||||
import { ApiOperation, ApiTags } from '@nestjs/swagger';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
import { AssetResponseDto } from 'src/dtos/asset-response.dto';
|
||||
import { AuthDto } from 'src/dtos/auth.dto';
|
||||
import { PersonResponseDto } from 'src/dtos/person.dto';
|
||||
import {
|
||||
MetadataSearchDto,
|
||||
PlacesResponseDto,
|
||||
SearchDto,
|
||||
SearchExploreResponseDto,
|
||||
SearchPeopleDto,
|
||||
SearchPlacesDto,
|
||||
|
@ -23,12 +22,6 @@ import { SearchService } from 'src/services/search.service';
|
|||
export class SearchController {
|
||||
constructor(private service: SearchService) {}
|
||||
|
||||
@Get()
|
||||
@ApiOperation({ deprecated: true })
|
||||
search(@Auth() auth: AuthDto, @Query() dto: SearchDto): Promise<SearchResponseDto> {
|
||||
return this.service.search(auth, dto);
|
||||
}
|
||||
|
||||
@Post('metadata')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
searchMetadata(@Auth() auth: AuthDto, @Body() dto: MetadataSearchDto): Promise<SearchResponseDto> {
|
||||
|
|
|
@ -199,53 +199,6 @@ export class SmartSearchDto extends BaseSearchDto {
|
|||
query!: string;
|
||||
}
|
||||
|
||||
// TODO: remove after implementing new search filters
|
||||
/** @deprecated */
|
||||
export class SearchDto {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@Optional()
|
||||
q?: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@Optional()
|
||||
query?: string;
|
||||
|
||||
@ValidateBoolean({ optional: true })
|
||||
smart?: boolean;
|
||||
|
||||
/** @deprecated */
|
||||
@ValidateBoolean({ optional: true })
|
||||
clip?: boolean;
|
||||
|
||||
@IsEnum(AssetType)
|
||||
@Optional()
|
||||
type?: AssetType;
|
||||
|
||||
@ValidateBoolean({ optional: true })
|
||||
recent?: boolean;
|
||||
|
||||
@ValidateBoolean({ optional: true })
|
||||
motion?: boolean;
|
||||
|
||||
@ValidateBoolean({ optional: true })
|
||||
withArchived?: boolean;
|
||||
|
||||
@IsInt()
|
||||
@Min(1)
|
||||
@Type(() => Number)
|
||||
@Optional()
|
||||
page?: number;
|
||||
|
||||
@IsInt()
|
||||
@Min(1)
|
||||
@Max(1000)
|
||||
@Type(() => Number)
|
||||
@Optional()
|
||||
size?: number;
|
||||
}
|
||||
|
||||
export class SearchPlacesDto {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
|
|
|
@ -129,10 +129,6 @@ export interface AssetExploreOptions extends AssetExploreFieldOptions {
|
|||
unnest?: boolean;
|
||||
}
|
||||
|
||||
export interface MetadataSearchOptions {
|
||||
numResults: number;
|
||||
}
|
||||
|
||||
export interface AssetFullSyncOptions {
|
||||
ownerId: string;
|
||||
lastCreationDate?: Date;
|
||||
|
@ -188,7 +184,6 @@ export interface IAssetRepository {
|
|||
upsertJobStatus(jobStatus: Partial<AssetJobStatusEntity>): Promise<void>;
|
||||
getAssetIdByCity(userId: string, options: AssetExploreFieldOptions): Promise<SearchExploreItem<string>>;
|
||||
getAssetIdByTag(userId: string, options: AssetExploreFieldOptions): Promise<SearchExploreItem<string>>;
|
||||
searchMetadata(query: string, userIds: string[], options: MetadataSearchOptions): Promise<AssetEntity[]>;
|
||||
getAllForUserFullSync(options: AssetFullSyncOptions): Promise<AssetEntity[]>;
|
||||
getChangedDeltaSync(options: AssetDeltaSyncOptions): Promise<AssetEntity[]>;
|
||||
}
|
||||
|
|
|
@ -5,29 +5,6 @@ import { Paginated } from 'src/utils/pagination';
|
|||
|
||||
export const ISearchRepository = 'ISearchRepository';
|
||||
|
||||
export enum SearchStrategy {
|
||||
SMART = 'SMART',
|
||||
TEXT = 'TEXT',
|
||||
}
|
||||
|
||||
export interface SearchFilter {
|
||||
id?: string;
|
||||
userId: string;
|
||||
type?: AssetType;
|
||||
isFavorite?: boolean;
|
||||
isArchived?: boolean;
|
||||
city?: string;
|
||||
state?: string;
|
||||
country?: string;
|
||||
make?: string;
|
||||
model?: string;
|
||||
objects?: string[];
|
||||
tags?: string[];
|
||||
recent?: boolean;
|
||||
motion?: boolean;
|
||||
debug?: boolean;
|
||||
}
|
||||
|
||||
export interface SearchResult<T> {
|
||||
/** total matches */
|
||||
total: number;
|
||||
|
|
|
@ -738,37 +738,6 @@ WHERE
|
|||
LIMIT
|
||||
12
|
||||
|
||||
-- AssetRepository.searchMetadata
|
||||
SELECT
|
||||
asset.*,
|
||||
e.*,
|
||||
COALESCE("si"."tags", array[]::text[]) AS "tags",
|
||||
COALESCE("si"."objects", array[]::text[]) AS "objects"
|
||||
FROM
|
||||
"assets" "asset"
|
||||
INNER JOIN "exif" "e" ON asset."id" = e."assetId"
|
||||
LEFT JOIN "smart_info" "si" ON si."assetId" = asset."id"
|
||||
WHERE
|
||||
(
|
||||
"asset"."isVisible" = true
|
||||
AND "asset"."ownerId" IN ($1)
|
||||
AND "asset"."isArchived" = $2
|
||||
AND (
|
||||
(
|
||||
e."exifTextSearchableColumn" || COALESCE(
|
||||
si."smartInfoTextSearchableColumn",
|
||||
to_tsvector('english', '')
|
||||
)
|
||||
) @@ PLAINTO_TSQUERY('english', $3)
|
||||
OR asset."originalFileName" = $4
|
||||
)
|
||||
)
|
||||
AND ("asset"."deletedAt" IS NULL)
|
||||
ORDER BY
|
||||
"asset"."fileCreatedAt" DESC
|
||||
LIMIT
|
||||
250
|
||||
|
||||
-- AssetRepository.getAllForUserFullSync
|
||||
SELECT
|
||||
"asset"."id" AS "asset_id",
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import path from 'node:path';
|
||||
import { Chunked, ChunkedArray, DummyValue, GenerateSql } from 'src/decorators';
|
||||
import { AlbumEntity, AssetOrder } from 'src/entities/album.entity';
|
||||
import { AssetJobStatusEntity } from 'src/entities/asset-job-status.entity';
|
||||
|
@ -23,7 +22,6 @@ import {
|
|||
LivePhotoSearchOptions,
|
||||
MapMarker,
|
||||
MapMarkerSearchOptions,
|
||||
MetadataSearchOptions,
|
||||
MonthDay,
|
||||
TimeBucketItem,
|
||||
TimeBucketOptions,
|
||||
|
@ -700,94 +698,6 @@ export class AssetRepository implements IAssetRepository {
|
|||
return builder;
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.STRING, [DummyValue.UUID], { numResults: 250 }] })
|
||||
async searchMetadata(
|
||||
query: string,
|
||||
userIds: string[],
|
||||
{ numResults }: MetadataSearchOptions,
|
||||
): Promise<AssetEntity[]> {
|
||||
const rows = await this.getBuilder({
|
||||
userIds: userIds,
|
||||
exifInfo: false,
|
||||
isArchived: false,
|
||||
})
|
||||
.select('asset.*')
|
||||
.addSelect('e.*')
|
||||
.addSelect('COALESCE(si.tags, array[]::text[])', 'tags')
|
||||
.addSelect('COALESCE(si.objects, array[]::text[])', 'objects')
|
||||
.innerJoin('exif', 'e', 'asset."id" = e."assetId"')
|
||||
.leftJoin('smart_info', 'si', 'si."assetId" = asset."id"')
|
||||
.andWhere(
|
||||
new Brackets((qb) => {
|
||||
qb.where(
|
||||
`(e."exifTextSearchableColumn" || COALESCE(si."smartInfoTextSearchableColumn", to_tsvector('english', '')))
|
||||
@@ PLAINTO_TSQUERY('english', :query)`,
|
||||
{ query },
|
||||
).orWhere('asset."originalFileName" = :path', { path: path.parse(query).name });
|
||||
}),
|
||||
)
|
||||
.addOrderBy('asset.fileCreatedAt', 'DESC')
|
||||
.limit(numResults)
|
||||
.getRawMany();
|
||||
|
||||
return rows.map(
|
||||
({
|
||||
tags,
|
||||
objects,
|
||||
country,
|
||||
state,
|
||||
city,
|
||||
description,
|
||||
model,
|
||||
make,
|
||||
dateTimeOriginal,
|
||||
exifImageHeight,
|
||||
exifImageWidth,
|
||||
exposureTime,
|
||||
fNumber,
|
||||
fileSizeInByte,
|
||||
focalLength,
|
||||
iso,
|
||||
latitude,
|
||||
lensModel,
|
||||
longitude,
|
||||
modifyDate,
|
||||
projectionType,
|
||||
timeZone,
|
||||
...assetInfo
|
||||
}) =>
|
||||
({
|
||||
exifInfo: {
|
||||
city,
|
||||
country,
|
||||
dateTimeOriginal,
|
||||
description,
|
||||
exifImageHeight,
|
||||
exifImageWidth,
|
||||
exposureTime,
|
||||
fNumber,
|
||||
fileSizeInByte,
|
||||
focalLength,
|
||||
iso,
|
||||
latitude,
|
||||
lensModel,
|
||||
longitude,
|
||||
make,
|
||||
model,
|
||||
modifyDate,
|
||||
projectionType,
|
||||
state,
|
||||
timeZone,
|
||||
},
|
||||
smartInfo: {
|
||||
tags,
|
||||
objects,
|
||||
},
|
||||
...assetInfo,
|
||||
}) as AssetEntity,
|
||||
);
|
||||
}
|
||||
|
||||
@GenerateSql({
|
||||
params: [
|
||||
{
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
import { mapAsset } from 'src/dtos/asset-response.dto';
|
||||
import { SearchDto } from 'src/dtos/search.dto';
|
||||
import { SystemConfigKey } from 'src/entities/system-config.entity';
|
||||
import { IAssetRepository } from 'src/interfaces/asset.interface';
|
||||
import { ILoggerRepository } from 'src/interfaces/logger.interface';
|
||||
import { IMachineLearningRepository } from 'src/interfaces/machine-learning.interface';
|
||||
|
@ -97,119 +95,4 @@ describe(SearchService.name, () => {
|
|||
expect(result).toEqual(expectedResponse);
|
||||
});
|
||||
});
|
||||
|
||||
describe('search', () => {
|
||||
it('should throw an error if query is missing', async () => {
|
||||
await expect(sut.search(authStub.user1, { q: '' })).rejects.toThrow('Missing query');
|
||||
});
|
||||
|
||||
it('should search by metadata if `clip` option is false', async () => {
|
||||
const dto: SearchDto = { q: 'test query', clip: false };
|
||||
assetMock.searchMetadata.mockResolvedValueOnce([assetStub.image]);
|
||||
partnerMock.getAll.mockResolvedValueOnce([]);
|
||||
const expectedResponse = {
|
||||
albums: {
|
||||
total: 0,
|
||||
count: 0,
|
||||
items: [],
|
||||
facets: [],
|
||||
},
|
||||
assets: {
|
||||
total: 1,
|
||||
count: 1,
|
||||
items: [mapAsset(assetStub.image)],
|
||||
facets: [],
|
||||
nextPage: null,
|
||||
},
|
||||
};
|
||||
|
||||
const result = await sut.search(authStub.user1, dto);
|
||||
|
||||
expect(result).toEqual(expectedResponse);
|
||||
expect(assetMock.searchMetadata).toHaveBeenCalledWith(dto.q, [authStub.user1.user.id], { numResults: 250 });
|
||||
expect(searchMock.searchSmart).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should search archived photos if `withArchived` option is true', async () => {
|
||||
const dto: SearchDto = { q: 'test query', clip: true, withArchived: true };
|
||||
const embedding = [1, 2, 3];
|
||||
searchMock.searchSmart.mockResolvedValueOnce({ items: [assetStub.image], hasNextPage: false });
|
||||
machineMock.encodeText.mockResolvedValueOnce(embedding);
|
||||
partnerMock.getAll.mockResolvedValueOnce([]);
|
||||
const expectedResponse = {
|
||||
albums: {
|
||||
total: 0,
|
||||
count: 0,
|
||||
items: [],
|
||||
facets: [],
|
||||
},
|
||||
assets: {
|
||||
total: 1,
|
||||
count: 1,
|
||||
items: [mapAsset(assetStub.image)],
|
||||
facets: [],
|
||||
nextPage: null,
|
||||
},
|
||||
};
|
||||
|
||||
const result = await sut.search(authStub.user1, dto);
|
||||
|
||||
expect(result).toEqual(expectedResponse);
|
||||
expect(searchMock.searchSmart).toHaveBeenCalledWith(
|
||||
{ page: 1, size: 100 },
|
||||
{
|
||||
userIds: [authStub.user1.user.id],
|
||||
embedding,
|
||||
withArchived: true,
|
||||
},
|
||||
);
|
||||
expect(assetMock.searchMetadata).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should search by CLIP if `clip` option is true', async () => {
|
||||
const dto: SearchDto = { q: 'test query', clip: true };
|
||||
const embedding = [1, 2, 3];
|
||||
searchMock.searchSmart.mockResolvedValueOnce({ items: [assetStub.image], hasNextPage: false });
|
||||
machineMock.encodeText.mockResolvedValueOnce(embedding);
|
||||
partnerMock.getAll.mockResolvedValueOnce([]);
|
||||
const expectedResponse = {
|
||||
albums: {
|
||||
total: 0,
|
||||
count: 0,
|
||||
items: [],
|
||||
facets: [],
|
||||
},
|
||||
assets: {
|
||||
total: 1,
|
||||
count: 1,
|
||||
items: [mapAsset(assetStub.image)],
|
||||
facets: [],
|
||||
nextPage: null,
|
||||
},
|
||||
};
|
||||
|
||||
const result = await sut.search(authStub.user1, dto);
|
||||
|
||||
expect(result).toEqual(expectedResponse);
|
||||
expect(searchMock.searchSmart).toHaveBeenCalledWith(
|
||||
{ page: 1, size: 100 },
|
||||
{
|
||||
userIds: [authStub.user1.user.id],
|
||||
embedding,
|
||||
withArchived: false,
|
||||
},
|
||||
);
|
||||
expect(assetMock.searchMetadata).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it.each([
|
||||
{ key: SystemConfigKey.MACHINE_LEARNING_ENABLED },
|
||||
{ key: SystemConfigKey.MACHINE_LEARNING_CLIP_ENABLED },
|
||||
])('should throw an error if clip is requested but disabled', async ({ key }) => {
|
||||
const dto: SearchDto = { q: 'test query', clip: true };
|
||||
configMock.load.mockResolvedValue([{ key, value: false }]);
|
||||
|
||||
await expect(sut.search(authStub.user1, dto)).rejects.toThrow('Smart search is not enabled');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,7 +6,6 @@ import { PersonResponseDto } from 'src/dtos/person.dto';
|
|||
import {
|
||||
MetadataSearchDto,
|
||||
PlacesResponseDto,
|
||||
SearchDto,
|
||||
SearchPeopleDto,
|
||||
SearchPlacesDto,
|
||||
SearchResponseDto,
|
||||
|
@ -23,7 +22,7 @@ import { IMachineLearningRepository } from 'src/interfaces/machine-learning.inte
|
|||
import { IMetadataRepository } from 'src/interfaces/metadata.interface';
|
||||
import { IPartnerRepository } from 'src/interfaces/partner.interface';
|
||||
import { IPersonRepository } from 'src/interfaces/person.interface';
|
||||
import { ISearchRepository, SearchExploreItem, SearchStrategy } from 'src/interfaces/search.interface';
|
||||
import { ISearchRepository, SearchExploreItem } from 'src/interfaces/search.interface';
|
||||
import { ISystemConfigRepository } from 'src/interfaces/system-config.interface';
|
||||
|
||||
@Injectable()
|
||||
|
@ -145,60 +144,6 @@ export class SearchService {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: remove after implementing new search filters
|
||||
/** @deprecated */
|
||||
async search(auth: AuthDto, dto: SearchDto): Promise<SearchResponseDto> {
|
||||
await this.configCore.requireFeature(FeatureFlag.SEARCH);
|
||||
const { machineLearning } = await this.configCore.getConfig();
|
||||
const query = dto.q || dto.query;
|
||||
if (!query) {
|
||||
throw new Error('Missing query');
|
||||
}
|
||||
|
||||
let strategy = SearchStrategy.TEXT;
|
||||
if (dto.smart || dto.clip) {
|
||||
await this.configCore.requireFeature(FeatureFlag.SMART_SEARCH);
|
||||
strategy = SearchStrategy.SMART;
|
||||
}
|
||||
|
||||
const userIds = await this.getUserIdsToSearch(auth);
|
||||
const page = dto.page ?? 1;
|
||||
|
||||
let nextPage: string | null = null;
|
||||
let assets: AssetEntity[] = [];
|
||||
switch (strategy) {
|
||||
case SearchStrategy.SMART: {
|
||||
const embedding = await this.machineLearning.encodeText(
|
||||
machineLearning.url,
|
||||
{ text: query },
|
||||
machineLearning.clip,
|
||||
);
|
||||
|
||||
const { hasNextPage, items } = await this.searchRepository.searchSmart(
|
||||
{ page, size: dto.size || 100 },
|
||||
{
|
||||
userIds,
|
||||
embedding,
|
||||
withArchived: !!dto.withArchived,
|
||||
},
|
||||
);
|
||||
if (hasNextPage) {
|
||||
nextPage = (page + 1).toString();
|
||||
}
|
||||
assets = items;
|
||||
break;
|
||||
}
|
||||
case SearchStrategy.TEXT: {
|
||||
assets = await this.assetRepository.searchMetadata(query, userIds, { numResults: dto.size || 250 });
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return this.mapResponse(assets, nextPage);
|
||||
}
|
||||
|
||||
private async getUserIdsToSearch(auth: AuthDto): Promise<string[]> {
|
||||
const userIds: string[] = [auth.user.id];
|
||||
const partners = await this.partnerRepository.getAll(auth.user.id);
|
||||
|
|
|
@ -35,7 +35,6 @@ export const newAssetRepositoryMock = (): Mocked<IAssetRepository> => {
|
|||
softDeleteAll: vitest.fn(),
|
||||
getAssetIdByCity: vitest.fn(),
|
||||
getAssetIdByTag: vitest.fn(),
|
||||
searchMetadata: vitest.fn(),
|
||||
getAllForUserFullSync: vitest.fn(),
|
||||
getChangedDeltaSync: vitest.fn(),
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue