You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@lucene.apache.org by Chris Hostetter <ho...@fucit.org> on 2016/05/06 05:40:17 UTC
Re: lucene-solr:master: SOLR-8972: Add GraphHandler and
GraphMLResponseWriter to support graph visualizations
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: jbernste@apache.org
: Reply-To: dev@lucene.apache.org
: To: commits@lucene.apache.org
: 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 <jb...@apache.org>
: Authored: Thu May 5 14:27:05 2016 -0400
: Committer: jbernste <jb...@apache.org>
: 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: dev-unsubscribe@lucene.apache.org
For additional commands, e-mail: dev-help@lucene.apache.org
Re: lucene-solr:master: SOLR-8972: Add GraphHandler and
GraphMLResponseWriter to support graph visualizations
Posted by Joel Bernstein <jo...@gmail.com>.
I committed a fix this sorry for the noise.
Joel Bernstein
http://joelsolr.blogspot.com/
On Fri, May 6, 2016 at 8:03 AM, Joel Bernstein <jo...@gmail.com> wrote:
> 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 <ho...@fucit.org>
> 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: jbernste@apache.org
>> : Reply-To: dev@lucene.apache.org
>> : To: commits@lucene.apache.org
>> : 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 <jb...@apache.org>
>> : Authored: Thu May 5 14:27:05 2016 -0400
>> : Committer: jbernste <jb...@apache.org>
>> : 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: dev-unsubscribe@lucene.apache.org
>> For additional commands, e-mail: dev-help@lucene.apache.org
>>
>>
>
Re: lucene-solr:master: SOLR-8972: Add GraphHandler and
GraphMLResponseWriter to support graph visualizations
Posted by Joel Bernstein <jo...@gmail.com>.
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 <ho...@fucit.org>
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: jbernste@apache.org
> : Reply-To: dev@lucene.apache.org
> : To: commits@lucene.apache.org
> : 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 <jb...@apache.org>
> : Authored: Thu May 5 14:27:05 2016 -0400
> : Committer: jbernste <jb...@apache.org>
> : 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: dev-unsubscribe@lucene.apache.org
> For additional commands, e-mail: dev-help@lucene.apache.org
>
>