package lux.solr; import java.io.IOException; import java.util.ArrayList; import javax.xml.stream.XMLStreamException; import lux.DocWriter; import lux.Evaluator; import lux.exception.LuxException; import lux.index.FieldRole; import lux.index.IndexConfiguration; import lux.index.XmlIndexer; import lux.xml.tinybin.TinyBinary; import net.sf.saxon.om.NodeInfo; import net.sf.saxon.s9api.SaxonApiException; import net.sf.saxon.s9api.Serializer; import net.sf.saxon.s9api.XdmNode; import net.sf.saxon.tree.tiny.TinyNodeImpl; import org.apache.solr.client.solrj.request.UpdateRequest; import org.apache.solr.common.SolrInputDocument; import org.apache.solr.common.params.ModifiableSolrParams; import org.apache.solr.common.params.ShardParams; import org.apache.solr.core.SolrCore; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.request.SolrQueryRequestBase; import org.apache.solr.response.SolrQueryResponse; import org.apache.solr.update.DeleteUpdateCommand; import org.apache.solr.update.UpdateHandler; import org.apache.solr.update.processor.UpdateRequestProcessor; import org.apache.solr.update.processor.UpdateRequestProcessorChain; import org.slf4j.LoggerFactory; /** * Used for updates (write, delete and commit) from within XQuery (lux:insert) and XSLT (xsl:result-document) * TODO: refactor into two classes: one for cloud, one for local? */ public class SolrDocWriter implements DocWriter { private final SolrCore core; private final XQueryComponent xqueryComponent; private final String uriFieldName; private final String xmlFieldName; SolrDocWriter(XQueryComponent xQueryComponent, SolrCore core) { this.core = core; this.xqueryComponent = xQueryComponent; IndexConfiguration indexConfig = xQueryComponent.getSolrIndexConfig().getIndexConfig(); uriFieldName = indexConfig.getFieldName(FieldRole.URI); xmlFieldName = indexConfig.getFieldName(FieldRole.XML_STORE); } @Override public void write(NodeInfo node, String uri) { UpdateHandler updateHandler = core.getUpdateHandler(); // Create a version of the document for saving to the transaction log, // or for cloud update via HTTP SolrInputDocument solrDoc = new SolrInputDocument(); solrDoc.addField(uriFieldName, uri); if (isCloud()) { // TODO: write as binary, but we need to enable the binary update request writer for this // TinyBinary tinybin = new TinyBinary(((TinyNodeImpl)node).getTree()); // solrDoc.addField(xmlFieldName, tinybin.getByteBuffer().array()); Serializer serializer = xqueryComponent.solrIndexConfig.checkoutSerializer(); try { String xmlString = serializer.serializeNodeToString(new XdmNode(node)); solrDoc.addField(xmlFieldName, xmlString); } catch (SaxonApiException e) { throw new LuxException (e); } finally { xqueryComponent.solrIndexConfig.returnSerializer(serializer); } // TODO -- if we can determine this doc only gets added locally?? // solrDoc.addField(xmlFieldName, node); } else if (updateHandler.getUpdateLog() != null) { if (node instanceof TinyNodeImpl) { TinyBinary tinybin = new TinyBinary(((TinyNodeImpl)node).getTree()); solrDoc.addField(xmlFieldName, tinybin.getByteBuffer()); } else { String xml = node.toString(); solrDoc.addField(xmlFieldName, xml); } } if (isCloud()) { writeToCloud (solrDoc, uri); } else { writeLocal (solrDoc, node, uri); } } private void writeToCloud (SolrInputDocument solrDoc, String uri) { ArrayList<String> urls = xqueryComponent.getShardURLs(true); LoggerFactory.getLogger(getClass()).debug ("writing " + uri + " to cloud at " + urls); SolrQueryResponse rsp = new SolrQueryResponse(); SolrQueryRequest req = UpdateDocCommand.makeSolrRequest(core); ((ModifiableSolrParams)req.getParams()).add(ShardParams.SHARDS, urls.toArray(new String[urls.size()])); UpdateRequest updateReq = new UpdateRequest(); updateReq.add(solrDoc); UpdateDocCommand cmd = new UpdateDocCommand(req, solrDoc, null, uri); UpdateRequestProcessorChain updateChain = xqueryComponent.getCore().getUpdateProcessingChain("lux-update-chain"); try { UpdateRequestProcessor processor = updateChain.createProcessor(req, rsp); processor.processAdd(cmd); processor.finish(); } catch (IOException e) { throw new LuxException (e); } } private void writeLocal (SolrInputDocument solrDoc, NodeInfo node, String uri) { XmlIndexer indexer = null; try { indexer = xqueryComponent.getSolrIndexConfig().checkoutXmlIndexer(); try { indexer.index (node, uri); } catch (XMLStreamException e) { throw new LuxException(e); } UpdateDocCommand cmd = new UpdateDocCommand(core, indexer.createLuceneDocument(), uri); cmd.solrDoc = solrDoc; core.getUpdateHandler().addDoc(cmd); } catch (IOException e) { throw new LuxException (e); } finally { if (indexer != null) { xqueryComponent.getSolrIndexConfig().returnXmlIndexer(indexer); } } } @Override public void delete(String uri) { DeleteUpdateCommand cmd = new DeleteUpdateCommand(makeSolrQueryRequest()); /* cmd.fromCommitted = true; cmd.fromPending = true; */ cmd.id = uri; try { if (isCloud()) { deleteCloud(cmd); } else { core.getUpdateHandler().delete(cmd); } } catch (IOException e) { throw new LuxException (e); } } private void deleteCloud (DeleteUpdateCommand cmd) throws IOException { UpdateRequestProcessorChain updateChain = xqueryComponent.getCore().getUpdateProcessingChain("lux-update-chain"); SolrQueryResponse rsp = new SolrQueryResponse(); SolrQueryRequest req = UpdateDocCommand.makeSolrRequest(core); UpdateRequestProcessor processor = updateChain.createProcessor(req, rsp); processor.processDelete(cmd); processor.finish(); } @Override public void deleteAll() { DeleteUpdateCommand cmd = new DeleteUpdateCommand( makeSolrQueryRequest()); /* cmd.fromCommitted = true; cmd.fromPending = true; */ cmd.query = "*:*"; try { if (isCloud()) { deleteCloud(cmd); } else { core.getUpdateHandler().deleteByQuery(cmd); } } catch (IOException e) { throw new LuxException(e); } } private SolrQueryRequestBase makeSolrQueryRequest() { return new SolrQueryRequestBase(core, new ModifiableSolrParams()) {}; } @Override public void commit(Evaluator eval) { SolrQueryContext context = (SolrQueryContext) eval.getQueryContext(); context.setCommitPending(true); } /** * commits, but does not close the underlying index */ @Override public void close(Evaluator eval) { // do not attempt to close the index; Solr will take care of that commit (eval); } private boolean isCloud () { return xqueryComponent.getCurrentShards() != null; } } /* * This Source Code Form is subject to the terms of the Mozilla Public License, * v. 2.0. If a copy of the MPL was not distributed with this file, You can * obtain one at http://mozilla.org/MPL/2.0/. */