From c93c1aba2dafcdd8de8ba01c2724886dbca2caaf Mon Sep 17 00:00:00 2001 From: Braun Patrik Date: Mon, 9 May 2016 21:43:52 +0200 Subject: [PATCH] implementing search --- backend/middlewares/GalleryMWs.ts | 32 +++++++-- backend/model/DiskManger.ts | 2 +- backend/model/ISearchManager.ts | 3 + backend/model/memory/SearchManager.ts | 9 +++ backend/model/mongoose/MongoGalleryManager.ts | 33 +++++++++- backend/model/mongoose/MongoSearchManager.ts | 65 +++++++++++++++++++ backend/model/mongoose/entities/PhotoModel.ts | 9 ++- common/Utils.ts | 2 +- common/entities/Photo.ts | 10 +-- common/entities/SearchResult.ts | 5 +- frontend/app/gallery/gallery.component.html | 10 ++- frontend/app/gallery/gallery.service.ts | 32 ++++++++- .../gallery/grid/grid.gallery.component.html | 1 - .../gallery/grid/grid.gallery.component.ts | 9 ++- .../lightbox/lightbox.gallery.component.ts | 4 +- .../gallery/photo/photo.gallery.component.ts | 6 +- .../search/search.gallery.component.html | 10 +-- .../search/search.gallery.component.ts | 16 ++++- 18 files changed, 218 insertions(+), 40 deletions(-) diff --git a/backend/middlewares/GalleryMWs.ts b/backend/middlewares/GalleryMWs.ts index 03814897..0c4857cc 100644 --- a/backend/middlewares/GalleryMWs.ts +++ b/backend/middlewares/GalleryMWs.ts @@ -7,6 +7,8 @@ import {Config} from "../config/Config"; import {ObjectManagerRepository} from "../model/ObjectManagerRepository"; import {AutoCompleteItem} from "../../common/entities/AutoCompleteItem"; import {ContentWrapper} from "../../common/entities/ConentWrapper"; +import {SearchResult} from "../../common/entities/SearchResult"; +import {Photo} from "../../common/entities/Photo"; export class GalleryMWs { @@ -27,12 +29,19 @@ export class GalleryMWs { if (err || !directory) { return next(new Error(ErrorCodes.GENERAL_ERROR, err)); } + + //remove cyclic reference + directory.photos.forEach((photo:Photo) => { + photo.directory = null; + }); + req.resultPipe = new ContentWrapper(directory, null); return next(); }); } + public static loadImage(req:Request, res:Response, next:NextFunction) { if (!(req.params.imagePath)) { return next(); @@ -49,14 +58,29 @@ export class GalleryMWs { public static search(req:Request, res:Response, next:NextFunction) { - //TODO: implement - return next(new Error(ErrorCodes.GENERAL_ERROR)); + + ObjectManagerRepository.getInstance().getSearchManager().search(req.params.text, (err, result:SearchResult) => { + if (err || !result) { + return next(new Error(ErrorCodes.GENERAL_ERROR, err)); + } + req.resultPipe = new ContentWrapper(null, result); + return next(); + }); } public static instantSearch(req:Request, res:Response, next:NextFunction) { - //TODO: implement - return next(new Error(ErrorCodes.GENERAL_ERROR)); + if (!(req.params.text)) { + return next(); + } + + ObjectManagerRepository.getInstance().getSearchManager().instantSearch(req.params.text, (err, result:SearchResult) => { + if (err || !result) { + return next(new Error(ErrorCodes.GENERAL_ERROR, err)); + } + req.resultPipe = new ContentWrapper(null, result); + return next(); + }); } public static autocomplete(req:Request, res:Response, next:NextFunction) { diff --git a/backend/model/DiskManger.ts b/backend/model/DiskManger.ts index db9e35f3..534c5ef0 100644 --- a/backend/model/DiskManger.ts +++ b/backend/model/DiskManger.ts @@ -30,7 +30,7 @@ export class DiskManager { if (DiskManager.isImage(fullFilePath)) { let dimensions = sizeOf(fullFilePath); - directory.photos.push(new Photo(1, file, dimensions.width, dimensions.height)); + directory.photos.push(new Photo(1, file, directory, dimensions.width, dimensions.height)); } } diff --git a/backend/model/ISearchManager.ts b/backend/model/ISearchManager.ts index 981fdcf2..0ff7ab7b 100644 --- a/backend/model/ISearchManager.ts +++ b/backend/model/ISearchManager.ts @@ -1,4 +1,7 @@ import {AutoCompleteItem} from "../../common/entities/AutoCompleteItem"; +import {SearchResult} from "../../common/entities/SearchResult"; export interface ISearchManager { autocomplete(text, cb:(error:any, result:Array) => void); + search(text, cb:(error:any, result:SearchResult) => void); + instantSearch(text, cb:(error:any, result:SearchResult) => void); } \ No newline at end of file diff --git a/backend/model/memory/SearchManager.ts b/backend/model/memory/SearchManager.ts index db5026bd..792d66a6 100644 --- a/backend/model/memory/SearchManager.ts +++ b/backend/model/memory/SearchManager.ts @@ -1,5 +1,6 @@ import {AutoCompleteItem} from "../../../common/entities/AutoCompleteItem"; import {ISearchManager} from "../ISearchManager"; +import {SearchResult} from "../../../common/entities/SearchResult"; export class SearchManager implements ISearchManager { @@ -8,5 +9,13 @@ export class SearchManager implements ISearchManager { throw new Error("not implemented"); } + search(text, cb:(error:any, result:SearchResult) => void) { + throw new Error("not implemented"); + } + + instantSearch(text, cb:(error:any, result:SearchResult) => 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 d571b8a5..b8d6db06 100644 --- a/backend/model/mongoose/MongoGalleryManager.ts +++ b/backend/model/mongoose/MongoGalleryManager.ts @@ -5,6 +5,7 @@ import {DiskManager} from "../DiskManger"; import {Utils} from "../../../common/Utils"; import {DirectoryModel} from "./entities/DirectoryModel"; import {PhotoModel} from "./entities/PhotoModel"; +import {Photo} from "../../../common/entities/Photo"; export class MongoGalleryManager implements IGalleryManager { @@ -22,7 +23,7 @@ export class MongoGalleryManager implements IGalleryManager { if (err || !res) { return this.indexDirectory(relativeDirectoryName, cb); } - return cb(err, res); + return cb(err, this.modelToEntity(res)); }); } @@ -46,12 +47,38 @@ export class MongoGalleryManager implements IGalleryManager { }); scannedDirectory.photos = arr; - DirectoryModel.create(scannedDirectory, (err)=> { - return cb(err, scannedDirectory); + DirectoryModel.create(scannedDirectory, (err, savedDir)=> { + scannedDirectory.photos.forEach((value:any) => { + value['directory'] = savedDir; + value.save(); + }); + return cb(err, this.modelToEntity(scannedDirectory)); }); }); } + private modelToEntity(directroy:any):Directory { + console.log("modelToEntity"); + // console.log(directroy); + let directoryEntity = new Directory(directroy._id); + Utils.updateKeys(directoryEntity, directroy); + directroy.photos.forEach((photo) => { + let photoEntity = new Photo(null, null, null, null, null); + Utils.updateKeys(photoEntity, photo); + console.log(photoEntity); + directoryEntity.photos.push(photoEntity); + }); + directroy.directories.forEach((dir) => { + let dirEntity = new Directory(null, null, null, null, null, null); + Utils.updateKeys(dirEntity, dir); + console.log(dir); + console.log(dirEntity); + directoryEntity.directories.push(dirEntity); + }); + + return directoryEntity; + + } } \ No newline at end of file diff --git a/backend/model/mongoose/MongoSearchManager.ts b/backend/model/mongoose/MongoSearchManager.ts index 23b92782..92d38f2b 100644 --- a/backend/model/mongoose/MongoSearchManager.ts +++ b/backend/model/mongoose/MongoSearchManager.ts @@ -2,6 +2,7 @@ import {AutoCompleteItem, AutoCompeleteTypes} from "../../../common/entities/Aut import {ISearchManager} from "../ISearchManager"; import {DirectoryModel} from "./entities/DirectoryModel"; import {PhotoModel} from "./entities/PhotoModel"; +import {SearchResult} from "../../../common/entities/SearchResult"; export class MongoSearchManager implements ISearchManager { @@ -35,6 +36,70 @@ export class MongoSearchManager implements ISearchManager { }); } + search(text, cb:(error:any, result:SearchResult) => void) { + console.log("instantSearch: " + text); + let result:SearchResult = new SearchResult(); + result.searchText = text; + PhotoModel.find({ + name: { + $regex: text, + $options: "i" + } + }).populate('directory', 'name path').exec((err, res:Array) => { + if (err || !res) { + return cb(err, null); + } + result.photos = res; + + DirectoryModel.find({ + name: { + $regex: text, + $options: "i" + } + }).select('name').exec((err, res:Array) => { + if (err || !res) { + return cb(err, null); + } + result.directories = res; + return cb(null, result); + }); + + + }); + } + + instantSearch(text, cb:(error:any, result:SearchResult) => void) { + console.log("instantSearch: " + text); + let result:SearchResult = new SearchResult(); + result.searchText = text; + PhotoModel.find({ + name: { + $regex: text, + $options: "i" + } + }).limit(10).populate('directory', 'name path').exec((err, res:Array) => { + if (err || !res) { + return cb(err, null); + } + result.photos = res; + + DirectoryModel.find({ + name: { + $regex: text, + $options: "i" + } + }).limit(10).exec((err, res:Array) => { + if (err || !res) { + return cb(err, null); + } + result.directories = res; + return cb(null, result); + }); + + + }); + } + private encapsulateAutoComplete(values:Array, type:AutoCompeleteTypes) { let res = []; values.forEach((value)=> { diff --git a/backend/model/mongoose/entities/PhotoModel.ts b/backend/model/mongoose/entities/PhotoModel.ts index 3b5b153f..9b06abfe 100644 --- a/backend/model/mongoose/entities/PhotoModel.ts +++ b/backend/model/mongoose/entities/PhotoModel.ts @@ -1,8 +1,15 @@ import {DatabaseManager} from "../DatabaseManager"; +import {Schema} from "mongoose"; + export var PhotoModel = DatabaseManager.getInstance().getModel('photo',{ name:String, width:Number, - height:Number + height: Number, + directory: { + type: Schema.Types.ObjectId, + ref: 'directory' + }, }); + \ No newline at end of file diff --git a/common/Utils.ts b/common/Utils.ts index 0428b849..73800e92 100644 --- a/common/Utils.ts +++ b/common/Utils.ts @@ -20,7 +20,7 @@ export class Utils { } public static updateKeys(targetObject, sourceObject) { - Object.keys(sourceObject).forEach((key)=> { + Object.keys(sourceObject).forEach((key)=> { if (typeof targetObject[key] === "undefined") { return; } diff --git a/common/entities/Photo.ts b/common/entities/Photo.ts index 9c7e0e85..329c78f7 100644 --- a/common/entities/Photo.ts +++ b/common/entities/Photo.ts @@ -1,14 +1,14 @@ import {Utils} from "../Utils"; import {Directory} from "./Directory"; export class Photo { - constructor(public id:number, public name:string, public width:number, public height:number) { + constructor(public id?:number, public name?:string, public directory?:Directory, public width?:number, public height?:number) { } - public static getThumbnailPath(directory:Directory, photo:Photo) { - return Utils.concatUrls("/api/gallery/content/", directory.path, directory.name, photo.name, "thumbnail"); + public static getThumbnailPath(photo:Photo) { + return Utils.concatUrls("/api/gallery/content/", photo.directory.path, photo.directory.name, photo.name, "thumbnail"); } - public static getPhotoPath(directory:Directory, photo:Photo) { - return Utils.concatUrls("/api/gallery/content/", directory.path, directory.name, photo.name); + public static getPhotoPath(photo:Photo) { + return Utils.concatUrls("/api/gallery/content/", photo.directory.path, photo.directory.name, photo.name); } } \ No newline at end of file diff --git a/common/entities/SearchResult.ts b/common/entities/SearchResult.ts index 7f582cb1..1bdf4378 100644 --- a/common/entities/SearchResult.ts +++ b/common/entities/SearchResult.ts @@ -2,11 +2,8 @@ import {Directory} from "./Directory"; import {Photo} from "./Photo"; export class SearchResult { + public searchText:string; public directories:Array; public photos:Array; - constructor(directories:Array, photos:Array) { - this.directories = directories; - this.photos = photos; - } } \ No newline at end of file diff --git a/frontend/app/gallery/gallery.component.html b/frontend/app/gallery/gallery.component.html index a9b6fdb1..54f9ceb6 100644 --- a/frontend/app/gallery/gallery.component.html +++ b/frontend/app/gallery/gallery.component.html @@ -7,6 +7,14 @@
- + + + +
+
Searching for: {{_galleryService.content.searchResult.searchText}}
+
+ +
+
\ No newline at end of file diff --git a/frontend/app/gallery/gallery.service.ts b/frontend/app/gallery/gallery.service.ts index fa54886d..808d0b31 100644 --- a/frontend/app/gallery/gallery.service.ts +++ b/frontend/app/gallery/gallery.service.ts @@ -4,12 +4,16 @@ import {Injectable} from "@angular/core"; import {NetworkService} from "../model/network/network.service.ts"; import {Message} from "../../../common/entities/Message"; import {ContentWrapper} from "../../../common/entities/ConentWrapper"; +import {Photo} from "../../../common/entities/Photo"; +import {Directory} from "../../../common/entities/Directory"; @Injectable() export class GalleryService { public content:ContentWrapper; - + private lastDirectory:Directory; + private searchId:any; + constructor(private _networkService:NetworkService) { this.content = new ContentWrapper(); } @@ -18,13 +22,21 @@ export class GalleryService { return this._networkService.getJson("/gallery/content/" + directoryName).then( (message:Message) => { if (!message.error && message.result) { + message.result.directory.photos.forEach((photo:Photo) => { + photo.directory = message.result.directory; + }); + this.lastDirectory = message.result.directory; this.content = message.result; } return message; }); } + //TODO: cache public search(text:string):Promise> { + if (text === null || text === '') { + return Promise.resolve(new Message(null, null)); + } return this._networkService.getJson("/gallery/search/" + text).then( (message:Message) => { if (!message.error && message.result) { @@ -34,7 +46,24 @@ export class GalleryService { }); } + //TODO: cache (together with normal search) public instantSearch(text:string):Promise> { + if (text === null || text === '') { + this.content.directory = this.lastDirectory; + this.content.searchResult = null; + clearTimeout(this.searchId); + return Promise.resolve(new Message(null, null)); + } + + if (this.searchId != null) { + clearTimeout(this.searchId); + + } + this.searchId = setTimeout(() => { + this.search(text); + this.searchId = null; + }, 3000); //TODO: set timeout to config + return this._networkService.getJson("/gallery/instant-search/" + text).then( (message:Message) => { if (!message.error && message.result) { @@ -42,6 +71,7 @@ export class GalleryService { } return message; }); + } } diff --git a/frontend/app/gallery/grid/grid.gallery.component.html b/frontend/app/gallery/grid/grid.gallery.component.html index 7ccbb3f3..04998a8a 100644 --- a/frontend/app/gallery/grid/grid.gallery.component.html +++ b/frontend/app/gallery/grid/grid.gallery.component.html @@ -3,7 +3,6 @@ *ngFor="let gridPhoto of photosToRender" (click)="lightbox.show(gridPhoto.photo)" [photo]="gridPhoto.photo" - [directory]="directory" [style.width.px]="gridPhoto.renderWidth" [style.height.px]="gridPhoto.renderHeight" [style.marginLeft.px]="IMAGE_MARGIN" diff --git a/frontend/app/gallery/grid/grid.gallery.component.ts b/frontend/app/gallery/grid/grid.gallery.component.ts index 861c456d..92ba7dbd 100644 --- a/frontend/app/gallery/grid/grid.gallery.component.ts +++ b/frontend/app/gallery/grid/grid.gallery.component.ts @@ -10,7 +10,6 @@ import { QueryList, AfterViewInit } from "@angular/core"; -import {Directory} from "../../../../common/entities/Directory"; import {Photo} from "../../../../common/entities/Photo"; import {GalleryPhotoComponent} from "../photo/photo.gallery.component"; import {GridRowBuilder} from "./GridRowBuilder"; @@ -27,7 +26,7 @@ export class GalleryGridComponent implements OnChanges,AfterViewInit { @ViewChild('gridContainer') gridContainer:ElementRef; @ViewChildren(GalleryPhotoComponent) gridPhotoQL:QueryList; - @Input() directory:Directory; + @Input() photos:Array; @Input() lightbox:GalleryLightboxComponent; photosToRender:Array = []; @@ -41,7 +40,7 @@ export class GalleryGridComponent implements OnChanges,AfterViewInit { } ngOnChanges() { - // this.renderPhotos(); + this.renderPhotos(); } onResize() { @@ -92,9 +91,9 @@ export class GalleryGridComponent implements OnChanges,AfterViewInit { this.photosToRender = []; let i = 0; - while (i < this.directory.photos.length) { + while (i < this.photos.length) { - let photoRowBuilder = new GridRowBuilder(this.directory.photos, i, this.IMAGE_MARGIN, containerWidth); + let photoRowBuilder = new GridRowBuilder(this.photos, i, this.IMAGE_MARGIN, containerWidth); photoRowBuilder.addPhotos(this.TARGET_COL_COUNT); photoRowBuilder.adjustRowHeightBetween(minRowHeight, maxRowHeight); diff --git a/frontend/app/gallery/lightbox/lightbox.gallery.component.ts b/frontend/app/gallery/lightbox/lightbox.gallery.component.ts index 274614f4..3ac24fbe 100644 --- a/frontend/app/gallery/lightbox/lightbox.gallery.component.ts +++ b/frontend/app/gallery/lightbox/lightbox.gallery.component.ts @@ -150,14 +150,14 @@ export class GalleryLightboxComponent { if (!this.activePhoto) { return ""; } - return Photo.getPhotoPath(this.activePhoto.directory, this.activePhoto.photo); + return Photo.getPhotoPath(this.activePhoto.photo); } getThumbnailPath() { if (!this.activePhoto) { return ""; } - return Photo.getThumbnailPath(this.activePhoto.directory, this.activePhoto.photo); + return Photo.getThumbnailPath(this.activePhoto.photo); } private getBodyScrollTop() { diff --git a/frontend/app/gallery/photo/photo.gallery.component.ts b/frontend/app/gallery/photo/photo.gallery.component.ts index c493bf8b..699f6531 100644 --- a/frontend/app/gallery/photo/photo.gallery.component.ts +++ b/frontend/app/gallery/photo/photo.gallery.component.ts @@ -2,7 +2,6 @@ import {Component, Input, ElementRef, ViewChild} from "@angular/core"; import {Photo} from "../../../../common/entities/Photo"; -import {Directory} from "../../../../common/entities/Directory"; import {IRenderable, Dimension} from "../../model/IRenderable"; @Component({ @@ -11,15 +10,14 @@ import {IRenderable, Dimension} from "../../model/IRenderable"; styleUrls: ['app/gallery/photo/photo.gallery.component.css'], }) export class GalleryPhotoComponent implements IRenderable { - @Input() photo:Photo; - @Input() directory:Directory; + @Input() photo:Photo; @ViewChild("image") imageRef:ElementRef; constructor() { } getPhotoPath() { - return Photo.getThumbnailPath(this.directory, this.photo); + return Photo.getThumbnailPath(this.photo); } diff --git a/frontend/app/gallery/search/search.gallery.component.html b/frontend/app/gallery/search/search.gallery.component.html index ba05ab1e..d66d3c97 100644 --- a/frontend/app/gallery/search/search.gallery.component.html +++ b/frontend/app/gallery/search/search.gallery.component.html @@ -1,8 +1,9 @@
-