2016-12-28 18:50:05 +08:00
|
|
|
import {AutoCompleteItem, SearchTypes} from "../../../common/entities/AutoCompleteItem";
|
|
|
|
import {ISearchManager} from "../interfaces/ISearchManager";
|
|
|
|
import {SearchResultDTO} from "../../../common/entities/SearchResult";
|
|
|
|
import {MySQLConnection} from "./MySQLConnection";
|
|
|
|
import {PhotoEntity} from "./enitites/PhotoEntity";
|
2016-12-28 19:30:26 +08:00
|
|
|
import {DirectoryEntity} from "./enitites/DirectoryEntity";
|
2016-12-28 18:50:05 +08:00
|
|
|
import {PositionMetaData} from "../../../common/entities/PhotoDTO";
|
|
|
|
|
|
|
|
export class SearchManager implements ISearchManager {
|
|
|
|
|
2017-07-08 04:54:18 +08:00
|
|
|
isSupported(): boolean {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
autocomplete(text: string, cb: (error: any, result: Array<AutoCompleteItem>) => void) {
|
|
|
|
|
|
|
|
MySQLConnection.getConnection().then(async connection => {
|
|
|
|
try {
|
|
|
|
let result: Array<AutoCompleteItem> = [];
|
|
|
|
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 => <Array<string>>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 => <PositionMetaData>JSON.parse(r.metadataPositionData))
|
|
|
|
.map(pm => <Array<string>>[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 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);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
search(text: string, searchType: SearchTypes, cb: (error: any, result: SearchResultDTO) => void) {
|
|
|
|
MySQLConnection.getConnection().then(async connection => {
|
|
|
|
|
|
|
|
let result: SearchResultDTO = <SearchResultDTO>{
|
|
|
|
searchText: text,
|
|
|
|
searchType: searchType,
|
|
|
|
directories: [],
|
|
|
|
photos: []
|
|
|
|
};
|
|
|
|
|
|
|
|
let query = connection
|
|
|
|
.getRepository(PhotoEntity)
|
|
|
|
.createQueryBuilder("photo");
|
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
.innerJoinAndSelect("photo.directory", "directory")
|
|
|
|
.getMany();
|
|
|
|
|
|
|
|
|
|
|
|
if (photos) {
|
|
|
|
for (let i = 0; i < photos.length; i++) {
|
|
|
|
photos[i].metadata.keywords = <any>JSON.parse(<any>photos[i].metadata.keywords);
|
|
|
|
photos[i].metadata.cameraData = <any>JSON.parse(<any>photos[i].metadata.cameraData);
|
|
|
|
photos[i].metadata.positionData = <any>JSON.parse(<any>photos[i].metadata.positionData);
|
|
|
|
photos[i].metadata.size = <any>JSON.parse(<any>photos[i].metadata.size);
|
2016-12-28 18:50:05 +08:00
|
|
|
}
|
2017-07-08 04:54:18 +08:00
|
|
|
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);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
instantSearch(text: string, cb: (error: any, result: SearchResultDTO) => void) {
|
|
|
|
MySQLConnection.getConnection().then(async connection => {
|
|
|
|
|
|
|
|
let result: SearchResultDTO = <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();
|
|
|
|
|
|
|
|
|
|
|
|
if (photos) {
|
|
|
|
for (let i = 0; i < photos.length; i++) {
|
|
|
|
photos[i].metadata.keywords = <any>JSON.parse(<any>photos[i].metadata.keywords);
|
|
|
|
photos[i].metadata.cameraData = <any>JSON.parse(<any>photos[i].metadata.cameraData);
|
|
|
|
photos[i].metadata.positionData = <any>JSON.parse(<any>photos[i].metadata.positionData);
|
|
|
|
photos[i].metadata.size = <any>JSON.parse(<any>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();
|
|
|
|
|
|
|
|
result.directories = directories;
|
|
|
|
|
|
|
|
return cb(null, result);
|
|
|
|
}).catch((error) => {
|
|
|
|
return cb(error, null);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
private encapsulateAutoComplete(values: Array<string>, type: SearchTypes) {
|
|
|
|
let res = [];
|
|
|
|
values.forEach((value) => {
|
|
|
|
res.push(new AutoCompleteItem(value, type));
|
|
|
|
});
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private autoCompleteItemsUnique(array: Array<AutoCompleteItem>) {
|
|
|
|
let a = array.concat();
|
|
|
|
for (let i = 0; i < a.length; ++i) {
|
|
|
|
for (let j = i + 1; j < a.length; ++j) {
|
|
|
|
if (a[i].equals(a[j]))
|
|
|
|
a.splice(j--, 1);
|
|
|
|
}
|
2016-12-28 18:50:05 +08:00
|
|
|
}
|
2017-07-08 04:54:18 +08:00
|
|
|
|
|
|
|
return a;
|
|
|
|
}
|
|
|
|
}
|