sortedPagesWithHits = new TreeSet<>();
- //extract pages of interest, sorted
-
SolrQuery q = new SolrQuery();
q.setShowDebugInfo(DEBUG); //debug
- String query = keywords.stream().map(keyword -> "/.*" + KeywordSearchUtil.escapeLuceneQuery(keyword) + ".*/").collect(Collectors.joining(" "));
- q.setQuery(LuceneQuery.HIGHLIGHT_FIELD_REGEX + ":" + query);
- q.setFields("id");
- if (chunkId == null) {
- q.addFilterQuery(Server.Schema.ID.toString() + ":" + this.solrObjectId + "_*");
- } else {
- q.addFilterQuery(Server.Schema.ID.toString() + ":" + this.solrDocumentId);
- }
+ q.setQuery(queryString);
+ q.setFields(Server.Schema.ID.toString()); //for this case we only need the document ids
+ q.addFilterQuery(Server.Schema.ID.toString() + ":" + this.solrObjectId + Server.ID_CHUNK_SEP + "*");
+
try {
QueryResponse response = solrServer.query(q, METHOD.POST);
for (SolrDocument resultDoc : response.getResults()) {
final String resultDocumentId = resultDoc.getFieldValue(Server.Schema.ID.toString()).toString();
// Put the solr chunk id in the map
- final int separatorIndex = resultDocumentId.indexOf(Server.ID_CHUNK_SEP);
- if (-1 != separatorIndex) {
- sortedPagesWithHits.add(Integer.parseInt(resultDocumentId.substring(separatorIndex + 1)));
+ String resultChunkID = StringUtils.substringAfter(resultDocumentId, Server.ID_CHUNK_SEP);
+ if (StringUtils.isNotBlank(resultChunkID)) {
+ sortedPagesWithHits.add(Integer.parseInt(resultChunkID));
} else {
sortedPagesWithHits.add(0);
}
@@ -268,63 +277,79 @@ class AccountsText implements IndexedText, TextMarkupLookup {
}
@Override
+ @NbBundle.Messages({"AccountsText.getMarkup.noMatchMsg="
+ + "There were no keyword hits on this page.
"
+ + "The keyword could have been in the file name."
+ + "
Advance to another page if present, or to view the original text, choose File Text"
+ + "
in the drop down menu to the right...
",
+ "AccountsText.getMarkup.queryFailedMsg="
+ + "Failed to retrieve keyword hit results."
+ + "
Confirm that Autopsy can connect to the Solr server. "
+ + "
"})
public String getText() {
loadPageInfo(); //inits once
- String highLightField = LuceneQuery.HIGHLIGHT_FIELD_REGEX;
-
SolrQuery q = new SolrQuery();
q.setShowDebugInfo(DEBUG); //debug
- String query = keywords.stream().map(keyword -> "/.*" + KeywordSearchUtil.escapeLuceneQuery(keyword) + ".*/").collect(Collectors.joining(" "));
- q.setQuery(LuceneQuery.HIGHLIGHT_FIELD_REGEX + ":" + query);
+ q.addHighlightField(HIGHLIGHT_FIELD);
+ q.setQuery(queryString);
- String contentIdStr;
- if (hasChunks) {
- contentIdStr = solrObjectId + "_" + Integer.toString(this.currentPage);
- } else {
- contentIdStr = this.solrDocumentId;
- }
+ //set the documentID filter
+ String queryDocumentID = this.solrObjectId + Server.ID_CHUNK_SEP + this.currentPage;
+ q.addFilterQuery(Server.Schema.ID.toString() + ":" + queryDocumentID);
- final String filterQuery = Server.Schema.ID.toString() + ":" + KeywordSearchUtil.escapeLuceneQuery(contentIdStr);
- q.addFilterQuery(filterQuery);
- q.addHighlightField(highLightField); //for exact highlighting, try content_ws field (with stored="true" in Solr schema)
-
- //tune the highlighter
+ //configure the highlighter
q.setParam("hl.useFastVectorHighlighter", "true"); //fast highlighter scales better than standard one NON-NLS
q.setParam("hl.tag.pre", HIGHLIGHT_PRE); //makes sense for FastVectorHighlighter only NON-NLS
q.setParam("hl.tag.post", HIGHLIGHT_POST); //makes sense for FastVectorHighlighter only NON-NLS
q.setParam("hl.fragListBuilder", "single"); //makes sense for FastVectorHighlighter only NON-NLS
-
- //docs says makes sense for the original Highlighter only, but not really
- q.setParam("hl.maxAnalyzedChars", Server.HL_ANALYZE_CHARS_UNLIMITED); //NON-NLS
+ q.setParam("hl.maxAnalyzedChars", Server.HL_ANALYZE_CHARS_UNLIMITED); //docs says makes sense for the original Highlighter only, but not really //NON-NLS
try {
- QueryResponse response = solrServer.query(q, METHOD.POST);
- Map>> responseHighlight = response.getHighlighting();
-
- Map> responseHighlightID = responseHighlight.get(contentIdStr);
- if (responseHighlightID == null) {
- return NbBundle.getMessage(this.getClass(), "HighlightedMatchesSource.getMarkup.noMatchMsg");
+ //extract highlighting and bail early on null responses
+ Map>> highlightingPerDocument = solrServer.query(q, METHOD.POST).getHighlighting();
+ Map> highlightingPerField = highlightingPerDocument.get(queryDocumentID);
+ if (highlightingPerField == null) {
+ return Bundle.AccountsText_getMarkup_noMatchMsg();
}
- List contentHighlights = responseHighlightID.get(highLightField);
- if (contentHighlights == null) {
- return NbBundle.getMessage(this.getClass(), "HighlightedMatchesSource.getMarkup.noMatchMsg");
- } else {
- // extracted content (minus highlight tags) is HTML-escaped
- String highlightedContent = contentHighlights.get(0).trim();
- highlightedContent = insertAnchors(highlightedContent);
-
- return "" + highlightedContent + "
"; //NON-NLS
+ List highlights = highlightingPerField.get(HIGHLIGHT_FIELD);
+ if (highlights == null) {
+ return Bundle.AccountsText_getMarkup_noMatchMsg();
}
+
+ //There should only be one item
+ String highlighting = highlights.get(0).trim();
+
+ /*
+ * use regex matcher to iterate over occurences of HIGHLIGHT_PRE,
+ * and prepend them with an anchor tag.
+ */
+ Matcher m = ANCHOR_DETECTION_PATTERN.matcher(highlighting);
+ StringBuffer sb = new StringBuffer(highlighting.length());
+ int count = 0;
+ while (m.find()) {
+ count++;
+ m.appendReplacement(sb, INSERT_PREFIX + count + INSERT_POSTFIX);
+ }
+ m.appendTail(sb);
+
+ //store total hits for this page, now that we know it
+ this.numberOfHitsPerPage.put(this.currentPage, count);
+ if (this.currentItem() == 0 && this.hasNextItem()) {
+ this.nextItem();
+ }
+
+ // extracted content (minus highlight tags) is HTML-escaped
+ return "" + sb.toString() + "
"; //NON-NLS
} catch (Exception ex) {
LOGGER.log(Level.WARNING, "Error executing Solr highlighting query: " + keywords, ex); //NON-NLS
- return NbBundle.getMessage(this.getClass(), "HighlightedMatchesSource.getMarkup.queryFailedMsg");
+ return Bundle.AccountsText_getMarkup_queryFailedMsg();
}
}
@Override
public String toString() {
- return getDisplayName();
+ return displayName;
}
@Override
@@ -334,7 +359,7 @@ class AccountsText implements IndexedText, TextMarkupLookup {
@Override
public String getAnchorPrefix() {
- return ANCHOR_PREFIX;
+ return ANCHOR_NAME_PREFIX;
}
@Override
@@ -344,33 +369,4 @@ class AccountsText implements IndexedText, TextMarkupLookup {
}
return this.numberOfHitsPerPage.get(this.currentPage);
}
-
- private String insertAnchors(String searchableContent) {
-
- final String insertPre = "$0"; //$0 will insert current regex match //NON-NLS
-
- Matcher m = Pattern.compile(HIGHLIGHT_PRE).matcher(searchableContent);
- StringBuffer sb = new StringBuffer(searchableContent.length());
- int count;
- for (count = 0; m.find(); count++) {
- m.appendReplacement(sb, insertPre + count + insertPost);
- }
- m.appendTail(sb);
-
- //store total hits for this page, now that we know it
- this.numberOfHitsPerPage.put(this.currentPage, count);
- if (this.currentItem() == 0 && this.hasNextItem()) {
- this.nextItem();
- }
-
- return sb.toString();
- }
-
- @Override
- @Deprecated
- // factory method to create an instance of this object
- public AccountsText createInstance(long objectId, String keywordHitQuery, boolean isRegex, String originalQuery) {
- return new AccountsText(String.valueOf(objectId), Collections.emptySet());
- }
}
diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/ExtractedContentViewer.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/ExtractedContentViewer.java
index 2ffccf5ce3..2dfaa0b4ec 100644
--- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/ExtractedContentViewer.java
+++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/ExtractedContentViewer.java
@@ -184,7 +184,7 @@ public class ExtractedContentViewer implements DataContentViewer {
*/
BlackboardArtifact artifact = nodeLookup.lookup(BlackboardArtifact.class);
if (null != artifact) {
- /**
+ /*
* For keyword hit artifacts, add the text of the artifact that hit,
* not the hit artifact; otherwise add the text for the artifact.
*/
diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java
index cd9d1572c9..33a5a1d443 100644
--- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java
+++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java
@@ -40,32 +40,32 @@ import java.util.Collection;
import java.util.List;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.logging.Level;
-import org.openide.util.NbBundle;
-import org.sleuthkit.autopsy.coreutils.Logger;
import javax.swing.AbstractAction;
import org.apache.solr.client.solrj.SolrQuery;
+import org.apache.solr.client.solrj.SolrRequest;
import org.apache.solr.client.solrj.SolrServerException;
+import org.apache.solr.client.solrj.impl.HttpSolrServer;
+import org.apache.solr.client.solrj.impl.XMLResponseParser;
import org.apache.solr.client.solrj.request.CoreAdminRequest;
+import org.apache.solr.client.solrj.response.CoreAdminResponse;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.client.solrj.response.TermsResponse;
-import org.apache.solr.client.solrj.SolrRequest;
-import org.apache.solr.client.solrj.impl.HttpSolrServer;
-import org.apache.solr.common.util.NamedList;
-import org.openide.modules.InstalledFileLocator;
-import org.openide.modules.Places;
-import org.sleuthkit.autopsy.casemodule.Case;
-import org.sleuthkit.autopsy.coreutils.ModuleSettings;
-import org.sleuthkit.autopsy.coreutils.PlatformUtil;
-import org.sleuthkit.datamodel.Content;
-import org.apache.solr.common.SolrInputDocument;
-import org.apache.solr.client.solrj.impl.XMLResponseParser;
-import org.apache.solr.client.solrj.response.CoreAdminResponse;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;
import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrInputDocument;
+import org.apache.solr.common.util.NamedList;
+import org.openide.modules.InstalledFileLocator;
+import org.openide.modules.Places;
+import org.openide.util.NbBundle;
+import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.Case.CaseType;
-import org.sleuthkit.autopsy.coreutils.UNCPathUtilities;
import org.sleuthkit.autopsy.core.UserPreferences;
+import org.sleuthkit.autopsy.coreutils.Logger;
+import org.sleuthkit.autopsy.coreutils.ModuleSettings;
+import org.sleuthkit.autopsy.coreutils.PlatformUtil;
+import org.sleuthkit.autopsy.coreutils.UNCPathUtilities;
+import org.sleuthkit.datamodel.Content;
/**
* Handles management of a either a local or centralized Solr server and its
@@ -157,7 +157,7 @@ public class Server {
private static final Logger logger = Logger.getLogger(Server.class.getName());
private static final String DEFAULT_CORE_NAME = "coreCase"; //NON-NLS
public static final String CORE_EVT = "CORE_EVT"; //NON-NLS
- public static final char ID_CHUNK_SEP = '_';
+ public static final String ID_CHUNK_SEP = "_";
private String javaPath = "java"; //NON-NLS
public static final Charset DEFAULT_INDEXED_TEXT_CHARSET = Charset.forName("UTF-8"); ///< default Charset to index text as
private static final int MAX_SOLR_MEM_MB = 512; //TODO set dynamically based on avail. system resources