Fixes to search (#1639)

This commit is contained in:
Vishnu Mohandas 2024-01-09 11:25:10 +05:30 committed by GitHub
commit 5bbf227344
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 111 additions and 80 deletions

View file

@ -1,11 +1,14 @@
import "dart:convert";
import "dart:io";
import "dart:math";
import "package:computer/computer.dart";
import "package:logging/logging.dart";
import "package:photos/core/constants.dart";
import "package:photos/core/event_bus.dart";
import "package:photos/events/location_tag_updated_event.dart";
import "package:photos/models/api/entity/type.dart";
import "package:photos/models/file/file.dart";
import "package:photos/models/local_entity_data.dart";
import "package:photos/models/location/location.dart";
import 'package:photos/models/location_tag/location_tag.dart';
@ -17,7 +20,7 @@ import "package:shared_preferences/shared_preferences.dart";
class LocationService {
late SharedPreferences prefs;
final Logger _logger = Logger((LocationService).toString());
final List<City> _cities = [];
final Computer _computer = Computer.shared();
LocationService._privateConstructor();
@ -25,6 +28,8 @@ class LocationService {
static const kCitiesRemotePath = "https://assets.ente.io/world_cities.json";
List<City> _cities = [];
void init(SharedPreferences preferences) {
prefs = preferences;
if (FeatureFlagService.instance.isInternalUserOrDebugBuild()) {
@ -39,8 +44,19 @@ class LocationService {
);
}
List<City> getAllCities() {
return _cities;
Future<Map<City, List<EnteFile>>> getFilesInCity(
List<EnteFile> allFiles,
String query,
) async {
final result = await _computer.compute(
getCityResults,
param: {
"query": query,
"cities": _cities,
"files": allFiles,
},
);
return result;
}
Future<Iterable<LocalEntity<LocationTag>>> getLocationTags() {
@ -77,14 +93,6 @@ class LocationService {
}
}
///The area bounded by the location tag becomes more elliptical with increase
///in the magnitude of the latitude on the caritesian plane. When latitude is
///0 degrees, the ellipse is a circle with a = b = r. When latitude incrases,
///the major axis (a) has to be scaled by the secant of the latitude.
double _scaleFactor(double lat) {
return 1 / cos(lat * (pi / 180));
}
Future<List<LocalEntity<LocationTag>>> enclosingLocationTags(
Location fileCoordinates,
) async {
@ -110,22 +118,6 @@ class LocationService {
}
}
bool isFileInsideLocationTag(
Location centerPoint,
Location fileCoordinates,
double radius,
) {
final a =
(radius * _scaleFactor(centerPoint.latitude!)) / kilometersPerDegree;
final b = radius / kilometersPerDegree;
final x = centerPoint.latitude! - fileCoordinates.latitude!;
final y = centerPoint.longitude! - fileCoordinates.longitude!;
if ((x * x) / (a * a) + (y * y) / (b * b) <= 1) {
return true;
}
return false;
}
/// returns [lat, lng]
List<String>? convertLocationToDMS(Location centerPoint) {
if (centerPoint.latitude == null || centerPoint.longitude == null) {
@ -218,14 +210,15 @@ class LocationService {
Future<void> _loadCities() async {
try {
final data =
final file =
await RemoteAssetsService.instance.getAsset(kCitiesRemotePath);
final citiesJson = json.decode(await data.readAsString());
final List<dynamic> jsonData = citiesJson['data'];
final cities =
jsonData.map<City>((jsonItem) => City.fromMap(jsonItem)).toList();
_cities.clear();
_cities.addAll(cities);
final startTime = DateTime.now();
_cities =
await _computer.compute(parseCities, param: {"filePath": file.path});
final endTime = DateTime.now();
_logger.info(
"Loaded cities in ${(endTime.millisecondsSinceEpoch - startTime.millisecondsSinceEpoch)}ms",
);
_logger.info("Loaded cities");
} catch (e, s) {
_logger.severe("Failed to load cities", e, s);
@ -233,6 +226,71 @@ class LocationService {
}
}
Future<List<City>> parseCities(Map args) async {
final file = File(args["filePath"]);
final citiesJson = json.decode(await file.readAsString());
final List<dynamic> jsonData = citiesJson['data'];
final cities =
jsonData.map<City>((jsonItem) => City.fromMap(jsonItem)).toList();
return cities;
}
Map<City, List<EnteFile>> getCityResults(Map args) {
final query = (args["query"] as String).toLowerCase();
final cities = args["cities"] as List<City>;
final files = args["files"] as List<EnteFile>;
final matchingCities = cities.where(
(city) => city.city.toLowerCase().contains(query),
);
final Map<City, List<EnteFile>> results = {};
for (final city in matchingCities) {
final List<EnteFile> matchingFiles = [];
final cityLocation = Location(latitude: city.lat, longitude: city.lng);
for (final file in files) {
if (file.hasLocation) {
if (isFileInsideLocationTag(
cityLocation,
file.location!,
defaultCityRadius,
)) {
matchingFiles.add(file);
}
}
}
if (matchingFiles.isNotEmpty) {
results[city] = matchingFiles;
}
}
return results;
}
bool isFileInsideLocationTag(
Location centerPoint,
Location fileCoordinates,
double radius,
) {
final a =
(radius * _scaleFactor(centerPoint.latitude!)) / kilometersPerDegree;
final b = radius / kilometersPerDegree;
final x = centerPoint.latitude! - fileCoordinates.latitude!;
final y = centerPoint.longitude! - fileCoordinates.longitude!;
if ((x * x) / (a * a) + (y * y) / (b * b) <= 1) {
return true;
}
return false;
}
///The area bounded by the location tag becomes more elliptical with increase
///in the magnitude of the latitude on the caritesian plane. When latitude is
///0 degrees, the ellipse is a circle with a = b = r. When latitude incrases,
///the major axis (a) has to be scaled by the secant of the latitude.
double _scaleFactor(double lat) {
return 1 / cos(lat * (pi / 180));
}
class City {
final String city;
final String country;

View file

@ -3,7 +3,6 @@ import "dart:math";
import "package:flutter/cupertino.dart";
import "package:intl/intl.dart";
import 'package:logging/logging.dart';
import "package:photos/core/constants.dart";
import 'package:photos/core/event_bus.dart';
import 'package:photos/data/holidays.dart';
import 'package:photos/data/months.dart';
@ -18,7 +17,6 @@ import "package:photos/models/file/extensions/file_props.dart";
import 'package:photos/models/file/file.dart';
import 'package:photos/models/file/file_type.dart';
import "package:photos/models/local_entity_data.dart";
import "package:photos/models/location/location.dart";
import "package:photos/models/location_tag/location_tag.dart";
import 'package:photos/models/search/album_search_result.dart';
import 'package:photos/models/search/generic_search_result.dart';
@ -620,7 +618,7 @@ class SearchService {
for (EnteFile file in allFiles) {
if (file.hasLocation) {
for (LocalEntity<LocationTag> tag in result.keys) {
if (LocationService.instance.isFileInsideLocationTag(
if (isFileInsideLocationTag(
tag.item.centerPoint,
file.location!,
tag.item.radius,
@ -639,7 +637,7 @@ class SearchService {
return false;
}
for (LocalEntity<LocationTag> tag in locationTagEntities) {
if (LocationService.instance.isFileInsideLocationTag(
if (isFileInsideLocationTag(
tag.item.centerPoint,
file.location!,
tag.item.radius,
@ -684,36 +682,9 @@ class SearchService {
}
Future<List<GenericSearchResult>> getCityResults(String query) async {
final startTime = DateTime.now().microsecondsSinceEpoch;
final List<GenericSearchResult> searchResults = [];
final cities = LocationService.instance.getAllCities();
final matchingCities = <City>[];
final queryLower = query.toLowerCase();
for (City city in cities) {
if (city.city.toLowerCase().startsWith(queryLower)) {
matchingCities.add(city);
}
}
final files = await getAllFiles();
final Map<City, List<EnteFile>> results = {};
for (final city in matchingCities) {
final List<EnteFile> matchingFiles = [];
final cityLocation = Location(latitude: city.lat, longitude: city.lng);
for (final file in files) {
if (file.hasLocation) {
if (LocationService.instance.isFileInsideLocationTag(
cityLocation,
file.location!,
defaultCityRadius,
)) {
matchingFiles.add(file);
}
}
}
if (matchingFiles.isNotEmpty) {
results[city] = matchingFiles;
}
}
final results = await LocationService.instance.getFilesInCity(files, query);
final List<GenericSearchResult> searchResults = [];
for (final entry in results.entries) {
searchResults.add(
GenericSearchResult(
@ -723,9 +694,6 @@ class SearchService {
),
);
}
final endTime = DateTime.now().microsecondsSinceEpoch;
_logger
.info("Time taken " + ((endTime - startTime) / 1000).toString() + "ms");
return searchResults;
}
@ -745,7 +713,7 @@ class SearchService {
for (EnteFile file in allFiles) {
if (file.hasLocation) {
for (LocalEntity<LocationTag> tag in tagToItemsMap.keys) {
if (LocationService.instance.isFileInsideLocationTag(
if (isFileInsideLocationTag(
tag.item.centerPoint,
file.location!,
tag.item.radius,

View file

@ -133,12 +133,12 @@ class EmbeddingStore {
return;
}
final inputs = <EmbeddingsDecoderInput>[];
final fileMap = await FilesDB.instance
.getFilesFromIDs(remoteEmbeddings.map((e) => e.fileID).toList());
for (final embedding in remoteEmbeddings) {
final file = await FilesDB.instance.getAnyUploadedFile(embedding.fileID);
if (file == null) {
continue;
}
final fileKey = getFileKey(file);
final file = fileMap[embedding.fileID];
final fileKey = getFileKey(file!);
final input = EmbeddingsDecoderInput(embedding, fileKey);
inputs.add(input);
}

View file

@ -13,6 +13,7 @@ import 'package:photos/events/embedding_updated_event.dart';
import "package:photos/events/file_uploaded_event.dart";
import "package:photos/models/embedding.dart";
import "package:photos/models/file/file.dart";
import "package:photos/services/collections_service.dart";
import "package:photos/services/semantic_search/embedding_store.dart";
import "package:photos/services/semantic_search/frameworks/ggml.dart";
import "package:photos/services/semantic_search/frameworks/ml_framework.dart";
@ -215,8 +216,12 @@ class SemanticSearchService {
final filesMap = await FilesDB.instance
.getFilesFromIDs(queryResults.map((e) => e.id).toList());
final results = <EnteFile>[];
final ignoredCollections =
CollectionsService.instance.getHiddenCollectionIds();
for (final result in queryResults) {
if (filesMap.containsKey(result.id)) {
final file = filesMap[result.id];
if (file != null && !ignoredCollections.contains(file.collectionID)) {
results.add(filesMap[result.id]!);
}
}

View file

@ -62,7 +62,7 @@ class _DynamicLocationGalleryWidgetState
final stopWatch = Stopwatch()..start();
final copyOfFiles = List<EnteFile>.from(result.files);
copyOfFiles.removeWhere((f) {
return !LocationService.instance.isFileInsideLocationTag(
return !isFileInsideLocationTag(
InheritedLocationTagData.of(context).centerPoint,
f.location!,
selectedRadius,

View file

@ -205,7 +205,7 @@ class _LocationGalleryWidgetState extends State<LocationGalleryWidget> {
final stopWatch = Stopwatch()..start();
final filesInLocation = allFilesWithLocation;
filesInLocation.removeWhere((f) {
return !LocationService.instance.isFileInsideLocationTag(
return !isFileInsideLocationTag(
centerPoint,
f.location!,
selectedRadius,

View file

@ -12,7 +12,7 @@ description: ente photos application
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 0.8.32+552
version: 0.8.33+553
environment:
sdk: ">=3.0.0 <4.0.0"