From 69489b8cb7111b584e3a69d87460bf959a798544 Mon Sep 17 00:00:00 2001 From: Braun Patrik Date: Wed, 4 May 2016 18:34:54 +0200 Subject: [PATCH] implementing autocomplete call --- backend/middlewares/GalleryMWs.ts | 14 +++++- backend/model/ISearchManager.ts | 4 ++ backend/model/ObjectManagerRepository.ts | 15 +++++++ backend/model/memory/SearchManager.ts | 13 ++++++ backend/model/mongoose/MongoGalleryManager.ts | 34 +++----------- backend/model/mongoose/MongoSearchManager.ts | 45 +++++++++++++++++++ .../model/mongoose/entities/DirectoryModel.ts | 16 +++++++ backend/model/mongoose/entities/PhotoModel.ts | 8 ++++ backend/routes/GalleryRouter.ts | 10 ++--- common/entities/AutoCompleteItem.ts | 2 +- common/entities/Photo.ts | 4 +- frontend/app/gallery/gallery.component.ts | 2 +- frontend/app/gallery/gallery.service.ts | 2 +- .../gallery/photo/photo.gallery.component.ts | 2 +- .../gallery/search/autocomplete.service.ts | 10 ++--- .../search/search.gallery.component.ts | 37 ++++++++------- .../model/network/authentication.service.ts | 3 +- 17 files changed, 160 insertions(+), 61 deletions(-) create mode 100644 backend/model/ISearchManager.ts create mode 100644 backend/model/memory/SearchManager.ts create mode 100644 backend/model/mongoose/MongoSearchManager.ts create mode 100644 backend/model/mongoose/entities/DirectoryModel.ts create mode 100644 backend/model/mongoose/entities/PhotoModel.ts diff --git a/backend/middlewares/GalleryMWs.ts b/backend/middlewares/GalleryMWs.ts index 6a802a0c..3e6fca45 100644 --- a/backend/middlewares/GalleryMWs.ts +++ b/backend/middlewares/GalleryMWs.ts @@ -7,6 +7,7 @@ import {GalleryManager} from "../model/memory/GalleryManager"; import {Directory} from "../../common/entities/Directory"; import {Config} from "../config/Config"; import {ObjectManagerRepository} from "../model/ObjectManagerRepository"; +import {AutoCompleteItem} from "../../common/entities/AutoCompleteItem"; export class GalleryMWs { @@ -56,8 +57,17 @@ export class GalleryMWs { } public static autocomplete(req:Request, res:Response, next:NextFunction){ - //TODO: implement - return next(new Error(ErrorCodes.GENERAL_ERROR)); + if(!(req.params.text)){ + return next(); + } + + ObjectManagerRepository.getInstance().getSearchManager().autocomplete(req.params.text,(err,items:Array) => { + if(err || !items){ + return next(new Error(ErrorCodes.GENERAL_ERROR,err)); + } + req.resultPipe = items; + return next(); + }); } diff --git a/backend/model/ISearchManager.ts b/backend/model/ISearchManager.ts new file mode 100644 index 00000000..132756a7 --- /dev/null +++ b/backend/model/ISearchManager.ts @@ -0,0 +1,4 @@ +import {AutoCompleteItem} from "../../common/entities/AutoCompleteItem"; +export interface ISearchManager { + autocomplete(text, cb:(error: any,result:Array) => void); +} \ No newline at end of file diff --git a/backend/model/ObjectManagerRepository.ts b/backend/model/ObjectManagerRepository.ts index 523e4887..4214b9bf 100644 --- a/backend/model/ObjectManagerRepository.ts +++ b/backend/model/ObjectManagerRepository.ts @@ -4,21 +4,27 @@ import {MongoGalleryManager} from "./mongoose/MongoGalleryManager"; import {MongoUserManager} from "./mongoose/MongoUserManager"; import {GalleryManager} from "./memory/GalleryManager"; import {UserManager} from "./memory/UserManager"; +import {ISearchManager} from "./ISearchManager"; +import {MongoSearchManager} from "./mongoose/MongoSearchManager"; +import {SearchManager} from "./memory/SearchManager"; export class ObjectManagerRepository{ private _galleryManager:IGalleryManager; private _userManager:IUserManager; + private _searchManager:ISearchManager; private static _instance:ObjectManagerRepository = null; public static InitMongoManagers(){ ObjectManagerRepository.getInstance().setGalleryManager(new MongoGalleryManager()); ObjectManagerRepository.getInstance().setUserManager(new MongoUserManager()); + ObjectManagerRepository.getInstance().setSearchManager(new MongoSearchManager()); } public static MemoryMongoManagers(){ ObjectManagerRepository.getInstance().setGalleryManager(new GalleryManager()); ObjectManagerRepository.getInstance().setUserManager(new UserManager()); + ObjectManagerRepository.getInstance().setSearchManager(new SearchManager()); } public static getInstance(){ @@ -44,4 +50,13 @@ export class ObjectManagerRepository{ setUserManager(value:IUserManager) { this._userManager = value; } + + getSearchManager():ISearchManager { + return this._searchManager; + } + + setSearchManager(value:ISearchManager) { + this._searchManager = value; + } + } \ No newline at end of file diff --git a/backend/model/memory/SearchManager.ts b/backend/model/memory/SearchManager.ts new file mode 100644 index 00000000..5da42d9c --- /dev/null +++ b/backend/model/memory/SearchManager.ts @@ -0,0 +1,13 @@ +import {AutoCompleteItem} from "../../../common/entities/AutoCompleteItem"; +import {ISearchManager} from "../ISearchManager"; + +export class SearchManager implements ISearchManager{ + + + autocomplete(text, cb:(error: any,result:Array) => void){ + throw new Error("not implemented"); + } + + + +} \ No newline at end of file diff --git a/backend/model/mongoose/MongoGalleryManager.ts b/backend/model/mongoose/MongoGalleryManager.ts index 17e0dad1..ae3c3357 100644 --- a/backend/model/mongoose/MongoGalleryManager.ts +++ b/backend/model/mongoose/MongoGalleryManager.ts @@ -1,46 +1,26 @@ import * as path from 'path'; -import {Schema} from "mongoose"; import {Directory} from "../../../common/entities/Directory"; import {IGalleryManager} from "../IGalleryManager"; -import {DatabaseManager} from "./DatabaseManager"; import {DiskManager} from "../DiskManger"; import {Utils} from "../../../common/Utils"; +import {DirectoryModel} from "./entities/DirectoryModel"; +import {PhotoModel} from "./entities/PhotoModel"; export class MongoGalleryManager implements IGalleryManager{ - private DirectoryModel; - private PhotoModel; constructor(){ - this.DirectoryModel = DatabaseManager.getInstance().getModel('directory',{ - name:String, - path:String, - lastUpdate:Date, - directories: [{ - type: Schema.Types.ObjectId, - ref: 'directory' - }], - photos: [{ - type: Schema.Types.ObjectId, - ref: 'photo' - }] - }); - this.PhotoModel = DatabaseManager.getInstance().getModel('photo',{ - name:String, - width:Number, - height:Number - }); } public listDirectory(relativeDirectoryName, cb:(error: any,result:Directory) => void){ let directoryName = path.basename(relativeDirectoryName); let directoryParent = path.join( path.dirname(relativeDirectoryName),"/"); - this.DirectoryModel.findOne({name:directoryName, path: directoryParent}).populate('photos').populate('directories').exec( (err,res) =>{ + DirectoryModel.findOne({name:directoryName, path: directoryParent}).populate('photos').populate('directories').exec( (err,res) =>{ if(err || !res){ return this.indexDirectory(relativeDirectoryName,cb); } - return cb(err,res); + return cb(err, res); }); } @@ -49,7 +29,7 @@ export class MongoGalleryManager implements IGalleryManager{ DiskManager.scanDirectory(relativeDirectoryName,(err,scannedDirectory)=>{ let arr = []; scannedDirectory.directories.forEach((value) => { - let dir = new this.DirectoryModel(value); + let dir = new DirectoryModel(value); Utils.setKeys(dir,value); dir.save(); arr.push(dir); @@ -57,14 +37,14 @@ export class MongoGalleryManager implements IGalleryManager{ scannedDirectory.directories = arr; arr = []; scannedDirectory.photos.forEach((value) => { - let p = new this.PhotoModel(value); + let p = new PhotoModel(value); Utils.setKeys(p,value); p.save(); arr.push(p); }); scannedDirectory.photos = arr; - this.DirectoryModel.create(scannedDirectory,(err)=>{ + DirectoryModel.create(scannedDirectory,(err)=>{ return cb(err,scannedDirectory); }); diff --git a/backend/model/mongoose/MongoSearchManager.ts b/backend/model/mongoose/MongoSearchManager.ts new file mode 100644 index 00000000..c9f7f59f --- /dev/null +++ b/backend/model/mongoose/MongoSearchManager.ts @@ -0,0 +1,45 @@ +import {AutoCompleteItem, AutoCompeleteTypes} from "../../../common/entities/AutoCompleteItem"; +import {ISearchManager} from "../ISearchManager"; +import {DirectoryModel} from "./entities/DirectoryModel"; +import {PhotoModel} from "./entities/PhotoModel"; + +export class MongoSearchManager implements ISearchManager{ + + + constructor(){ + } + + + autocomplete(text, cb:(error: any,result:Array) => void){ + + console.log("autocomplete: " + text); + let items:Array = []; + PhotoModel.find({name: { $regex: text, $options: "i" } }).limit(10).select('name').exec( (err,res) =>{ + if(err || !res){ + return cb(err,null); + } + items = items.concat(this.encapsulateAutoComplete(res.map(r => r.name),AutoCompeleteTypes.image)); + + DirectoryModel.find({name: { $regex: text, $options: "i" } }).limit(10).select('name').exec( (err,res) =>{ + if(err || !res){ + return cb(err,null); + } + items = items.concat(this.encapsulateAutoComplete(res.map(r => r.name),AutoCompeleteTypes.directory)); + return cb(null,items); + }); + + + }); + } + + private encapsulateAutoComplete(values:Array,type: AutoCompeleteTypes){ + let res = []; + values.forEach((value)=>{ + res.push(new AutoCompleteItem(value,type)); + }); + return res; + } + + + +} \ No newline at end of file diff --git a/backend/model/mongoose/entities/DirectoryModel.ts b/backend/model/mongoose/entities/DirectoryModel.ts new file mode 100644 index 00000000..add51bc9 --- /dev/null +++ b/backend/model/mongoose/entities/DirectoryModel.ts @@ -0,0 +1,16 @@ +import {DatabaseManager} from "../DatabaseManager"; +import {Schema} from "mongoose"; + +export var DirectoryModel = DatabaseManager.getInstance().getModel('directory',{ + name:String, + path:String, + lastUpdate:Date, + directories: [{ + type: Schema.Types.ObjectId, + ref: 'directory' + }], + photos: [{ + type: Schema.Types.ObjectId, + ref: 'photo' + }] +}); \ No newline at end of file diff --git a/backend/model/mongoose/entities/PhotoModel.ts b/backend/model/mongoose/entities/PhotoModel.ts new file mode 100644 index 00000000..3b5b153f --- /dev/null +++ b/backend/model/mongoose/entities/PhotoModel.ts @@ -0,0 +1,8 @@ +import {DatabaseManager} from "../DatabaseManager"; + +export var PhotoModel = DatabaseManager.getInstance().getModel('photo',{ + name:String, + width:Number, + height:Number +}); + diff --git a/backend/routes/GalleryRouter.ts b/backend/routes/GalleryRouter.ts index 5c02c61b..18ec667c 100644 --- a/backend/routes/GalleryRouter.ts +++ b/backend/routes/GalleryRouter.ts @@ -17,7 +17,7 @@ export class GalleryRouter{ } private addDirectoryList() { - this.app.get(["/api/gallery/:directory(*)","/api/gallery/","/api/gallery//"], + this.app.get(["/api/gallery/content/:directory(*)","/api/gallery/","/api/gallery//"], AuthenticationMWs.authenticate, GalleryMWs.listDirectory, RenderingMWs.renderResult @@ -26,7 +26,7 @@ export class GalleryRouter{ private addGetImage() { - this.app.get(["/api/gallery/:imagePath(*\.(jpg|bmp|png|gif|jpeg))"], + this.app.get(["/api/gallery/content/:imagePath(*\.(jpg|bmp|png|gif|jpeg))"], AuthenticationMWs.authenticate, GalleryMWs.loadImage, RenderingMWs.renderFile @@ -34,7 +34,7 @@ export class GalleryRouter{ }; private addGetImageThumbnail() { - this.app.get("/api/gallery/:imagePath(*\.(jpg|bmp|png|gif|jpeg))/thumbnail/:size?", + this.app.get("/api/gallery/content/:imagePath(*\.(jpg|bmp|png|gif|jpeg))/thumbnail/:size?", AuthenticationMWs.authenticate, GalleryMWs.loadImage, ThumbnailGeneratorMWs.generateThumbnail, @@ -51,8 +51,8 @@ export class GalleryRouter{ }; private addAutoComplete() { - this.app.get("/api/gallery/autocomplete", - AuthenticationMWs.authenticate, + this.app.get("/api/gallery/autocomplete/:text", + // AuthenticationMWs.authenticate, GalleryMWs.autocomplete, RenderingMWs.renderResult ); diff --git a/common/entities/AutoCompleteItem.ts b/common/entities/AutoCompleteItem.ts index e03907da..30b17fd7 100644 --- a/common/entities/AutoCompleteItem.ts +++ b/common/entities/AutoCompleteItem.ts @@ -5,6 +5,6 @@ export enum AutoCompeleteTypes { } export class AutoCompleteItem{ - constructor(text:string, type:AutoCompeleteTypes){} + constructor(public text:string,public type:AutoCompeleteTypes){} } diff --git a/common/entities/Photo.ts b/common/entities/Photo.ts index 4d61ab6a..0074d003 100644 --- a/common/entities/Photo.ts +++ b/common/entities/Photo.ts @@ -5,9 +5,9 @@ export class Photo { } public static getThumbnailPath(directory:Directory,photo:Photo){ - return Utils.concatUrls("/api/gallery",directory.path,directory.name,photo.name,"thumbnail"); + return Utils.concatUrls("/api/gallery/content/",directory.path,directory.name,photo.name,"thumbnail"); } public static getPhotoPath(directory:Directory,photo:Photo){ - return Utils.concatUrls("/api/gallery",directory.path,directory.name,photo.name); + return Utils.concatUrls("/api/gallery/content/",directory.path,directory.name,photo.name); } } \ No newline at end of file diff --git a/frontend/app/gallery/gallery.component.ts b/frontend/app/gallery/gallery.component.ts index f80a1a82..047bf799 100644 --- a/frontend/app/gallery/gallery.component.ts +++ b/frontend/app/gallery/gallery.component.ts @@ -1,6 +1,6 @@ /// -import {Component, OnInit, QueryList, ViewChild, AfterViewInit} from '@angular/core'; +import {Component, OnInit} from '@angular/core'; import {AuthenticationService} from "../model/network/authentication.service.ts"; import {Router, RouteParams} from "@angular/router-deprecated"; import {GalleryService} from "./gallery.service"; diff --git a/frontend/app/gallery/gallery.service.ts b/frontend/app/gallery/gallery.service.ts index 31649397..652a9da7 100644 --- a/frontend/app/gallery/gallery.service.ts +++ b/frontend/app/gallery/gallery.service.ts @@ -15,7 +15,7 @@ export class GalleryService extends NetworkService{ } public getDirectory(directoryName:string): Promise>{ - return this.getJson("/gallery/"+directoryName); + return this.getJson("/gallery/content/"+directoryName); } diff --git a/frontend/app/gallery/photo/photo.gallery.component.ts b/frontend/app/gallery/photo/photo.gallery.component.ts index ce8e4726..8be7d38b 100644 --- a/frontend/app/gallery/photo/photo.gallery.component.ts +++ b/frontend/app/gallery/photo/photo.gallery.component.ts @@ -20,7 +20,7 @@ export class GalleryPhotoComponent implements IRenderable{ } getPhotoPath(){ - return Utils.concatUrls("/api/gallery",this.directory.path,this.directory.name,this.photo.name,"thumbnail"); + return Photo.getThumbnailPath(this.directory,this.photo); } diff --git a/frontend/app/gallery/search/autocomplete.service.ts b/frontend/app/gallery/search/autocomplete.service.ts index 5c3d34cb..52fc3ef6 100644 --- a/frontend/app/gallery/search/autocomplete.service.ts +++ b/frontend/app/gallery/search/autocomplete.service.ts @@ -3,6 +3,8 @@ import {Injectable} from "@angular/core"; import {Http} from "@angular/http"; import {NetworkService} from "../../model/network/network.service"; +import {AutoCompleteItem} from "../../../../common/entities/AutoCompleteItem"; +import {Message} from "../../../../common/entities/Message"; @Injectable() export class AutoCompleteService extends NetworkService { @@ -11,11 +13,9 @@ export class AutoCompleteService extends NetworkService { constructor(_http:Http) { super(_http); } - - arraytmp:Array = ["Afghanistan", "Albania", "Algeria", "Andorra", "Angola", "Anguilla", "Antigua & Barbuda", "Argentina", "Armenia", "Aruba", "Australia", "Austria", "Azerbaijan", "Bahamas", "Bahrain", "Bangladesh", "Barbados", "Belarus", "Belgium", "Belize", "Benin", "Bermuda", "Bhutan", "Bolivia", "Bosnia & Herzegovina", "Botswana", "Brazil", "British Virgin Islands", "Brunei", "Bulgaria", "Burkina Faso", "Burundi", "Cambodia", "Cameroon", "Cape Verde", "Cayman Islands", "Chad", "Chile", "China", "Colombia", "Congo", "Cook Islands", "Costa Rica", "Cote D Ivoire", "Croatia", "Cruise Ship", "Cuba", "Cyprus", "Czech Republic", "Denmark", "Djibouti", "Dominica", "Dominican Republic", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Estonia", "Ethiopia", "Falkland Islands", "Faroe Islands", "Fiji", "Finland", "France", "French Polynesia", "French West Indies", "Gabon", "Gambia", "Georgia", "Germany", "Ghana", "Gibraltar", "Greece", "Greenland", "Grenada", "Guam", "Guatemala", "Guernsey", "Guinea", "Guinea Bissau", "Guyana", "Haiti", "Honduras", "Hong Kong", "Hungary", "Iceland", "India", "Indonesia", "Iran", "Iraq", "Ireland", "Isle of Man", "Israel", "Italy", "Jamaica", "Japan", "Jersey", "Jordan", "Kazakhstan", "Kenya", "Kuwait", "Kyrgyz Republic", "Laos", "Latvia", "Lebanon", "Lesotho", "Liberia", "Libya", "Liechtenstein", "Lithuania", "Luxembourg", "Macau", "Macedonia", "Madagascar", "Malawi", "Malaysia", "Maldives", "Mali", "Malta", "Mauritania", "Mauritius", "Mexico", "Moldova", "Monaco", "Mongolia", "Montenegro", "Montserrat", "Morocco", "Mozambique", "Namibia", "Nepal", "Netherlands", "Netherlands Antilles", "New Caledonia", "New Zealand", "Nicaragua", "Niger", "Nigeria", "Norway", "Oman", "Pakistan", "Palestine", "Panama", "Papua New Guinea", "Paraguay", "Peru", "Philippines", "Poland", "Portugal", "Puerto Rico", "Qatar", "Reunion", "Romania", "Russia", "Rwanda", "Saint Pierre & Miquelon", "Samoa", "San Marino", "Satellite", "Saudi Arabia", "Senegal", "Serbia", "Seychelles", "Sierra Leone", "Singapore", "Slovakia", "Slovenia", "South Africa", "South Korea", "Spain", "Sri Lanka", "St Kitts & Nevis", "St Lucia", "St Vincent", "St. Lucia", "Sudan", "Suriname", "Swaziland", "Sweden", "Switzerland", "Syria", "Taiwan", "Tajikistan", "Tanzania", "Thailand", "Timor L'Este", "Togo", "Tonga", "Trinidad & Tobago", "Tunisia", "Turkey", "Turkmenistan", "Turks & Caicos", "Uganda", "Ukraine", "United Arab Emirates", "United Kingdom", "Uruguay", "Uzbekistan", "Venezuela", "Vietnam", "Virgin Islands (US)", "Yemen", "Zambia", "Zimbabwe"]; - - public autoComplete(text:string) { - return this.arraytmp.filter(t => t.toLowerCase().indexOf(text.toLowerCase()) > -1).slice(0,10); + + public autoComplete(text:string): Promise >>{ + return this.getJson("/gallery/autocomplete/"+text); } diff --git a/frontend/app/gallery/search/search.gallery.component.ts b/frontend/app/gallery/search/search.gallery.component.ts index 9c2b489e..19125cb9 100644 --- a/frontend/app/gallery/search/search.gallery.component.ts +++ b/frontend/app/gallery/search/search.gallery.component.ts @@ -2,6 +2,8 @@ import {Component} from "@angular/core"; import {AutoCompleteService} from "./autocomplete.service"; +import {AutoCompleteItem} from "../../../../common/entities/AutoCompleteItem"; +import {Message} from "../../../../common/entities/Message"; @Component({ selector: 'gallery-search', @@ -11,32 +13,37 @@ import {AutoCompleteService} from "./autocomplete.service"; }) export class GallerySearchComponent { - autoCompleteItems:Array = []; + autoCompleteItems:Array = []; constructor(private _autoCompleteService:AutoCompleteService) { } getSuggestions(event:KeyboardEvent){ - let searchText = (event.target).value; - let result = []; + let searchText = (event.target).value; if(searchText.length > 0) { - result = this._autoCompleteService.autoComplete(searchText); + this._autoCompleteService.autoComplete(searchText).then((message:Message>) =>{ + if(message.error){ + //TODO: implement + console.error(message.error); + return; + } + this.showSuggestions(message.result,searchText); + }); } - this.showSuggestions(result,searchText); } - private showSuggestions(suggestions:Array,searchText:string){ + private showSuggestions(suggestions:Array,searchText:string){ this.autoCompleteItems = []; - suggestions.forEach((value)=>{ - let preIndex = value.toLowerCase().indexOf(searchText.toLowerCase()); - let item = new AutoCompleteItem(); + suggestions.forEach((item)=>{ + let preIndex = item.text.toLowerCase().indexOf(searchText.toLowerCase()); + let renderItem = new AutoCompleteRenderItem(); if(preIndex > -1){ - item.preText = value.substring(0,preIndex); - item.highLightText = value.substring(preIndex, preIndex + searchText.length); - item.postText = value.substring(preIndex + searchText.length); + renderItem.preText = item.text.substring(0,preIndex); + renderItem.highLightText = item.text.substring(preIndex, preIndex + searchText.length); + renderItem.postText = item.text.substring(preIndex + searchText.length); }else{ - item.postText = value; + renderItem.postText = item.text; } - this.autoCompleteItems.push(item); + this.autoCompleteItems.push(renderItem); }); } @@ -45,7 +52,7 @@ export class GallerySearchComponent { } -class AutoCompleteItem{ +class AutoCompleteRenderItem{ constructor(public preText:string = "",public highLightText:string = "", public postText:string = ""){ } diff --git a/frontend/app/model/network/authentication.service.ts b/frontend/app/model/network/authentication.service.ts index e69557cf..3f713bf6 100644 --- a/frontend/app/model/network/authentication.service.ts +++ b/frontend/app/model/network/authentication.service.ts @@ -7,6 +7,7 @@ import {UserService} from "./user.service.ts"; import {LoginCredential} from "../../../../common/entities/LoginCredential"; import {Message} from "../../../../common/entities/Message"; import { Cookie } from 'ng2-cookies/ng2-cookies'; +import {ErrorCodes} from "../../../../common/entities/Error"; declare module ServerInject{ export var user; @@ -46,7 +47,7 @@ export class AuthenticationService{ public login(credential:LoginCredential){ this._userService.login(credential).then( (message:Message) =>{ if(message.error){ - console.log(message.error); + console.log(ErrorCodes[message.error.code] + "message: "+ message.error.message); }else{ this.setUser(message.result); }