Add SearchEngine logging to the Developer Panel

This commit is contained in:
Jim Blake 2014-05-05 16:53:36 -04:00
parent 06a3acaaa0
commit 6178aa0347
14 changed files with 774 additions and 193 deletions

View file

@ -4,9 +4,7 @@ package edu.cornell.mannlib.vitro.webapp.rdfservice.impl.logging;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.ListIterator;
import java.util.regex.Pattern;
import org.apache.commons.lang.StringUtils;
@ -15,6 +13,7 @@ import org.apache.commons.logging.LogFactory;
import edu.cornell.mannlib.vitro.webapp.utils.developer.DeveloperSettings;
import edu.cornell.mannlib.vitro.webapp.utils.developer.Key;
import edu.cornell.mannlib.vitro.webapp.utils.developer.loggers.StackTraceUtility;
/**
* Writes the log message for the LoggingRDFService.
@ -43,26 +42,32 @@ import edu.cornell.mannlib.vitro.webapp.utils.developer.Key;
public class RDFServiceLogger implements AutoCloseable {
private static final Log log = LogFactory.getLog(RDFServiceLogger.class);
private final Object[] args;
private static boolean isEnabled() {
return log.isInfoEnabled()
&& DeveloperSettings.getInstance().getBoolean(
Key.LOGGING_RDF_ENABLE);
}
private final Object[] args;
private final StackTraceUtility stackTrace;
private boolean isEnabled;
private boolean traceRequested;
private Pattern queryStringRestriction;
private Pattern callStackRestriction;
private String methodName;
private List<StackTraceElement> trace = Collections.emptyList();
private long startTime;
public RDFServiceLogger(Object... args) {
this.args = args;
this.stackTrace = new StackTraceUtility(LoggingRDFService.class,
isEnabled());
try {
getProperties();
if (isEnabled && log.isInfoEnabled()) {
loadStackTrace();
if (passesQueryRestriction() && passesStackRestriction()) {
if (isEnabled()) {
if (passesQueryRestriction()
&& stackTrace
.passesStackRestriction(callStackRestriction)) {
this.startTime = System.currentTimeMillis();
}
}
@ -73,7 +78,6 @@ public class RDFServiceLogger implements AutoCloseable {
private void getProperties() {
DeveloperSettings settings = DeveloperSettings.getInstance();
isEnabled = settings.getBoolean(Key.LOGGING_RDF_ENABLE);
traceRequested = settings.getBoolean(Key.LOGGING_RDF_STACK_TRACE);
queryStringRestriction = patternFromSettings(settings,
Key.LOGGING_RDF_QUERY_RESTRICTION);
@ -95,61 +99,6 @@ public class RDFServiceLogger implements AutoCloseable {
}
}
private void loadStackTrace() {
StackTraceElement[] stack = Thread.currentThread().getStackTrace();
List<StackTraceElement> list = new ArrayList<StackTraceElement>(
Arrays.asList(stack));
trimStackTraceAtBeginning(list);
trimStackTraceAtEnd(list);
removeJenaClassesFromStackTrace(list);
if (list.isEmpty()) {
this.methodName = "UNKNOWN";
} else {
this.methodName = list.get(0).getMethodName();
}
this.trace = list;
log.debug("Stack array: " + Arrays.toString(stack));
log.debug("Stack trace: " + this.trace);
}
private void trimStackTraceAtBeginning(List<StackTraceElement> list) {
ListIterator<StackTraceElement> iter = list.listIterator();
while (iter.hasNext()) {
StackTraceElement ste = iter.next();
if (ste.getClassName().equals(LoggingRDFService.class.getName())) {
break;
} else {
iter.remove();
}
}
}
private void trimStackTraceAtEnd(List<StackTraceElement> list) {
ListIterator<StackTraceElement> iter = list.listIterator();
boolean trimming = false;
while (iter.hasNext()) {
StackTraceElement ste = iter.next();
if (trimming) {
iter.remove();
} else if (ste.getClassName().contains("ApplicationFilterChain")) {
trimming = true;
}
}
}
private void removeJenaClassesFromStackTrace(List<StackTraceElement> list) {
ListIterator<StackTraceElement> iter = list.listIterator();
while (iter.hasNext()) {
StackTraceElement ste = iter.next();
if (ste.getClassName().startsWith("com.hp.hpl.jena.")) {
iter.remove();
}
}
}
private boolean passesQueryRestriction() {
if (queryStringRestriction == null) {
return true;
@ -168,23 +117,6 @@ public class RDFServiceLogger implements AutoCloseable {
return StringUtils.join(stringArgs, " ");
}
private boolean passesStackRestriction() {
if (callStackRestriction == null) {
return true;
}
String q = assembleCallStackString();
return callStackRestriction.matcher(q).find();
}
private String assembleCallStackString() {
StringBuilder stack = new StringBuilder();
for (StackTraceElement ste : trace) {
stack.append(ste.getClassName()).append(" ")
.append(ste.getMethodName()).append(" ");
}
return stack.deleteCharAt(stack.length() - 1).toString();
}
@Override
public void close() {
try {
@ -194,28 +126,14 @@ public class RDFServiceLogger implements AutoCloseable {
float elapsedSeconds = (endTime - startTime) / 1000.0F;
String cleanArgs = Arrays.deepToString(args).replaceAll(
"[\\n\\r\\t]+", " ");
String formattedTrace = formatStackTrace();
String formattedTrace = stackTrace.format(traceRequested);
log.info(String.format("%8.3f %s %s %s", elapsedSeconds,
methodName, cleanArgs, formattedTrace));
stackTrace.getMethodName(), cleanArgs, formattedTrace));
}
} catch (Exception e) {
log.error("Failed to write log record", e);
}
}
private String formatStackTrace() {
StringBuilder sb = new StringBuilder();
if (traceRequested) {
for (StackTraceElement ste : trace) {
sb.append(String.format("\n line %4d, %s",
ste.getLineNumber(), ste.getClassName()));
}
sb.append("\n ...");
}
return sb.toString();
}
}

View file

@ -0,0 +1,297 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.searchengine;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.regex.Pattern;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchInputDocument;
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchQuery;
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchResponse;
import edu.cornell.mannlib.vitro.webapp.utils.developer.DeveloperSettings;
import edu.cornell.mannlib.vitro.webapp.utils.developer.Key;
import edu.cornell.mannlib.vitro.webapp.utils.developer.loggers.StackTraceUtility;
import edu.cornell.mannlib.vitro.webapp.utils.searchengine.Formatter;
/**
* Logging the SearchEngine, for the Developer's panel.
*/
public abstract class SearchEngineLogger implements AutoCloseable {
private static final Log log = LogFactory.getLog(SearchEngineLogger.class);
// ----------------------------------------------------------------------
// Factory
// ----------------------------------------------------------------------
public static SearchEngineLogger doAdd(SearchInputDocument[] docs) {
return new AddLogger(Arrays.asList(docs));
}
public static SearchEngineLogger doAdd(Collection<SearchInputDocument> docs) {
return new AddLogger(docs);
}
public static SearchEngineLogger doDeleteById(String[] ids) {
return new DeleteIdsLogger(Arrays.asList(ids));
}
public static SearchEngineLogger doDeleteById(Collection<String> ids) {
return new DeleteIdsLogger(ids);
}
public static SearchEngineLogger doDeleteByQuery(String query) {
return new DeleteQueryLogger(query);
}
public static SearchEngineLogger doQuery(SearchQuery query) {
return new QueryLogger(query);
}
// ----------------------------------------------------------------------
// Abstract instance
// ----------------------------------------------------------------------
private final long startTime;
protected final boolean enabled;
public SearchEngineLogger(Key enableKey) {
this.startTime = System.currentTimeMillis();
this.enabled = log.isInfoEnabled()
&& DeveloperSettings.getInstance().getBoolean(enableKey);
}
protected float elapsedSeconds() {
long endTime = System.currentTimeMillis();
return (endTime - startTime) / 1000.0F;
}
@Override
public void close() {
if (enabled) {
try {
writeToLog();
} catch (Exception e) {
log.error("Failed to write log record", e);
}
}
}
public void setSearchResponse(SearchResponse response) {
throw new UnsupportedOperationException(this.getClass().getSimpleName()
+ " does not support setSearchResponse()");
}
protected abstract void writeToLog();
// ----------------------------------------------------------------------
// Concrete sub-classes
// ----------------------------------------------------------------------
private static class AddLogger extends SearchEngineLogger {
private final List<SearchInputDocument> docs;
private final boolean passesRestrictions;
AddLogger(Collection<SearchInputDocument> docs) {
super(Key.SEARCH_INDEX_ENABLE);
this.docs = restrictDocsByUriOrName(docs);
this.passesRestrictions = enabled && passesDocumentRestriction()
&& this.docs.size() > 0;
}
private List<SearchInputDocument> restrictDocsByUriOrName(
Collection<SearchInputDocument> rawDocs) {
if (!enabled) {
return Collections.emptyList();
}
String restriction = DeveloperSettings.getInstance().getString(
Key.SEARCH_INDEX_URI_OR_NAME_RESTRICTION);
if (restriction.isEmpty()) {
return new ArrayList<>(rawDocs);
}
List<SearchInputDocument> list = new ArrayList<>();
for (SearchInputDocument doc : rawDocs) {
if (passesUriOrNameRestriction(doc, restriction)) {
list.add(doc);
}
}
return list;
}
private boolean passesUriOrNameRestriction(SearchInputDocument doc,
String restriction) {
try {
return Pattern.matches(restriction, Formatter.format(doc));
} catch (Exception e) {
log.warn("Failed to test URI or Name restriction: '"
+ restriction + "'", e);
return true;
}
}
private boolean passesDocumentRestriction() {
String restriction = DeveloperSettings.getInstance().getString(
Key.SEARCH_INDEX_DOCUMENT_RESTRICTION);
if (!restriction.isEmpty()) {
try {
return Pattern.matches(restriction, docContents());
} catch (Exception e) {
log.warn("Failed to test document restriction: '"
+ restriction + "'", e);
}
}
return true;
}
@Override
public void writeToLog() {
if (!passesRestrictions) {
return;
}
if (showDocumentContents()) {
log.info(String.format("%8.3f added %d documents: \n%s",
elapsedSeconds(), docs.size(), docContents()));
} else {
log.info(String.format("%8.3f added %d documents: \n%s",
elapsedSeconds(), docs.size(), docUris()));
}
}
private boolean showDocumentContents() {
return DeveloperSettings.getInstance().getBoolean(
Key.SEARCH_INDEX_SHOW_DOCUMENTS);
}
private String docUris() {
StringBuilder sb = new StringBuilder();
for (SearchInputDocument doc : docs) {
sb.append(Formatter.getValueFromField(doc, "URI"))
.append(" - ")
.append(Formatter.getValueFromField(doc, "nameRaw"))
.append("\n");
}
return sb.toString();
}
private String docContents() {
StringBuilder sb = new StringBuilder();
for (SearchInputDocument doc : docs) {
sb.append(Formatter.format(doc));
}
return sb.toString();
}
}
private static class DeleteIdsLogger extends SearchEngineLogger {
private final List<String> ids;
DeleteIdsLogger(Collection<String> ids) {
super(Key.SEARCH_DELETIONS_ENABLE);
this.ids = new ArrayList<>(ids);
}
@Override
public void writeToLog() {
log.info(String.format(
"%8.3f deleted these %d search documents: %s\n",
elapsedSeconds(), ids.size(), StringUtils.join(ids, ", ")));
}
}
private static class DeleteQueryLogger extends SearchEngineLogger {
private final String query;
DeleteQueryLogger(String query) {
super(Key.SEARCH_DELETIONS_ENABLE);
this.query = query;
}
@Override
public void writeToLog() {
log.info(String.format(
"%8.3f delete documents as found by this query: %s\n",
elapsedSeconds(), query));
}
}
public static class QueryLogger extends SearchEngineLogger {
private final SearchQuery query;
private final StackTraceUtility stackTrace;
private final boolean passesRestrictions;
private SearchResponse response;
QueryLogger(SearchQuery query) {
super(Key.SEARCH_ENGINE_ENABLE);
this.query = query;
this.stackTrace = new StackTraceUtility(SearchEngineWrapper.class,
enabled);
this.passesRestrictions = enabled && passesQueryRestriction()
&& passesStackRestriction();
log.debug("QueryLogger: enabled=" + enabled + ", query=" + query
+ ", passes=" + passesRestrictions);
}
private boolean passesStackRestriction() {
return stackTrace.passesStackRestriction(DeveloperSettings
.getInstance().getString(
Key.SEARCH_ENGINE_STACK_RESTRICTION));
}
private boolean passesQueryRestriction() {
String restriction = DeveloperSettings.getInstance().getString(
Key.SEARCH_ENGINE_QUERY_RESTRICTION);
if (StringUtils.isEmpty(restriction)) {
return true;
}
try {
return Pattern.matches(restriction, Formatter.format(query));
} catch (Exception e) {
log.warn("Failed to test query restriction: '" + restriction
+ "'", e);
return true;
}
}
@Override
public void setSearchResponse(SearchResponse response) {
this.response = response;
}
@Override
public void writeToLog() {
if (!passesRestrictions) {
return;
}
String results = (showSearchResults()) ? Formatter.format(response)
: " returned " + response.getResults().size()
+ " results.\n";
String trace = stackTrace.format(showStackTrace());
log.info(String.format("%8.3f %s%s%s", elapsedSeconds(),
Formatter.format(query), results, trace));
}
private boolean showSearchResults() {
return DeveloperSettings.getInstance().getBoolean(
Key.SEARCH_ENGINE_ADD_RESULTS);
}
private boolean showStackTrace() {
return DeveloperSettings.getInstance().getBoolean(
Key.SEARCH_ENGINE_ADD_STACK_TRACE);
}
}
}

View file

@ -116,15 +116,19 @@ public class SearchEngineWrapper implements SearchEngine {
@Override
public void add(SearchInputDocument... docs) throws SearchEngineException {
confirmActive();
innerEngine.add(docs);
try (SearchEngineLogger l = SearchEngineLogger.doAdd(docs)) {
confirmActive();
innerEngine.add(docs);
}
}
@Override
public void add(Collection<SearchInputDocument> docs)
throws SearchEngineException {
confirmActive();
innerEngine.add(docs);
try (SearchEngineLogger l = SearchEngineLogger.doAdd(docs)) {
confirmActive();
innerEngine.add(docs);
}
}
@Override
@ -141,20 +145,26 @@ public class SearchEngineWrapper implements SearchEngine {
@Override
public void deleteById(String... ids) throws SearchEngineException {
confirmActive();
innerEngine.deleteById(ids);
try (SearchEngineLogger l = SearchEngineLogger.doDeleteById(ids)) {
confirmActive();
innerEngine.deleteById(ids);
}
}
@Override
public void deleteById(Collection<String> ids) throws SearchEngineException {
confirmActive();
innerEngine.deleteById(ids);
try (SearchEngineLogger l = SearchEngineLogger.doDeleteById(ids)) {
confirmActive();
innerEngine.deleteById(ids);
}
}
@Override
public void deleteByQuery(String query) throws SearchEngineException {
confirmActive();
innerEngine.deleteByQuery(query);
try (SearchEngineLogger l = SearchEngineLogger.doDeleteByQuery(query)) {
confirmActive();
innerEngine.deleteByQuery(query);
}
}
@Override
@ -171,8 +181,12 @@ public class SearchEngineWrapper implements SearchEngine {
@Override
public SearchResponse query(SearchQuery query) throws SearchEngineException {
confirmActive();
return innerEngine.query(query);
try (SearchEngineLogger l = SearchEngineLogger.doQuery(query)) {
confirmActive();
SearchResponse response = innerEngine.query(query);
l.setSearchResponse(response);
return response;
}
}
}

View file

@ -29,6 +29,11 @@ public class BaseSearchFacetField implements SearchFacetField {
return values;
}
@Override
public String toString() {
return "BaseSearchFacetField[name=" + name + ", values=" + values + "]";
}
/**
* A foundation class for implementing SearchFacetField.Count.
*/
@ -51,5 +56,10 @@ public class BaseSearchFacetField implements SearchFacetField {
return count;
}
@Override
public String toString() {
return "BaseCount[name=" + name + ", count=" + count + "]";
}
}
}

View file

@ -77,4 +77,10 @@ public class BaseSearchInputDocument implements SearchInputDocument {
return new BaseSearchInputField(name);
}
@Override
public String toString() {
return "BaseSearchInputDocument[fieldMap=" + fieldMap
+ ", documentBoost=" + documentBoost + "]";
}
}

View file

@ -61,4 +61,10 @@ public class BaseSearchInputField implements SearchInputField {
}
}
@Override
public String toString() {
return "BaseSearchInputField[name=" + name + ", valueList="
+ valueList + ", boost=" + boost + "]";
}
}

View file

@ -48,4 +48,10 @@ public class BaseSearchResponse implements SearchResponse {
return new ArrayList<>(facetFields.values());
}
@Override
public String toString() {
return "BaseSearchResponse[highlighting=" + highlighting
+ ", facetFields=" + facetFields + ", results=" + results + "]";
}
}

View file

@ -72,4 +72,10 @@ public class BaseSearchResultDocument implements SearchResultDocument {
return fieldValuesMap;
}
@Override
public String toString() {
return "BaseSearchResultDocument [uniqueId=" + uniqueId
+ ", fieldValuesMap=" + fieldValuesMap + "]";
}
}

View file

@ -86,6 +86,63 @@ public enum Key {
PAGE_CONTENTS_LOG_CUSTOM_SHORT_VIEW(
"developer.pageContents.logCustomShortView", true),
/**
* Tell the SearchEngineLogger to log all indexing operations.
*/
SEARCH_INDEX_ENABLE("developer.searchIndex.enable", true),
/**
* Add the document contents to the log of indexing operations.
*/
SEARCH_INDEX_SHOW_DOCUMENTS("developer.searchIndex.showDocuments", true),
/**
* Log indexing operations only if the document contents match this regular
* expression.
*/
SEARCH_INDEX_URI_OR_NAME_RESTRICTION(
"developer.searchIndex.uriOrNameRestriction", false),
/**
* Log indexing operations only if the document contents match this regular
* expression.
*/
SEARCH_INDEX_DOCUMENT_RESTRICTION(
"developer.searchIndex.documentRestriction", false),
/**
* Tell the SearchEngineLogger to log all index deletions.
*/
SEARCH_DELETIONS_ENABLE("developer.searchDeletions.enable", true),
/**
* Tell the SearchEngineLogger to log all search operations.
*/
SEARCH_ENGINE_ENABLE("developer.searchEngine.enable", true),
/**
* Add the stack trace to the log of search operations.
*/
SEARCH_ENGINE_ADD_STACK_TRACE("developer.searchEngine.addStackTrace", true),
/**
* Add the search results to the log of search operations.
*/
SEARCH_ENGINE_ADD_RESULTS("developer.searchEngine.addResults", true),
/**
* Log search operations only if the query matches this regular expression.
*/
SEARCH_ENGINE_QUERY_RESTRICTION("developer.searchEngine.queryRestriction",
false),
/**
* Log search operations only if the stack trace matches this regular
* expression.
*/
SEARCH_ENGINE_STACK_RESTRICTION("developer.searchEngine.stackRestriction",
false),
/**
* Enable the PolicyDecisionLogger.
*/

View file

@ -0,0 +1,139 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.utils.developer.loggers;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.ListIterator;
import java.util.regex.Pattern;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Records and process the stack trace so a developer's logger can (1) test
* against it, and (2) display it.
*
* If the "enabled" flag is not set, no processing is done, and the stack trace
* is empty. That way, there is essentially no overhead in creating a disabled
* instance.
*/
public class StackTraceUtility {
private static final Log log = LogFactory.getLog(StackTraceUtility.class);
private final Class<?> lowestClassInStackTrace;
private final List<StackTraceElement> stackTrace;
private final String methodName;
public StackTraceUtility(Class<?> lowestClassInStackTrace, boolean enabled) {
this.lowestClassInStackTrace = lowestClassInStackTrace;
this.stackTrace = enabled ? loadStackTrace() : Collections
.<StackTraceElement> emptyList();
this.methodName = (this.stackTrace.isEmpty()) ? "UNKNOWN"
: this.stackTrace.get(0).getMethodName();
}
private List<StackTraceElement> loadStackTrace() {
StackTraceElement[] stack = Thread.currentThread().getStackTrace();
List<StackTraceElement> list = new ArrayList<StackTraceElement>(
Arrays.asList(stack));
trimStackTraceAtBeginning(list);
trimStackTraceAtEnd(list);
removeJenaClassesFromStackTrace(list);
log.debug("Stack array: " + Arrays.toString(stack));
log.debug("Stack trace: " + list);
return Collections.unmodifiableList(list);
}
private void trimStackTraceAtBeginning(List<StackTraceElement> list) {
ListIterator<StackTraceElement> iter = list.listIterator();
while (iter.hasNext()) {
StackTraceElement ste = iter.next();
if (ste.getClassName().equals(lowestClassInStackTrace.getName())) {
break;
} else {
iter.remove();
}
}
}
private void trimStackTraceAtEnd(List<StackTraceElement> list) {
ListIterator<StackTraceElement> iter = list.listIterator();
boolean trimming = false;
while (iter.hasNext()) {
StackTraceElement ste = iter.next();
if (trimming) {
iter.remove();
} else if (ste.getClassName().contains("ApplicationFilterChain")) {
trimming = true;
}
}
}
private void removeJenaClassesFromStackTrace(List<StackTraceElement> list) {
ListIterator<StackTraceElement> iter = list.listIterator();
while (iter.hasNext()) {
StackTraceElement ste = iter.next();
if (ste.getClassName().startsWith("com.hp.hpl.jena.")) {
iter.remove();
}
}
}
public boolean passesStackRestriction(String restriction) {
if (StringUtils.isEmpty(restriction)) {
return true;
} else {
try {
return passesStackRestriction(Pattern.compile(restriction));
} catch (Exception e) {
log.warn("Failed when testing stack restriction: '"
+ restriction + "'");
return true;
}
}
}
public boolean passesStackRestriction(Pattern restriction) {
if (restriction == null) {
return true;
}
String q = assembleCallStackString();
return restriction.matcher(q).find();
}
private String assembleCallStackString() {
StringBuilder stack = new StringBuilder();
for (StackTraceElement ste : stackTrace) {
stack.append(ste.getClassName()).append(" ")
.append(ste.getMethodName()).append(" ");
}
return stack.deleteCharAt(stack.length() - 1).toString();
}
public String format(boolean requested) {
StringBuilder sb = new StringBuilder();
if (requested) {
for (StackTraceElement ste : stackTrace) {
sb.append(String.format(" line %4d, %s \n",
ste.getLineNumber(), ste.getClassName()));
}
sb.append(" ...\n");
}
return sb.toString();
}
public String getMethodName() {
return this.methodName;
}
}

View file

@ -0,0 +1,145 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.utils.searchengine;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchFacetField;
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchInputDocument;
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchInputField;
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchQuery;
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchQuery.Order;
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchResponse;
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchResultDocument;
/**
* Produce readable displays of objects related to the search engine.
*/
public class Formatter {
public static String format(SearchInputDocument doc) {
StringBuilder sb = new StringBuilder();
sb.append("SearchInputDocument: Name='"
+ getValueFromField(doc, "nameRaw") + "', URI='"
+ getValueFromField(doc, "URI") + "', boost='"
+ doc.getDocumentBoost() + "', " + doc.getFieldMap().size()
+ " fields \n");
for (SearchInputField field : new TreeMap<>(doc.getFieldMap()).values()) {
sb.append(format(field, " ").append('\n'));
}
return sb.toString();
}
public static String getValueFromField(SearchInputDocument doc,
String fieldName) {
SearchInputField field = doc.getField(fieldName);
if (field == null) {
return "UNKNOWN";
} else {
Object value = field.getFirstValue();
return (value == null) ? "UNKNOWN" : String.valueOf(value);
}
}
public static String format(SearchInputField field) {
return format(field, "").toString();
}
private static StringBuilder format(SearchInputField field, String padding) {
StringBuilder sb = new StringBuilder();
sb.append(padding).append(
"SearchInputField: Name='" + field.getName() + "', boost='"
+ field.getBoost() + "', " + field.getValues().size()
+ " values \n");
for (Object value : field.getValues()) {
sb.append(padding).append(" '").append(String.valueOf(value))
.append("' \n");
}
return sb;
}
public static String format(SearchQuery query) {
Set<String> returnFields = query.getFieldsToReturn();
Map<String, Order> sortFields = query.getSortFields();
Set<String> filters = query.getFilters();
Set<String> facets = query.getFacetFields();
return "SearchQuery start='"
+ query.getStart()
+ "', rows='"
+ query.getRows()
+ "', text='"
+ query.getQuery()
+ (returnFields.isEmpty() ? "', No return fields"
: ("', return fields=" + returnFields))
+ (sortFields.isEmpty() ? ", No sort fields"
: (", sort fields=" + sortFields))
+ (filters.isEmpty() ? ", No filters"
: (", filters=" + filters))
+ (facets.isEmpty() ? ", No facets" : (", facets=" + facets
+ " facet limit=" + query.getFacetLimit()
+ ", facet minimum=" + query.getFacetMinCount()))
+ "\n";
}
public static String format(SearchResponse response) {
StringBuilder sb = new StringBuilder();
sb.append("SearchResponse: " + response.getResults().size()
+ " results, " + response.getFacetFields().size()
+ " facet fields, " + response.getHighlighting().size()
+ " highlights\n");
for (SearchFacetField facet : response.getFacetFields()) {
sb.append(format(facet, " "));
}
for (SearchResultDocument result : response.getResults()) {
sb.append(format(result, " "));
}
return sb.toString();
}
private static StringBuilder format(SearchFacetField facet, String padding) {
StringBuilder sb = new StringBuilder();
sb.append(padding).append("SearchFacetField: name=")
.append(facet.getName()).append(", ")
.append(facet.getValues().size()).append(" values");
return sb;
}
private static String format(SearchResultDocument doc, String padding) {
StringBuilder sb = new StringBuilder();
int valuesCount = 0;
for (Collection<Object> values : doc.getFieldValuesMap().values()) {
valuesCount += values.size();
}
sb.append("SearchResultDocument: Name='"
+ getValueFromField(doc, "nameRaw") + "', URI='"
+ getValueFromField(doc, "URI") + "', "
+ doc.getFieldNames().size() + " fields with " + valuesCount
+ " values \n");
for (String fieldName : new TreeMap<>(doc.getFieldValuesMap()).keySet()) {
sb.append(padding).append(
"Field: Name='" + fieldName + "', "
+ doc.getFieldValues(fieldName).size()
+ " values \n");
for (Object value : doc.getFieldValues(fieldName)) {
sb.append(padding).append(" '").append(String.valueOf(value))
.append("' \n");
}
}
return sb.toString();
}
public static String getValueFromField(SearchResultDocument doc,
String fieldName) {
Object value = doc.getFirstValue(fieldName);
return (value == null) ? "UNKNOWN" : String.valueOf(value);
}
}