Initial work on keyword search

This commit is contained in:
Knut Forkalsrud 2010-10-23 14:01:21 -07:00
parent 5149259970
commit 7eeb7d9bae
7 changed files with 234 additions and 32 deletions

View file

@ -8,7 +8,6 @@ import java.io.FileInputStream;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
@ -22,14 +21,13 @@ import org.forkalsrud.album.db.DirectoryProps;
/**
* @author knut
*/
public class DirectoryEntry extends Entry {
public class DirectoryEntry extends EntryWithChildren {
final static String CACHE_FILE = "cache.properties";
final static String OVERRIDE_FILE = "album.properties";
DirectoryDatabase db;
DirectoryProps cache;
List<Entry> children = new ArrayList<Entry>();
boolean childrenLoaded = false;
Comparator<Entry> sort = null;
Date earliest = null;
@ -53,14 +51,10 @@ public class DirectoryEntry extends Entry {
@Override
public boolean isFile() {
return false;
}
public List<Entry> getContents() {
loadChildren();
return children;
return super.getContents();
}
@Override
@ -75,18 +69,10 @@ public class DirectoryEntry extends Entry {
return super.getCaption();
}
public void addContents(Entry entry) {
children.add(entry);
}
@Override
public Entry get(File f) {
loadChildren();
for (Entry e : children) {
if (f.equals(e.file)) {
return e;
}
}
return null;
return super.get(f);
}
protected synchronized void loadChildren() {
@ -139,20 +125,6 @@ public class DirectoryEntry extends Entry {
Collections.sort(children, sort);
}
void fillLinkedList() {
Entry prev = null;
for (Entry e : children) {
e.prev = prev;
if (prev != null) {
prev.next = e;
}
prev = e;
}
if (prev != null) {
prev.next = null;
}
}
private void populate(DirectoryProps props) throws ParseException {

View file

@ -31,6 +31,18 @@ public abstract class Entry {
this.file = file;
}
protected Entry(Entry other) {
this(other.file);
this.thumbnail = other.thumbnail;
this.caption = other.caption;
this.date = other.date;
/*
this.next = other.next;
this.prev = other.prev;
this.parent = other.parent;
*/
}
public abstract boolean isFile();

View file

@ -0,0 +1,66 @@
/**
*
*/
package org.forkalsrud.album.exif;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
/**
* @author knut
*
*/
public class EntryWithChildren extends Entry {
protected List<Entry> children = new ArrayList<Entry>();
protected EntryWithChildren(Entry root) {
super(root);
}
protected EntryWithChildren(File path) {
super(path);
}
/* (non-Javadoc)
* @see org.forkalsrud.album.exif.Entry#isFile()
*/
@Override
public boolean isFile() {
return false;
}
void fillLinkedList() {
Entry prev = null;
for (Entry e : children) {
e.prev = prev;
if (prev != null) {
prev.next = e;
}
prev = e;
}
if (prev != null) {
prev.next = null;
}
}
public List<Entry> getContents() {
return children;
}
public void addContents(Entry entry) {
children.add(entry);
}
public Entry get(File f) {
for (Entry e : children) {
if (f.equals(e.file)) {
return e;
}
}
return null;
}
}

View file

@ -0,0 +1,58 @@
package org.forkalsrud.album.exif;
import java.util.LinkedList;
public class SearchEngine {
DirectoryEntry root;
SearchResults results;
LinkedList<DirectoryEntry> queue;
public SearchEngine(DirectoryEntry root) {
this.root = root;
this.results = new SearchResults(root);
this.queue = new LinkedList<DirectoryEntry>();
this.queue.add(root);
}
public SearchResults search(String query) {
while (!queue.isEmpty()) {
DirectoryEntry next = queue.removeFirst();
processDirectory(next, query);
}
results.fillLinkedList();
return results;
}
void processDirectory(DirectoryEntry dir, String query) {
tryAttribute(dir, dir.getName(), query);
tryAttribute(dir, dir.getCaption(), query);
tryAttribute(dir, dir.getPath().getAbsolutePath(), query);
for (Entry e : dir.getContents()) {
if (!e.isFile()) {
queue.add((DirectoryEntry)e);
continue;
}
FileEntry file = (FileEntry)e;
tryAttribute(file, file.getName(), query);
tryAttribute(file, file.getCaption(), query);
tryAttribute(file, file.getPath().getAbsolutePath(), query);
}
}
void tryAttribute(Entry entry, String attr, String query) {
if (isMatch(attr, query)) {
results.addMatch(entry);
}
}
boolean isMatch(String candidate, String query) {
return candidate != null && candidate.indexOf(query) >= 0;
}
}

View file

@ -0,0 +1,23 @@
package org.forkalsrud.album.exif;
public class SearchEntry extends Entry {
protected int score;
protected SearchEntry(SearchResults parent, Entry entry) {
super(entry);
this.parent = parent;
this.score = 1;
}
@Override
public boolean isFile() {
return true;
}
public void addScore() {
this.score += 1;
}
}

View file

@ -0,0 +1,39 @@
package org.forkalsrud.album.exif;
import java.io.File;
import java.util.Comparator;
import java.util.HashMap;
public class SearchResults extends EntryWithChildren {
protected HashMap<File, SearchEntry> dupes = new HashMap<File, SearchEntry>();
Comparator<SearchEntry> sort = new Comparator<SearchEntry>() {
public int compare(SearchEntry e1, SearchEntry e2) {
return Integer.valueOf(e2.score).compareTo(e1.score);
}
};
protected SearchResults(DirectoryEntry root) {
super(root);
}
public void addMatch(Entry entry) {
File path = entry.getPath();
SearchEntry existing = dupes.get(path);
if (existing == null) {
existing = new SearchEntry(this, entry);
dupes.put(path, existing);
children.add(existing);
} else {
existing.addScore();
}
}
}

View file

@ -27,6 +27,8 @@ import org.forkalsrud.album.db.ThumbnailDatabase;
import org.forkalsrud.album.exif.DirectoryEntry;
import org.forkalsrud.album.exif.Entry;
import org.forkalsrud.album.exif.FileEntry;
import org.forkalsrud.album.exif.SearchEngine;
import org.forkalsrud.album.exif.SearchResults;
import org.forkalsrud.album.exif.Thumbnail;
import org.springframework.web.util.HtmlUtils;
@ -207,6 +209,12 @@ public class AlbumServlet
handleEdit(req, res, (FileEntry)resolveEntry(pathInfo));
return;
}
if (pathInfo.endsWith(".search")) {
pathInfo = pathInfo.substring(0, pathInfo.length() - ".search".length());
handleSearch(req, res, (DirectoryEntry)resolveEntry(pathInfo));
return;
}
File file = new File(base, pathInfo);
if (!file.canRead()) {
@ -283,6 +291,27 @@ public class AlbumServlet
}
}
void handleSearch(HttpServletRequest req, HttpServletResponse res, DirectoryEntry entry) {
try {
String query = req.getParameter("q");
SearchEngine search = new SearchEngine(entry);
SearchResults results = search.search(query);
res.setContentType("text/html");
req.setAttribute("entry", results);
req.setAttribute("thmb", new Integer(250));
req.setAttribute("full", new Integer(800));
RequestDispatcher rd = req.getRequestDispatcher("/WEB-INF/velocity/photo.vm");
rd.forward(req, res);
} catch (Exception ex) {
throw new RuntimeException("sadness", ex);
}
}
boolean etagMatches(HttpServletRequest req, String fileEtag) {
String cacheControl = req.getHeader("Cache-Control");
@ -401,6 +430,9 @@ public class AlbumServlet
Calendar cal = Calendar.getInstance();
public String year(Date d) {
if (d == null) {
return "";
}
cal.setTime(d);
return String.valueOf(cal.get(Calendar.YEAR));
}