mirror of
https://github.com/immich-app/immich.git
synced 2025-01-27 22:22:45 +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
mobile/openapi
open-api
server
src
controllers
dtos
interfaces
queries
repositories
services
test/repositories
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": {
|
"/audit/deletes": {
|
||||||
"get": {
|
"get": {
|
||||||
"operationId": "getAuditDeletes",
|
"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": {
|
"/search/cities": {
|
||||||
"get": {
|
"get": {
|
||||||
"operationId": "getAssetsByCity",
|
"operationId": "getAssetsByCity",
|
||||||
|
|
|
@ -600,31 +600,6 @@ export type FileChecksumResponseDto = {
|
||||||
export type FileReportFixDto = {
|
export type FileReportFixDto = {
|
||||||
items: FileReportItemDto[];
|
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 = {
|
export type SearchExploreItem = {
|
||||||
data: AssetResponseDto;
|
data: AssetResponseDto;
|
||||||
value: string;
|
value: string;
|
||||||
|
@ -680,6 +655,31 @@ export type MetadataSearchDto = {
|
||||||
withPeople?: boolean;
|
withPeople?: boolean;
|
||||||
withStacked?: 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 = {
|
export type PlacesResponseDto = {
|
||||||
admin1name?: string;
|
admin1name?: string;
|
||||||
admin2name?: string;
|
admin2name?: string;
|
||||||
|
@ -1530,106 +1530,6 @@ export function updateAsset({ id, updateAssetDto }: {
|
||||||
body: 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 }: {
|
export function getAuditDeletes({ after, entityType, userId }: {
|
||||||
after: string;
|
after: string;
|
||||||
entityType: EntityType;
|
entityType: EntityType;
|
||||||
|
@ -2201,36 +2101,6 @@ export function fixAuditFiles({ fileReportFixDto }: {
|
||||||
body: 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) {
|
export function getAssetsByCity(opts?: Oazapfts.RequestOpts) {
|
||||||
return oazapfts.ok(oazapfts.fetchJson<{
|
return oazapfts.ok(oazapfts.fetchJson<{
|
||||||
status: 200;
|
status: 200;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Param, Post, Put, Query } from '@nestjs/common';
|
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 { AssetResponseDto, MemoryLaneResponseDto } from 'src/dtos/asset-response.dto';
|
||||||
import {
|
import {
|
||||||
AssetBulkDeleteDto,
|
AssetBulkDeleteDto,
|
||||||
|
@ -12,30 +12,13 @@ import {
|
||||||
UpdateAssetDto,
|
UpdateAssetDto,
|
||||||
} from 'src/dtos/asset.dto';
|
} from 'src/dtos/asset.dto';
|
||||||
import { AuthDto } from 'src/dtos/auth.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 { UpdateStackParentDto } from 'src/dtos/stack.dto';
|
||||||
import { Auth, Authenticated, SharedLinkRoute } from 'src/middleware/auth.guard';
|
import { Auth, Authenticated, SharedLinkRoute } from 'src/middleware/auth.guard';
|
||||||
import { Route } from 'src/middleware/file-upload.interceptor';
|
import { Route } from 'src/middleware/file-upload.interceptor';
|
||||||
import { AssetService } from 'src/services/asset.service';
|
import { AssetService } from 'src/services/asset.service';
|
||||||
import { SearchService } from 'src/services/search.service';
|
|
||||||
import { UUIDParamDto } from 'src/validation';
|
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')
|
@ApiTags('Asset')
|
||||||
@Controller(Route.ASSET)
|
@Controller(Route.ASSET)
|
||||||
@Authenticated()
|
@Authenticated()
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { AlbumController } from 'src/controllers/album.controller';
|
||||||
import { APIKeyController } from 'src/controllers/api-key.controller';
|
import { APIKeyController } from 'src/controllers/api-key.controller';
|
||||||
import { AppController } from 'src/controllers/app.controller';
|
import { AppController } from 'src/controllers/app.controller';
|
||||||
import { AssetControllerV1 } from 'src/controllers/asset-v1.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 { AuditController } from 'src/controllers/audit.controller';
|
||||||
import { AuthController } from 'src/controllers/auth.controller';
|
import { AuthController } from 'src/controllers/auth.controller';
|
||||||
import { DownloadController } from 'src/controllers/download.controller';
|
import { DownloadController } from 'src/controllers/download.controller';
|
||||||
|
@ -34,7 +34,6 @@ export const controllers = [
|
||||||
AppController,
|
AppController,
|
||||||
AssetController,
|
AssetController,
|
||||||
AssetControllerV1,
|
AssetControllerV1,
|
||||||
AssetsController,
|
|
||||||
AuditController,
|
AuditController,
|
||||||
AuthController,
|
AuthController,
|
||||||
DownloadController,
|
DownloadController,
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
import { Body, Controller, Get, HttpCode, HttpStatus, Post, Query } from '@nestjs/common';
|
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 { AssetResponseDto } from 'src/dtos/asset-response.dto';
|
||||||
import { AuthDto } from 'src/dtos/auth.dto';
|
import { AuthDto } from 'src/dtos/auth.dto';
|
||||||
import { PersonResponseDto } from 'src/dtos/person.dto';
|
import { PersonResponseDto } from 'src/dtos/person.dto';
|
||||||
import {
|
import {
|
||||||
MetadataSearchDto,
|
MetadataSearchDto,
|
||||||
PlacesResponseDto,
|
PlacesResponseDto,
|
||||||
SearchDto,
|
|
||||||
SearchExploreResponseDto,
|
SearchExploreResponseDto,
|
||||||
SearchPeopleDto,
|
SearchPeopleDto,
|
||||||
SearchPlacesDto,
|
SearchPlacesDto,
|
||||||
|
@ -23,12 +22,6 @@ import { SearchService } from 'src/services/search.service';
|
||||||
export class SearchController {
|
export class SearchController {
|
||||||
constructor(private service: SearchService) {}
|
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')
|
@Post('metadata')
|
||||||
@HttpCode(HttpStatus.OK)
|
@HttpCode(HttpStatus.OK)
|
||||||
searchMetadata(@Auth() auth: AuthDto, @Body() dto: MetadataSearchDto): Promise<SearchResponseDto> {
|
searchMetadata(@Auth() auth: AuthDto, @Body() dto: MetadataSearchDto): Promise<SearchResponseDto> {
|
||||||
|
|
|
@ -199,53 +199,6 @@ export class SmartSearchDto extends BaseSearchDto {
|
||||||
query!: string;
|
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 {
|
export class SearchPlacesDto {
|
||||||
@IsString()
|
@IsString()
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
|
|
|
@ -129,10 +129,6 @@ export interface AssetExploreOptions extends AssetExploreFieldOptions {
|
||||||
unnest?: boolean;
|
unnest?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MetadataSearchOptions {
|
|
||||||
numResults: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AssetFullSyncOptions {
|
export interface AssetFullSyncOptions {
|
||||||
ownerId: string;
|
ownerId: string;
|
||||||
lastCreationDate?: Date;
|
lastCreationDate?: Date;
|
||||||
|
@ -188,7 +184,6 @@ export interface IAssetRepository {
|
||||||
upsertJobStatus(jobStatus: Partial<AssetJobStatusEntity>): Promise<void>;
|
upsertJobStatus(jobStatus: Partial<AssetJobStatusEntity>): Promise<void>;
|
||||||
getAssetIdByCity(userId: string, options: AssetExploreFieldOptions): Promise<SearchExploreItem<string>>;
|
getAssetIdByCity(userId: string, options: AssetExploreFieldOptions): Promise<SearchExploreItem<string>>;
|
||||||
getAssetIdByTag(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[]>;
|
getAllForUserFullSync(options: AssetFullSyncOptions): Promise<AssetEntity[]>;
|
||||||
getChangedDeltaSync(options: AssetDeltaSyncOptions): Promise<AssetEntity[]>;
|
getChangedDeltaSync(options: AssetDeltaSyncOptions): Promise<AssetEntity[]>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,29 +5,6 @@ import { Paginated } from 'src/utils/pagination';
|
||||||
|
|
||||||
export const ISearchRepository = 'ISearchRepository';
|
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> {
|
export interface SearchResult<T> {
|
||||||
/** total matches */
|
/** total matches */
|
||||||
total: number;
|
total: number;
|
||||||
|
|
|
@ -738,37 +738,6 @@ WHERE
|
||||||
LIMIT
|
LIMIT
|
||||||
12
|
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
|
-- AssetRepository.getAllForUserFullSync
|
||||||
SELECT
|
SELECT
|
||||||
"asset"."id" AS "asset_id",
|
"asset"."id" AS "asset_id",
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
import path from 'node:path';
|
|
||||||
import { Chunked, ChunkedArray, DummyValue, GenerateSql } from 'src/decorators';
|
import { Chunked, ChunkedArray, DummyValue, GenerateSql } from 'src/decorators';
|
||||||
import { AlbumEntity, AssetOrder } from 'src/entities/album.entity';
|
import { AlbumEntity, AssetOrder } from 'src/entities/album.entity';
|
||||||
import { AssetJobStatusEntity } from 'src/entities/asset-job-status.entity';
|
import { AssetJobStatusEntity } from 'src/entities/asset-job-status.entity';
|
||||||
|
@ -23,7 +22,6 @@ import {
|
||||||
LivePhotoSearchOptions,
|
LivePhotoSearchOptions,
|
||||||
MapMarker,
|
MapMarker,
|
||||||
MapMarkerSearchOptions,
|
MapMarkerSearchOptions,
|
||||||
MetadataSearchOptions,
|
|
||||||
MonthDay,
|
MonthDay,
|
||||||
TimeBucketItem,
|
TimeBucketItem,
|
||||||
TimeBucketOptions,
|
TimeBucketOptions,
|
||||||
|
@ -700,94 +698,6 @@ export class AssetRepository implements IAssetRepository {
|
||||||
return builder;
|
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({
|
@GenerateSql({
|
||||||
params: [
|
params: [
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
import { mapAsset } from 'src/dtos/asset-response.dto';
|
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 { IAssetRepository } from 'src/interfaces/asset.interface';
|
||||||
import { ILoggerRepository } from 'src/interfaces/logger.interface';
|
import { ILoggerRepository } from 'src/interfaces/logger.interface';
|
||||||
import { IMachineLearningRepository } from 'src/interfaces/machine-learning.interface';
|
import { IMachineLearningRepository } from 'src/interfaces/machine-learning.interface';
|
||||||
|
@ -97,119 +95,4 @@ describe(SearchService.name, () => {
|
||||||
expect(result).toEqual(expectedResponse);
|
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 {
|
import {
|
||||||
MetadataSearchDto,
|
MetadataSearchDto,
|
||||||
PlacesResponseDto,
|
PlacesResponseDto,
|
||||||
SearchDto,
|
|
||||||
SearchPeopleDto,
|
SearchPeopleDto,
|
||||||
SearchPlacesDto,
|
SearchPlacesDto,
|
||||||
SearchResponseDto,
|
SearchResponseDto,
|
||||||
|
@ -23,7 +22,7 @@ import { IMachineLearningRepository } from 'src/interfaces/machine-learning.inte
|
||||||
import { IMetadataRepository } from 'src/interfaces/metadata.interface';
|
import { IMetadataRepository } from 'src/interfaces/metadata.interface';
|
||||||
import { IPartnerRepository } from 'src/interfaces/partner.interface';
|
import { IPartnerRepository } from 'src/interfaces/partner.interface';
|
||||||
import { IPersonRepository } from 'src/interfaces/person.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';
|
import { ISystemConfigRepository } from 'src/interfaces/system-config.interface';
|
||||||
|
|
||||||
@Injectable()
|
@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[]> {
|
private async getUserIdsToSearch(auth: AuthDto): Promise<string[]> {
|
||||||
const userIds: string[] = [auth.user.id];
|
const userIds: string[] = [auth.user.id];
|
||||||
const partners = await this.partnerRepository.getAll(auth.user.id);
|
const partners = await this.partnerRepository.getAll(auth.user.id);
|
||||||
|
|
|
@ -35,7 +35,6 @@ export const newAssetRepositoryMock = (): Mocked<IAssetRepository> => {
|
||||||
softDeleteAll: vitest.fn(),
|
softDeleteAll: vitest.fn(),
|
||||||
getAssetIdByCity: vitest.fn(),
|
getAssetIdByCity: vitest.fn(),
|
||||||
getAssetIdByTag: vitest.fn(),
|
getAssetIdByTag: vitest.fn(),
|
||||||
searchMetadata: vitest.fn(),
|
|
||||||
getAllForUserFullSync: vitest.fn(),
|
getAllForUserFullSync: vitest.fn(),
|
||||||
getChangedDeltaSync: vitest.fn(),
|
getChangedDeltaSync: vitest.fn(),
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue