You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by no...@apache.org on 2019/08/08 08:05:10 UTC
[lucene-solr] 01/01: SOLR-13682: command line option to export data
to a file
This is an automated email from the ASF dual-hosted git repository.
noble pushed a commit to branch jira/SOLR-13682
in repository https://gitbox.apache.org/repos/asf/lucene-solr.git
commit 696f83df963d296d2cf2b639d2d5d1c5c317edd0
Author: noble <no...@apache.org>
AuthorDate: Thu Aug 8 18:04:44 2019 +1000
SOLR-13682: command line option to export data to a file
---
.../src/java/org/apache/solr/util/ExportTool.java | 208 +++++++++++++++++++++
.../src/java/org/apache/solr/util/SolrCLI.java | 3 +
2 files changed, 211 insertions(+)
diff --git a/solr/core/src/java/org/apache/solr/util/ExportTool.java b/solr/core/src/java/org/apache/solr/util/ExportTool.java
new file mode 100644
index 0000000..0b77f80
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/util/ExportTool.java
@@ -0,0 +1,208 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.solr.util;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Consumer;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.OptionBuilder;
+import org.apache.solr.client.solrj.SolrClient;
+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.StreamingResponseCallback;
+import org.apache.solr.client.solrj.impl.CloudSolrClient;
+import org.apache.solr.client.solrj.impl.StreamingBinaryResponseParser;
+import org.apache.solr.client.solrj.request.GenericSolrRequest;
+import org.apache.solr.client.solrj.request.QueryRequest;
+import org.apache.solr.common.SolrDocument;
+import org.apache.solr.common.SolrInputDocument;
+import org.apache.solr.common.params.CursorMarkParams;
+import org.apache.solr.common.params.MapSolrParams;
+import org.apache.solr.common.util.FastWriter;
+import org.apache.solr.common.util.JavaBinCodec;
+import org.apache.solr.common.util.NamedList;
+import org.apache.solr.common.util.SolrJSONWriter;
+
+import static org.apache.solr.common.params.CommonParams.JAVABIN;
+
+public class ExportTool extends SolrCLI.ToolBase {
+ @Override
+ public String getName() {
+ return "export";
+ }
+
+ @Override
+ public Option[] getOptions() {
+ return OPTIONS;
+ }
+
+ @Override
+ protected void runImpl(CommandLine cli) throws Exception {
+ String url = cli.getOptionValue("url");
+ String format = cli.getOptionValue("format", JAVABIN);
+ int idx = url.lastIndexOf('/');
+ String baseurl = url.substring(0, idx);
+ String coll = url.substring(idx + 1);
+ String file = cli.getOptionValue("out",
+ JAVABIN.equals(format) ? coll + ".javabin" : coll + ".json");
+ String maxDocsStr = cli.getOptionValue("limit", String.valueOf(Long.MAX_VALUE));
+ long maxDocs = Long.parseLong(maxDocsStr);
+ if (JAVABIN.equals(format)) {
+ writeJavabinDocs(baseurl, coll, file, maxDocs);
+ } else {
+ writeJsonLDocs(baseurl, coll, file, maxDocs);
+ }
+ }
+
+ public static void writeJsonLDocs(String baseurl, String coll, String file, long maxDocs) throws IOException, SolrServerException {
+ SolrClient solrClient = new CloudSolrClient.Builder(Collections.singletonList(baseurl)).build();
+ FileOutputStream fos = new FileOutputStream(file);
+ Writer writer = FastWriter.wrap(new OutputStreamWriter(fos));
+
+ SolrJSONWriter jsonw = new SolrJSONWriter(writer);
+ jsonw.setIndent(false);
+ Consumer<SolrInputDocument> consumer = doc -> {
+ try {
+ jsonw.writeObj(doc);
+ writer.append('\n');
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ };
+
+ try {
+ streamDocsWithCursorMark(coll, maxDocs, solrClient, consumer);
+ } finally {
+ solrClient.close();
+ fos.close();
+ }
+ }
+
+ public static void writeJavabinDocs(String baseurl, String coll, String file, long maxDocs) throws IOException, SolrServerException {
+ SolrClient solrClient = new CloudSolrClient.Builder(Collections.singletonList(baseurl)).build();
+ FileOutputStream fos = new FileOutputStream(file);
+ JavaBinCodec codec = new JavaBinCodec(fos, null);
+ codec.writeTag(JavaBinCodec.NAMED_LST, 2);
+ codec.writeStr("params");
+ codec.writeNamedList(new NamedList<>());
+ codec.writeStr("docs");
+ codec.writeTag(JavaBinCodec.ITERATOR);
+ long[] docsWritten = new long[1];
+ docsWritten[0] = 0;
+ Consumer<SolrInputDocument> consumer = d -> {
+ try {
+ codec.writeSolrInputDocument(d);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ };
+ try {
+ streamDocsWithCursorMark(coll, maxDocs, solrClient, consumer);
+ } finally {
+ codec.writeTag(JavaBinCodec.END);
+ solrClient.close();
+ codec.close();
+ fos.close();
+ }
+ }
+
+ private static void streamDocsWithCursorMark(String coll, long maxDocs, SolrClient solrClient,
+ Consumer<SolrInputDocument> consumer) throws SolrServerException, IOException {
+ long[] docsWritten = new long[]{0};
+ NamedList<Object> rsp1 = solrClient.request(new GenericSolrRequest(SolrRequest.METHOD.GET, "/schema/uniquekey",
+ new MapSolrParams(Collections.singletonMap("collection", coll))));
+ String uniqueKey = (String) rsp1.get("uniqueKey");
+ NamedList<Object> rsp;
+ SolrQuery q = (new SolrQuery("*:*"))
+ .setParam("collection", coll)
+ .setRows(100)
+ .setSort(SolrQuery.SortClause.asc(uniqueKey));
+
+ String cursorMark = CursorMarkParams.CURSOR_MARK_START;
+ boolean done = false;
+ while (!done) {
+ if (docsWritten[0] >= maxDocs) break;
+ QueryRequest request = new QueryRequest(q);
+
+ request.setResponseParser(new StreamingBinaryResponseParser(new StreamingResponseCallback() {
+ @Override
+ public void streamSolrDocument(SolrDocument doc) {
+ SolrInputDocument document = new SolrInputDocument();
+ doc.forEach((s, o) -> {
+ if (s.equals("_version_")) return;
+ if (o instanceof List) {
+ if(((List) o).size() ==1) o = ((List) o).get(0);
+ }
+ document.addField(s, o);
+ });
+
+ consumer.accept(document);
+ docsWritten[0]++;
+ }
+
+ @Override
+ public void streamDocListInfo(long numFound, long start, Float maxScore) {
+
+ }
+ }));
+ q.set(CursorMarkParams.CURSOR_MARK_PARAM, cursorMark);
+ rsp = solrClient.request(request);
+ String nextCursorMark = (String) rsp.get(CursorMarkParams.CURSOR_MARK_NEXT);
+ if (nextCursorMark == null || Objects.equals(cursorMark, nextCursorMark)) {
+ break;
+ }
+ cursorMark = nextCursorMark;
+ }
+ }
+
+ private static final Option[] OPTIONS = {
+ OptionBuilder
+ .withArgName("#")
+ .hasArg()
+ .isRequired(true)
+ .withDescription("Address of the collection, example http://localhost:8983/solr/gettingstarted")
+ .create("url"),
+ OptionBuilder
+ .withArgName("#")
+ .hasArg()
+ .isRequired(false)
+ .withDescription("file name . defaults to collection-name.<format>")
+ .create("out"),
+ OptionBuilder
+ .withArgName("#")
+ .hasArg()
+ .isRequired(false)
+ .withDescription("format either json or javabin, default to javabin. file extension would be .javabin")
+ .create("format"),
+ OptionBuilder
+ .withArgName("#")
+ .hasArg()
+ .isRequired(false)
+ .withDescription("Max number of docs to download. by default it goes on till all docs are downloaded")
+ .create("limit"),
+ };
+}
diff --git a/solr/core/src/java/org/apache/solr/util/SolrCLI.java b/solr/core/src/java/org/apache/solr/util/SolrCLI.java
index d016592..fea69cd 100755
--- a/solr/core/src/java/org/apache/solr/util/SolrCLI.java
+++ b/solr/core/src/java/org/apache/solr/util/SolrCLI.java
@@ -417,6 +417,8 @@ public class SolrCLI implements CLIO {
return new AuthTool();
else if ("autoscaling".equals(toolType))
return new AutoscalingTool();
+ else if ("export".equals(toolType))
+ return new ExportTool();
// If you add a built-in tool to this class, add it here to avoid
// classpath scanning
@@ -448,6 +450,7 @@ public class SolrCLI implements CLIO {
formatter.printHelp("cp", getToolOptions(new ZkCpTool()));
formatter.printHelp("mv", getToolOptions(new ZkMvTool()));
formatter.printHelp("ls", getToolOptions(new ZkLsTool()));
+ formatter.printHelp("export", getToolOptions(new ExportTool()));
List<Class<Tool>> toolClasses = findToolClassesInPackage("org.apache.solr.util");
for (Class<Tool> next : toolClasses) {