mirror of
https://github.com/immich-app/immich.git
synced 2025-07-07 10:14:08 -04:00
feat(server): add /search/statistics resource (#18885)
This commit is contained in:
parent
ecb16d9907
commit
fb4be6e231
3
mobile/openapi/README.md
generated
3
mobile/openapi/README.md
generated
@ -177,6 +177,7 @@ Class | Method | HTTP request | Description
|
||||
*SearchApi* | [**getAssetsByCity**](doc//SearchApi.md#getassetsbycity) | **GET** /search/cities |
|
||||
*SearchApi* | [**getExploreData**](doc//SearchApi.md#getexploredata) | **GET** /search/explore |
|
||||
*SearchApi* | [**getSearchSuggestions**](doc//SearchApi.md#getsearchsuggestions) | **GET** /search/suggestions |
|
||||
*SearchApi* | [**searchAssetStatistics**](doc//SearchApi.md#searchassetstatistics) | **POST** /search/statistics |
|
||||
*SearchApi* | [**searchAssets**](doc//SearchApi.md#searchassets) | **POST** /search/metadata |
|
||||
*SearchApi* | [**searchPerson**](doc//SearchApi.md#searchperson) | **GET** /search/person |
|
||||
*SearchApi* | [**searchPlaces**](doc//SearchApi.md#searchplaces) | **GET** /search/places |
|
||||
@ -425,6 +426,7 @@ Class | Method | HTTP request | Description
|
||||
- [SearchFacetCountResponseDto](doc//SearchFacetCountResponseDto.md)
|
||||
- [SearchFacetResponseDto](doc//SearchFacetResponseDto.md)
|
||||
- [SearchResponseDto](doc//SearchResponseDto.md)
|
||||
- [SearchStatisticsResponseDto](doc//SearchStatisticsResponseDto.md)
|
||||
- [SearchSuggestionType](doc//SearchSuggestionType.md)
|
||||
- [ServerAboutResponseDto](doc//ServerAboutResponseDto.md)
|
||||
- [ServerApkLinksDto](doc//ServerApkLinksDto.md)
|
||||
@ -453,6 +455,7 @@ Class | Method | HTTP request | Description
|
||||
- [StackCreateDto](doc//StackCreateDto.md)
|
||||
- [StackResponseDto](doc//StackResponseDto.md)
|
||||
- [StackUpdateDto](doc//StackUpdateDto.md)
|
||||
- [StatisticsSearchDto](doc//StatisticsSearchDto.md)
|
||||
- [SyncAckDeleteDto](doc//SyncAckDeleteDto.md)
|
||||
- [SyncAckDto](doc//SyncAckDto.md)
|
||||
- [SyncAckSetDto](doc//SyncAckSetDto.md)
|
||||
|
2
mobile/openapi/lib/api.dart
generated
2
mobile/openapi/lib/api.dart
generated
@ -214,6 +214,7 @@ part 'model/search_explore_response_dto.dart';
|
||||
part 'model/search_facet_count_response_dto.dart';
|
||||
part 'model/search_facet_response_dto.dart';
|
||||
part 'model/search_response_dto.dart';
|
||||
part 'model/search_statistics_response_dto.dart';
|
||||
part 'model/search_suggestion_type.dart';
|
||||
part 'model/server_about_response_dto.dart';
|
||||
part 'model/server_apk_links_dto.dart';
|
||||
@ -242,6 +243,7 @@ part 'model/source_type.dart';
|
||||
part 'model/stack_create_dto.dart';
|
||||
part 'model/stack_response_dto.dart';
|
||||
part 'model/stack_update_dto.dart';
|
||||
part 'model/statistics_search_dto.dart';
|
||||
part 'model/sync_ack_delete_dto.dart';
|
||||
part 'model/sync_ack_dto.dart';
|
||||
part 'model/sync_ack_set_dto.dart';
|
||||
|
47
mobile/openapi/lib/api/search_api.dart
generated
47
mobile/openapi/lib/api/search_api.dart
generated
@ -193,6 +193,53 @@ class SearchApi {
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Performs an HTTP 'POST /search/statistics' operation and returns the [Response].
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [StatisticsSearchDto] statisticsSearchDto (required):
|
||||
Future<Response> searchAssetStatisticsWithHttpInfo(StatisticsSearchDto statisticsSearchDto,) async {
|
||||
// ignore: prefer_const_declarations
|
||||
final apiPath = r'/search/statistics';
|
||||
|
||||
// ignore: prefer_final_locals
|
||||
Object? postBody = statisticsSearchDto;
|
||||
|
||||
final queryParams = <QueryParam>[];
|
||||
final headerParams = <String, String>{};
|
||||
final formParams = <String, String>{};
|
||||
|
||||
const contentTypes = <String>['application/json'];
|
||||
|
||||
|
||||
return apiClient.invokeAPI(
|
||||
apiPath,
|
||||
'POST',
|
||||
queryParams,
|
||||
postBody,
|
||||
headerParams,
|
||||
formParams,
|
||||
contentTypes.isEmpty ? null : contentTypes.first,
|
||||
);
|
||||
}
|
||||
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [StatisticsSearchDto] statisticsSearchDto (required):
|
||||
Future<SearchStatisticsResponseDto?> searchAssetStatistics(StatisticsSearchDto statisticsSearchDto,) async {
|
||||
final response = await searchAssetStatisticsWithHttpInfo(statisticsSearchDto,);
|
||||
if (response.statusCode >= HttpStatus.badRequest) {
|
||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||
}
|
||||
// When a remote server returns no body with a status of 204, we shall not decode it.
|
||||
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
|
||||
// FormatException when trying to decode an empty string.
|
||||
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
|
||||
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'SearchStatisticsResponseDto',) as SearchStatisticsResponseDto;
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Performs an HTTP 'POST /search/metadata' operation and returns the [Response].
|
||||
/// Parameters:
|
||||
///
|
||||
|
4
mobile/openapi/lib/api_client.dart
generated
4
mobile/openapi/lib/api_client.dart
generated
@ -484,6 +484,8 @@ class ApiClient {
|
||||
return SearchFacetResponseDto.fromJson(value);
|
||||
case 'SearchResponseDto':
|
||||
return SearchResponseDto.fromJson(value);
|
||||
case 'SearchStatisticsResponseDto':
|
||||
return SearchStatisticsResponseDto.fromJson(value);
|
||||
case 'SearchSuggestionType':
|
||||
return SearchSuggestionTypeTypeTransformer().decode(value);
|
||||
case 'ServerAboutResponseDto':
|
||||
@ -540,6 +542,8 @@ class ApiClient {
|
||||
return StackResponseDto.fromJson(value);
|
||||
case 'StackUpdateDto':
|
||||
return StackUpdateDto.fromJson(value);
|
||||
case 'StatisticsSearchDto':
|
||||
return StatisticsSearchDto.fromJson(value);
|
||||
case 'SyncAckDeleteDto':
|
||||
return SyncAckDeleteDto.fromJson(value);
|
||||
case 'SyncAckDto':
|
||||
|
99
mobile/openapi/lib/model/search_statistics_response_dto.dart
generated
Normal file
99
mobile/openapi/lib/model/search_statistics_response_dto.dart
generated
Normal file
@ -0,0 +1,99 @@
|
||||
//
|
||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||
//
|
||||
// @dart=2.18
|
||||
|
||||
// ignore_for_file: unused_element, unused_import
|
||||
// ignore_for_file: always_put_required_named_parameters_first
|
||||
// ignore_for_file: constant_identifier_names
|
||||
// ignore_for_file: lines_longer_than_80_chars
|
||||
|
||||
part of openapi.api;
|
||||
|
||||
class SearchStatisticsResponseDto {
|
||||
/// Returns a new [SearchStatisticsResponseDto] instance.
|
||||
SearchStatisticsResponseDto({
|
||||
required this.total,
|
||||
});
|
||||
|
||||
int total;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is SearchStatisticsResponseDto &&
|
||||
other.total == total;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
// ignore: unnecessary_parenthesis
|
||||
(total.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'SearchStatisticsResponseDto[total=$total]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json[r'total'] = this.total;
|
||||
return json;
|
||||
}
|
||||
|
||||
/// Returns a new [SearchStatisticsResponseDto] instance and imports its values from
|
||||
/// [value] if it's a [Map], null otherwise.
|
||||
// ignore: prefer_constructors_over_static_methods
|
||||
static SearchStatisticsResponseDto? fromJson(dynamic value) {
|
||||
upgradeDto(value, "SearchStatisticsResponseDto");
|
||||
if (value is Map) {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return SearchStatisticsResponseDto(
|
||||
total: mapValueOfType<int>(json, r'total')!,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static List<SearchStatisticsResponseDto> listFromJson(dynamic json, {bool growable = false,}) {
|
||||
final result = <SearchStatisticsResponseDto>[];
|
||||
if (json is List && json.isNotEmpty) {
|
||||
for (final row in json) {
|
||||
final value = SearchStatisticsResponseDto.fromJson(row);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toList(growable: growable);
|
||||
}
|
||||
|
||||
static Map<String, SearchStatisticsResponseDto> mapFromJson(dynamic json) {
|
||||
final map = <String, SearchStatisticsResponseDto>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
||||
for (final entry in json.entries) {
|
||||
final value = SearchStatisticsResponseDto.fromJson(entry.value);
|
||||
if (value != null) {
|
||||
map[entry.key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
// maps a json object with a list of SearchStatisticsResponseDto-objects as value to a dart map
|
||||
static Map<String, List<SearchStatisticsResponseDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||
final map = <String, List<SearchStatisticsResponseDto>>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
// ignore: parameter_assignments
|
||||
json = json.cast<String, dynamic>();
|
||||
for (final entry in json.entries) {
|
||||
map[entry.key] = SearchStatisticsResponseDto.listFromJson(entry.value, growable: growable,);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/// The list of required keys that must be present in a JSON.
|
||||
static const requiredKeys = <String>{
|
||||
'total',
|
||||
};
|
||||
}
|
||||
|
500
mobile/openapi/lib/model/statistics_search_dto.dart
generated
Normal file
500
mobile/openapi/lib/model/statistics_search_dto.dart
generated
Normal file
@ -0,0 +1,500 @@
|
||||
//
|
||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||
//
|
||||
// @dart=2.18
|
||||
|
||||
// ignore_for_file: unused_element, unused_import
|
||||
// ignore_for_file: always_put_required_named_parameters_first
|
||||
// ignore_for_file: constant_identifier_names
|
||||
// ignore_for_file: lines_longer_than_80_chars
|
||||
|
||||
part of openapi.api;
|
||||
|
||||
class StatisticsSearchDto {
|
||||
/// Returns a new [StatisticsSearchDto] instance.
|
||||
StatisticsSearchDto({
|
||||
this.city,
|
||||
this.country,
|
||||
this.createdAfter,
|
||||
this.createdBefore,
|
||||
this.description,
|
||||
this.deviceId,
|
||||
this.isEncoded,
|
||||
this.isFavorite,
|
||||
this.isMotion,
|
||||
this.isNotInAlbum,
|
||||
this.isOffline,
|
||||
this.lensModel,
|
||||
this.libraryId,
|
||||
this.make,
|
||||
this.model,
|
||||
this.personIds = const [],
|
||||
this.rating,
|
||||
this.state,
|
||||
this.tagIds = const [],
|
||||
this.takenAfter,
|
||||
this.takenBefore,
|
||||
this.trashedAfter,
|
||||
this.trashedBefore,
|
||||
this.type,
|
||||
this.updatedAfter,
|
||||
this.updatedBefore,
|
||||
this.visibility,
|
||||
});
|
||||
|
||||
String? city;
|
||||
|
||||
String? country;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
DateTime? createdAfter;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
DateTime? createdBefore;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
String? description;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
String? deviceId;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
bool? isEncoded;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
bool? isFavorite;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
bool? isMotion;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
bool? isNotInAlbum;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
bool? isOffline;
|
||||
|
||||
String? lensModel;
|
||||
|
||||
String? libraryId;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
String? make;
|
||||
|
||||
String? model;
|
||||
|
||||
List<String> personIds;
|
||||
|
||||
/// Minimum value: -1
|
||||
/// Maximum value: 5
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
num? rating;
|
||||
|
||||
String? state;
|
||||
|
||||
List<String> tagIds;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
DateTime? takenAfter;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
DateTime? takenBefore;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
DateTime? trashedAfter;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
DateTime? trashedBefore;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
AssetTypeEnum? type;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
DateTime? updatedAfter;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
DateTime? updatedBefore;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
AssetVisibility? visibility;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is StatisticsSearchDto &&
|
||||
other.city == city &&
|
||||
other.country == country &&
|
||||
other.createdAfter == createdAfter &&
|
||||
other.createdBefore == createdBefore &&
|
||||
other.description == description &&
|
||||
other.deviceId == deviceId &&
|
||||
other.isEncoded == isEncoded &&
|
||||
other.isFavorite == isFavorite &&
|
||||
other.isMotion == isMotion &&
|
||||
other.isNotInAlbum == isNotInAlbum &&
|
||||
other.isOffline == isOffline &&
|
||||
other.lensModel == lensModel &&
|
||||
other.libraryId == libraryId &&
|
||||
other.make == make &&
|
||||
other.model == model &&
|
||||
_deepEquality.equals(other.personIds, personIds) &&
|
||||
other.rating == rating &&
|
||||
other.state == state &&
|
||||
_deepEquality.equals(other.tagIds, tagIds) &&
|
||||
other.takenAfter == takenAfter &&
|
||||
other.takenBefore == takenBefore &&
|
||||
other.trashedAfter == trashedAfter &&
|
||||
other.trashedBefore == trashedBefore &&
|
||||
other.type == type &&
|
||||
other.updatedAfter == updatedAfter &&
|
||||
other.updatedBefore == updatedBefore &&
|
||||
other.visibility == visibility;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
// ignore: unnecessary_parenthesis
|
||||
(city == null ? 0 : city!.hashCode) +
|
||||
(country == null ? 0 : country!.hashCode) +
|
||||
(createdAfter == null ? 0 : createdAfter!.hashCode) +
|
||||
(createdBefore == null ? 0 : createdBefore!.hashCode) +
|
||||
(description == null ? 0 : description!.hashCode) +
|
||||
(deviceId == null ? 0 : deviceId!.hashCode) +
|
||||
(isEncoded == null ? 0 : isEncoded!.hashCode) +
|
||||
(isFavorite == null ? 0 : isFavorite!.hashCode) +
|
||||
(isMotion == null ? 0 : isMotion!.hashCode) +
|
||||
(isNotInAlbum == null ? 0 : isNotInAlbum!.hashCode) +
|
||||
(isOffline == null ? 0 : isOffline!.hashCode) +
|
||||
(lensModel == null ? 0 : lensModel!.hashCode) +
|
||||
(libraryId == null ? 0 : libraryId!.hashCode) +
|
||||
(make == null ? 0 : make!.hashCode) +
|
||||
(model == null ? 0 : model!.hashCode) +
|
||||
(personIds.hashCode) +
|
||||
(rating == null ? 0 : rating!.hashCode) +
|
||||
(state == null ? 0 : state!.hashCode) +
|
||||
(tagIds.hashCode) +
|
||||
(takenAfter == null ? 0 : takenAfter!.hashCode) +
|
||||
(takenBefore == null ? 0 : takenBefore!.hashCode) +
|
||||
(trashedAfter == null ? 0 : trashedAfter!.hashCode) +
|
||||
(trashedBefore == null ? 0 : trashedBefore!.hashCode) +
|
||||
(type == null ? 0 : type!.hashCode) +
|
||||
(updatedAfter == null ? 0 : updatedAfter!.hashCode) +
|
||||
(updatedBefore == null ? 0 : updatedBefore!.hashCode) +
|
||||
(visibility == null ? 0 : visibility!.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'StatisticsSearchDto[city=$city, country=$country, createdAfter=$createdAfter, createdBefore=$createdBefore, description=$description, deviceId=$deviceId, isEncoded=$isEncoded, isFavorite=$isFavorite, isMotion=$isMotion, isNotInAlbum=$isNotInAlbum, isOffline=$isOffline, lensModel=$lensModel, libraryId=$libraryId, make=$make, model=$model, personIds=$personIds, rating=$rating, state=$state, tagIds=$tagIds, takenAfter=$takenAfter, takenBefore=$takenBefore, trashedAfter=$trashedAfter, trashedBefore=$trashedBefore, type=$type, updatedAfter=$updatedAfter, updatedBefore=$updatedBefore, visibility=$visibility]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
if (this.city != null) {
|
||||
json[r'city'] = this.city;
|
||||
} else {
|
||||
// json[r'city'] = null;
|
||||
}
|
||||
if (this.country != null) {
|
||||
json[r'country'] = this.country;
|
||||
} else {
|
||||
// json[r'country'] = null;
|
||||
}
|
||||
if (this.createdAfter != null) {
|
||||
json[r'createdAfter'] = this.createdAfter!.toUtc().toIso8601String();
|
||||
} else {
|
||||
// json[r'createdAfter'] = null;
|
||||
}
|
||||
if (this.createdBefore != null) {
|
||||
json[r'createdBefore'] = this.createdBefore!.toUtc().toIso8601String();
|
||||
} else {
|
||||
// json[r'createdBefore'] = null;
|
||||
}
|
||||
if (this.description != null) {
|
||||
json[r'description'] = this.description;
|
||||
} else {
|
||||
// json[r'description'] = null;
|
||||
}
|
||||
if (this.deviceId != null) {
|
||||
json[r'deviceId'] = this.deviceId;
|
||||
} else {
|
||||
// json[r'deviceId'] = null;
|
||||
}
|
||||
if (this.isEncoded != null) {
|
||||
json[r'isEncoded'] = this.isEncoded;
|
||||
} else {
|
||||
// json[r'isEncoded'] = null;
|
||||
}
|
||||
if (this.isFavorite != null) {
|
||||
json[r'isFavorite'] = this.isFavorite;
|
||||
} else {
|
||||
// json[r'isFavorite'] = null;
|
||||
}
|
||||
if (this.isMotion != null) {
|
||||
json[r'isMotion'] = this.isMotion;
|
||||
} else {
|
||||
// json[r'isMotion'] = null;
|
||||
}
|
||||
if (this.isNotInAlbum != null) {
|
||||
json[r'isNotInAlbum'] = this.isNotInAlbum;
|
||||
} else {
|
||||
// json[r'isNotInAlbum'] = null;
|
||||
}
|
||||
if (this.isOffline != null) {
|
||||
json[r'isOffline'] = this.isOffline;
|
||||
} else {
|
||||
// json[r'isOffline'] = null;
|
||||
}
|
||||
if (this.lensModel != null) {
|
||||
json[r'lensModel'] = this.lensModel;
|
||||
} else {
|
||||
// json[r'lensModel'] = null;
|
||||
}
|
||||
if (this.libraryId != null) {
|
||||
json[r'libraryId'] = this.libraryId;
|
||||
} else {
|
||||
// json[r'libraryId'] = null;
|
||||
}
|
||||
if (this.make != null) {
|
||||
json[r'make'] = this.make;
|
||||
} else {
|
||||
// json[r'make'] = null;
|
||||
}
|
||||
if (this.model != null) {
|
||||
json[r'model'] = this.model;
|
||||
} else {
|
||||
// json[r'model'] = null;
|
||||
}
|
||||
json[r'personIds'] = this.personIds;
|
||||
if (this.rating != null) {
|
||||
json[r'rating'] = this.rating;
|
||||
} else {
|
||||
// json[r'rating'] = null;
|
||||
}
|
||||
if (this.state != null) {
|
||||
json[r'state'] = this.state;
|
||||
} else {
|
||||
// json[r'state'] = null;
|
||||
}
|
||||
json[r'tagIds'] = this.tagIds;
|
||||
if (this.takenAfter != null) {
|
||||
json[r'takenAfter'] = this.takenAfter!.toUtc().toIso8601String();
|
||||
} else {
|
||||
// json[r'takenAfter'] = null;
|
||||
}
|
||||
if (this.takenBefore != null) {
|
||||
json[r'takenBefore'] = this.takenBefore!.toUtc().toIso8601String();
|
||||
} else {
|
||||
// json[r'takenBefore'] = null;
|
||||
}
|
||||
if (this.trashedAfter != null) {
|
||||
json[r'trashedAfter'] = this.trashedAfter!.toUtc().toIso8601String();
|
||||
} else {
|
||||
// json[r'trashedAfter'] = null;
|
||||
}
|
||||
if (this.trashedBefore != null) {
|
||||
json[r'trashedBefore'] = this.trashedBefore!.toUtc().toIso8601String();
|
||||
} else {
|
||||
// json[r'trashedBefore'] = null;
|
||||
}
|
||||
if (this.type != null) {
|
||||
json[r'type'] = this.type;
|
||||
} else {
|
||||
// json[r'type'] = null;
|
||||
}
|
||||
if (this.updatedAfter != null) {
|
||||
json[r'updatedAfter'] = this.updatedAfter!.toUtc().toIso8601String();
|
||||
} else {
|
||||
// json[r'updatedAfter'] = null;
|
||||
}
|
||||
if (this.updatedBefore != null) {
|
||||
json[r'updatedBefore'] = this.updatedBefore!.toUtc().toIso8601String();
|
||||
} else {
|
||||
// json[r'updatedBefore'] = null;
|
||||
}
|
||||
if (this.visibility != null) {
|
||||
json[r'visibility'] = this.visibility;
|
||||
} else {
|
||||
// json[r'visibility'] = null;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
/// Returns a new [StatisticsSearchDto] instance and imports its values from
|
||||
/// [value] if it's a [Map], null otherwise.
|
||||
// ignore: prefer_constructors_over_static_methods
|
||||
static StatisticsSearchDto? fromJson(dynamic value) {
|
||||
upgradeDto(value, "StatisticsSearchDto");
|
||||
if (value is Map) {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return StatisticsSearchDto(
|
||||
city: mapValueOfType<String>(json, r'city'),
|
||||
country: mapValueOfType<String>(json, r'country'),
|
||||
createdAfter: mapDateTime(json, r'createdAfter', r''),
|
||||
createdBefore: mapDateTime(json, r'createdBefore', r''),
|
||||
description: mapValueOfType<String>(json, r'description'),
|
||||
deviceId: mapValueOfType<String>(json, r'deviceId'),
|
||||
isEncoded: mapValueOfType<bool>(json, r'isEncoded'),
|
||||
isFavorite: mapValueOfType<bool>(json, r'isFavorite'),
|
||||
isMotion: mapValueOfType<bool>(json, r'isMotion'),
|
||||
isNotInAlbum: mapValueOfType<bool>(json, r'isNotInAlbum'),
|
||||
isOffline: mapValueOfType<bool>(json, r'isOffline'),
|
||||
lensModel: mapValueOfType<String>(json, r'lensModel'),
|
||||
libraryId: mapValueOfType<String>(json, r'libraryId'),
|
||||
make: mapValueOfType<String>(json, r'make'),
|
||||
model: mapValueOfType<String>(json, r'model'),
|
||||
personIds: json[r'personIds'] is Iterable
|
||||
? (json[r'personIds'] as Iterable).cast<String>().toList(growable: false)
|
||||
: const [],
|
||||
rating: num.parse('${json[r'rating']}'),
|
||||
state: mapValueOfType<String>(json, r'state'),
|
||||
tagIds: json[r'tagIds'] is Iterable
|
||||
? (json[r'tagIds'] as Iterable).cast<String>().toList(growable: false)
|
||||
: const [],
|
||||
takenAfter: mapDateTime(json, r'takenAfter', r''),
|
||||
takenBefore: mapDateTime(json, r'takenBefore', r''),
|
||||
trashedAfter: mapDateTime(json, r'trashedAfter', r''),
|
||||
trashedBefore: mapDateTime(json, r'trashedBefore', r''),
|
||||
type: AssetTypeEnum.fromJson(json[r'type']),
|
||||
updatedAfter: mapDateTime(json, r'updatedAfter', r''),
|
||||
updatedBefore: mapDateTime(json, r'updatedBefore', r''),
|
||||
visibility: AssetVisibility.fromJson(json[r'visibility']),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static List<StatisticsSearchDto> listFromJson(dynamic json, {bool growable = false,}) {
|
||||
final result = <StatisticsSearchDto>[];
|
||||
if (json is List && json.isNotEmpty) {
|
||||
for (final row in json) {
|
||||
final value = StatisticsSearchDto.fromJson(row);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toList(growable: growable);
|
||||
}
|
||||
|
||||
static Map<String, StatisticsSearchDto> mapFromJson(dynamic json) {
|
||||
final map = <String, StatisticsSearchDto>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
||||
for (final entry in json.entries) {
|
||||
final value = StatisticsSearchDto.fromJson(entry.value);
|
||||
if (value != null) {
|
||||
map[entry.key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
// maps a json object with a list of StatisticsSearchDto-objects as value to a dart map
|
||||
static Map<String, List<StatisticsSearchDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||
final map = <String, List<StatisticsSearchDto>>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
// ignore: parameter_assignments
|
||||
json = json.cast<String, dynamic>();
|
||||
for (final entry in json.entries) {
|
||||
map[entry.key] = StatisticsSearchDto.listFromJson(entry.value, growable: growable,);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/// The list of required keys that must be present in a JSON.
|
||||
static const requiredKeys = <String>{
|
||||
};
|
||||
}
|
||||
|
@ -5158,6 +5158,48 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"/search/statistics": {
|
||||
"post": {
|
||||
"operationId": "searchAssetStatistics",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/StatisticsSearchDto"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/SearchStatisticsResponseDto"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"bearer": []
|
||||
},
|
||||
{
|
||||
"cookie": []
|
||||
},
|
||||
{
|
||||
"api_key": []
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"Search"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/search/suggestions": {
|
||||
"get": {
|
||||
"operationId": "getSearchSuggestions",
|
||||
@ -12069,6 +12111,17 @@
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"SearchStatisticsResponseDto": {
|
||||
"properties": {
|
||||
"total": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"total"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"SearchSuggestionType": {
|
||||
"enum": [
|
||||
"country",
|
||||
@ -12974,6 +13027,125 @@
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"StatisticsSearchDto": {
|
||||
"properties": {
|
||||
"city": {
|
||||
"nullable": true,
|
||||
"type": "string"
|
||||
},
|
||||
"country": {
|
||||
"nullable": true,
|
||||
"type": "string"
|
||||
},
|
||||
"createdAfter": {
|
||||
"format": "date-time",
|
||||
"type": "string"
|
||||
},
|
||||
"createdBefore": {
|
||||
"format": "date-time",
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"deviceId": {
|
||||
"type": "string"
|
||||
},
|
||||
"isEncoded": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"isFavorite": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"isMotion": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"isNotInAlbum": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"isOffline": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"lensModel": {
|
||||
"nullable": true,
|
||||
"type": "string"
|
||||
},
|
||||
"libraryId": {
|
||||
"format": "uuid",
|
||||
"nullable": true,
|
||||
"type": "string"
|
||||
},
|
||||
"make": {
|
||||
"type": "string"
|
||||
},
|
||||
"model": {
|
||||
"nullable": true,
|
||||
"type": "string"
|
||||
},
|
||||
"personIds": {
|
||||
"items": {
|
||||
"format": "uuid",
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"rating": {
|
||||
"maximum": 5,
|
||||
"minimum": -1,
|
||||
"type": "number"
|
||||
},
|
||||
"state": {
|
||||
"nullable": true,
|
||||
"type": "string"
|
||||
},
|
||||
"tagIds": {
|
||||
"items": {
|
||||
"format": "uuid",
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"takenAfter": {
|
||||
"format": "date-time",
|
||||
"type": "string"
|
||||
},
|
||||
"takenBefore": {
|
||||
"format": "date-time",
|
||||
"type": "string"
|
||||
},
|
||||
"trashedAfter": {
|
||||
"format": "date-time",
|
||||
"type": "string"
|
||||
},
|
||||
"trashedBefore": {
|
||||
"format": "date-time",
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/AssetTypeEnum"
|
||||
}
|
||||
]
|
||||
},
|
||||
"updatedAfter": {
|
||||
"format": "date-time",
|
||||
"type": "string"
|
||||
},
|
||||
"updatedBefore": {
|
||||
"format": "date-time",
|
||||
"type": "string"
|
||||
},
|
||||
"visibility": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/AssetVisibility"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"SyncAckDeleteDto": {
|
||||
"properties": {
|
||||
"types": {
|
||||
|
@ -995,6 +995,38 @@ export type SmartSearchDto = {
|
||||
withDeleted?: boolean;
|
||||
withExif?: boolean;
|
||||
};
|
||||
export type StatisticsSearchDto = {
|
||||
city?: string | null;
|
||||
country?: string | null;
|
||||
createdAfter?: string;
|
||||
createdBefore?: string;
|
||||
description?: string;
|
||||
deviceId?: string;
|
||||
isEncoded?: boolean;
|
||||
isFavorite?: boolean;
|
||||
isMotion?: boolean;
|
||||
isNotInAlbum?: boolean;
|
||||
isOffline?: boolean;
|
||||
lensModel?: string | null;
|
||||
libraryId?: string | null;
|
||||
make?: string;
|
||||
model?: string | null;
|
||||
personIds?: string[];
|
||||
rating?: number;
|
||||
state?: string | null;
|
||||
tagIds?: string[];
|
||||
takenAfter?: string;
|
||||
takenBefore?: string;
|
||||
trashedAfter?: string;
|
||||
trashedBefore?: string;
|
||||
"type"?: AssetTypeEnum;
|
||||
updatedAfter?: string;
|
||||
updatedBefore?: string;
|
||||
visibility?: AssetVisibility;
|
||||
};
|
||||
export type SearchStatisticsResponseDto = {
|
||||
total: number;
|
||||
};
|
||||
export type ServerAboutResponseDto = {
|
||||
build?: string;
|
||||
buildImage?: string;
|
||||
@ -2882,6 +2914,18 @@ export function searchSmart({ smartSearchDto }: {
|
||||
body: smartSearchDto
|
||||
})));
|
||||
}
|
||||
export function searchAssetStatistics({ statisticsSearchDto }: {
|
||||
statisticsSearchDto: StatisticsSearchDto;
|
||||
}, opts?: Oazapfts.RequestOpts) {
|
||||
return oazapfts.ok(oazapfts.fetchJson<{
|
||||
status: 200;
|
||||
data: SearchStatisticsResponseDto;
|
||||
}>("/search/statistics", oazapfts.json({
|
||||
...opts,
|
||||
method: "POST",
|
||||
body: statisticsSearchDto
|
||||
})));
|
||||
}
|
||||
export function getSearchSuggestions({ country, includeNull, make, model, state, $type }: {
|
||||
country?: string;
|
||||
includeNull?: boolean;
|
||||
|
@ -11,8 +11,10 @@ import {
|
||||
SearchPeopleDto,
|
||||
SearchPlacesDto,
|
||||
SearchResponseDto,
|
||||
SearchStatisticsResponseDto,
|
||||
SearchSuggestionRequestDto,
|
||||
SmartSearchDto,
|
||||
StatisticsSearchDto,
|
||||
} from 'src/dtos/search.dto';
|
||||
import { Auth, Authenticated } from 'src/middleware/auth.guard';
|
||||
import { SearchService } from 'src/services/search.service';
|
||||
@ -29,6 +31,13 @@ export class SearchController {
|
||||
return this.service.searchMetadata(auth, dto);
|
||||
}
|
||||
|
||||
@Post('statistics')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@Authenticated()
|
||||
searchAssetStatistics(@Auth() auth: AuthDto, @Body() dto: StatisticsSearchDto): Promise<SearchStatisticsResponseDto> {
|
||||
return this.service.searchStatistics(auth, dto);
|
||||
}
|
||||
|
||||
@Post('random')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@Authenticated()
|
||||
|
@ -37,12 +37,6 @@ class BaseSearchDto {
|
||||
@ValidateAssetVisibility({ optional: true })
|
||||
visibility?: AssetVisibility;
|
||||
|
||||
@ValidateBoolean({ optional: true })
|
||||
withDeleted?: boolean;
|
||||
|
||||
@ValidateBoolean({ optional: true })
|
||||
withExif?: boolean;
|
||||
|
||||
@ValidateDate({ optional: true })
|
||||
createdBefore?: Date;
|
||||
|
||||
@ -92,13 +86,6 @@ class BaseSearchDto {
|
||||
@Optional({ nullable: true, emptyToNull: true })
|
||||
lensModel?: string | null;
|
||||
|
||||
@IsInt()
|
||||
@Min(1)
|
||||
@Max(1000)
|
||||
@Type(() => Number)
|
||||
@Optional()
|
||||
size?: number;
|
||||
|
||||
@ValidateBoolean({ optional: true })
|
||||
isNotInAlbum?: boolean;
|
||||
|
||||
@ -115,7 +102,22 @@ class BaseSearchDto {
|
||||
rating?: number;
|
||||
}
|
||||
|
||||
export class RandomSearchDto extends BaseSearchDto {
|
||||
class BaseSearchWithResultsDto extends BaseSearchDto {
|
||||
@ValidateBoolean({ optional: true })
|
||||
withDeleted?: boolean;
|
||||
|
||||
@ValidateBoolean({ optional: true })
|
||||
withExif?: boolean;
|
||||
|
||||
@IsInt()
|
||||
@Min(1)
|
||||
@Max(1000)
|
||||
@Type(() => Number)
|
||||
@Optional()
|
||||
size?: number;
|
||||
}
|
||||
|
||||
export class RandomSearchDto extends BaseSearchWithResultsDto {
|
||||
@ValidateBoolean({ optional: true })
|
||||
withStacked?: boolean;
|
||||
|
||||
@ -179,7 +181,14 @@ export class MetadataSearchDto extends RandomSearchDto {
|
||||
page?: number;
|
||||
}
|
||||
|
||||
export class SmartSearchDto extends BaseSearchDto {
|
||||
export class StatisticsSearchDto extends BaseSearchDto {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@Optional()
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export class SmartSearchDto extends BaseSearchWithResultsDto {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
query!: string;
|
||||
@ -299,6 +308,11 @@ export class SearchResponseDto {
|
||||
assets!: SearchAssetResponseDto;
|
||||
}
|
||||
|
||||
export class SearchStatisticsResponseDto {
|
||||
@ApiProperty({ type: 'integer' })
|
||||
total!: number;
|
||||
}
|
||||
|
||||
class SearchExploreItem {
|
||||
value!: string;
|
||||
data!: AssetResponseDto;
|
||||
|
@ -20,6 +20,20 @@ limit
|
||||
offset
|
||||
$7
|
||||
|
||||
-- SearchRepository.searchStatistics
|
||||
select
|
||||
count(*) as "total"
|
||||
from
|
||||
"assets"
|
||||
inner join "exif" on "assets"."id" = "exif"."assetId"
|
||||
where
|
||||
"assets"."visibility" = $1
|
||||
and "assets"."fileCreatedAt" >= $2
|
||||
and "exif"."lensModel" = $3
|
||||
and "assets"."ownerId" = any ($4::uuid[])
|
||||
and "assets"."isFavorite" = $5
|
||||
and "assets"."deletedAt" is null
|
||||
|
||||
-- SearchRepository.searchRandom
|
||||
(
|
||||
select
|
||||
|
@ -185,6 +185,7 @@ export class SearchRepository {
|
||||
async searchMetadata(pagination: SearchPaginationOptions, options: AssetSearchOptions) {
|
||||
const orderDirection = (options.orderDirection?.toLowerCase() || 'desc') as OrderByDirection;
|
||||
const items = await searchAssetBuilder(this.db, options)
|
||||
.selectAll('assets')
|
||||
.orderBy('assets.fileCreatedAt', orderDirection)
|
||||
.limit(pagination.size + 1)
|
||||
.offset((pagination.page - 1) * pagination.size)
|
||||
@ -193,6 +194,22 @@ export class SearchRepository {
|
||||
return paginationHelper(items, pagination.size);
|
||||
}
|
||||
|
||||
@GenerateSql({
|
||||
params: [
|
||||
{
|
||||
takenAfter: DummyValue.DATE,
|
||||
lensModel: DummyValue.STRING,
|
||||
isFavorite: true,
|
||||
userIds: [DummyValue.UUID],
|
||||
},
|
||||
],
|
||||
})
|
||||
searchStatistics(options: AssetSearchOptions) {
|
||||
return searchAssetBuilder(this.db, options)
|
||||
.select((qb) => qb.fn.countAll<number>().as('total'))
|
||||
.executeTakeFirstOrThrow();
|
||||
}
|
||||
|
||||
@GenerateSql({
|
||||
params: [
|
||||
100,
|
||||
@ -209,10 +226,12 @@ export class SearchRepository {
|
||||
const uuid = randomUUID();
|
||||
const builder = searchAssetBuilder(this.db, options);
|
||||
const lessThan = builder
|
||||
.selectAll('assets')
|
||||
.where('assets.id', '<', uuid)
|
||||
.orderBy(sql`random()`)
|
||||
.limit(size);
|
||||
const greaterThan = builder
|
||||
.selectAll('assets')
|
||||
.where('assets.id', '>', uuid)
|
||||
.orderBy(sql`random()`)
|
||||
.limit(size);
|
||||
@ -241,6 +260,7 @@ export class SearchRepository {
|
||||
return this.db.transaction().execute(async (trx) => {
|
||||
await sql`set local vchordrq.probes = ${sql.lit(probes[VectorIndex.CLIP])}`.execute(trx);
|
||||
const items = await searchAssetBuilder(trx, options)
|
||||
.selectAll('assets')
|
||||
.innerJoin('smart_search', 'assets.id', 'smart_search.assetId')
|
||||
.orderBy(sql`smart_search.embedding <=> ${options.embedding}`)
|
||||
.limit(pagination.size + 1)
|
||||
|
@ -10,9 +10,11 @@ import {
|
||||
SearchPeopleDto,
|
||||
SearchPlacesDto,
|
||||
SearchResponseDto,
|
||||
SearchStatisticsResponseDto,
|
||||
SearchSuggestionRequestDto,
|
||||
SearchSuggestionType,
|
||||
SmartSearchDto,
|
||||
StatisticsSearchDto,
|
||||
} from 'src/dtos/search.dto';
|
||||
import { AssetOrder, AssetVisibility } from 'src/enum';
|
||||
import { BaseService } from 'src/services/base.service';
|
||||
@ -67,6 +69,15 @@ export class SearchService extends BaseService {
|
||||
return this.mapResponse(items, hasNextPage ? (page + 1).toString() : null, { auth });
|
||||
}
|
||||
|
||||
async searchStatistics(auth: AuthDto, dto: StatisticsSearchDto): Promise<SearchStatisticsResponseDto> {
|
||||
const userIds = await this.getUserIdsToSearch(auth);
|
||||
|
||||
return await this.searchRepository.searchStatistics({
|
||||
...dto,
|
||||
userIds,
|
||||
});
|
||||
}
|
||||
|
||||
async searchRandom(auth: AuthDto, dto: RandomSearchDto): Promise<AssetResponseDto[]> {
|
||||
if (dto.visibility === AssetVisibility.LOCKED) {
|
||||
requireElevatedPermission(auth);
|
||||
|
@ -291,7 +291,6 @@ export function searchAssetBuilder(kysely: Kysely<DB>, options: AssetSearchBuild
|
||||
return kysely
|
||||
.withPlugin(joinDeduplicationPlugin)
|
||||
.selectFrom('assets')
|
||||
.selectAll('assets')
|
||||
.where('assets.visibility', '=', visibility)
|
||||
.$if(!!options.tagIds && options.tagIds.length > 0, (qb) => hasTags(qb, options.tagIds!))
|
||||
.$if(!!options.personIds && options.personIds.length > 0, (qb) => hasPeople(qb, options.personIds!))
|
||||
|
Loading…
x
Reference in New Issue
Block a user