package lux.solr; import java.util.ArrayList; import javax.xml.transform.TransformerException; import javax.xml.transform.URIResolver; import lux.CachingDocReader; import lux.LuxURIResolver; import lux.exception.LuxException; import lux.exception.NotFoundException; import lux.index.FieldRole; import lux.index.IndexConfiguration; import lux.search.LuxSearcher; import net.sf.saxon.s9api.XdmNode; import org.apache.lucene.search.Sort; import org.apache.solr.common.SolrDocument; import org.apache.solr.common.SolrDocumentList; import org.apache.solr.common.params.CommonParams; import org.apache.solr.common.params.ModifiableSolrParams; import org.apache.solr.handler.component.ResponseBuilder; import org.apache.solr.response.SolrQueryResponse; import org.apache.solr.search.SortSpec; /** * Retrieves documents Solr, both local and distributed (SolrCloud); called from fn:doc() */ public class SolrURIResolver extends LuxURIResolver { private final XQueryComponent xqueryComponent; private final String xmlFieldName; private final String idFieldName; SolrURIResolver(XQueryComponent xqueryComponent, URIResolver systemURIResolver) { super (systemURIResolver, null, xqueryComponent.getSolrIndexConfig().getIndexConfig().getFieldName(FieldRole.URI)); this.xqueryComponent = xqueryComponent; IndexConfiguration indexConfig = xqueryComponent.getSolrIndexConfig().getIndexConfig(); this.xmlFieldName = indexConfig.getFieldName(FieldRole.XML_STORE); this.idFieldName = indexConfig.getFieldName(FieldRole.ID); } @Override public XdmNode getDocument(String uri) throws TransformerException { String[] shards = xqueryComponent.getCurrentShards(); if (shards != null) { return getDocumentDistrib (uri); } else { return super.getDocument (uri); } } private XdmNode getDocumentDistrib(String uri) throws NotFoundException { ModifiableSolrParams params = new ModifiableSolrParams(); params.add((CommonParams.Q), uriFieldName + ":\"" + uri.replaceAll("([\\\\\\\"])", "\\$1") + '"'); params.add(CommonParams.FL, uriFieldName, xmlFieldName, idFieldName); params.add(CommonParams.WT, "javabin"); params.add(CommonParams.VERSION, "2"); params.add("distrib", "true"); String[] shards = xqueryComponent.getCurrentShards(); StringBuilder shardBuffer = new StringBuilder(shards[0]); for (int i = 0; i < shards.length; i++) { shardBuffer.append(',').append(shards[i]); } params.add("shards", shardBuffer.toString()); CloudQueryRequest req = new CloudQueryRequest(xqueryComponent.getCore(), params, new SortSpec(Sort.RELEVANCE, 1)); // don't need to query for docids? req.setNextStage(ResponseBuilder.STAGE_GET_FIELDS); SolrQueryResponse response = new SolrQueryResponse(); // TODO: we could probably figure out which shard has the document and only query that one // instead of broadcasting this request to all of them xqueryComponent.getSearchHandler().handleRequest(req, response); if (response.getException() != null) { throw new LuxException ("An error occurred while retrieving " + uri, response.getException()); } SolrDocumentList docs = (SolrDocumentList) response.getValues().get("response"); if (docs.isEmpty()) { throw new NotFoundException ("document '" + uri + "' not found"); } if (docs.size() > 1) { throw new NotFoundException ("found " + docs.size() + " documents with uri='" + uri + "'"); } SolrDocument doc = docs.get(0); Long docID = (Long) doc.get(idFieldName); Object xml = doc.get(xmlFieldName); if (xml instanceof ArrayList<?>) { // why does this come back as an ArrayList? xml = ((ArrayList<?>)xml).get(0); } String xmlString = null; byte[] xmlBytes = null; if (xml instanceof String) { xmlString = (String) xml; } else { // Must be a tinybin if it's not a String xmlBytes = (byte[]) xml; } XdmNode node = getDocReader().createXdmNode(docID, uri, xmlString, xmlBytes); doc.removeFields(xmlFieldName); node.getUnderlyingNode().getDocumentRoot().setUserData(SolrDocument.class.getName(), doc); return node; } @Override public LuxSearcher getSearcher() { return xqueryComponent.getEvaluator().getSearcher(); } @Override public CachingDocReader getDocReader() { return xqueryComponent.getEvaluator().getDocReader(); } }