1
0
mirror of https://github.com/xuthus83/pigallery2.git synced 2025-01-14 14:43:17 +08:00

implementing mysql autocomplete

This commit is contained in:
Braun Patrik 2016-12-28 11:50:05 +01:00
parent 53ab3d34a9
commit 3ad2f6612f
11 changed files with 127 additions and 40 deletions

View File

@ -6,7 +6,7 @@ import {DirectoryDTO} from "../../common/entities/DirectoryDTO";
import {ObjectManagerRepository} from "../model/ObjectManagerRepository"; import {ObjectManagerRepository} from "../model/ObjectManagerRepository";
import {AutoCompleteItem, SearchTypes} from "../../common/entities/AutoCompleteItem"; import {AutoCompleteItem, SearchTypes} from "../../common/entities/AutoCompleteItem";
import {ContentWrapper} from "../../common/entities/ConentWrapper"; import {ContentWrapper} from "../../common/entities/ConentWrapper";
import {SearchResult} from "../../common/entities/SearchResult"; import {SearchResultDTO} from "../../common/entities/SearchResult";
import {PhotoDTO} from "../../common/entities/PhotoDTO"; import {PhotoDTO} from "../../common/entities/PhotoDTO";
import {Config} from "../config/Config"; import {Config} from "../config/Config";
import {ProjectPath} from "../ProjectPath"; import {ProjectPath} from "../ProjectPath";
@ -87,7 +87,7 @@ export class GalleryMWs {
type = parseInt(req.query.type); type = parseInt(req.query.type);
} }
ObjectManagerRepository.getInstance().getSearchManager().search(req.params.text, type, (err, result:SearchResult) => { ObjectManagerRepository.getInstance().getSearchManager().search(req.params.text, type, (err, result: SearchResultDTO) => {
if (err || !result) { if (err || !result) {
return next(new Error(ErrorCodes.GENERAL_ERROR, err)); return next(new Error(ErrorCodes.GENERAL_ERROR, err));
} }
@ -107,7 +107,7 @@ export class GalleryMWs {
} }
ObjectManagerRepository.getInstance().getSearchManager().instantSearch(req.params.text, (err, result:SearchResult) => { ObjectManagerRepository.getInstance().getSearchManager().instantSearch(req.params.text, (err, result: SearchResultDTO) => {
if (err || !result) { if (err || !result) {
return next(new Error(ErrorCodes.GENERAL_ERROR, err)); return next(new Error(ErrorCodes.GENERAL_ERROR, err));
} }

View File

@ -25,7 +25,7 @@ export class ObjectManagerRepository {
MySQLConnection.init().then(() => { MySQLConnection.init().then(() => {
const GalleryManager = require("./mysql/GalleryManager").GalleryManager; const GalleryManager = require("./mysql/GalleryManager").GalleryManager;
const UserManager = require("./mysql/UserManager").UserManager; const UserManager = require("./mysql/UserManager").UserManager;
const SearchManager = require("./memory/SearchManager").SearchManager; const SearchManager = require("./mysql/SearchManager").SearchManager;
ObjectManagerRepository.getInstance().setGalleryManager(new GalleryManager()); ObjectManagerRepository.getInstance().setGalleryManager(new GalleryManager());
ObjectManagerRepository.getInstance().setUserManager(new UserManager()); ObjectManagerRepository.getInstance().setUserManager(new UserManager());
ObjectManagerRepository.getInstance().setSearchManager(new SearchManager()); ObjectManagerRepository.getInstance().setSearchManager(new SearchManager());

View File

@ -1,7 +1,7 @@
import {AutoCompleteItem, SearchTypes} from "../../../common/entities/AutoCompleteItem"; import {AutoCompleteItem, SearchTypes} from "../../../common/entities/AutoCompleteItem";
import {SearchResult} from "../../../common/entities/SearchResult"; import {SearchResultDTO} from "../../../common/entities/SearchResult";
export interface ISearchManager { export interface ISearchManager {
autocomplete(text: string, cb: (error: any, result: Array<AutoCompleteItem>) => void): void; autocomplete(text: string, cb: (error: any, result: Array<AutoCompleteItem>) => void): void;
search(text: string, searchType: SearchTypes, cb: (error: any, result: SearchResult) => void): void; search(text: string, searchType: SearchTypes, cb: (error: any, result: SearchResultDTO) => void): void;
instantSearch(text: string, cb: (error: any, result: SearchResult) => void): void; instantSearch(text: string, cb: (error: any, result: SearchResultDTO) => void): void;
} }

View File

@ -1,6 +1,6 @@
import {AutoCompleteItem, SearchTypes} from "../../../common/entities/AutoCompleteItem"; import {AutoCompleteItem, SearchTypes} from "../../../common/entities/AutoCompleteItem";
import {ISearchManager} from "../interfaces/ISearchManager"; import {ISearchManager} from "../interfaces/ISearchManager";
import {SearchResult} from "../../../common/entities/SearchResult"; import {SearchResultDTO} from "../../../common/entities/SearchResult";
export class SearchManager implements ISearchManager { export class SearchManager implements ISearchManager {
@ -9,11 +9,11 @@ export class SearchManager implements ISearchManager {
throw new Error("not implemented"); throw new Error("not implemented");
} }
search(text: string, searchType: SearchTypes, cb: (error: any, result: SearchResult) => void) { search(text: string, searchType: SearchTypes, cb: (error: any, result: SearchResultDTO) => void) {
throw new Error("not implemented"); throw new Error("not implemented");
} }
instantSearch(text: string, cb: (error: any, result: SearchResult) => void) { instantSearch(text: string, cb: (error: any, result: SearchResultDTO) => void) {
throw new Error("not implemented"); throw new Error("not implemented");
} }

View File

@ -0,0 +1,101 @@
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";
import {DirectoryEnitity} from "./enitites/DirectoryEntity";
import {PositionMetaData} from "../../../common/entities/PhotoDTO";
export class SearchManager implements ISearchManager {
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(DirectoryEnitity);
(await photoRepository
.createQueryBuilder('photo')
.select('DISTINCT(photo.metadataKeywords)')
.where('photo.metadata.keywords LIKE :text COLLATE utf8_general_ci', {text: "%" + text + "%"})
.setLimit(5)
.getRawMany<{metadataKeywords: string}>())
.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<{metadataPositionData: string}>())
.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<{name: string}>())
.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<{name: string}>())
.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) {
throw new Error("not implemented");
}
instantSearch(text: string, cb: (error: any, result: SearchResultDTO) => void) {
throw new Error("not implemented");
}
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);
}
}
return a;
}
}

View File

@ -42,9 +42,9 @@ export class ConfigClass {
public Client:ClientConfig = { public Client:ClientConfig = {
thumbnailSizes: [200, 400, 600], thumbnailSizes: [200, 400, 600],
Search: { Search: {
searchEnabled: false, searchEnabled: true,
instantSearchEnabled: false, instantSearchEnabled: true,
autocompleteEnabled: false autocompleteEnabled: true
}, },
concurrentThumbnailGenerations: 1, concurrentThumbnailGenerations: 1,
enableCache: false, enableCache: false,

View File

@ -1,11 +1,11 @@
import {DirectoryDTO} from "./DirectoryDTO"; import {DirectoryDTO} from "./DirectoryDTO";
import {SearchResult} from "./SearchResult"; import {SearchResultDTO} from "./SearchResult";
export class ContentWrapper { export class ContentWrapper {
public directory: DirectoryDTO; public directory: DirectoryDTO;
public searchResult:SearchResult; public searchResult: SearchResultDTO;
constructor(directory: DirectoryDTO = null, searchResult: SearchResult = null) { constructor(directory: DirectoryDTO = null, searchResult: SearchResultDTO = null) {
this.directory = directory; this.directory = directory;
this.searchResult = searchResult; this.searchResult = searchResult;
} }

View File

@ -1,9 +1,9 @@
import {DirectoryDTO} from "./DirectoryDTO"; import {DirectoryDTO} from "./DirectoryDTO";
import {PhotoDTO} from "./PhotoDTO"; import {PhotoDTO} from "./PhotoDTO";
import {SearchTypes} from "./AutoCompleteItem"; import {SearchTypes} from "./AutoCompleteItem";
export class SearchResult { export interface SearchResultDTO {
public searchText:string = ""; searchText: string;
public searchType:SearchTypes; searchType: SearchTypes;
public directories: Array<DirectoryDTO> = []; directories: Array<DirectoryDTO>;
public photos: Array<PhotoDTO> = []; photos: Array<PhotoDTO>;
} }

View File

@ -8,8 +8,8 @@
"license": "MIT", "license": "MIT",
"main": "./backend/server.js", "main": "./backend/server.js",
"scripts": { "scripts": {
"postinstall": "tsc -p frontend && tsc -p backend && tsc -p test/backend && tsc -p common", "postinstall": "tsc",
"pretest": "tsc -p frontend && tsc -p backend && tsc -p test/backend", "pretest": "tsc",
"test": "karma start karma.conf.js --single-run && mocha --recursive test/backend/unit", "test": "karma start karma.conf.js --single-run && mocha --recursive test/backend/unit",
"start": "node ./backend/server" "start": "node ./backend/server"
}, },
@ -53,7 +53,6 @@
"zone.js": "^0.7.4" "zone.js": "^0.7.4"
}, },
"devDependencies": { "devDependencies": {
"@types/core-js": "^0.9.35",
"@types/express": "^4.0.34", "@types/express": "^4.0.34",
"@types/express-session": "0.0.32", "@types/express-session": "0.0.32",
"@types/jasmine": "^2.5.38", "@types/jasmine": "^2.5.38",

View File

@ -1,13 +0,0 @@
{
"compilerOptions": {
"target": "es5",
"sourceMap": true,
"module": "commonjs",
"emitDecoratorMetadata": true,
"experimentalDecorators": true
},
"exclude": [
"node_modules",
"typings"
]
}

View File

@ -8,7 +8,8 @@
"experimentalDecorators": true, "experimentalDecorators": true,
"lib": [ "lib": [
"es2015", "es2015",
"dom" "dom",
"es2015.promise"
], ],
"suppressImplicitAnyIndexErrors": false, "suppressImplicitAnyIndexErrors": false,
"typeRoots": [ "typeRoots": [
@ -16,7 +17,6 @@
] ]
}, },
"exclude": [ "exclude": [
"node_modules", "node_modules"
"node_modules/@types"
] ]
} }