Ok, I didn't realize that. I'll dig into it. Joel Bernstein http://joelsolr.blogspot.com/
On Fri, May 6, 2016 at 1:40 AM, Chris Hostetter <[email protected]> wrote: > > Joel: adding /graph to the list of ImplicitPlugins has broken > MinimalSchemaTest.testAllConfiguredHandlers (see recent jenkins failures) > > > > : Date: Thu, 5 May 2016 20:29:33 +0000 (UTC) > : From: [email protected] > : Reply-To: [email protected] > : To: [email protected] > : Subject: lucene-solr:master: SOLR-8972: Add GraphHandler and > : GraphMLResponseWriter to support graph visualizations > : > : Repository: lucene-solr > : Updated Branches: > : refs/heads/master 7d4f38738 -> be1cb9a1c > : > : > : SOLR-8972: Add GraphHandler and GraphMLResponseWriter to support graph > visualizations > : > : > : Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo > : Commit: > http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/be1cb9a1 > : Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/be1cb9a1 > : Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/be1cb9a1 > : > : Branch: refs/heads/master > : Commit: be1cb9a1cde4dd426305f22620734d018f21dd82 > : Parents: 7d4f387 > : Author: jbernste <[email protected]> > : Authored: Thu May 5 14:27:05 2016 -0400 > : Committer: jbernste <[email protected]> > : Committed: Thu May 5 16:36:19 2016 -0400 > : > : ---------------------------------------------------------------------- > : .../src/java/org/apache/solr/core/SolrCore.java | 1 + > : .../org/apache/solr/handler/GraphHandler.java | 282 > +++++++++++++++++++ > : .../solr/response/GraphMLResponseWriter.java | 167 +++++++++++ > : solr/core/src/resources/ImplicitPlugins.json | 7 + > : .../response/TestGraphMLResponseWriter.java | 155 ++++++++++ > : .../solrj/io/graph/GraphExpressionTest.java | 173 +++++++++--- > : .../solrj/io/stream/StreamExpressionTest.java | 1 - > : 7 files changed, 743 insertions(+), 43 deletions(-) > : ---------------------------------------------------------------------- > : > : > : > http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/be1cb9a1/solr/core/src/java/org/apache/solr/core/SolrCore.java > : ---------------------------------------------------------------------- > : diff --git a/solr/core/src/java/org/apache/solr/core/SolrCore.java > b/solr/core/src/java/org/apache/solr/core/SolrCore.java > : index b94b3d8..d5cde16 100644 > : --- a/solr/core/src/java/org/apache/solr/core/SolrCore.java > : +++ b/solr/core/src/java/org/apache/solr/core/SolrCore.java > : @@ -2111,6 +2111,7 @@ public final class SolrCore implements > SolrInfoMBean, Closeable { > : m.put("standard", m.get("xml")); > : m.put(CommonParams.JSON, new JSONResponseWriter()); > : m.put("geojson", new GeoJSONResponseWriter()); > : + m.put("graphml", new GraphMLResponseWriter()); > : m.put("python", new PythonResponseWriter()); > : m.put("php", new PHPResponseWriter()); > : m.put("phps", new PHPSerializedResponseWriter()); > : > : > http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/be1cb9a1/solr/core/src/java/org/apache/solr/handler/GraphHandler.java > : ---------------------------------------------------------------------- > : diff --git > a/solr/core/src/java/org/apache/solr/handler/GraphHandler.java > b/solr/core/src/java/org/apache/solr/handler/GraphHandler.java > : new file mode 100644 > : index 0000000..a6e2ce1 > : --- /dev/null > : +++ b/solr/core/src/java/org/apache/solr/handler/GraphHandler.java > : @@ -0,0 +1,282 @@ > : +package org.apache.solr.handler; > : + > : +/* > : + * 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. > : + */ > : + > : +import java.io.IOException; > : +import java.lang.invoke.MethodHandles; > : +import java.util.HashMap; > : +import java.util.List; > : +import java.util.Map; > : +import java.util.Map.Entry; > : + > : +import org.apache.solr.client.solrj.io.SolrClientCache; > : +import org.apache.solr.client.solrj.io.Tuple; > : +import org.apache.solr.client.solrj.io.comp.StreamComparator; > : +import org.apache.solr.client.solrj.io.graph.GatherNodesStream; > : +import org.apache.solr.client.solrj.io.graph.ShortestPathStream; > : +import org.apache.solr.client.solrj.io.graph.Traversal; > : +import org.apache.solr.client.solrj.io.ops.ConcatOperation; > : +import org.apache.solr.client.solrj.io.ops.DistinctOperation; > : +import org.apache.solr.client.solrj.io.ops.GroupOperation; > : +import org.apache.solr.client.solrj.io.ops.ReplaceOperation; > : +import org.apache.solr.client.solrj.io.stream.*; > : +import org.apache.solr.client.solrj.io.stream.expr.Explanation; > : +import org.apache.solr.client.solrj.io.stream.expr.Expressible; > : +import org.apache.solr.client.solrj.io.stream.expr.StreamFactory; > : +import org.apache.solr.client.solrj.io.stream.metrics.CountMetric; > : +import org.apache.solr.client.solrj.io.stream.metrics.MaxMetric; > : +import org.apache.solr.client.solrj.io.stream.metrics.MeanMetric; > : +import org.apache.solr.client.solrj.io.stream.metrics.MinMetric; > : +import org.apache.solr.client.solrj.io.stream.metrics.SumMetric; > : +import org.apache.solr.common.SolrException; > : +import org.apache.solr.common.params.CommonParams; > : +import org.apache.solr.common.params.ModifiableSolrParams; > : +import org.apache.solr.common.params.SolrParams; > : +import org.apache.solr.common.util.NamedList; > : +import org.apache.solr.core.CloseHook; > : +import org.apache.solr.core.CoreContainer; > : +import org.apache.solr.core.SolrCore; > : +import org.apache.solr.request.SolrQueryRequest; > : +import org.apache.solr.response.SolrQueryResponse; > : +import org.apache.solr.security.AuthorizationContext; > : +import org.apache.solr.security.PermissionNameProvider; > : +import org.apache.solr.util.plugin.SolrCoreAware; > : +import org.slf4j.Logger; > : +import org.slf4j.LoggerFactory; > : + > : +public class GraphHandler extends RequestHandlerBase implements > SolrCoreAware, PermissionNameProvider { > : + > : + private StreamFactory streamFactory = new StreamFactory(); > : + private static final Logger logger = > LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); > : + private String coreName; > : + > : + @Override > : + public PermissionNameProvider.Name > getPermissionName(AuthorizationContext request) { > : + return PermissionNameProvider.Name.READ_PERM; > : + } > : + > : + public void inform(SolrCore core) { > : + > : + /* The stream factory will always contain the zkUrl for the given > collection > : + * Adds default streams with their corresponding function names. > These > : + * defaults can be overridden or added to in the solrConfig in the > stream > : + * RequestHandler def. Example config override > : + * <lst name="streamFunctions"> > : + * <str > name="group">org.apache.solr.client.solrj.io.stream.ReducerStream</str> > : + * <str > name="count">org.apache.solr.client.solrj.io.stream.RecordCountStream</str> > : + * </lst> > : + * */ > : + > : + String defaultCollection = null; > : + String defaultZkhost = null; > : + CoreContainer coreContainer = > core.getCoreDescriptor().getCoreContainer(); > : + this.coreName = core.getName(); > : + > : + if(coreContainer.isZooKeeperAware()) { > : + defaultCollection = core.getCoreDescriptor().getCollectionName(); > : + defaultZkhost = > core.getCoreDescriptor().getCoreContainer().getZkController().getZkServerAddress(); > : + streamFactory.withCollectionZkHost(defaultCollection, > defaultZkhost); > : + streamFactory.withDefaultZkHost(defaultZkhost); > : + } > : + > : + streamFactory > : + // streams > : + .withFunctionName("search", CloudSolrStream.class) > : + .withFunctionName("merge", MergeStream.class) > : + .withFunctionName("unique", UniqueStream.class) > : + .withFunctionName("top", RankStream.class) > : + .withFunctionName("group", GroupOperation.class) > : + .withFunctionName("reduce", ReducerStream.class) > : + .withFunctionName("parallel", ParallelStream.class) > : + .withFunctionName("rollup", RollupStream.class) > : + .withFunctionName("stats", StatsStream.class) > : + .withFunctionName("innerJoin", InnerJoinStream.class) > : + .withFunctionName("leftOuterJoin", LeftOuterJoinStream.class) > : + .withFunctionName("hashJoin", HashJoinStream.class) > : + .withFunctionName("outerHashJoin", OuterHashJoinStream.class) > : + .withFunctionName("facet", FacetStream.class) > : + .withFunctionName("update", UpdateStream.class) > : + .withFunctionName("jdbc", JDBCStream.class) > : + .withFunctionName("intersect", IntersectStream.class) > : + .withFunctionName("complement", ComplementStream.class) > : + .withFunctionName("daemon", DaemonStream.class) > : + .withFunctionName("topic", TopicStream.class) > : + .withFunctionName("shortestPath", ShortestPathStream.class) > : + .withFunctionName("gatherNodes", GatherNodesStream.class) > : + .withFunctionName("sort", SortStream.class) > : + > : + > : + // metrics > : + .withFunctionName("min", MinMetric.class) > : + .withFunctionName("max", MaxMetric.class) > : + .withFunctionName("avg", MeanMetric.class) > : + .withFunctionName("sum", SumMetric.class) > : + .withFunctionName("count", CountMetric.class) > : + > : + // tuple manipulation operations > : + .withFunctionName("replace", ReplaceOperation.class) > : + .withFunctionName("concat", ConcatOperation.class) > : + > : + // stream reduction operations > : + .withFunctionName("group", GroupOperation.class) > : + .withFunctionName("distinct", DistinctOperation.class); > : + > : + // This pulls all the overrides and additions from the config > : + Object functionMappingsObj = initArgs.get("streamFunctions"); > : + if(null != functionMappingsObj){ > : + NamedList<?> functionMappings = (NamedList<?>)functionMappingsObj; > : + for(Entry<String,?> functionMapping : functionMappings){ > : + Class<?> clazz = > core.getResourceLoader().findClass((String)functionMapping.getValue(), > Expressible.class); > : + streamFactory.withFunctionName(functionMapping.getKey(), clazz); > : + } > : + } > : + } > : + > : + public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse > rsp) throws Exception { > : + SolrParams params = req.getParams(); > : + params = adjustParams(params); > : + req.setParams(params); > : + > : + > : + TupleStream tupleStream = null; > : + > : + try { > : + tupleStream = > this.streamFactory.constructStream(params.get("expr")); > : + } catch (Exception e) { > : + //Catch exceptions that occur while the stream is being created. > This will include streaming expression parse rules. > : + SolrException.log(logger, e); > : + Map requestContext = req.getContext(); > : + requestContext.put("stream", new DummyErrorStream(e)); > : + return; > : + } > : + > : + StreamContext context = new StreamContext(); > : + context.setSolrClientCache(StreamHandler.clientCache); > : + context.put("core", this.coreName); > : + Traversal traversal = new Traversal(); > : + context.put("traversal", traversal); > : + tupleStream.setStreamContext(context); > : + Map requestContext = req.getContext(); > : + requestContext.put("stream", new TimerStream(new > ExceptionStream(tupleStream))); > : + requestContext.put("traversal", traversal); > : + } > : + > : + public String getDescription() { > : + return "StreamHandler"; > : + } > : + > : + public String getSource() { > : + return null; > : + } > : + > : + > : + public static class DummyErrorStream extends TupleStream { > : + private Exception e; > : + > : + public DummyErrorStream(Exception e) { > : + this.e = e; > : + } > : + public StreamComparator getStreamSort() { > : + return null; > : + } > : + > : + public void close() { > : + } > : + > : + public void open() { > : + } > : + > : + public Exception getException() { > : + return this.e; > : + } > : + > : + public void setStreamContext(StreamContext context) { > : + } > : + > : + public List<TupleStream> children() { > : + return null; > : + } > : + > : + @Override > : + public Explanation toExplanation(StreamFactory factory) throws > IOException { > : + return null; > : + } > : + > : + public Tuple read() { > : + String msg = e.getMessage(); > : + Map m = new HashMap(); > : + m.put("EOF", true); > : + m.put("EXCEPTION", msg); > : + return new Tuple(m); > : + } > : + } > : + > : + > : + private SolrParams adjustParams(SolrParams params) { > : + ModifiableSolrParams adjustedParams = new ModifiableSolrParams(); > : + adjustedParams.add(params); > : + adjustedParams.add(CommonParams.OMIT_HEADER, "true"); > : + return adjustedParams; > : + } > : + > : + public static class TimerStream extends TupleStream { > : + > : + private long begin; > : + private TupleStream tupleStream; > : + > : + public TimerStream(TupleStream tupleStream) { > : + this.tupleStream = tupleStream; > : + } > : + > : + public StreamComparator getStreamSort() { > : + return this.tupleStream.getStreamSort(); > : + } > : + > : + public void close() throws IOException { > : + this.tupleStream.close(); > : + } > : + > : + public void open() throws IOException { > : + this.begin = System.nanoTime(); > : + this.tupleStream.open(); > : + } > : + > : + public void setStreamContext(StreamContext context) { > : + this.tupleStream.setStreamContext(context); > : + } > : + > : + public List<TupleStream> children() { > : + return this.tupleStream.children(); > : + } > : + > : + @Override > : + public Explanation toExplanation(StreamFactory factory) throws > IOException { > : + return null; > : + } > : + > : + public Tuple read() throws IOException { > : + Tuple tuple = this.tupleStream.read(); > : + if(tuple.EOF) { > : + long totalTime = (System.nanoTime() - begin) / 1000000; > : + tuple.fields.put("RESPONSE_TIME", totalTime); > : + } > : + return tuple; > : + } > : + } > : + > : +} > : \ No newline at end of file > : > : > http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/be1cb9a1/solr/core/src/java/org/apache/solr/response/GraphMLResponseWriter.java > : ---------------------------------------------------------------------- > : diff --git > a/solr/core/src/java/org/apache/solr/response/GraphMLResponseWriter.java > b/solr/core/src/java/org/apache/solr/response/GraphMLResponseWriter.java > : new file mode 100644 > : index 0000000..d941991 > : --- /dev/null > : +++ > b/solr/core/src/java/org/apache/solr/response/GraphMLResponseWriter.java > : @@ -0,0 +1,167 @@ > : +package org.apache.solr.response; > : + > : +/* > : + * 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. > : + */ > : + > : +import java.io.IOException; > : +import java.io.PrintWriter; > : +import java.io.Writer; > : +import java.lang.invoke.MethodHandles; > : +import java.util.Iterator; > : +import java.util.List; > : +import java.util.ArrayList; > : + > : +import org.apache.solr.client.solrj.io.graph.Traversal; > : +import org.apache.solr.client.solrj.io.stream.TupleStream; > : +import org.apache.solr.client.solrj.io.Tuple; > : +import org.apache.solr.common.util.NamedList; > : +import org.apache.solr.handler.GraphHandler; > : +import org.apache.solr.request.SolrQueryRequest; > : +import org.slf4j.Logger; > : +import org.slf4j.LoggerFactory; > : + > : + > : +public class GraphMLResponseWriter implements QueryResponseWriter { > : + > : + private static final Logger logger = > LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); > : + > : + public void init(NamedList args) { > : + /* NOOP */ > : + } > : + > : + public String getContentType(SolrQueryRequest req, SolrQueryResponse > res) { > : + return "application/xml"; > : + } > : + > : + public void write(Writer writer, SolrQueryRequest req, > SolrQueryResponse res) throws IOException { > : + > : + Exception e1 = res.getException(); > : + if(e1 != null) { > : + e1.printStackTrace(new PrintWriter(writer)); > : + return; > : + } > : + > : + TupleStream stream = (TupleStream)req.getContext().get("stream"); > : + > : + if(stream instanceof GraphHandler.DummyErrorStream) { > : + GraphHandler.DummyErrorStream d = > (GraphHandler.DummyErrorStream)stream; > : + Exception e = d.getException(); > : + e.printStackTrace(new PrintWriter(writer)); > : + return; > : + } > : + > : + > : + Traversal traversal = (Traversal)req.getContext().get("traversal"); > : + PrintWriter printWriter = new PrintWriter(writer); > : + > : + try { > : + > : + stream.open(); > : + > : + Tuple tuple = null; > : + > : + int edgeCount = 0; > : + > : + printWriter.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); > : + printWriter.println("<graphml xmlns=\" > http://graphml.graphdrawing.org/xmlns\" "); > : + printWriter.println("xmlns:xsi=\" > http://www.w3.org/2001/XMLSchema-instance\" "); > : + printWriter.print("xsi:schemaLocation=\" > http://graphml.graphdrawing.org/xmlns "); > : + printWriter.println(" > http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd\">"); > : + > : + printWriter.println("<graph id=\"G\" edgedefault=\"directed\">"); > : + > : + while (true) { > : + //Output the graph > : + tuple = stream.read(); > : + if (tuple.EOF) { > : + break; > : + } > : + > : + String id = tuple.getString("node"); > : + > : + if (traversal.isMultiCollection()) { > : + id = tuple.getString("collection") + "." + id; > : + } > : + > : + writer.write("<node id=\""+replace(id)+"\""); > : + > : + List<String> outfields = new ArrayList(); > : + Iterator<String> keys = tuple.fields.keySet().iterator(); > : + while(keys.hasNext()) { > : + String key = keys.next(); > : + if(key.equals("node") || key.equals("ancestors") || > key.equals("collection")) { > : + continue; > : + } else { > : + outfields.add(key); > : + } > : + } > : + > : + if (outfields.size() > 0) { > : + printWriter.println(">"); > : + for (String nodeAttribute : outfields) { > : + Object o = tuple.get(nodeAttribute); > : + if (o != null) { > : + printWriter.println("<data key=\""+nodeAttribute+"\">" + > o.toString() + "</data>"); > : + } > : + } > : + printWriter.println("</node>"); > : + } else { > : + printWriter.println("/>"); > : + } > : + > : + List<String> ancestors = tuple.getStrings("ancestors"); > : + > : + if(ancestors != null) { > : + for (String ancestor : ancestors) { > : + ++edgeCount; > : + writer.write("<edge id=\"" + edgeCount + "\" "); > : + writer.write(" source=\"" + replace(ancestor) + "\" "); > : + printWriter.println(" target=\"" + replace(id) + "\"/>"); > : + } > : + } > : + } > : + > : + writer.write("</graph></graphml>"); > : + } finally { > : + stream.close(); > : + } > : + } > : + > : + private String replace(String s) { > : + if(s.indexOf(">") > -1) { > : + s = s.replace(">", ">"); > : + } > : + > : + if(s.indexOf("<") > -1) { > : + s = s.replace("<", "<"); > : + } > : + > : + if(s.indexOf("\"")> -1) { > : + s = s.replace("\"", """); > : + } > : + > : + if(s.indexOf("'") > -1) { > : + s = s.replace("'", "'"); > : + } > : + > : + if(s.indexOf("&") > -1) { > : + s = s.replace("&", "&"); > : + } > : + > : + return s; > : + } > : +} > : \ No newline at end of file > : > : > http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/be1cb9a1/solr/core/src/resources/ImplicitPlugins.json > : ---------------------------------------------------------------------- > : diff --git a/solr/core/src/resources/ImplicitPlugins.json > b/solr/core/src/resources/ImplicitPlugins.json > : index 8c0549a..325bf91 100644 > : --- a/solr/core/src/resources/ImplicitPlugins.json > : +++ b/solr/core/src/resources/ImplicitPlugins.json > : @@ -84,6 +84,13 @@ > : "distrib": false > : } > : }, > : + "/graph": { > : + "class": "solr.GraphHandler", > : + "invariants": { > : + "wt": "graphml", > : + "distrib": false > : + } > : + }, > : "/stream": { > : "class": "solr.StreamHandler", > : "invariants": { > : > : > http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/be1cb9a1/solr/core/src/test/org/apache/solr/response/TestGraphMLResponseWriter.java > : ---------------------------------------------------------------------- > : diff --git > a/solr/core/src/test/org/apache/solr/response/TestGraphMLResponseWriter.java > b/solr/core/src/test/org/apache/solr/response/TestGraphMLResponseWriter.java > : new file mode 100644 > : index 0000000..283f64d > : --- /dev/null > : +++ > b/solr/core/src/test/org/apache/solr/response/TestGraphMLResponseWriter.java > : @@ -0,0 +1,155 @@ > : +package org.apache.solr.response; > : + > : +/* > : + * 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. > : + */ > : + > : +import java.io.StringWriter; > : +import java.util.Map; > : +import java.util.HashMap; > : +import java.util.List; > : +import java.util.ArrayList; > : +import java.util.Iterator; > : + > : +import org.apache.solr.SolrTestCaseJ4; > : +import org.apache.solr.client.solrj.io.comp.StreamComparator; > : +import org.apache.solr.client.solrj.io.graph.Traversal; > : +import org.apache.solr.client.solrj.io.stream.TupleStream; > : +import org.apache.solr.client.solrj.io.stream.StreamContext; > : +import org.apache.solr.client.solrj.io.stream.expr.StreamFactory; > : +import org.apache.solr.client.solrj.io.Tuple; > : +import org.apache.solr.client.solrj.io.stream.expr.Explanation; > : +import org.apache.solr.request.SolrQueryRequest; > : +import org.junit.BeforeClass; > : +import org.junit.Test; > : + > : +public class TestGraphMLResponseWriter extends SolrTestCaseJ4 { > : + @BeforeClass > : + public static void beforeClass() throws Exception { > : + System.setProperty("enable.update.log", "false"); // schema12 > doesn't support _version_ > : + initCore("solrconfig.xml","schema12.xml"); > : + } > : + > : + @Test > : + public void testGraphMLOutput() throws Exception { > : + SolrQueryRequest request = req("blah", "blah"); // Just need a > request to attach the stream and traversal to. > : + SolrQueryResponse response = new SolrQueryResponse(); > : + Map context = request.getContext(); > : + TupleStream stream = new TestStream(); //Simulates a > GatherNodesStream > : + Traversal traversal = new Traversal(); > : + context.put("traversal", traversal); > : + context.put("stream", stream); > : + StringWriter writer = new StringWriter(); > : + > : + GraphMLResponseWriter graphMLResponseWriter = new > GraphMLResponseWriter(); > : + graphMLResponseWriter.write(writer, request, response); > : + String graphML = writer.toString(); > : + > : + //Validate the nodes > : + String error = h.validateXPath(graphML, > : + "//graph/node[1][@id ='bill']", > : + "//graph/node[2][@id ='jim']", > : + "//graph/node[3][@id ='max']"); > : + if(error != null) { > : + throw new Exception(error); > : + } > : + //Validate the edges > : + error = h.validateXPath(graphML, > : + "//graph/edge[1][@source ='jim']", > : + "//graph/edge[1][@target ='bill']", > : + "//graph/edge[2][@source ='max']", > : + "//graph/edge[2][@target ='bill']", > : + "//graph/edge[3][@source ='max']", > : + "//graph/edge[3][@target ='jim']", > : + "//graph/edge[4][@source ='jim']", > : + "//graph/edge[4][@target ='max']" > : + ); > : + > : + if(error != null) { > : + throw new Exception(error); > : + } > : + > : + } > : + > : + private class TestStream extends TupleStream { > : + > : + private Iterator<Tuple> tuples; > : + > : + public TestStream() { > : + //Create some nodes > : + List<Tuple> testTuples = new ArrayList(); > : + Map m1 = new HashMap(); > : + > : + List<String> an1 = new ArrayList(); > : + an1.add("jim"); > : + an1.add("max"); > : + m1.put("node", "bill"); > : + m1.put("ancestors", an1); > : + testTuples.add(new Tuple(m1)); > : + > : + Map m2 = new HashMap(); > : + List<String> an2 = new ArrayList(); > : + an2.add("max"); > : + m2.put("node", "jim"); > : + m2.put("ancestors", an2); > : + testTuples.add(new Tuple(m2)); > : + > : + Map m3 = new HashMap(); > : + List<String> an3 = new ArrayList(); > : + an3.add("jim"); > : + m3.put("node", "max"); > : + m3.put("ancestors", an3); > : + testTuples.add(new Tuple(m3)); > : + > : + tuples = testTuples.iterator(); > : + } > : + > : + public StreamComparator getStreamSort() { > : + return null; > : + } > : + > : + public void close() { > : + > : + } > : + > : + public void open() { > : + > : + } > : + > : + public List<TupleStream> children() { > : + return null; > : + } > : + > : + public Tuple read() { > : + if(tuples.hasNext()) { > : + return tuples.next(); > : + } else { > : + Map map = new HashMap(); > : + map.put("EOF", true); > : + return new Tuple(map); > : + } > : + } > : + > : + public void setStreamContext(StreamContext streamContext) { > : + > : + } > : + > : + public Explanation toExplanation(StreamFactory factory) { > : + return null; > : + } > : + > : + } > : +} > : > : > http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/be1cb9a1/solr/solrj/src/test/org/apache/solr/client/solrj/io/graph/GraphExpressionTest.java > : ---------------------------------------------------------------------- > : diff --git > a/solr/solrj/src/test/org/apache/solr/client/solrj/io/graph/GraphExpressionTest.java > b/solr/solrj/src/test/org/apache/solr/client/solrj/io/graph/GraphExpressionTest.java > : index 53f7126..c429fe8 100644 > : --- > a/solr/solrj/src/test/org/apache/solr/client/solrj/io/graph/GraphExpressionTest.java > : +++ > b/solr/solrj/src/test/org/apache/solr/client/solrj/io/graph/GraphExpressionTest.java > : @@ -18,6 +18,8 @@ package org.apache.solr.client.solrj.io.graph; > : */ > : > : import java.io.IOException; > : +import java.io.InputStreamReader; > : +import java.io.InputStream; > : import java.util.ArrayList; > : import java.util.Collections; > : import java.util.HashMap; > : @@ -28,6 +30,10 @@ import java.util.Set; > : > : import org.apache.lucene.util.LuceneTestCase; > : import org.apache.lucene.util.LuceneTestCase.Slow; > : +import org.apache.solr.client.solrj.SolrRequest; > : +import org.apache.solr.client.solrj.embedded.JettySolrRunner; > : +import org.apache.solr.client.solrj.impl.HttpSolrClient; > : +import org.apache.solr.client.solrj.impl.InputStreamResponseParser; > : import org.apache.solr.client.solrj.io.SolrClientCache; > : import org.apache.solr.client.solrj.io.Tuple; > : import org.apache.solr.client.solrj.io.comp.ComparatorOrder; > : @@ -43,9 +49,12 @@ import > org.apache.solr.client.solrj.io.stream.metrics.MeanMetric; > : import org.apache.solr.client.solrj.io.stream.metrics.MinMetric; > : import org.apache.solr.client.solrj.io.stream.metrics.SumMetric; > : import org.apache.solr.client.solrj.request.CollectionAdminRequest; > : +import org.apache.solr.client.solrj.request.QueryRequest; > : import org.apache.solr.client.solrj.request.UpdateRequest; > : import org.apache.solr.cloud.AbstractDistribZkTestBase; > : import org.apache.solr.cloud.SolrCloudTestCase; > : +import org.apache.solr.common.params.ModifiableSolrParams; > : +import org.apache.solr.common.util.NamedList; > : import org.junit.Before; > : import org.junit.BeforeClass; > : import org.junit.Test; > : @@ -266,8 +275,8 @@ public class GraphExpressionTest extends > SolrCloudTestCase { > : .withFunctionName("max", MaxMetric.class); > : > : String expr = "gatherNodes(collection1, " + > : - "walk=\"product1->product_s\"," + > : - "gather=\"basket_s\")"; > : + "walk=\"product1->product_s\"," + > : + "gather=\"basket_s\")"; > : > : stream = (GatherNodesStream)factory.constructStream(expr); > : stream.setStreamContext(context); > : @@ -284,9 +293,9 @@ public class GraphExpressionTest extends > SolrCloudTestCase { > : > : //Test maxDocFreq param > : String docFreqExpr = "gatherNodes(collection1, " + > : - "walk=\"product1, product7->product_s\"," + > : - "maxDocFreq=\"2\","+ > : - "gather=\"basket_s\")"; > : + "walk=\"product1, product7->product_s\"," + > : + "maxDocFreq=\"2\","+ > : + "gather=\"basket_s\")"; > : > : stream = (GatherNodesStream)factory.constructStream(docFreqExpr); > : stream.setStreamContext(context); > : @@ -299,9 +308,9 @@ public class GraphExpressionTest extends > SolrCloudTestCase { > : > : > : String expr2 = "gatherNodes(collection1, " + > : - expr+","+ > : - "walk=\"node->basket_s\"," + > : - "gather=\"product_s\", count(*), > avg(price_f), sum(price_f), min(price_f), max(price_f))"; > : + expr+","+ > : + "walk=\"node->basket_s\"," + > : + "gather=\"product_s\", count(*), avg(price_f), sum(price_f), > min(price_f), max(price_f))"; > : > : stream = (GatherNodesStream)factory.constructStream(expr2); > : > : @@ -338,8 +347,8 @@ public class GraphExpressionTest extends > SolrCloudTestCase { > : > : //Test list of root nodes > : expr = "gatherNodes(collection1, " + > : - "walk=\"product4, product7->product_s\"," + > : - "gather=\"basket_s\")"; > : + "walk=\"product4, product7->product_s\"," + > : + "gather=\"basket_s\")"; > : > : stream = (GatherNodesStream)factory.constructStream(expr); > : > : @@ -356,8 +365,8 @@ public class GraphExpressionTest extends > SolrCloudTestCase { > : //Test with negative filter query > : > : expr = "gatherNodes(collection1, " + > : - "walk=\"product4, product7->product_s\"," + > : - "gather=\"basket_s\", > fq=\"-basket_s:basket4\")"; > : + "walk=\"product4, product7->product_s\"," + > : + "gather=\"basket_s\", fq=\"-basket_s:basket4\")"; > : > : stream = (GatherNodesStream)factory.constructStream(expr); > : > : @@ -406,8 +415,8 @@ public class GraphExpressionTest extends > SolrCloudTestCase { > : .withFunctionName("max", MaxMetric.class); > : > : String expr = "gatherNodes(collection1, " + > : - "walk=\"bill->from_s\"," + > : - "gather=\"to_s\")"; > : + "walk=\"bill->from_s\"," + > : + "gather=\"to_s\")"; > : > : stream = (GatherNodesStream)factory.constructStream(expr); > : stream.setStreamContext(context); > : @@ -423,9 +432,9 @@ public class GraphExpressionTest extends > SolrCloudTestCase { > : //Test scatter branches, leaves and trackTraversal > : > : expr = "gatherNodes(collection1, " + > : - "walk=\"bill->from_s\"," + > : - "gather=\"to_s\","+ > : - "scatter=\"branches, leaves\", trackTraversal=\"true\")"; > : + "walk=\"bill->from_s\"," + > : + "gather=\"to_s\","+ > : + "scatter=\"branches, leaves\", trackTraversal=\"true\")"; > : > : stream = (GatherNodesStream)factory.constructStream(expr); > : context = new StreamContext(); > : @@ -461,9 +470,9 @@ public class GraphExpressionTest extends > SolrCloudTestCase { > : // Test query root > : > : expr = "gatherNodes(collection1, " + > : - "search(collection1, q=\"message_t:jim\", fl=\"from_s\", > sort=\"from_s asc\"),"+ > : - "walk=\"from_s->from_s\"," + > : - "gather=\"to_s\")"; > : + "search(collection1, q=\"message_t:jim\", fl=\"from_s\", > sort=\"from_s asc\"),"+ > : + "walk=\"from_s->from_s\"," + > : + "gather=\"to_s\")"; > : > : stream = (GatherNodesStream)factory.constructStream(expr); > : context = new StreamContext(); > : @@ -482,9 +491,9 @@ public class GraphExpressionTest extends > SolrCloudTestCase { > : // Test query root scatter branches > : > : expr = "gatherNodes(collection1, " + > : - "search(collection1, q=\"message_t:jim\", fl=\"from_s\", > sort=\"from_s asc\"),"+ > : - "walk=\"from_s->from_s\"," + > : - "gather=\"to_s\", scatter=\"branches, leaves\")"; > : + "search(collection1, q=\"message_t:jim\", fl=\"from_s\", > sort=\"from_s asc\"),"+ > : + "walk=\"from_s->from_s\"," + > : + "gather=\"to_s\", scatter=\"branches, leaves\")"; > : > : stream = (GatherNodesStream)factory.constructStream(expr); > : context = new StreamContext(); > : @@ -505,14 +514,14 @@ public class GraphExpressionTest extends > SolrCloudTestCase { > : assertTrue(tuples.get(3).getLong("level").equals(new Long(1))); > : > : expr = "gatherNodes(collection1, " + > : - "search(collection1, q=\"message_t:jim\", fl=\"from_s\", > sort=\"from_s asc\"),"+ > : - "walk=\"from_s->from_s\"," + > : - "gather=\"to_s\")"; > : + "search(collection1, q=\"message_t:jim\", fl=\"from_s\", > sort=\"from_s asc\"),"+ > : + "walk=\"from_s->from_s\"," + > : + "gather=\"to_s\")"; > : > : String expr2 = "gatherNodes(collection1, " + > : - expr+","+ > : - "walk=\"node->from_s\"," + > : - "gather=\"to_s\")"; > : + expr+","+ > : + "walk=\"node->from_s\"," + > : + "gather=\"to_s\")"; > : > : stream = (GatherNodesStream)factory.constructStream(expr2); > : context = new StreamContext(); > : @@ -548,14 +557,14 @@ public class GraphExpressionTest extends > SolrCloudTestCase { > : > : > : expr = "gatherNodes(collection1, " + > : - "search(collection1, q=\"message_t:jim\", fl=\"from_s\", > sort=\"from_s asc\"),"+ > : - "walk=\"from_s->from_s\"," + > : - "gather=\"to_s\")"; > : + "search(collection1, q=\"message_t:jim\", fl=\"from_s\", > sort=\"from_s asc\"),"+ > : + "walk=\"from_s->from_s\"," + > : + "gather=\"to_s\")"; > : > : expr2 = "gatherNodes(collection1, " + > : - expr+","+ > : - "walk=\"node->from_s\"," + > : - "gather=\"to_s\", scatter=\"branches, leaves\")"; > : + expr+","+ > : + "walk=\"node->from_s\"," + > : + "gather=\"to_s\", scatter=\"branches, leaves\")"; > : > : stream = (GatherNodesStream)factory.constructStream(expr2); > : context = new StreamContext(); > : @@ -589,14 +598,14 @@ public class GraphExpressionTest extends > SolrCloudTestCase { > : .commit(cluster.getSolrClient(), COLLECTION); > : > : expr = "gatherNodes(collection1, " + > : - "search(collection1, q=\"message_t:jim\", fl=\"from_s\", > sort=\"from_s asc\"),"+ > : - "walk=\"from_s->from_s\"," + > : - "gather=\"to_s\", trackTraversal=\"true\")"; > : + "search(collection1, q=\"message_t:jim\", fl=\"from_s\", > sort=\"from_s asc\"),"+ > : + "walk=\"from_s->from_s\"," + > : + "gather=\"to_s\", trackTraversal=\"true\")"; > : > : expr2 = "gatherNodes(collection1, " + > : - expr+","+ > : - "walk=\"node->from_s\"," + > : - "gather=\"to_s\", scatter=\"branches, leaves\", > trackTraversal=\"true\")"; > : + expr+","+ > : + "walk=\"node->from_s\"," + > : + "gather=\"to_s\", scatter=\"branches, leaves\", > trackTraversal=\"true\")"; > : > : stream = (GatherNodesStream)factory.constructStream(expr2); > : context = new StreamContext(); > : @@ -634,6 +643,84 @@ public class GraphExpressionTest extends > SolrCloudTestCase { > : > : } > : > : + @Test > : + public void testGraphHandler() throws Exception { > : + > : + > : + new UpdateRequest() > : + .add(id, "0", "from_s", "bill", "to_s", "jim", "message_t", > "Hello jim") > : + .add(id, "1", "from_s", "bill", "to_s", "sam", "message_t", > "Hello sam") > : + .add(id, "2", "from_s", "bill", "to_s", "max", "message_t", > "Hello max") > : + .add(id, "3", "from_s", "max", "to_s", "kip", "message_t", > "Hello kip") > : + .add(id, "4", "from_s", "sam", "to_s", "steve", "message_t", > "Hello steve") > : + .add(id, "5", "from_s", "jim", "to_s", "ann", "message_t", > "Hello steve") > : + .commit(cluster.getSolrClient(), COLLECTION); > : + > : + commit(); > : + > : + List<JettySolrRunner> runners = cluster.getJettySolrRunners(); > : + JettySolrRunner runner = runners.get(0); > : + String url = runner.getBaseUrl().toString(); > : + > : + HttpSolrClient client = new HttpSolrClient(url); > : + ModifiableSolrParams params = new ModifiableSolrParams(); > : + > : + > : + String expr = "sort(by=\"node asc\", gatherNodes(collection1, " + > : + "walk=\"bill->from_s\"," + > : + "trackTraversal=\"true\"," + > : + "gather=\"to_s\"))"; > : + > : + params.add("expr", expr); > : + QueryRequest query = new QueryRequest(params); > : + query.setPath("/collection1/graph"); > : + > : + query.setResponseParser(new InputStreamResponseParser("xml")); > : + query.setMethod(SolrRequest.METHOD.POST); > : + > : + NamedList<Object> genericResponse = client.request(query); > : + > : + > : + InputStream stream = (InputStream)genericResponse.get("stream"); > : + InputStreamReader reader = new InputStreamReader(stream, "UTF-8"); > : + String xml = readString(reader); > : + //Validate the nodes > : + String error = h.validateXPath(xml, > : + "//graph/node[1][@id ='jim']", > : + "//graph/node[2][@id ='max']", > : + "//graph/node[3][@id ='sam']"); > : + if(error != null) { > : + throw new Exception(error); > : + } > : + //Validate the edges > : + error = h.validateXPath(xml, > : + "//graph/edge[1][@source ='bill']", > : + "//graph/edge[1][@target ='jim']", > : + "//graph/edge[2][@source ='bill']", > : + "//graph/edge[2][@target ='max']", > : + "//graph/edge[3][@source ='bill']", > : + "//graph/edge[3][@target ='sam']"); > : + > : + if(error != null) { > : + throw new Exception(error); > : + } > : + > : + client.close(); > : + } > : + > : + private String readString(InputStreamReader reader) throws Exception{ > : + StringBuilder builder = new StringBuilder(); > : + int c = 0; > : + while((c = reader.read()) != -1) { > : + builder.append(((char)c)); > : + } > : + > : + return builder.toString(); > : + } > : + > : + > : + > : + > : protected List<Tuple> getTuples(TupleStream tupleStream) throws > IOException { > : tupleStream.open(); > : List<Tuple> tuples = new ArrayList(); > : @@ -675,7 +762,9 @@ public class GraphExpressionTest extends > SolrCloudTestCase { > : throw new Exception("Longs not equal:"+expected+" : "+actual); > : } > : > : + > : + > : return true; > : } > : > : -} > : +} > : \ No newline at end of file > : > : > http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/be1cb9a1/solr/solrj/src/test/org/apache/solr/client/solrj/io/stream/StreamExpressionTest.java > : ---------------------------------------------------------------------- > : diff --git > a/solr/solrj/src/test/org/apache/solr/client/solrj/io/stream/StreamExpressionTest.java > b/solr/solrj/src/test/org/apache/solr/client/solrj/io/stream/StreamExpressionTest.java > : index 9a0653a..f0b7b7b 100644 > : --- > a/solr/solrj/src/test/org/apache/solr/client/solrj/io/stream/StreamExpressionTest.java > : +++ > b/solr/solrj/src/test/org/apache/solr/client/solrj/io/stream/StreamExpressionTest.java > : @@ -175,7 +175,6 @@ public class StreamExpressionTest extends > SolrCloudTestCase { > : assert(tuples.size() == 3); > : assertOrder(tuples, 0, 3, 4); > : assertLong(tuples.get(1), "a_i", 3); > : - > : } > : > : @Test > : > : > > -Hoss > http://www.lucidworks.com/ > > > --------------------------------------------------------------------- > To unsubscribe, e-mail: [email protected] > For additional commands, e-mail: [email protected] > >
