From f099dc6eb05ee33e69db829871fc12fa77898cc7 Mon Sep 17 00:00:00 2001 From: Braun Patrik Date: Sat, 8 Jul 2017 00:18:24 +0200 Subject: [PATCH] bug fixing --- backend/middlewares/GalleryMWs.ts | 51 ++-- backend/model/interfaces/ISearchManager.ts | 6 +- backend/model/memory/SearchManager.ts | 22 +- backend/model/mysql/SearchManager.ts | 248 +++++++++--------- common/entities/DirectoryDTO.ts | 13 + frontend/app/app.component.ts | 5 +- frontend/app/gallery/cache.gallery.service.ts | 20 +- frontend/app/gallery/gallery.component.css | 7 +- frontend/app/gallery/gallery.component.html | 14 +- frontend/app/gallery/gallery.service.ts | 22 +- .../search/search.gallery.component.ts | 4 + 11 files changed, 206 insertions(+), 206 deletions(-) diff --git a/backend/middlewares/GalleryMWs.ts b/backend/middlewares/GalleryMWs.ts index 2867570..70070a8 100644 --- a/backend/middlewares/GalleryMWs.ts +++ b/backend/middlewares/GalleryMWs.ts @@ -4,9 +4,8 @@ import {NextFunction, Request, Response} from "express"; import {Error, ErrorCodes} from "../../common/entities/Error"; import {DirectoryDTO} from "../../common/entities/DirectoryDTO"; import {ObjectManagerRepository} from "../model/ObjectManagerRepository"; -import {AutoCompleteItem, SearchTypes} from "../../common/entities/AutoCompleteItem"; +import {SearchTypes} from "../../common/entities/AutoCompleteItem"; import {ContentWrapper} from "../../common/entities/ConentWrapper"; -import {SearchResultDTO} from "../../common/entities/SearchResult"; import {PhotoDTO} from "../../common/entities/PhotoDTO"; import {ProjectPath} from "../ProjectPath"; import {Logger} from "../Logger"; @@ -92,7 +91,7 @@ export class GalleryMWs { } - public static search(req: Request, res: Response, next: NextFunction) { + public static async search(req: Request, res: Response, next: NextFunction) { if (Config.Client.Search.searchEnabled === false) { return next(); } @@ -105,18 +104,21 @@ export class GalleryMWs { if (req.query.type) { type = parseInt(req.query.type); } + try { + const result = await ObjectManagerRepository.getInstance().SearchManager.search(req.params.text, type); - ObjectManagerRepository.getInstance().SearchManager.search(req.params.text, type, (err, result: SearchResultDTO) => { - if (err || !result) { - return next(new Error(ErrorCodes.GENERAL_ERROR, err)); - } + result.directories.forEach(dir => dir.photos = dir.photos || []); req.resultPipe = new ContentWrapper(null, result); return next(); - }); + } catch (err) { + + Logger.warn(LOG_TAG, "Error during searching", err); + console.error(err); + return next(new Error(ErrorCodes.GENERAL_ERROR, err)); + } } - - public static instantSearch(req: Request, res: Response, next: NextFunction) { + public static async instantSearch(req: Request, res: Response, next: NextFunction) { if (Config.Client.Search.instantSearchEnabled === false) { return next(); } @@ -125,17 +127,20 @@ export class GalleryMWs { return next(); } + try { + const result = await ObjectManagerRepository.getInstance().SearchManager.instantSearch(req.params.text); - ObjectManagerRepository.getInstance().SearchManager.instantSearch(req.params.text, (err, result: SearchResultDTO) => { - if (err || !result) { - return next(new Error(ErrorCodes.GENERAL_ERROR, err)); - } + result.directories.forEach(dir => dir.photos = dir.photos || []); req.resultPipe = new ContentWrapper(null, result); return next(); - }); + } catch (err) { + Logger.warn(LOG_TAG, "Error during searching", err); + console.error(err); + return next(new Error(ErrorCodes.GENERAL_ERROR, err)); + } } - public static autocomplete(req: Request, res: Response, next: NextFunction) { + public static async autocomplete(req: Request, res: Response, next: NextFunction) { if (Config.Client.Search.autocompleteEnabled === false) { return next(); } @@ -143,13 +148,15 @@ export class GalleryMWs { return next(); } - ObjectManagerRepository.getInstance().SearchManager.autocomplete(req.params.text, (err, items: Array) => { - if (err || !items) { - return next(new Error(ErrorCodes.GENERAL_ERROR, err)); - } - req.resultPipe = items; + try { + req.resultPipe = await ObjectManagerRepository.getInstance().SearchManager.autocomplete(req.params.text); return next(); - }); + } catch (err) { + Logger.warn(LOG_TAG, "Error during searching", err); + console.error(err); + return next(new Error(ErrorCodes.GENERAL_ERROR, err)); + } + } diff --git a/backend/model/interfaces/ISearchManager.ts b/backend/model/interfaces/ISearchManager.ts index e082b0a..e0b4206 100644 --- a/backend/model/interfaces/ISearchManager.ts +++ b/backend/model/interfaces/ISearchManager.ts @@ -1,8 +1,8 @@ import {AutoCompleteItem, SearchTypes} from "../../../common/entities/AutoCompleteItem"; import {SearchResultDTO} from "../../../common/entities/SearchResult"; export interface ISearchManager { - autocomplete(text: string, cb: (error: any, result: Array) => void): void; - search(text: string, searchType: SearchTypes, cb: (error: any, result: SearchResultDTO) => void): void; - instantSearch(text: string, cb: (error: any, result: SearchResultDTO) => void): void; + autocomplete(text: string): Promise; + search(text: string, searchType: SearchTypes): Promise; + instantSearch(text: string): Promise; isSupported(): boolean; } diff --git a/backend/model/memory/SearchManager.ts b/backend/model/memory/SearchManager.ts index 526a8a0..a8d7172 100644 --- a/backend/model/memory/SearchManager.ts +++ b/backend/model/memory/SearchManager.ts @@ -3,22 +3,22 @@ import {ISearchManager} from "../interfaces/ISearchManager"; import {SearchResultDTO} from "../../../common/entities/SearchResult"; export class SearchManager implements ISearchManager { + autocomplete(text: string): Promise { + throw new Error("Method not implemented."); + } + + search(text: string, searchType: SearchTypes): Promise { + throw new Error("Method not implemented."); + } + + instantSearch(text: string): Promise { + throw new Error("Method not implemented."); + } isSupported(): boolean { return false; } - autocomplete(text: string, cb: (error: any, result: Array) => void) { - throw new Error("not implemented"); - } - - search(text: string, searchType: SearchTypes, cb: (error: any, result: SearchResultDTO) => void) { - throw new Error("not implemented"); - } - - instantSearch(text: string, cb: (error: any, result: SearchResultDTO) => void) { - throw new Error("not implemented"); - } } diff --git a/backend/model/mysql/SearchManager.ts b/backend/model/mysql/SearchManager.ts index 24240cd..d04e20e 100644 --- a/backend/model/mysql/SearchManager.ts +++ b/backend/model/mysql/SearchManager.ts @@ -12,163 +12,157 @@ export class SearchManager implements ISearchManager { return true; } - autocomplete(text: string, cb: (error: any, result: Array) => void) { + async autocomplete(text: string) { - MySQLConnection.getConnection().then(async connection => { - try { - let result: Array = []; - let photoRepository = connection.getRepository(PhotoEntity); - let directoryRepository = connection.getRepository(DirectoryEntity); + const connection = await MySQLConnection.getConnection(); + + let result: Array = []; + let photoRepository = connection.getRepository(PhotoEntity); + let directoryRepository = connection.getRepository(DirectoryEntity); - (await photoRepository - .createQueryBuilder('photo') - .select('DISTINCT(photo.metadataKeywords)') - .where('photo.metadata.keywords LIKE :text COLLATE utf8_general_ci', {text: "%" + text + "%"}) - .setLimit(5) - .getRawMany()) - .map(r => >JSON.parse(r.metadataKeywords)) - .forEach(keywords => { - result = result.concat(this.encapsulateAutoComplete(keywords.filter(k => k.toLowerCase().indexOf(text.toLowerCase()) != -1), SearchTypes.keyword)); - }); + (await photoRepository + .createQueryBuilder('photo') + .select('DISTINCT(photo.metadataKeywords)') + .where('photo.metadata.keywords LIKE :text COLLATE utf8_general_ci', {text: "%" + text + "%"}) + .setLimit(5) + .getRawMany()) + .map(r => >JSON.parse(r.metadataKeywords)) + .forEach(keywords => { + result = result.concat(this.encapsulateAutoComplete(keywords.filter(k => k.toLowerCase().indexOf(text.toLowerCase()) != -1), SearchTypes.keyword)); + }); - (await photoRepository - .createQueryBuilder('photo') - .select('DISTINCT(photo.metadataPositionData)') - .where('photo.metadata.positionData LIKE :text COLLATE utf8_general_ci', {text: "%" + text + "%"}) - .setLimit(5) - .getRawMany()) - .map(r => JSON.parse(r.metadataPositionData)) - .map(pm => >[pm.city || "", pm.country || "", pm.state || ""]) - .forEach(positions => { - result = result.concat(this.encapsulateAutoComplete(positions.filter(p => p.toLowerCase().indexOf(text.toLowerCase()) != -1), SearchTypes.position)); - }); + (await photoRepository + .createQueryBuilder('photo') + .select('DISTINCT(photo.metadataPositionData)') + .where('photo.metadata.positionData LIKE :text COLLATE utf8_general_ci', {text: "%" + text + "%"}) + .setLimit(5) + .getRawMany()) + .map(r => JSON.parse(r.metadataPositionData)) + .filter(pm => !!pm) + .map(pm => >[pm.city || "", pm.country || "", pm.state || ""]) + .forEach(positions => { + result = result.concat(this.encapsulateAutoComplete(positions.filter(p => p.toLowerCase().indexOf(text.toLowerCase()) != -1), SearchTypes.position)); + }); - result = result.concat(this.encapsulateAutoComplete((await photoRepository - .createQueryBuilder('photo') - .select('DISTINCT(photo.name)') - .where('photo.name LIKE :text COLLATE utf8_general_ci', {text: "%" + text + "%"}) - .setLimit(5) - .getRawMany()) - .map(r => r.name), SearchTypes.image)); + result = result.concat(this.encapsulateAutoComplete((await photoRepository + .createQueryBuilder('photo') + .select('DISTINCT(photo.name)') + .where('photo.name LIKE :text COLLATE utf8_general_ci', {text: "%" + text + "%"}) + .setLimit(5) + .getRawMany()) + .map(r => r.name), SearchTypes.image)); - result = result.concat(this.encapsulateAutoComplete((await directoryRepository - .createQueryBuilder('dir') - .select('DISTINCT(dir.name)') - .where('dir.name LIKE :text COLLATE utf8_general_ci', {text: "%" + text + "%"}) - .setLimit(5) - .getRawMany()) - .map(r => r.name), SearchTypes.directory)); + result = result.concat(this.encapsulateAutoComplete((await directoryRepository + .createQueryBuilder('dir') + .select('DISTINCT(dir.name)') + .where('dir.name LIKE :text COLLATE utf8_general_ci', {text: "%" + text + "%"}) + .setLimit(5) + .getRawMany()) + .map(r => r.name), SearchTypes.directory)); - return cb(null, this.autoCompleteItemsUnique(result)); - } catch (error) { - return cb(error, null); - } - - }).catch((error) => { - return cb(error, null); - }); + return this.autoCompleteItemsUnique(result); } - search(text: string, searchType: SearchTypes, cb: (error: any, result: SearchResultDTO) => void) { - MySQLConnection.getConnection().then(async connection => { + async search(text: string, searchType: SearchTypes) { + const connection = await MySQLConnection.getConnection(); - let result: SearchResultDTO = { - searchText: text, - searchType: searchType, - directories: [], - photos: [] - }; + let result: SearchResultDTO = { + searchText: text, + searchType: searchType, + directories: [], + photos: [] + }; - let query = connection - .getRepository(PhotoEntity) - .createQueryBuilder("photo"); + let query = connection + .getRepository(PhotoEntity) + .createQueryBuilder("photo") + .innerJoinAndSelect("photo.directory", "directory") + .orderBy("photo.metadata.creationDate", "ASC"); - if (!searchType || searchType === SearchTypes.image) { - query.orWhere('photo.name LIKE :text COLLATE utf8_general_ci', {text: "%" + text + "%"}); + if (!searchType || searchType === SearchTypes.directory) { + query.orWhere('directory.name LIKE :text COLLATE utf8_general_ci', {text: "%" + text + "%"}); + } + + if (!searchType || searchType === SearchTypes.image) { + query.orWhere('photo.name LIKE :text COLLATE utf8_general_ci', {text: "%" + text + "%"}); + } + + if (!searchType || searchType === SearchTypes.position) { + query.orWhere('photo.metadata.positionData LIKE :text COLLATE utf8_general_ci', {text: "%" + text + "%"}); + } + if (!searchType || searchType === SearchTypes.keyword) { + query.orWhere('photo.metadata.keywords LIKE :text COLLATE utf8_general_ci', {text: "%" + text + "%"}); + } + let photos = await query + .getMany(); + + + if (photos) { + for (let i = 0; i < photos.length; i++) { + photos[i].metadata.keywords = JSON.parse(photos[i].metadata.keywords); + photos[i].metadata.cameraData = JSON.parse(photos[i].metadata.cameraData); + photos[i].metadata.positionData = JSON.parse(photos[i].metadata.positionData); + photos[i].metadata.size = JSON.parse(photos[i].metadata.size); } + result.photos = photos; + } - if (!searchType || searchType === SearchTypes.position) { - query.orWhere('photo.metadata.positionData LIKE :text COLLATE utf8_general_ci', {text: "%" + text + "%"}); - } - if (!searchType || searchType === SearchTypes.keyword) { - query.orWhere('photo.metadata.keywords LIKE :text COLLATE utf8_general_ci', {text: "%" + text + "%"}); - } - let photos = await query - .innerJoinAndSelect("photo.directory", "directory") - .getMany(); + result.directories = await connection + .getRepository(DirectoryEntity) + .createQueryBuilder("dir") + .where('dir.name LIKE :text COLLATE utf8_general_ci', {text: "%" + text + "%"}) + .getMany(); - if (photos) { - for (let i = 0; i < photos.length; i++) { - photos[i].metadata.keywords = JSON.parse(photos[i].metadata.keywords); - photos[i].metadata.cameraData = JSON.parse(photos[i].metadata.cameraData); - photos[i].metadata.positionData = JSON.parse(photos[i].metadata.positionData); - photos[i].metadata.size = JSON.parse(photos[i].metadata.size); - } - result.photos = photos; - } - - result.directories = await connection - .getRepository(DirectoryEntity) - .createQueryBuilder("dir") - .where('dir.name LIKE :text COLLATE utf8_general_ci', {text: "%" + text + "%"}) - .getMany(); - - - return cb(null, result); - }).catch((error) => { - return cb(error, null); - }); + return result; } - instantSearch(text: string, cb: (error: any, result: SearchResultDTO) => void) { - MySQLConnection.getConnection().then(async connection => { + async instantSearch(text: string) { + const connection = await MySQLConnection.getConnection(); - let result: SearchResultDTO = { - searchText: text, - directories: [], - photos: [] - }; + let result: SearchResultDTO = { + searchText: text, + directories: [], + photos: [] + }; - let photos = await connection - .getRepository(PhotoEntity) - .createQueryBuilder("photo") - .where('photo.metadata.keywords LIKE :text COLLATE utf8_general_ci', {text: "%" + text + "%"}) - .orWhere('photo.metadata.positionData LIKE :text COLLATE utf8_general_ci', {text: "%" + text + "%"}) - .orWhere('photo.name LIKE :text COLLATE utf8_general_ci', {text: "%" + text + "%"}) - .innerJoinAndSelect("photo.directory", "directory") - .setLimit(10) - .getMany(); + let photos = await connection + .getRepository(PhotoEntity) + .createQueryBuilder("photo") + .orderBy("photo.metadata.creationDate", "ASC") + .where('photo.metadata.keywords LIKE :text COLLATE utf8_general_ci', {text: "%" + text + "%"}) + .orWhere('photo.metadata.positionData LIKE :text COLLATE utf8_general_ci', {text: "%" + text + "%"}) + .orWhere('photo.name LIKE :text COLLATE utf8_general_ci', {text: "%" + text + "%"}) + .innerJoinAndSelect("photo.directory", "directory") + .setLimit(10) + .getMany(); - if (photos) { - for (let i = 0; i < photos.length; i++) { - photos[i].metadata.keywords = JSON.parse(photos[i].metadata.keywords); - photos[i].metadata.cameraData = JSON.parse(photos[i].metadata.cameraData); - photos[i].metadata.positionData = JSON.parse(photos[i].metadata.positionData); - photos[i].metadata.size = JSON.parse(photos[i].metadata.size); - } - result.photos = photos; + if (photos) { + for (let i = 0; i < photos.length; i++) { + photos[i].metadata.keywords = JSON.parse(photos[i].metadata.keywords); + photos[i].metadata.cameraData = JSON.parse(photos[i].metadata.cameraData); + photos[i].metadata.positionData = JSON.parse(photos[i].metadata.positionData); + photos[i].metadata.size = JSON.parse(photos[i].metadata.size); } + result.photos = photos; + } - let directories = await connection - .getRepository(DirectoryEntity) - .createQueryBuilder("dir") - .where('dir.name LIKE :text COLLATE utf8_general_ci', {text: "%" + text + "%"}) - .setLimit(10) - .getMany(); + let directories = await connection + .getRepository(DirectoryEntity) + .createQueryBuilder("dir") + .where('dir.name LIKE :text COLLATE utf8_general_ci', {text: "%" + text + "%"}) + .setLimit(10) + .getMany(); - result.directories = directories; + result.directories = directories; - return cb(null, result); - }).catch((error) => { - return cb(error, null); - }); + return result; } private encapsulateAutoComplete(values: Array, type: SearchTypes) { diff --git a/common/entities/DirectoryDTO.ts b/common/entities/DirectoryDTO.ts index 1fd41d3..f095db6 100644 --- a/common/entities/DirectoryDTO.ts +++ b/common/entities/DirectoryDTO.ts @@ -9,3 +9,16 @@ export interface DirectoryDTO { directories: Array; photos: Array; } + +export module DirectoryUtil { + export const addReferences = (dir: DirectoryDTO) => { + dir.photos.forEach((photo: PhotoDTO) => { + photo.directory = dir; + }); + + dir.directories.forEach((directory: DirectoryDTO) => { + addReferences(directory); + directory.parent = dir; + }); + } +} diff --git a/frontend/app/app.component.ts b/frontend/app/app.component.ts index 936b045..1b19a79 100644 --- a/frontend/app/app.component.ts +++ b/frontend/app/app.component.ts @@ -2,6 +2,8 @@ import {Component, OnInit} from "@angular/core"; import {AuthenticationService} from "./model/network/authentication.service"; import {UserDTO} from "../../common/entities/UserDTO"; import {Router} from "@angular/router"; +import {Config} from "../../common/config/public/Config"; +import {Title} from "@angular/platform-browser"; @Component({ @@ -11,10 +13,11 @@ import {Router} from "@angular/router"; }) export class AppComponent implements OnInit { - constructor(private _router: Router, private _authenticationService: AuthenticationService) { + constructor(private _router: Router, private _authenticationService: AuthenticationService, private _title: Title) { } ngOnInit() { + this._title.setTitle(Config.Client.applicationTitle); this._authenticationService.user.subscribe((user: UserDTO) => { if (user != null) { if (this._router.isActive('login', true)) { diff --git a/frontend/app/gallery/cache.gallery.service.ts b/frontend/app/gallery/cache.gallery.service.ts index d8b9c72..ffeff81 100644 --- a/frontend/app/gallery/cache.gallery.service.ts +++ b/frontend/app/gallery/cache.gallery.service.ts @@ -1,6 +1,6 @@ import {Injectable} from "@angular/core"; import {PhotoDTO} from "../../../common/entities/PhotoDTO"; -import {DirectoryDTO} from "../../../common/entities/DirectoryDTO"; +import {DirectoryDTO, DirectoryUtil} from "../../../common/entities/DirectoryDTO"; import {Utils} from "../../../common/Utils"; import {Config} from "../../../common/config/public/Config"; @@ -16,23 +16,7 @@ export class GalleryCacheService { if (value != null) { let directory: DirectoryDTO = JSON.parse(value); - - //Add references - let addDir = (dir: DirectoryDTO) => { - dir.photos.forEach((photo: PhotoDTO) => { - photo.directory = dir; - }); - - dir.directories.forEach((directory: DirectoryDTO) => { - addDir(directory); - directory.parent = dir; - }); - - - }; - addDir(directory); - - + DirectoryUtil.addReferences(directory); return directory; } return null; diff --git a/frontend/app/gallery/gallery.component.css b/frontend/app/gallery/gallery.component.css index dc9c0cd..82ff503 100644 --- a/frontend/app/gallery/gallery.component.css +++ b/frontend/app/gallery/gallery.component.css @@ -4,4 +4,9 @@ gallery-map { display: block; height: 80px; width: 100px; -} \ No newline at end of file +} + +.directories { + margin-right: 1px; + margin-left: 1px; +} diff --git a/frontend/app/gallery/gallery.component.html b/frontend/app/gallery/gallery.component.html index 2ae242a..15dba22 100644 --- a/frontend/app/gallery/gallery.component.html +++ b/frontend/app/gallery/gallery.component.html @@ -14,9 +14,10 @@
- - +
+ +
@@ -31,14 +32,15 @@ - {{_galleryService.content.searchResult.searchText}} + {{_galleryService.content.value.searchResult.searchText}} -
- +
+
diff --git a/frontend/app/gallery/gallery.service.ts b/frontend/app/gallery/gallery.service.ts index cf4f8f2..dd239fd 100644 --- a/frontend/app/gallery/gallery.service.ts +++ b/frontend/app/gallery/gallery.service.ts @@ -1,8 +1,7 @@ import {Injectable} from "@angular/core"; import {NetworkService} from "../model/network/network.service"; import {ContentWrapper} from "../../../common/entities/ConentWrapper"; -import {PhotoDTO} from "../../../common/entities/PhotoDTO"; -import {DirectoryDTO} from "../../../common/entities/DirectoryDTO"; +import {DirectoryDTO, DirectoryUtil} from "../../../common/entities/DirectoryDTO"; import {SearchTypes} from "../../../common/entities/AutoCompleteItem"; import {GalleryCacheService} from "./cache.gallery.service"; import {BehaviorSubject} from "rxjs/BehaviorSubject"; @@ -42,6 +41,7 @@ export class GalleryService { cw = await this.networkService.getJson("/gallery/content/" + directoryName + "?sk=" + this._shareService.getSharingKey()); } } + if (cw == null) { cw = await this.networkService.getJson("/gallery/content/" + directoryName); } @@ -52,20 +52,8 @@ export class GalleryService { return; } - //Add references - let addDir = (dir: DirectoryDTO) => { - dir.photos.forEach((photo: PhotoDTO) => { - photo.directory = dir; - }); - dir.directories.forEach((directory: DirectoryDTO) => { - addDir(directory); - directory.parent = dir; - }); - - - }; - addDir(cw.directory); + DirectoryUtil.addReferences(cw.directory); this.lastDirectory = cw.directory; @@ -79,7 +67,7 @@ export class GalleryService { //TODO: cache public async search(text: string, type?: SearchTypes): Promise { clearTimeout(this.searchId); - if (text === null || text === '') { + if (text === null || text === '' || text.trim() == ".") { return null } @@ -94,7 +82,7 @@ export class GalleryService { //TODO: cache (together with normal search) public async instantSearch(text: string): Promise { - if (text === null || text === '') { + if (text === null || text === '' || text.trim() == ".") { const content = new ContentWrapper(); content.directory = this.lastDirectory; content.searchResult = null; diff --git a/frontend/app/gallery/search/search.gallery.component.ts b/frontend/app/gallery/search/search.gallery.component.ts index 2195937..1de87a2 100644 --- a/frontend/app/gallery/search/search.gallery.component.ts +++ b/frontend/app/gallery/search/search.gallery.component.ts @@ -97,6 +97,10 @@ export class GallerySearchComponent { if (!Config.Client.Search.autocompleteEnabled) { return } + if (searchText.trim() == ".") { + return; + } + if (searchText.trim().length > 0) { try {