- regex search: show file hits as child nodes under the term matches

- further refactor Lucene query to reuse that query for regex detail view
This commit is contained in:
adam-m 2011-12-27 17:14:14 -05:00
parent 2557aed21c
commit 3ea53996be
3 changed files with 100 additions and 16 deletions

View File

@ -0,0 +1,35 @@
package org.sleuthkit.autopsy.keywordsearch;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.sleuthkit.datamodel.Directory;
import org.sleuthkit.datamodel.FsContent;
import org.sleuthkit.datamodel.TskException;
public class KeywordSearchUtil {
private static final Logger logger = Logger.getLogger(KeywordSearchUtil.class.getName());
public static String buildDirName(FsContent f) {
String dirName = null;
StringBuilder dirNameB = new StringBuilder();
try {
Directory pd = f.getParentDirectory();
while (pd != null && pd.isRoot() == false) {
dirNameB.insert(0, "/");
dirNameB.insert(0, pd.getName());
pd = pd.getParentDirectory();
}
dirNameB.insert(0, "/");
} catch (TskException ex) {
logger.log(Level.WARNING, "Error getting path for fscontent id: " + Long.toString(f.getId()), ex);
} finally {
dirName = dirNameB.toString();
}
return dirName;
}
}

View File

@ -22,6 +22,8 @@ import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.solr.client.solrj.SolrQuery; import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrServerException; import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.response.QueryResponse; import org.apache.solr.client.solrj.response.QueryResponse;
@ -31,19 +33,27 @@ import org.openide.nodes.Node;
import org.openide.windows.TopComponent; import org.openide.windows.TopComponent;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.corecomponents.DataResultTopComponent; import org.sleuthkit.autopsy.corecomponents.DataResultTopComponent;
import org.sleuthkit.autopsy.corecomponents.TableFilterNode;
import org.sleuthkit.datamodel.FsContent; import org.sleuthkit.datamodel.FsContent;
import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.SleuthkitCase;
public class LuceneQuery implements KeywordSearchQuery { public class LuceneQuery implements KeywordSearchQuery {
private static final Logger logger = Logger.getLogger(LuceneQuery.class.getName());
private String query; private String query;
public LuceneQuery(String query) { public LuceneQuery(String query) {
this.query = query; this.query = query;
} }
@Override /**
public void execute() { * Just perform the query and return result without updating the GUI
* This utility is used in this class, can be potentially reused by other classes
* @param query
* @return matches List
*/
public List<FsContent> doQuery() throws RuntimeException {
List<FsContent> matches = new ArrayList<FsContent>(); List<FsContent> matches = new ArrayList<FsContent>();
boolean allMatchesFetched = false; boolean allMatchesFetched = false;
@ -80,18 +90,26 @@ public class LuceneQuery implements KeywordSearchQuery {
} }
} catch (SolrServerException ex) { } catch (SolrServerException ex) {
logger.log(Level.WARNING, "Error executing Lucene Solr Query: " + query, ex);
throw new RuntimeException(ex); throw new RuntimeException(ex);
// TODO: handle bad query strings, among other issues // TODO: handle bad query strings, among other issues
} catch (SQLException ex) { } catch (SQLException ex) {
// TODO: handle error getting files from database logger.log(Level.WARNING, "Error interpreting results from Lucene Solr Query: " + query, ex);
} }
} }
return matches;
}
@Override
public void execute() {
List<FsContent> matches = doQuery();
String pathText = "Lucene query: " + query; String pathText = "Lucene query: " + query;
Node rootNode = new KeywordSearchNode(matches, query); Node rootNode = new KeywordSearchNode(matches, query);
Node filteredRootNode = new TableFilterNode(rootNode, true);
TopComponent searchResultWin = DataResultTopComponent.createInstance("Keyword search", pathText, rootNode, matches.size()); TopComponent searchResultWin = DataResultTopComponent.createInstance("Keyword search", pathText, filteredRootNode, matches.size());
searchResultWin.requestActive(); // make it the active top component searchResultWin.requestActive(); // make it the active top component
} }

View File

@ -20,10 +20,13 @@ package org.sleuthkit.autopsy.keywordsearch;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -44,12 +47,13 @@ import org.openide.windows.TopComponent;
import org.sleuthkit.autopsy.corecomponents.DataResultTopComponent; import org.sleuthkit.autopsy.corecomponents.DataResultTopComponent;
import org.sleuthkit.autopsy.datamodel.KeyValueNode; import org.sleuthkit.autopsy.datamodel.KeyValueNode;
import org.sleuthkit.autopsy.datamodel.KeyValueThing; import org.sleuthkit.autopsy.datamodel.KeyValueThing;
import org.sleuthkit.datamodel.FsContent;
public class RegexQuery implements KeywordSearchQuery { public class RegexQuery implements KeywordSearchQuery {
private static final int TERMS_UNLIMITED = -1; private static final int TERMS_UNLIMITED = -1;
//corresponds to field in Solr schema, analyzed with white-space tokenizer only //corresponds to field in Solr schema, analyzed with white-space tokenizer only
private static final String TERMS_SEARCH_FIELD = "text_ws"; private static final String TERMS_SEARCH_FIELD = "content_ws";
private static final String TERMS_HANDLER = "/terms"; private static final String TERMS_HANDLER = "/terms";
private static final int TERMS_TIMEOUT = 90 * 1000; //in ms private static final int TERMS_TIMEOUT = 90 * 1000; //in ms
private String query; private String query;
@ -148,22 +152,49 @@ public class RegexQuery implements KeywordSearchQuery {
return new KeyValueNode(thing, Children.create(new RegexResultDetailsChildFactory(thing), true)); return new KeyValueNode(thing, Children.create(new RegexResultDetailsChildFactory(thing), true));
} }
class RegexResultDetailsChildFactory extends ChildFactory<KeyValueThing> { class RegexResultDetailsChildFactory extends ChildFactory<KeyValueThing> {
private KeyValueThing thing; private KeyValueThing thing;
RegexResultDetailsChildFactory(KeyValueThing thing) { RegexResultDetailsChildFactory(KeyValueThing thing) {
this.thing = thing; this.thing = thing;
} }
@Override @Override
protected boolean createKeys(List<KeyValueThing> toPopulate) { protected boolean createKeys(List<KeyValueThing> toPopulate) {
//query //use Lucene query to get files with regular expression match result
Map<String,Object> map = new LinkedHashMap<String,Object>(); final String keywordQuery = thing.getName();
map.put("#hits", -1); LuceneQuery filesQuery = new LuceneQuery(keywordQuery);
KeyValueThing t = new KeyValueThing("TEST", map, 1); List<FsContent> matches = filesQuery.doQuery();
//return toPopulate.addAll(things);
toPopulate.add(t); //get unique match result files
Set<FsContent> uniqueMatches = new TreeSet<FsContent>(new Comparator<FsContent>() {
@Override
public int compare(FsContent fsc1, FsContent fsc2) {
return (int) (fsc1.getId() - fsc2.getId());
}
});
uniqueMatches.addAll(matches);
int resID = 0;
for (FsContent f : uniqueMatches) {
Map<String, Object> resMap = new LinkedHashMap<String, Object>();
//final String name = f.getName();
final long id = f.getId();
//build dir name
String dirName = KeywordSearchUtil.buildDirName(f);
resMap.put("dir", dirName);
resMap.put("id", Long.toString(id));
final String name = dirName + f.getName();
resMap.put("name", name);
toPopulate.add(new KeyValueThing(name, resMap, ++resID));
}
//TODO fix showing of child attributes in the GUI (DataResultViewerTable issue?)
return true; return true;
} }
@ -175,7 +206,7 @@ public class RegexQuery implements KeywordSearchQuery {
@Override @Override
protected Node[] createNodesForKey(KeyValueThing thing) { protected Node[] createNodesForKey(KeyValueThing thing) {
Node [] nodes = new Node[1]; Node[] nodes = new Node[1];
nodes[0] = new KeyValueNode(thing, Children.LEAF); nodes[0] = new KeyValueNode(thing, Children.LEAF);
return nodes; return nodes;