NIHVIVO-3467 Refactor to use SolrQueryUtils.
This commit is contained in:
parent
b09c4b309c
commit
0dde805f3c
6 changed files with 371 additions and 144 deletions
|
@ -2,18 +2,22 @@
|
||||||
|
|
||||||
package edu.cornell.mannlib.vitro.webapp.controller.accounts.manageproxies.ajax;
|
package edu.cornell.mannlib.vitro.webapp.controller.accounts.manageproxies.ajax;
|
||||||
|
|
||||||
|
import static edu.cornell.mannlib.vitro.webapp.search.VitroSearchTermNames.AC_NAME_STEMMED;
|
||||||
|
import static edu.cornell.mannlib.vitro.webapp.search.VitroSearchTermNames.NAME_LOWERCASE_SINGLE_VALUED;
|
||||||
|
import static edu.cornell.mannlib.vitro.webapp.search.VitroSearchTermNames.NAME_RAW;
|
||||||
|
import static edu.cornell.mannlib.vitro.webapp.search.VitroSearchTermNames.NAME_UNSTEMMED;
|
||||||
|
import static edu.cornell.mannlib.vitro.webapp.search.VitroSearchTermNames.RDFTYPE;
|
||||||
|
import static edu.cornell.mannlib.vitro.webapp.search.VitroSearchTermNames.URI;
|
||||||
|
import static edu.cornell.mannlib.vitro.webapp.utils.solr.SolrQueryUtils.Conjunction.OR;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.servlet.ServletContext;
|
||||||
import javax.servlet.http.HttpServlet;
|
import javax.servlet.http.HttpServlet;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
import org.apache.commons.lang.StringUtils;
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
import org.apache.solr.client.solrj.SolrQuery;
|
import org.apache.solr.client.solrj.SolrQuery;
|
||||||
|
@ -21,16 +25,15 @@ import org.apache.solr.client.solrj.SolrQuery.ORDER;
|
||||||
import org.apache.solr.client.solrj.SolrServer;
|
import org.apache.solr.client.solrj.SolrServer;
|
||||||
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;
|
||||||
import org.apache.solr.common.SolrDocument;
|
|
||||||
import org.apache.solr.common.SolrDocumentList;
|
|
||||||
import org.json.JSONArray;
|
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
|
|
||||||
import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties;
|
import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties;
|
||||||
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
|
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
|
||||||
import edu.cornell.mannlib.vitro.webapp.controller.ajax.AbstractAjaxResponder;
|
import edu.cornell.mannlib.vitro.webapp.controller.ajax.AbstractAjaxResponder;
|
||||||
import edu.cornell.mannlib.vitro.webapp.search.VitroSearchTermNames;
|
|
||||||
import edu.cornell.mannlib.vitro.webapp.search.solr.SolrSetup;
|
import edu.cornell.mannlib.vitro.webapp.search.solr.SolrSetup;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.utils.solr.AutoCompleteWords;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.utils.solr.FieldMap;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.utils.solr.SolrQueryUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the basic auto-complete info for the profile selection.
|
* Get the basic auto-complete info for the profile selection.
|
||||||
|
@ -39,63 +42,42 @@ import edu.cornell.mannlib.vitro.webapp.search.solr.SolrSetup;
|
||||||
public class BasicProfilesGetter extends AbstractAjaxResponder {
|
public class BasicProfilesGetter extends AbstractAjaxResponder {
|
||||||
private static final Log log = LogFactory.getLog(BasicProfilesGetter.class);
|
private static final Log log = LogFactory.getLog(BasicProfilesGetter.class);
|
||||||
|
|
||||||
|
private static final String WORD_DELIMITER = "[, ]+";
|
||||||
|
private static final FieldMap RESPONSE_FIELDS = SolrQueryUtils
|
||||||
|
.fieldMap().put(URI, "uri").put(NAME_RAW, "label")
|
||||||
|
.put("bogus", "classLabel").put("bogus", "imageUrl");
|
||||||
|
|
||||||
private static final String PROPERTY_PROFILE_TYPES = "proxy.eligibleTypeList";
|
private static final String PROPERTY_PROFILE_TYPES = "proxy.eligibleTypeList";
|
||||||
private static final String PARAMETER_SEARCH_TERM = "term";
|
private static final String PARAMETER_SEARCH_TERM = "term";
|
||||||
private static final String DEFAULT_PROFILE_TYPES = "http://www.w3.org/2002/07/owl#Thing";
|
private static final String DEFAULT_PROFILE_TYPES = "http://www.w3.org/2002/07/owl#Thing";
|
||||||
|
|
||||||
private final String term;
|
private final String term;
|
||||||
private final List<String> completeWords;
|
private final AutoCompleteWords searchWords;
|
||||||
private final String partialWord;
|
private final List<String> profileTypes;
|
||||||
private final Collection<String> profileTypes;
|
|
||||||
|
|
||||||
public BasicProfilesGetter(HttpServlet servlet, VitroRequest vreq,
|
public BasicProfilesGetter(HttpServlet servlet, VitroRequest vreq,
|
||||||
HttpServletResponse resp) {
|
HttpServletResponse resp) {
|
||||||
super(servlet, vreq, resp);
|
super(servlet, vreq, resp);
|
||||||
|
|
||||||
this.term = getStringParameter(PARAMETER_SEARCH_TERM, "");
|
this.term = getStringParameter(PARAMETER_SEARCH_TERM, "");
|
||||||
|
this.searchWords = SolrQueryUtils.parseForAutoComplete(term,
|
||||||
List<String> termWords = figureTermWords();
|
WORD_DELIMITER);
|
||||||
if (termWords.isEmpty() || this.term.endsWith(" ")) {
|
|
||||||
this.completeWords = termWords;
|
|
||||||
this.partialWord = null;
|
|
||||||
} else {
|
|
||||||
this.completeWords = termWords.subList(0, termWords.size() - 1);
|
|
||||||
this.partialWord = termWords.get(termWords.size() - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.profileTypes = figureProfileTypes();
|
this.profileTypes = figureProfileTypes();
|
||||||
|
|
||||||
log.debug(this);
|
log.debug(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<String> figureTermWords() {
|
private List<String> figureProfileTypes() {
|
||||||
List<String> list = new ArrayList<String>();
|
|
||||||
String[] array = this.term.split("[, ]+");
|
|
||||||
for (String word : array) {
|
|
||||||
String trimmed = word.trim();
|
|
||||||
if (!trimmed.isEmpty()) {
|
|
||||||
list.add(trimmed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Collections.unmodifiableList(list);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Collection<String> figureProfileTypes() {
|
|
||||||
List<String> list = new ArrayList<String>();
|
|
||||||
String typesString = ConfigurationProperties.getBean(vreq).getProperty(
|
String typesString = ConfigurationProperties.getBean(vreq).getProperty(
|
||||||
PROPERTY_PROFILE_TYPES, DEFAULT_PROFILE_TYPES);
|
PROPERTY_PROFILE_TYPES, DEFAULT_PROFILE_TYPES);
|
||||||
String[] types = typesString.split(",");
|
List<String> list = SolrQueryUtils.parseWords(typesString,
|
||||||
for (String type : types) {
|
WORD_DELIMITER);
|
||||||
String trimmed = type.trim();
|
|
||||||
if (!trimmed.isEmpty()) {
|
|
||||||
list.add(trimmed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (list.isEmpty()) {
|
if (list.isEmpty()) {
|
||||||
log.error("No types configured for profile pages in "
|
log.error("No types configured for profile pages in "
|
||||||
+ PROPERTY_PROFILE_TYPES);
|
+ PROPERTY_PROFILE_TYPES);
|
||||||
}
|
}
|
||||||
return Collections.unmodifiableCollection(list);
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -106,13 +88,15 @@ public class BasicProfilesGetter extends AbstractAjaxResponder {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
SolrServer solr = SolrSetup.getSolrServer(servlet
|
ServletContext ctx = servlet.getServletContext();
|
||||||
.getServletContext());
|
SolrServer solr = SolrSetup.getSolrServer(ctx);
|
||||||
SolrQuery query = buildSolrQuery();
|
SolrQuery query = buildSolrQuery();
|
||||||
QueryResponse queryResponse = solr.query(query);
|
QueryResponse queryResponse = solr.query(query);
|
||||||
|
|
||||||
JSONArray jsonArray = parseResponse(queryResponse);
|
List<Map<String, String>> parsed = SolrQueryUtils
|
||||||
String response = jsonArray.toString();
|
.parseResponse(queryResponse, RESPONSE_FIELDS);
|
||||||
|
|
||||||
|
String response = assembleJsonResponse(parsed);
|
||||||
log.debug(response);
|
log.debug(response);
|
||||||
return response;
|
return response;
|
||||||
} catch (SolrServerException e) {
|
} catch (SolrServerException e) {
|
||||||
|
@ -123,110 +107,20 @@ public class BasicProfilesGetter extends AbstractAjaxResponder {
|
||||||
|
|
||||||
private SolrQuery buildSolrQuery() {
|
private SolrQuery buildSolrQuery() {
|
||||||
SolrQuery q = new SolrQuery();
|
SolrQuery q = new SolrQuery();
|
||||||
q.setFields(VitroSearchTermNames.NAME_RAW, VitroSearchTermNames.URI);
|
q.setFields(NAME_RAW, URI);
|
||||||
q.setSortField(VitroSearchTermNames.NAME_LOWERCASE_SINGLE_VALUED,
|
q.setSortField(NAME_LOWERCASE_SINGLE_VALUED, ORDER.asc);
|
||||||
ORDER.asc);
|
q.setFilterQueries(SolrQueryUtils.assembleConjunctiveQuery(RDFTYPE,
|
||||||
q.setFilterQueries(assembleTypeRestrictionQuery());
|
profileTypes, OR));
|
||||||
q.setStart(0);
|
q.setStart(0);
|
||||||
q.setRows(30);
|
q.setRows(30);
|
||||||
q.setQuery(buildQueryStringFromSearchTerm());
|
q.setQuery(searchWords.assembleQuery(NAME_UNSTEMMED, AC_NAME_STEMMED));
|
||||||
return q;
|
return q;
|
||||||
// use VitroSearchTermNames.NAME_LOWERCASE
|
|
||||||
// break the search term into words, then insert AND between the words
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
}
|
|
||||||
|
|
||||||
private String assembleTypeRestrictionQuery() {
|
|
||||||
List<String> terms = new ArrayList<String>();
|
|
||||||
for (String profileType : profileTypes) {
|
|
||||||
terms.add(VitroSearchTermNames.RDFTYPE + ":\"" + profileType + "\"");
|
|
||||||
}
|
|
||||||
String q = StringUtils.join(terms, " OR ");
|
|
||||||
log.debug("Type restriction query is '" + q + "'");
|
|
||||||
return q;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String buildQueryStringFromSearchTerm() {
|
|
||||||
List<String> terms = new ArrayList<String>();
|
|
||||||
for (String word : completeWords) {
|
|
||||||
terms.add(termForCompleteWord(word));
|
|
||||||
}
|
|
||||||
if (partialWord != null) {
|
|
||||||
terms.add(termForPartialWord(partialWord));
|
|
||||||
}
|
|
||||||
|
|
||||||
String q = StringUtils.join(terms, " AND ");
|
|
||||||
log.debug("Query string is '" + q + "'");
|
|
||||||
return q;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String termForCompleteWord(String word) {
|
|
||||||
return VitroSearchTermNames.NAME_UNSTEMMED + ":\"" + word + "\"";
|
|
||||||
}
|
|
||||||
|
|
||||||
private String termForPartialWord(String word) {
|
|
||||||
return VitroSearchTermNames.AC_NAME_STEMMED + ":\"" + word + "\"";
|
|
||||||
}
|
|
||||||
|
|
||||||
private JSONArray parseResponse(QueryResponse queryResponse) {
|
|
||||||
JSONArray jsonArray = new JSONArray();
|
|
||||||
|
|
||||||
if (queryResponse == null) {
|
|
||||||
log.error("Query response for a search was null");
|
|
||||||
return jsonArray;
|
|
||||||
}
|
|
||||||
|
|
||||||
SolrDocumentList docs = queryResponse.getResults();
|
|
||||||
|
|
||||||
if (docs == null) {
|
|
||||||
log.error("Docs for a search was null");
|
|
||||||
return jsonArray;
|
|
||||||
}
|
|
||||||
|
|
||||||
long hitCount = docs.getNumFound();
|
|
||||||
log.debug("Total number of hits = " + hitCount);
|
|
||||||
if (hitCount < 1) {
|
|
||||||
return jsonArray;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (SolrDocument doc : docs) {
|
|
||||||
try {
|
|
||||||
String uri = doc.get(VitroSearchTermNames.URI).toString();
|
|
||||||
|
|
||||||
Object nameRaw = doc.get(VitroSearchTermNames.NAME_RAW);
|
|
||||||
String name = null;
|
|
||||||
if (nameRaw instanceof List<?>) {
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
List<String> nameRawList = (List<String>) nameRaw;
|
|
||||||
name = nameRawList.get(0);
|
|
||||||
} else {
|
|
||||||
name = (String) nameRaw;
|
|
||||||
}
|
|
||||||
|
|
||||||
jsonArray.put(resultRow(uri, name));
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("problem getting usable individuals from search "
|
|
||||||
+ "hits" + e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return jsonArray;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map<String, String> resultRow(String uri, String name) {
|
|
||||||
Map<String, String> map = new HashMap<String, String>();
|
|
||||||
map.put("uri", uri);
|
|
||||||
map.put("label", name);
|
|
||||||
map.put("classLabel", "");
|
|
||||||
map.put("imageUrl", "");
|
|
||||||
return map;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "BasicProfilesGetter[term=" + term + ", completeWords="
|
return "BasicProfilesGetter[term=" + term + ", searchWords="
|
||||||
+ completeWords + ", partialWord=" + partialWord
|
+ searchWords + ", profileTypes=" + profileTypes + "]";
|
||||||
+ ", profileTypes=" + profileTypes + "]";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServlet;
|
import javax.servlet.http.HttpServlet;
|
||||||
|
@ -75,6 +76,18 @@ public abstract class AbstractAjaxResponder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assemble a list of maps into a single String representing a JSON array of
|
||||||
|
* objects with fields.
|
||||||
|
*/
|
||||||
|
protected String assembleJsonResponse(List<Map<String, String>> maps) {
|
||||||
|
JSONArray jsonArray = new JSONArray();
|
||||||
|
for (Map<String, String> map: maps) {
|
||||||
|
jsonArray.put(map);
|
||||||
|
}
|
||||||
|
return jsonArray.toString();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AJAX responders can use a parser that extends this class. The parser must
|
* AJAX responders can use a parser that extends this class. The parser must
|
||||||
* implement "parseSolutionRow()"
|
* implement "parseSolutionRow()"
|
||||||
|
|
|
@ -0,0 +1,87 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vitro.webapp.utils.solr;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A helper class for use with an Auto-complete query.
|
||||||
|
*
|
||||||
|
* Any word that is followed by a delimiter is considered to be complete, and
|
||||||
|
* should be matched exactly in the query. If there is a word on the end that is
|
||||||
|
* not followed by a delimiter, it is incomplete, and should act like a
|
||||||
|
* "starts-with" query.
|
||||||
|
*/
|
||||||
|
public class AutoCompleteWords {
|
||||||
|
private static final Log log = LogFactory.getLog(AutoCompleteWords.class);
|
||||||
|
|
||||||
|
private final String searchTerm;
|
||||||
|
private final String delimiterPattern;
|
||||||
|
private final List<String> completeWords;
|
||||||
|
private final String partialWord;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Package-access. Use SolrQueryUtils.parseForAutoComplete() to create an
|
||||||
|
* instance.
|
||||||
|
*/
|
||||||
|
AutoCompleteWords(String searchTerm, String delimiterPattern) {
|
||||||
|
this.searchTerm = searchTerm;
|
||||||
|
this.delimiterPattern = delimiterPattern;
|
||||||
|
|
||||||
|
List<String> termWords = figureTermWords();
|
||||||
|
if (termWords.isEmpty() || this.searchTerm.endsWith(" ")) {
|
||||||
|
this.completeWords = termWords;
|
||||||
|
this.partialWord = null;
|
||||||
|
} else {
|
||||||
|
this.completeWords = termWords.subList(0, termWords.size() - 1);
|
||||||
|
this.partialWord = termWords.get(termWords.size() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> figureTermWords() {
|
||||||
|
List<String> list = new ArrayList<String>();
|
||||||
|
String[] array = this.searchTerm.split(this.delimiterPattern);
|
||||||
|
for (String word : array) {
|
||||||
|
String trimmed = word.trim();
|
||||||
|
if (!trimmed.isEmpty()) {
|
||||||
|
list.add(trimmed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Collections.unmodifiableList(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String assembleQuery(String fieldNameForCompleteWords,
|
||||||
|
String fieldNameForPartialWord) {
|
||||||
|
List<String> terms = new ArrayList<String>();
|
||||||
|
for (String word : this.completeWords) {
|
||||||
|
terms.add(buildTerm(fieldNameForCompleteWords, word));
|
||||||
|
}
|
||||||
|
if (partialWord != null) {
|
||||||
|
terms.add(buildTerm(fieldNameForPartialWord, partialWord));
|
||||||
|
}
|
||||||
|
|
||||||
|
String q = StringUtils.join(terms, " AND ");
|
||||||
|
log.debug("Query string is '" + q + "'");
|
||||||
|
return q;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildTerm(String fieldName, String word) {
|
||||||
|
return fieldName + ":\"" + word + "\"";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "AutoCompleteWords[searchTerm='" + searchTerm
|
||||||
|
+ "', delimiterPattern='" + delimiterPattern
|
||||||
|
+ "', completeWords=" + completeWords + ", partialWord="
|
||||||
|
+ partialWord + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vitro.webapp.utils.solr;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A builder object that can assemble a map of Solr field names to JSON field
|
||||||
|
* names.
|
||||||
|
*
|
||||||
|
* Use like this:
|
||||||
|
*
|
||||||
|
* m = SolrQueryUtils.fieldMap().row("this", "that").row("2nd", "row").map();
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class FieldMap {
|
||||||
|
private final Map<String, String> m = new HashMap<String, String>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a row to the map
|
||||||
|
*/
|
||||||
|
public FieldMap put(String solrFieldName, String jsonFieldName) {
|
||||||
|
if (solrFieldName == null) {
|
||||||
|
throw new NullPointerException("solrFieldName may not be null.");
|
||||||
|
}
|
||||||
|
if (jsonFieldName == null) {
|
||||||
|
throw new NullPointerException("jsonFieldName may not be null.");
|
||||||
|
}
|
||||||
|
m.put(solrFieldName, jsonFieldName);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Release the map for use.
|
||||||
|
*/
|
||||||
|
public Map<String, String> map() {
|
||||||
|
return new HashMap<String, String>(m);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vitro.webapp.utils.solr;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.apache.solr.client.solrj.response.QueryResponse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Some static method to help in constructing Solr queries and parsing the
|
||||||
|
* results.
|
||||||
|
*/
|
||||||
|
public class SolrQueryUtils {
|
||||||
|
public enum Conjunction {
|
||||||
|
AND, OR;
|
||||||
|
|
||||||
|
public String joiner() {
|
||||||
|
return " " + this.name() + " ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an AutoCompleteWords object that can be used to build an
|
||||||
|
* auto-complete query.
|
||||||
|
*/
|
||||||
|
public static AutoCompleteWords parseForAutoComplete(String searchTerm,
|
||||||
|
String delimiterPattern) {
|
||||||
|
return new AutoCompleteWords(searchTerm, delimiterPattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a builder object that can assemble a map of Solr field names to
|
||||||
|
* JSON field names.
|
||||||
|
*/
|
||||||
|
public static FieldMap fieldMap() {
|
||||||
|
return new FieldMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a response into a list of maps, one map for each document.
|
||||||
|
*
|
||||||
|
* The Solr field names in the document are replaced by json field names in
|
||||||
|
* the result, according to the fieldMap.
|
||||||
|
*/
|
||||||
|
public static List<Map<String, String>> parseResponse(
|
||||||
|
QueryResponse queryResponse, FieldMap fieldMap) {
|
||||||
|
return new SolrResultsParser(queryResponse, fieldMap).parse();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Break a string into a list of words, according to a RegEx delimiter. Trim
|
||||||
|
* leading and trailing white space from each word.
|
||||||
|
*/
|
||||||
|
public static List<String> parseWords(String typesString,
|
||||||
|
String wordDelimiter) {
|
||||||
|
List<String> list = new ArrayList<String>();
|
||||||
|
String[] array = typesString.split(wordDelimiter);
|
||||||
|
for (String word : array) {
|
||||||
|
String trimmed = word.trim();
|
||||||
|
if (!trimmed.isEmpty()) {
|
||||||
|
list.add(trimmed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Glue these words together into a query on a given field, joined by either
|
||||||
|
* AND or OR.
|
||||||
|
*/
|
||||||
|
public static String assembleConjunctiveQuery(String fieldName,
|
||||||
|
Collection<String> words, Conjunction c) {
|
||||||
|
List<String> terms = new ArrayList<String>();
|
||||||
|
for (String word : words) {
|
||||||
|
terms.add(buildTerm(fieldName, word));
|
||||||
|
}
|
||||||
|
String q = StringUtils.join(terms, c.joiner());
|
||||||
|
return q;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String buildTerm(String fieldName, String word) {
|
||||||
|
return fieldName + ":\"" + word + "\"";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,103 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vitro.webapp.utils.solr;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
import org.apache.solr.client.solrj.response.QueryResponse;
|
||||||
|
import org.apache.solr.common.SolrDocument;
|
||||||
|
import org.apache.solr.common.SolrDocumentList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse this Solr response, creating a map of values for each document.
|
||||||
|
*
|
||||||
|
* The Solr field names in the document are replaced by json field names in the
|
||||||
|
* parsed results, according to the fieldMap.
|
||||||
|
*/
|
||||||
|
public class SolrResultsParser {
|
||||||
|
private static final Log log = LogFactory.getLog(SolrResultsParser.class);
|
||||||
|
|
||||||
|
private final QueryResponse queryResponse;
|
||||||
|
private final Map<String, String> fieldNameMapping;
|
||||||
|
|
||||||
|
public SolrResultsParser(QueryResponse queryResponse, FieldMap fieldMap) {
|
||||||
|
this.queryResponse = queryResponse;
|
||||||
|
this.fieldNameMapping = fieldMap.map();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the entire response into a list of maps.
|
||||||
|
*/
|
||||||
|
public List<Map<String, String>> parse() {
|
||||||
|
List<Map<String, String>> maps = new ArrayList<Map<String, String>>();
|
||||||
|
|
||||||
|
if (queryResponse == null) {
|
||||||
|
log.error("Query response for a search was null");
|
||||||
|
return maps;
|
||||||
|
}
|
||||||
|
|
||||||
|
SolrDocumentList docs = queryResponse.getResults();
|
||||||
|
if (docs == null) {
|
||||||
|
log.error("Docs for a search was null");
|
||||||
|
return maps;
|
||||||
|
}
|
||||||
|
log.debug("Total number of hits = " + docs.getNumFound());
|
||||||
|
|
||||||
|
for (SolrDocument doc : docs) {
|
||||||
|
maps.add(parseSingleDocument(doc));
|
||||||
|
}
|
||||||
|
|
||||||
|
return maps;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a map from this document, applying translation on the field names.
|
||||||
|
*/
|
||||||
|
private Map<String, String> parseSingleDocument(SolrDocument doc) {
|
||||||
|
Map<String, String> result = new HashMap<String, String>();
|
||||||
|
for (String solrFieldName : fieldNameMapping.keySet()) {
|
||||||
|
String jsonFieldName = fieldNameMapping.get(solrFieldName);
|
||||||
|
|
||||||
|
result.put(jsonFieldName, parseSingleValue(doc, solrFieldName));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a single value in the document
|
||||||
|
*/
|
||||||
|
private String parseSingleValue(SolrDocument doc, String key) {
|
||||||
|
Object rawValue = getFirstValue(doc.get(key));
|
||||||
|
|
||||||
|
if (rawValue == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
if (rawValue instanceof String) {
|
||||||
|
return (String) rawValue;
|
||||||
|
}
|
||||||
|
return String.valueOf(rawValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The result might be a list. If so, get the first element.
|
||||||
|
*/
|
||||||
|
private Object getFirstValue(Object rawValue) {
|
||||||
|
if (rawValue instanceof List<?>) {
|
||||||
|
List<?> list = (List<?>) rawValue;
|
||||||
|
if (list.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return list.get(0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return rawValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue