From 2130924780fb4ce14f561e5c3b5fcb6fd203fdd9 Mon Sep 17 00:00:00 2001 From: Dvir Dukhan Date: Wed, 1 May 2019 15:57:26 +0300 Subject: [PATCH 01/27] created redisconf branch --- .idea/compiler.xml | 16 ++++++++++++++ .idea/libraries/Maven__junit_junit_4_12.xml | 13 ++++++++++++ ...org_apache_commons_commons_lang3_3_8_1.xml | 13 ++++++++++++ ...__org_apache_commons_commons_pool2_2_0.xml | 13 ++++++++++++ ...n__org_apache_commons_commons_text_1_6.xml | 13 ++++++++++++ .../Maven__org_hamcrest_hamcrest_core_1_3.xml | 13 ++++++++++++ .../Maven__org_slf4j_slf4j_api_1_7_22.xml | 13 ++++++++++++ .../Maven__redis_clients_jedis_3_0_1.xml | 13 ++++++++++++ .idea/misc.xml | 10 +++++++++ .idea/modules.xml | 8 +++++++ .idea/vcs.xml | 6 ++++++ jredisgraph.iml | 21 +++++++++++++++++++ 12 files changed, 152 insertions(+) create mode 100644 .idea/compiler.xml create mode 100644 .idea/libraries/Maven__junit_junit_4_12.xml create mode 100644 .idea/libraries/Maven__org_apache_commons_commons_lang3_3_8_1.xml create mode 100644 .idea/libraries/Maven__org_apache_commons_commons_pool2_2_0.xml create mode 100644 .idea/libraries/Maven__org_apache_commons_commons_text_1_6.xml create mode 100644 .idea/libraries/Maven__org_hamcrest_hamcrest_core_1_3.xml create mode 100644 .idea/libraries/Maven__org_slf4j_slf4j_api_1_7_22.xml create mode 100644 .idea/libraries/Maven__redis_clients_jedis_3_0_1.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml create mode 100644 jredisgraph.iml diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..ad84ac6 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__junit_junit_4_12.xml b/.idea/libraries/Maven__junit_junit_4_12.xml new file mode 100644 index 0000000..d411041 --- /dev/null +++ b/.idea/libraries/Maven__junit_junit_4_12.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_apache_commons_commons_lang3_3_8_1.xml b/.idea/libraries/Maven__org_apache_commons_commons_lang3_3_8_1.xml new file mode 100644 index 0000000..33b78e9 --- /dev/null +++ b/.idea/libraries/Maven__org_apache_commons_commons_lang3_3_8_1.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_apache_commons_commons_pool2_2_0.xml b/.idea/libraries/Maven__org_apache_commons_commons_pool2_2_0.xml new file mode 100644 index 0000000..1fbd5ee --- /dev/null +++ b/.idea/libraries/Maven__org_apache_commons_commons_pool2_2_0.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_apache_commons_commons_text_1_6.xml b/.idea/libraries/Maven__org_apache_commons_commons_text_1_6.xml new file mode 100644 index 0000000..5a2fa29 --- /dev/null +++ b/.idea/libraries/Maven__org_apache_commons_commons_text_1_6.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_hamcrest_hamcrest_core_1_3.xml b/.idea/libraries/Maven__org_hamcrest_hamcrest_core_1_3.xml new file mode 100644 index 0000000..f58bbc1 --- /dev/null +++ b/.idea/libraries/Maven__org_hamcrest_hamcrest_core_1_3.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_slf4j_slf4j_api_1_7_22.xml b/.idea/libraries/Maven__org_slf4j_slf4j_api_1_7_22.xml new file mode 100644 index 0000000..636f75c --- /dev/null +++ b/.idea/libraries/Maven__org_slf4j_slf4j_api_1_7_22.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__redis_clients_jedis_3_0_1.xml b/.idea/libraries/Maven__redis_clients_jedis_3_0_1.xml new file mode 100644 index 0000000..dbe4595 --- /dev/null +++ b/.idea/libraries/Maven__redis_clients_jedis_3_0_1.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..80cd069 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..78409eb --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/jredisgraph.iml b/jredisgraph.iml new file mode 100644 index 0000000..0c182a2 --- /dev/null +++ b/jredisgraph.iml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 9bb87813b7f924559f08cef91c2c0f81602f871d Mon Sep 17 00:00:00 2001 From: Dvir Dukhan Date: Wed, 1 May 2019 16:07:48 +0300 Subject: [PATCH 02/27] changed gitignore --- .idea/compiler.xml | 16 -------------- .idea/libraries/Maven__junit_junit_4_12.xml | 13 ------------ ...org_apache_commons_commons_lang3_3_8_1.xml | 13 ------------ ...__org_apache_commons_commons_pool2_2_0.xml | 13 ------------ ...n__org_apache_commons_commons_text_1_6.xml | 13 ------------ .../Maven__org_hamcrest_hamcrest_core_1_3.xml | 13 ------------ .../Maven__org_slf4j_slf4j_api_1_7_22.xml | 13 ------------ .../Maven__redis_clients_jedis_3_0_1.xml | 13 ------------ .idea/misc.xml | 10 --------- .idea/modules.xml | 8 ------- .idea/vcs.xml | 6 ------ jredisgraph.iml | 21 ------------------- 12 files changed, 152 deletions(-) delete mode 100644 .idea/compiler.xml delete mode 100644 .idea/libraries/Maven__junit_junit_4_12.xml delete mode 100644 .idea/libraries/Maven__org_apache_commons_commons_lang3_3_8_1.xml delete mode 100644 .idea/libraries/Maven__org_apache_commons_commons_pool2_2_0.xml delete mode 100644 .idea/libraries/Maven__org_apache_commons_commons_text_1_6.xml delete mode 100644 .idea/libraries/Maven__org_hamcrest_hamcrest_core_1_3.xml delete mode 100644 .idea/libraries/Maven__org_slf4j_slf4j_api_1_7_22.xml delete mode 100644 .idea/libraries/Maven__redis_clients_jedis_3_0_1.xml delete mode 100644 .idea/misc.xml delete mode 100644 .idea/modules.xml delete mode 100644 .idea/vcs.xml delete mode 100644 jredisgraph.iml diff --git a/.idea/compiler.xml b/.idea/compiler.xml deleted file mode 100644 index ad84ac6..0000000 --- a/.idea/compiler.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__junit_junit_4_12.xml b/.idea/libraries/Maven__junit_junit_4_12.xml deleted file mode 100644 index d411041..0000000 --- a/.idea/libraries/Maven__junit_junit_4_12.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_apache_commons_commons_lang3_3_8_1.xml b/.idea/libraries/Maven__org_apache_commons_commons_lang3_3_8_1.xml deleted file mode 100644 index 33b78e9..0000000 --- a/.idea/libraries/Maven__org_apache_commons_commons_lang3_3_8_1.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_apache_commons_commons_pool2_2_0.xml b/.idea/libraries/Maven__org_apache_commons_commons_pool2_2_0.xml deleted file mode 100644 index 1fbd5ee..0000000 --- a/.idea/libraries/Maven__org_apache_commons_commons_pool2_2_0.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_apache_commons_commons_text_1_6.xml b/.idea/libraries/Maven__org_apache_commons_commons_text_1_6.xml deleted file mode 100644 index 5a2fa29..0000000 --- a/.idea/libraries/Maven__org_apache_commons_commons_text_1_6.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_hamcrest_hamcrest_core_1_3.xml b/.idea/libraries/Maven__org_hamcrest_hamcrest_core_1_3.xml deleted file mode 100644 index f58bbc1..0000000 --- a/.idea/libraries/Maven__org_hamcrest_hamcrest_core_1_3.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_slf4j_slf4j_api_1_7_22.xml b/.idea/libraries/Maven__org_slf4j_slf4j_api_1_7_22.xml deleted file mode 100644 index 636f75c..0000000 --- a/.idea/libraries/Maven__org_slf4j_slf4j_api_1_7_22.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__redis_clients_jedis_3_0_1.xml b/.idea/libraries/Maven__redis_clients_jedis_3_0_1.xml deleted file mode 100644 index dbe4595..0000000 --- a/.idea/libraries/Maven__redis_clients_jedis_3_0_1.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index 80cd069..0000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index 78409eb..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 35eb1dd..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/jredisgraph.iml b/jredisgraph.iml deleted file mode 100644 index 0c182a2..0000000 --- a/jredisgraph.iml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file From cc9faee6b06b5471025cd2a7aae9b38147b72528 Mon Sep 17 00:00:00 2001 From: DvirDukhan Date: Wed, 1 May 2019 16:08:49 +0300 Subject: [PATCH 03/27] changed gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 0740f5e..0dcfd34 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,6 @@ hs_err_pid* .classpath .project /.settings/ + +#intelij +.idea \ No newline at end of file From 6cbdcc58a8e333d02e770e271958eed576d31e04 Mon Sep 17 00:00:00 2001 From: DvirDukhan Date: Mon, 6 May 2019 11:37:01 +0300 Subject: [PATCH 04/27] Added graph entities and graph entity property classes --- .../com/redislabs/redisgraph/impl/Edge.java | 92 ++++++++++++++ .../redisgraph/impl/GraphEntity.java | 120 ++++++++++++++++++ .../com/redislabs/redisgraph/impl/Node.java | 70 ++++++++++ .../redislabs/redisgraph/impl/Property.java | 113 +++++++++++++++++ 4 files changed, 395 insertions(+) create mode 100644 src/main/java/com/redislabs/redisgraph/impl/Edge.java create mode 100644 src/main/java/com/redislabs/redisgraph/impl/GraphEntity.java create mode 100644 src/main/java/com/redislabs/redisgraph/impl/Node.java create mode 100644 src/main/java/com/redislabs/redisgraph/impl/Property.java diff --git a/src/main/java/com/redislabs/redisgraph/impl/Edge.java b/src/main/java/com/redislabs/redisgraph/impl/Edge.java new file mode 100644 index 0000000..c6cb0b4 --- /dev/null +++ b/src/main/java/com/redislabs/redisgraph/impl/Edge.java @@ -0,0 +1,92 @@ +package com.redislabs.redisgraph.impl; + +import java.util.Objects; + +/** + * A class represent an edge (graph entity). In addition to the base class id and properties, an edge shows its source, + * destination and relationship type + */ +public class Edge extends GraphEntity { + + //memebers + String relationshipType; + int source; + int destination; + + + //getters & setters + + /** + * @return the edge relationship type + */ + public String getRelationshipType() { + return relationshipType; + } + + /** + * @param relationshipType - the relationship type to be set. + */ + public void setRelationshipType(String relationshipType) { + this.relationshipType = relationshipType; + } + + + /** + * @return The id of the source node + */ + public int getSource() { + return source; + } + + /** + * @param source - The id of the source node to be set + */ + public void setSource(int source) { + this.source = source; + } + + /** + * + * @return the id of the destination node + */ + public int getDestination() { + return destination; + } + + /** + * + * @param destination - The id of the destination node to be set + */ + public void setDestination(int destination) { + this.destination = destination; + } + + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Edge)) return false; + if (!super.equals(o)) return false; + Edge edge = (Edge) o; + return source == edge.source && + destination == edge.destination && + Objects.equals(relationshipType, edge.relationshipType); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), relationshipType, source, destination); + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("Edge{"); + sb.append("relationshipType='").append(relationshipType).append('\''); + sb.append(", source=").append(source); + sb.append(", destination=").append(destination); + sb.append(", id=").append(id); + sb.append(", propertyMap=").append(propertyMap); + sb.append('}'); + return sb.toString(); + } +} diff --git a/src/main/java/com/redislabs/redisgraph/impl/GraphEntity.java b/src/main/java/com/redislabs/redisgraph/impl/GraphEntity.java new file mode 100644 index 0000000..b040036 --- /dev/null +++ b/src/main/java/com/redislabs/redisgraph/impl/GraphEntity.java @@ -0,0 +1,120 @@ +package com.redislabs.redisgraph.impl; + + +import com.redislabs.redisgraph.ResultSet.ResultSetScalarTypes; + +import java.util.*; + + +/** + * This is an abstract class for representing a graph entity. + * A graph entity has an id and a set of properties. The properties are mapped and accessed by their names. + */ +public abstract class GraphEntity { + + + + //members + + int id; + Map propertyMap = new HashMap<>(); + + + //setters & getters + + /** + * + * @return entity id + */ + public int getId() { + return id; + } + + /** + * + * @param id - entity id to be set + */ + public void setId(int id) { + this.id = id; + } + + + /** + * Adds a property to the entity, by composing name, type and value to a property object + * @param name + * @param type + * @param value + */ + public void addProperty(String name, ResultSetScalarTypes type, Object value){ + + addProperty(new Property(name, type, value)); + + } + + /** + * Add a property to the entity + * @param property + */ + public void addProperty (Property property){ + + + propertyMap.put(property.name, property); + } + + /** + * + * @return number of properties + */ + public int getNumberOfProperties(){ + return propertyMap.size(); + } + + + /** + * + * @param propertyName - property name as lookup key (String) + * @return property object, or null if key is not found + */ + public Property getProperty(String propertyName){ + return propertyMap.get(propertyName); + } + + + /** + * + * @param name - the name of the property to be removed + */ + public void removeProperty(String name){ + + propertyMap.remove(name); + + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof GraphEntity)) return false; + GraphEntity that = (GraphEntity) o; + return id == that.id && + Objects.equals(propertyMap, that.propertyMap); + } + + @Override + public int hashCode() { + return Objects.hash(id, propertyMap); + } + + + /** + * Default toString implementation. + * @return + */ + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("GraphEntity{"); + sb.append("id=").append(id); + sb.append(", propertyMap=").append(propertyMap); + sb.append('}'); + return sb.toString(); + } +} diff --git a/src/main/java/com/redislabs/redisgraph/impl/Node.java b/src/main/java/com/redislabs/redisgraph/impl/Node.java new file mode 100644 index 0000000..738b92d --- /dev/null +++ b/src/main/java/com/redislabs/redisgraph/impl/Node.java @@ -0,0 +1,70 @@ +package com.redislabs.redisgraph.impl; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * * A class represent an node (graph entity). In addition to the base class id and properties, a node has labels. + */ +public class Node extends GraphEntity { + + //members + List labels = new ArrayList<>(); + + /** + * @param label - a label to be add + */ + public void addLabel(String label) { + labels.add(label); + } + + /** + * @param label - a label to be removed + */ + public void removeLabel(String label) { + labels.remove(label); + } + + /** + * @param index - label index + * @return the proprty label + * @throws IndexOutOfBoundsException if the index is out of range + * ({@code index < 0 || index >= getNumberOfLabels()}) + */ + public String getLabel(int index) throws IndexOutOfBoundsException{ + return labels.get(index); + } + + /** + * + * @return the number of labels + */ + public int getNumberOfLabels() { + return labels.size(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Node)) return false; + if (!super.equals(o)) return false; + Node node = (Node) o; + return Objects.equals(labels, node.labels); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), labels); + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("Node{"); + sb.append("labels=").append(labels); + sb.append(", id=").append(id); + sb.append(", propertyMap=").append(propertyMap); + sb.append('}'); + return sb.toString(); + } +} diff --git a/src/main/java/com/redislabs/redisgraph/impl/Property.java b/src/main/java/com/redislabs/redisgraph/impl/Property.java new file mode 100644 index 0000000..58a8bd2 --- /dev/null +++ b/src/main/java/com/redislabs/redisgraph/impl/Property.java @@ -0,0 +1,113 @@ +package com.redislabs.redisgraph.impl; + +import com.redislabs.redisgraph.ResultSet; + +import java.util.Objects; + +/** + * A Graph entity property. Has a name, type, and value + */ +public class Property { + + //members + String name; + ResultSet.ResultSetScalarTypes type; + Object value; + + + /** + * Default constructor + */ + public Property() { + + } + + /** + * Parameterized constructor + * + * @param name + * @param type + * @param value + */ + public Property(String name, ResultSet.ResultSetScalarTypes type, Object value) { + this.name = name; + this.type = type; + this.value = value; + } + + //getters & setters + + /** + * @return property name + */ + public String getName() { + return name; + } + + /** + * @param name - property name to be set + */ + public void setName(String name) { + this.name = name; + } + + /** + * @return property type + */ + public ResultSet.ResultSetScalarTypes getType() { + return type; + } + + /** + * @param type property type to be set + */ + public void setType(ResultSet.ResultSetScalarTypes type) { + this.type = type; + } + + + /** + * @return property value + */ + public Object getValue() { + return value; + } + + + /** + * @param value property value to be set + */ + public void setValue(Object value) { + this.value = value; + } + + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Property)) return false; + Property property = (Property) o; + return Objects.equals(name, property.name) && + type == property.type && + Objects.equals(value, property.value); + } + + @Override + public int hashCode() { + return Objects.hash(name, type, value); + } + + /** + * Default toString implementation + * @return + */ + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("Property{"); + sb.append("name='").append(name).append('\''); + sb.append(", type=").append(type); + sb.append(", value=").append(value); + sb.append('}'); + return sb.toString(); + } +} From b437f9a955330ecc5a28c7de738a70c9af6baa03 Mon Sep 17 00:00:00 2001 From: DvirDukhan Date: Mon, 6 May 2019 11:43:42 +0300 Subject: [PATCH 05/27] Added Header interface and class implementation --- .../java/com/redislabs/redisgraph/Header.java | 20 ++++ .../redislabs/redisgraph/impl/HeaderImpl.java | 95 +++++++++++++++++++ 2 files changed, 115 insertions(+) create mode 100644 src/main/java/com/redislabs/redisgraph/Header.java create mode 100644 src/main/java/com/redislabs/redisgraph/impl/HeaderImpl.java diff --git a/src/main/java/com/redislabs/redisgraph/Header.java b/src/main/java/com/redislabs/redisgraph/Header.java new file mode 100644 index 0000000..09a882a --- /dev/null +++ b/src/main/java/com/redislabs/redisgraph/Header.java @@ -0,0 +1,20 @@ +package com.redislabs.redisgraph; + +import java.util.List; + +public interface Header { + + + public enum ResultSetColumnTypes { + COLUMN_UNKNOWN, + COLUMN_SCALAR, + COLUMN_NODE, + COLUMN_RELATION; + + } + + + List getSchemaNames(); + + List getSchemaTypes(); +} diff --git a/src/main/java/com/redislabs/redisgraph/impl/HeaderImpl.java b/src/main/java/com/redislabs/redisgraph/impl/HeaderImpl.java new file mode 100644 index 0000000..57de55b --- /dev/null +++ b/src/main/java/com/redislabs/redisgraph/impl/HeaderImpl.java @@ -0,0 +1,95 @@ +package com.redislabs.redisgraph.impl; + +import com.redislabs.redisgraph.Header; +import redis.clients.jedis.util.SafeEncoder; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * Query result header interface implementation + */ +public class HeaderImpl implements Header { + + //members + private final List> raw; + private final List schemaTypes = new ArrayList<>(); + private final List schemaNames = new ArrayList<>(); + + + /** + * Parameterized constructor + * A raw representation of a header (query response schema) is a list. + * Each entry in the list is a tuple (list of size 2). + * tuple[0] represents the type of the column, and tuple[1] represents the name of the column. + * + * @param raw - raw representation of a header + */ + public HeaderImpl(List> raw) { + this.raw = raw; + } + + + /** + * @return a list of column names, ordered by they appearance in the query + */ + @Override + public List getSchemaNames() { + if (schemaNames.size() == 0) { + buildSchema(); + } + return schemaNames; + } + + /** + * @return a list of column types, ordered by they appearance in the query + */ + @Override + public List getSchemaTypes() { + if (schemaTypes.size() == 0) { + buildSchema(); + } + return schemaTypes; + } + + /** + * Extracts schema names and types from the raw representation + */ + private void buildSchema() { + for (List tuple : this.raw) { + + //get type + ResultSetColumnTypes type = ResultSetColumnTypes.values()[(int) (long) tuple.get(0)]; + //get text + String text = SafeEncoder.encode((byte[]) tuple.get(1)); + if (type != null) { + schemaTypes.add(type); + schemaNames.add(text); + } + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof HeaderImpl)) return false; + HeaderImpl header = (HeaderImpl) o; + return Objects.equals(getSchemaTypes(), header.getSchemaTypes()) && + Objects.equals(getSchemaNames(), header.getSchemaNames()); + } + + @Override + public int hashCode() { + return Objects.hash(getSchemaTypes(), getSchemaNames()); + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("HeaderImpl{"); + sb.append("schemaTypes=").append(schemaTypes); + sb.append(", schemaNames=").append(schemaNames); + sb.append('}'); + return sb.toString(); + } +} From 3fc365552c21097cbd0afc1639977fc89c866c9d Mon Sep 17 00:00:00 2001 From: DvirDukhan Date: Mon, 6 May 2019 11:47:11 +0300 Subject: [PATCH 06/27] Added equals, hashCode, toString for RecordImpl --- .../redislabs/redisgraph/impl/RecordImpl.java | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/redislabs/redisgraph/impl/RecordImpl.java b/src/main/java/com/redislabs/redisgraph/impl/RecordImpl.java index cee061a..8aca26c 100644 --- a/src/main/java/com/redislabs/redisgraph/impl/RecordImpl.java +++ b/src/main/java/com/redislabs/redisgraph/impl/RecordImpl.java @@ -1,6 +1,7 @@ package com.redislabs.redisgraph.impl; import java.util.List; +import java.util.Objects; import com.redislabs.redisgraph.Record; @@ -55,9 +56,24 @@ public int size() { } @Override - public String toString() { - return this.values.toString(); + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof RecordImpl)) return false; + RecordImpl record = (RecordImpl) o; + return Objects.equals(header, record.header) && + Objects.equals(values, record.values); } + @Override + public int hashCode() { + return Objects.hash(header, values); + } + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("Record{"); + sb.append("values=").append(values); + sb.append('}'); + return sb.toString(); + } } From 3e828d7fd4a38c49e78ff4a87bacdc1d1f5eead7 Mon Sep 17 00:00:00 2001 From: DvirDukhan Date: Mon, 6 May 2019 11:48:36 +0300 Subject: [PATCH 07/27] Added documentation, equals, hashCode, toString for StatisticsImpl --- .../redisgraph/impl/StatisticsImpl.java | 97 ++++++++++++++++--- 1 file changed, 84 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/redislabs/redisgraph/impl/StatisticsImpl.java b/src/main/java/com/redislabs/redisgraph/impl/StatisticsImpl.java index 6333fd0..b8a738a 100644 --- a/src/main/java/com/redislabs/redisgraph/impl/StatisticsImpl.java +++ b/src/main/java/com/redislabs/redisgraph/impl/StatisticsImpl.java @@ -3,20 +3,41 @@ import java.util.EnumMap; import java.util.List; import java.util.Map; +import java.util.Objects; import com.redislabs.redisgraph.Statistics; import redis.clients.jedis.util.SafeEncoder; +/** + * Query result statistics interface implementation + */ public class StatisticsImpl implements Statistics { + //members private final List raw; private final Map statistics; - + + /** + * A raw representation of query exection statistics is a list of strings + * (byte arrays which need to be de-serialized). + * Each string is built in the form of "K:V" where K is statistics label and V is its value. + * @param raw a raw representation of the query execution statistics + */ StatisticsImpl(List raw){ this.raw = raw; this.statistics = new EnumMap<>(Statistics.Label.class); // lazy loaded } - + + + public List getRaw() { + return raw; + } + + /** + * + * @param label the requested statistic label as key + * @return a string with the value, if key exists, null otherwise + */ @Override public String getStringValue(Statistics.Label label) { return getStatistics().get(label); @@ -24,63 +45,113 @@ public String getStringValue(Statistics.Label label) { private Map getStatistics(){ if(statistics.size() == 0) { - for(byte[] touple : this.raw) { - String text = SafeEncoder.encode(touple); - String[] rowTouple = text.split(":"); - if(rowTouple.length == 2) { - Statistics.Label label = Statistics.Label.getEnum(rowTouple[0]); + for(byte[] tuple : this.raw) { + String text = SafeEncoder.encode(tuple); + String[] rowTuple = text.split(":"); + if(rowTuple.length == 2) { + Statistics.Label label = Statistics.Label.getEnum(rowTuple[0]); if(label != null) { - this.statistics.put( label, rowTouple[1].trim()); + this.statistics.put( label, rowTuple[1].trim()); } } } } return statistics; } - + + /** + * + * @param label the requested statistic label as key + * @return a string with the value, if key exists, 0 otherwise + */ public int getIntValue(Statistics.Label label) { String value = getStringValue(label); return value==null ? 0 : Integer.parseInt(value); } + /** + * + * @return number of nodes created after query execution + */ @Override public int nodesCreated() { return getIntValue(Label.NODES_CREATED); } + /** + * + * @return number of nodes deleted after query execution + */ @Override public int nodesDeleted() { return getIntValue(Label.NODES_DELETED); } + /** + * + * @return number of indices added after query execution + */ @Override public int indicesAdded() { return getIntValue(Label.INDICES_ADDED); } - - + + + /** + * + * @return number of lables added after query execution + */ @Override public int labelsAdded() { return getIntValue(Label.LABELS_ADDED); } + /** + * + * @return number of relationship deleted after query execution + */ @Override public int relationshipsDeleted() { return getIntValue(Label.RELATIONSHIPS_DELETED); } + /** + * + * @return number of relationship created after query execution + */ @Override public int relationshipsCreated() { return getIntValue(Label.RELATIONSHIPS_CREATED); } + /** + * + * @return number of properties set after query execution + */ @Override public int propertiesSet() { return getIntValue(Label.PROPERTIES_SET); } - + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof StatisticsImpl)) return false; + StatisticsImpl that = (StatisticsImpl) o; + return Objects.equals(getRaw(), that.getRaw()) && + Objects.equals(getStatistics(), that.getStatistics()); + } + + @Override + public int hashCode() { + return Objects.hash(getRaw(), getStatistics()); + } + @Override public String toString() { - return getStatistics().toString(); + final StringBuilder sb = new StringBuilder("StatisticsImpl{"); + sb.append("statistics=").append(getStatistics()); + sb.append('}'); + return sb.toString(); } } From 630c5d8ac99b4d4ee8b458f0558455772746fa6f Mon Sep 17 00:00:00 2001 From: DvirDukhan Date: Mon, 6 May 2019 11:51:29 +0300 Subject: [PATCH 08/27] Modified ResultSet interface to return a Header object instead of List, ResultSet size, added scalar types --- .../com/redislabs/redisgraph/ResultSet.java | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/redislabs/redisgraph/ResultSet.java b/src/main/java/com/redislabs/redisgraph/ResultSet.java index 7b8d339..7ac933a 100644 --- a/src/main/java/com/redislabs/redisgraph/ResultSet.java +++ b/src/main/java/com/redislabs/redisgraph/ResultSet.java @@ -6,12 +6,21 @@ /** * Hold a query result */ -public interface ResultSet extends Iterator{ - /** - * Return the query statistics - * @return statistics object - */ - Statistics getStatistics(); +public interface ResultSet extends Iterator { - List getHeader(); -} + public enum ResultSetScalarTypes { + PROPERTY_UNKNOWN, + PROPERTY_NULL, + PROPERTY_STRING, + PROPERTY_INTEGER, + PROPERTY_BOOLEAN, + PROPERTY_DOUBLE, + } + + public int size(); + + Statistics getStatistics(); + + Header getHeader(); + +} \ No newline at end of file From 31997c0f1b655f9d9cda73d6c1e6122d1e745e9e Mon Sep 17 00:00:00 2001 From: DvirDukhan Date: Mon, 6 May 2019 11:54:10 +0300 Subject: [PATCH 09/27] Modified ResultSet implementation to handle new compact query answer, as well as new interface modifications --- .../redisgraph/impl/ResultSetImpl.java | 331 ++++++++++++++---- 1 file changed, 261 insertions(+), 70 deletions(-) diff --git a/src/main/java/com/redislabs/redisgraph/impl/ResultSetImpl.java b/src/main/java/com/redislabs/redisgraph/impl/ResultSetImpl.java index 3e17805..0c0b201 100644 --- a/src/main/java/com/redislabs/redisgraph/impl/ResultSetImpl.java +++ b/src/main/java/com/redislabs/redisgraph/impl/ResultSetImpl.java @@ -3,78 +3,269 @@ import java.util.ArrayList; import java.util.List; import java.util.NoSuchElementException; -import java.util.stream.Collectors; +import java.util.Objects; -import com.redislabs.redisgraph.Record; -import com.redislabs.redisgraph.ResultSet; -import com.redislabs.redisgraph.Statistics; +import com.redislabs.redisgraph.*; import redis.clients.jedis.util.SafeEncoder; -public class ResultSetImpl implements ResultSet{ - - private final int totalResults; - private final List header; - private final List results; - private final Statistics statistics; - private int position = 0; - - public ResultSetImpl(List resp) { - - this.statistics = new StatisticsImpl((List)resp.get(1)); - - ArrayList> result = (ArrayList>) resp.get(0); - - // Empty result set - if(result == null || result.isEmpty()) { - header = new ArrayList<>(0); - totalResults = 0; - results = new ArrayList<>(0); - } else { - ArrayList headers = (ArrayList)result.get(0); - header = headers.stream().map( String::new).collect(Collectors.toList()); - - // First row is a header row - totalResults = result.size()-1; - results = new ArrayList<>(totalResults); - // Skips last row (runtime info) - for (int i = 1; i <= totalResults; i++) { - ArrayList row = result.get(i); - Record record = new RecordImpl(header, row.stream().map( obj -> { - if(obj instanceof byte[]) { - return SafeEncoder.encode((byte[])obj); - } - return obj; - }).collect(Collectors.toList())); - results.add(record); - } - } - } - - @Override - public List getHeader(){ - return header; - } - - @Override - public boolean hasNext() { - return position < results.size(); - } - - @Override - public Record next() { - if (!hasNext()) - throw new NoSuchElementException(); - return results.get(position++); - } - - @Override - public Statistics getStatistics() { - return statistics; - } - - @Override - public String toString() { - return this.header + "\n" + this.results + "\n" + this.statistics; - } +public class ResultSetImpl implements ResultSet { + + Header header = new HeaderImpl(new ArrayList<>()); + Statistics statistics = new StatisticsImpl(new ArrayList<>()); + + private final List results = new ArrayList<>(); + + private int position = 0; + + /** + * + * @param rawResponse the raw representation of response is at most 3 lists of objects. + * The last list is the statistics list. + */ + public ResultSetImpl(List rawResponse){ + + if(rawResponse.size() != 3){ + + parseStatistics(rawResponse.get(rawResponse.size()-1)); + + } + else{ + + parseHeader((List>)rawResponse.get(0)); + parseResult((List>)rawResponse.get(1)); + parseStatistics((List)rawResponse.get(2)); + } + } + + + /** + * + * @param rawResultSet - raw result set representation + */ + private void parseResult(List> rawResultSet) { + if (rawResultSet == null || rawResultSet.isEmpty()) { + return; + } else { + //go over each raw result + for (List row : rawResultSet) { + + List parsedRow = new ArrayList<>(); + //go over each object in the result + for (int i = 0; i < row.size(); i++) { + //get raw representation of the object + List obj = (List) row.get(i); + //get object type + Header.ResultSetColumnTypes objType = header.getSchemaTypes().get(i); + //deserialize according to type and + switch (objType) { + case COLUMN_NODE: + parsedRow.add(deserializeNode(obj)); + break; + case COLUMN_RELATION: + parsedRow.add(deserializeEdge(obj)); + break; + case COLUMN_SCALAR: { + parsedRow.add(deserializeScalar(obj)); + + } + } + + } + //create new record from deserialized objects + Record record = new RecordImpl(header.getSchemaNames(), parsedRow); + results.add(record); + + + } + + } + + } + + /** + * + * @param rawStatistics raw statistics representation + */ + private void parseStatistics(Object rawStatistics){ + statistics = new StatisticsImpl((List)rawStatistics); + } + + + /** + * + * @param rawHeader raw header representation + */ + private void parseHeader(List> rawHeader){ + header = new HeaderImpl(rawHeader); + } + + @Override + public Statistics getStatistics() { + return statistics; + } + + @Override + public Header getHeader(){ + return header; + } + + + /** + * @param rawNodeData - raw node object in the form of list of object + * rawNodeData.get(0) - id (long) + * rawNodeData.get(1) - a list y which contains the labels of this node. Each entry is a label id from the type of long + * rawNodeData.get(2) - a list which contains the properties of the node. + * @return Node object + */ + private Node deserializeNode(List rawNodeData) { + Node node = new Node(); + deserializeGraphEntityId(node, rawNodeData.get(0)); + List labelsIndices = (List) rawNodeData.get(1); + for (long labelIndex : labelsIndices) { + String label = RedisGraphAPI.getInstance().getLabel((int) labelIndex); + node.addLabel(label); + } + deserializeGraphEntityProperties(node, (List>) rawNodeData.get(2)); + + return node; + + } + + /** + * @param graphEntity graph entity + * @param rawEntityId raw representation of entity id to be set to the graph entity + */ + private void deserializeGraphEntityId(GraphEntity graphEntity, Object rawEntityId) { + int id = (int) (long) rawEntityId; + graphEntity.setId(id); + } + + + /** + * @param rawEdgeData - a list of objects + * rawEdgeData[0] - edge id + * rawEdgeData[1] - edge relationship type + * rawEdgeData[2] - edge source + * rawEdgeData[3] - edge destination + * rawEdgeData[4] - edge properties + * @return Edge object + */ + private Edge deserializeEdge(List rawEdgeData) { + Edge edge = new Edge(); + deserializeGraphEntityId(edge, rawEdgeData.get(0)); + + String relationshipType = RedisGraphAPI.getInstance().getRelationshipType((int) (long) rawEdgeData.get(1)); + edge.setRelationshipType(relationshipType); + + edge.setSource((int) (long) rawEdgeData.get(2)); + edge.setDestination((int) (long) rawEdgeData.get(3)); + + deserializeGraphEntityProperties(edge, (List>) rawEdgeData.get(4)); + + return edge; + } + + /** + * @param entity graph entity for adding the properties to + * @param rawProperties raw representation of a list of graph entity properties. Each entry is a list (rawProperty) + * is a raw representation of property, as follows: + * rawProperty.get(0) - property key + * rawProperty.get(1) - property type + * rawProperty.get(2) - property value + */ + void deserializeGraphEntityProperties(GraphEntity entity, List> rawProperties) { + + + for (List rawProperty : rawProperties) { + Property property = new Property(); + property.setName(RedisGraphAPI.getInstance().getPropertyName((int) (long) rawProperty.get(0))); + + //trimmed for getting to value using deserializeScalar + List propertyScalar = rawProperty.subList(1, rawProperty.size()); + property.setType(getScalarTypeFromObject(propertyScalar.get(0))); + property.setValue(deserializeScalar(propertyScalar)); + + entity.addProperty(property); + + } + + } + + /** + * @param rawScalarData - a list of object. list[0] is the scalar type, list[1] is the scalar value + * @return value of the specific scalar type + */ + private Object deserializeScalar(List rawScalarData) { + ResultSetScalarTypes type = getScalarTypeFromObject(rawScalarData.get(0)); + Object obj = rawScalarData.get(1); + switch (type) { + case PROPERTY_NULL: + return null; + case PROPERTY_BOOLEAN: + return Boolean.parseBoolean(SafeEncoder.encode((byte[])obj)); + case PROPERTY_DOUBLE: + return Double.parseDouble(SafeEncoder.encode((byte[])obj)); + case PROPERTY_INTEGER: + return (Integer) ((Long) obj).intValue(); + case PROPERTY_STRING: + return SafeEncoder.encode((byte[]) obj); + case PROPERTY_UNKNOWN: + default: + return obj; + } + } + + /** + * Auxiliary function to retrieve scalar types + * + * @param rawScalarType + * @return scalar type + */ + private ResultSetScalarTypes getScalarTypeFromObject(Object rawScalarType) { + return ResultSetScalarTypes.values()[(int) (long) rawScalarType]; + } + + @Override + public boolean hasNext() { + return position < results.size(); + } + + @Override + public Record next() { + if (!hasNext()) + throw new NoSuchElementException(); + return results.get(position++); + } + + + @Override + public int size() { + return results.size(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof ResultSetImpl)) return false; + ResultSetImpl resultSet = (ResultSetImpl) o; + return Objects.equals(getHeader(), resultSet.getHeader()) && + Objects.equals(getStatistics(), resultSet.getStatistics()) && + Objects.equals(results, resultSet.results); + } + + @Override + public int hashCode() { + return Objects.hash(getHeader(), getStatistics(), results); + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("ResultSetImpl{"); + sb.append("header=").append(header); + sb.append(", statistics=").append(statistics); + sb.append(", results=").append(results); + sb.append('}'); + return sb.toString(); + } } From b1865bfdadf06474f39418f9b2bcdb2024051107 Mon Sep 17 00:00:00 2001 From: DvirDukhan Date: Mon, 6 May 2019 11:59:35 +0300 Subject: [PATCH 10/27] Added documentation, send compact command method, static instance to access initialized instance. Added call procedure method, and calls to get graph labels, property names, and relationship types. --- .../redislabs/redisgraph/RedisGraphAPI.java | 183 +++++++++++++++++- .../java/com/redislabs/redisgraph/Utils.java | 24 +++ 2 files changed, 203 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/redislabs/redisgraph/Utils.java diff --git a/src/main/java/com/redislabs/redisgraph/RedisGraphAPI.java b/src/main/java/com/redislabs/redisgraph/RedisGraphAPI.java index 31f7b35..4be4284 100644 --- a/src/main/java/com/redislabs/redisgraph/RedisGraphAPI.java +++ b/src/main/java/com/redislabs/redisgraph/RedisGraphAPI.java @@ -1,8 +1,8 @@ package com.redislabs.redisgraph; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; +import java.util.*; +import java.util.stream.Collectors; + import com.redislabs.redisgraph.impl.ResultSetImpl; import org.apache.commons.text.translate.AggregateTranslator; @@ -22,6 +22,13 @@ public class RedisGraphAPI { private final Pool client; private final String graphId; + private List labels = new ArrayList<>(); + private List relationshipTypes = new ArrayList<>(); + private List propertyNames = new ArrayList<>(); + + private static RedisGraphAPI redisGraphAPI; + + private static final CharSequenceTranslator ESCAPE_CHYPER; static { @@ -60,6 +67,8 @@ public RedisGraphAPI(String graphId, String host, int port) { public RedisGraphAPI(String graphId, Pool jedis) { this.graphId = graphId; this.client = jedis; + redisGraphAPI = this; + } /** @@ -70,6 +79,7 @@ public RedisGraphAPI(String graphId, Pool jedis) { * @return a result set */ public ResultSet query(String query, Object ...args) { + StringBuilder sb = new StringBuilder(); if(args.length > 0) { for(int i=0; i largs = new ArrayList<>(Arrays.asList(args)); + largs.add("--COMPACT"); + String[] t = new String[largs.size()]; + binaryClient.sendCommand(provider, largs.toArray(t)); + return binaryClient; + } private Jedis getConnection() { return this.client.getResource(); } + + + /** + * Invokes stored procedures without arguments + * @param procedure procedure name to invoke + * @return + */ + public ResultSet callProcedure(String procedure ){ + return callProcedure(procedure, new ArrayList<>(), new HashMap<>()); + + + } + + + /** + * Invokes stored procedure with arguments + * @param procedure + * @param args + * @return + */ + public ResultSet callProcedure(String procedure, List args ){ + return callProcedure(procedure, args, new HashMap<>()); + + + } + + + /** + * + * @param index - index of label + * @return requested label + */ + public String getLabel(int index){ + if (index >= labels.size()){ + labels = getLabels(); + } + return labels.get(index); + } + + /** + * + * @return list of all the node labels in the graph + */ + private List getLabels() { + ResultSet resultSet = callProcedure("db.labels"); + ArrayList labels = new ArrayList<>(); + while (resultSet.hasNext()){ + Record record = resultSet.next(); + labels.add(record.getString(0)); + + } + return labels; + } + + + /** + * + * @param index index of the relationship type + * @return requested relationship type + */ + public String getRelationshipType(int index){ + if (index >= relationshipTypes.size()){ + relationshipTypes = getRelationshipTypes(); + } + return relationshipTypes.get(index); + } + + + /** + * + * @return a list of the edge relationship types in the graph + */ + private List getRelationshipTypes() { + ResultSet resultSet = callProcedure("db.relationshipTypes"); + ArrayList relationshipTypes = new ArrayList<>(); + while (resultSet.hasNext()){ + Record record = resultSet.next(); + relationshipTypes.add(record.getString(0)); + + } + return relationshipTypes; + } + + + /** + * + * @param index index of property name + * @return requested property + */ + public String getPropertyName(int index){ + if (index >= propertyNames.size()){ + propertyNames = getPropertyNames(); + } + return propertyNames.get(index); + } + + + /** + * + * @return a list of all the property names in the graph + */ + private List getPropertyNames() { + ResultSet resultSet = callProcedure("db.propertyKeys"); + ArrayList propertyNames = new ArrayList<>(); + while (resultSet.hasNext()){ + Record record = resultSet.next(); + propertyNames.add(record.getString(0)); + + } + return propertyNames; + } + + /** + * Invoke a stored procedure + * @param procedure + * @param args + * @param kwargs + * @return + */ + public ResultSet callProcedure(String procedure, List args , Map> kwargs ){ + + args = args.stream().map( s -> Utils.quoteString(s)).collect(Collectors.toList()); + StringBuilder q = new StringBuilder(); + q.append(String.format("CALL %s(%s)", procedure, String.join(",", args))); + List y = kwargs.getOrDefault("y", null); + if(y != null){ + q.append(String.join(",", y)); + } + return query(q.toString()); + + + } + + /** + * static function to access an initialized graph api + * @return an initialized instance of RedisGraphAPI + */ + public static RedisGraphAPI getInstance(){ + return redisGraphAPI; + } } diff --git a/src/main/java/com/redislabs/redisgraph/Utils.java b/src/main/java/com/redislabs/redisgraph/Utils.java new file mode 100644 index 0000000..d7abd46 --- /dev/null +++ b/src/main/java/com/redislabs/redisgraph/Utils.java @@ -0,0 +1,24 @@ +package com.redislabs.redisgraph; + +/** + * Utilities class + */ +public class Utils { + + /** + * + * @param str - a string + * @return the input string surounded with quotation marks, if needed + */ + public static String quoteString(String str){ + StringBuilder sb = new StringBuilder(); + if(str.charAt(0)!='"'){ + sb.append('"'); + } + sb.append(str); + if (str.charAt(str.length()-1)!= '"'){ + sb.append('"'); + } + return sb.toString(); + } +} From 1fa2318dc263fb79b62e5b7e849b1d29dd457e18 Mon Sep 17 00:00:00 2001 From: DvirDukhan Date: Mon, 6 May 2019 12:00:41 +0300 Subject: [PATCH 11/27] tests --- .../redisgraph/RedisGraphAPITest.java | 335 +++++++++++++----- 1 file changed, 247 insertions(+), 88 deletions(-) diff --git a/src/test/java/com/redislabs/redisgraph/RedisGraphAPITest.java b/src/test/java/com/redislabs/redisgraph/RedisGraphAPITest.java index f11abf5..cb94641 100644 --- a/src/test/java/com/redislabs/redisgraph/RedisGraphAPITest.java +++ b/src/test/java/com/redislabs/redisgraph/RedisGraphAPITest.java @@ -1,135 +1,294 @@ package com.redislabs.redisgraph; + import java.util.Arrays; +import java.util.List; import java.util.NoSuchElementException; +import com.redislabs.redisgraph.impl.Edge; +import com.redislabs.redisgraph.impl.Node; +import com.redislabs.redisgraph.impl.Property; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import com.redislabs.redisgraph.Statistics.Label; +import static com.redislabs.redisgraph.Header.ResultSetColumnTypes.*; + public class RedisGraphAPITest { RedisGraphAPI api; public RedisGraphAPITest() { api = new RedisGraphAPI("social"); - + /* Dummy call to generate graph so the first deleteGraph() won't fail */ api.query("CREATE ({name:'roi',age:32})"); } @Before - public void deleteGraph(){ - api.deleteGraph(); + public void deleteGraph() { + api.deleteGraph(); } - + @Test - public void testCreateNode(){ + public void testCreateNode() { // Create a node - ResultSet result = api.query("CREATE ({name:'roi',age:32})"); - Assert.assertFalse(result.hasNext()); - - try { - result.next(); - Assert.fail(); - }catch(NoSuchElementException e) {} - - Assert.assertEquals(1, result.getStatistics().nodesCreated()); - Assert.assertNull(result.getStatistics().getStringValue(Label.NODES_DELETED)); - Assert.assertNull(result.getStatistics().getStringValue(Label.RELATIONSHIPS_CREATED)); - Assert.assertNull(result.getStatistics().getStringValue(Label.RELATIONSHIPS_DELETED)); - Assert.assertEquals(2, result.getStatistics().propertiesSet()); - Assert.assertNotNull(result.getStatistics().getStringValue(Label.QUERY_INTERNAL_EXECUTION_TIME)); + ResultSet resultSet = api.query("CREATE ({name:'roi',age:32})"); + + + Assert.assertEquals(1, resultSet.getStatistics().nodesCreated()); + Assert.assertNull(resultSet.getStatistics().getStringValue(Label.NODES_DELETED)); + Assert.assertNull(resultSet.getStatistics().getStringValue(Label.RELATIONSHIPS_CREATED)); + Assert.assertNull(resultSet.getStatistics().getStringValue(Label.RELATIONSHIPS_DELETED)); + Assert.assertEquals(2, resultSet.getStatistics().propertiesSet()); + Assert.assertNotNull(resultSet.getStatistics().getStringValue(Label.QUERY_INTERNAL_EXECUTION_TIME)); + + + Assert.assertFalse(resultSet.hasNext()); + + try { + resultSet.next(); + Assert.fail(); + } catch (NoSuchElementException e) { + } } @Test - public void testCreateLabeledNode(){ + public void testCreateLabeledNode() { // Create a node with a label - ResultSet result = api.query("CREATE (:human{name:'danny',age:12})"); - Assert.assertFalse(result.hasNext()); - - Assert.assertEquals("1", result.getStatistics().getStringValue(Label.NODES_CREATED)); - Assert.assertEquals("2", result.getStatistics().getStringValue(Label.PROPERTIES_SET)); - Assert.assertNotNull(result.getStatistics().getStringValue(Label.QUERY_INTERNAL_EXECUTION_TIME)); + ResultSet resultSet = api.query("CREATE (:human{name:'danny',age:12})"); + Assert.assertFalse(resultSet.hasNext()); +// + Assert.assertEquals("1", resultSet.getStatistics().getStringValue(Label.NODES_CREATED)); + Assert.assertEquals("2", resultSet.getStatistics().getStringValue(Label.PROPERTIES_SET)); + Assert.assertNotNull(resultSet.getStatistics().getStringValue(Label.QUERY_INTERNAL_EXECUTION_TIME)); } @Test - public void testConnectNodes(){ + public void testConnectNodes() { // Create both source and destination nodes Assert.assertNotNull(api.query("CREATE (:person{name:'roi',age:32})")); Assert.assertNotNull(api.query("CREATE (:person{name:'amit',age:30})")); - - // Connect source and destination nodes. - ResultSet matchResult = api.query("MATCH (a:person), (b:person) WHERE (a.name = 'roi' AND b.name='amit') CREATE (a)-[:knows]->(a)"); - - Assert.assertFalse(matchResult.hasNext()); - Assert.assertNull(matchResult.getStatistics().getStringValue(Label.NODES_CREATED)); - Assert.assertNull(matchResult.getStatistics().getStringValue(Label.PROPERTIES_SET)); - Assert.assertEquals(1, matchResult.getStatistics().relationshipsCreated()); - Assert.assertEquals(0, matchResult.getStatistics().relationshipsDeleted()); - Assert.assertNotNull(matchResult.getStatistics().getStringValue(Label.QUERY_INTERNAL_EXECUTION_TIME)); + + // Connect source and destination nodes. + ResultSet resultSet = api.query("MATCH (a:person), (b:person) WHERE (a.name = 'roi' AND b.name='amit') CREATE (a)-[:knows]->(a)"); + + Assert.assertFalse(resultSet.hasNext()); + Assert.assertNull(resultSet.getStatistics().getStringValue(Label.NODES_CREATED)); + Assert.assertNull(resultSet.getStatistics().getStringValue(Label.PROPERTIES_SET)); + Assert.assertEquals(1, resultSet.getStatistics().relationshipsCreated()); + Assert.assertEquals(0, resultSet.getStatistics().relationshipsDeleted()); + Assert.assertNotNull(resultSet.getStatistics().getStringValue(Label.QUERY_INTERNAL_EXECUTION_TIME)); + } + + @Test + public void testDeleteNodes(){ + Assert.assertNotNull(api.query("CREATE (:person{name:'roi',age:32})")); + Assert.assertNotNull(api.query("CREATE (:person{name:'amit',age:30})")); + ResultSet deleteResult = api.query("MATCH (a:person) WHERE (a.name = 'roi') DELETE a"); + + Assert.assertFalse(deleteResult.hasNext()); + Assert.assertNull(deleteResult.getStatistics().getStringValue(Label.NODES_CREATED)); + Assert.assertNull(deleteResult.getStatistics().getStringValue(Label.PROPERTIES_SET)); + Assert.assertNull(deleteResult.getStatistics().getStringValue(Label.NODES_CREATED)); + Assert.assertNull(deleteResult.getStatistics().getStringValue(Label.RELATIONSHIPS_CREATED)); + Assert.assertNull(deleteResult.getStatistics().getStringValue(Label.RELATIONSHIPS_DELETED)); + Assert.assertEquals(1, deleteResult.getStatistics().nodesDeleted()); + Assert.assertNotNull(deleteResult.getStatistics().getStringValue(Label.QUERY_INTERNAL_EXECUTION_TIME)); + + Assert.assertNotNull(api.query("CREATE (:person{name:'roi',age:32})")); + Assert.assertNotNull(api.query("MATCH (a:person), (b:person) WHERE (a.name = 'roi' AND b.name='amit') CREATE (a)-[:knows]->(a)")); + deleteResult = api.query("MATCH (a:person) WHERE (a.name = 'roi') DELETE a"); + + Assert.assertFalse(deleteResult.hasNext()); + Assert.assertNull(deleteResult.getStatistics().getStringValue(Label.NODES_CREATED)); + Assert.assertNull(deleteResult.getStatistics().getStringValue(Label.PROPERTIES_SET)); + Assert.assertNull(deleteResult.getStatistics().getStringValue(Label.NODES_CREATED)); + Assert.assertNull(deleteResult.getStatistics().getStringValue(Label.RELATIONSHIPS_CREATED)); + Assert.assertNull(deleteResult.getStatistics().getStringValue(Label.RELATIONSHIPS_DELETED)); + Assert.assertEquals(1, deleteResult.getStatistics().nodesDeleted()); + + Assert.assertNotNull(deleteResult.getStatistics().getStringValue(Label.QUERY_INTERNAL_EXECUTION_TIME)); + + + + } + + @Test + public void testDeleteRelationship(){ + + Assert.assertNotNull(api.query("CREATE (:person{name:'roi',age:32})")); + Assert.assertNotNull(api.query("CREATE (:person{name:'amit',age:30})")); + Assert.assertNotNull(api.query("MATCH (a:person), (b:person) WHERE (a.name = 'roi' AND b.name='amit') CREATE (a)-[:knows]->(a)")); + ResultSet deleteResult = api.query("MATCH (a:person)-[e]->() WHERE (a.name = 'roi') DELETE e"); + + Assert.assertFalse(deleteResult.hasNext()); + Assert.assertNull(deleteResult.getStatistics().getStringValue(Label.NODES_CREATED)); + Assert.assertNull(deleteResult.getStatistics().getStringValue(Label.PROPERTIES_SET)); + Assert.assertNull(deleteResult.getStatistics().getStringValue(Label.NODES_CREATED)); + Assert.assertNull(deleteResult.getStatistics().getStringValue(Label.RELATIONSHIPS_CREATED)); + Assert.assertNull(deleteResult.getStatistics().getStringValue(Label.NODES_DELETED)); + Assert.assertEquals(1, deleteResult.getStatistics().relationshipsDeleted()); + + Assert.assertNotNull(deleteResult.getStatistics().getStringValue(Label.QUERY_INTERNAL_EXECUTION_TIME)); + } - + + @Test - public void testIndex(){ + public void testIndex() { // Create both source and destination nodes - Assert.assertNotNull(api.query("CREATE (:person{name:'roi',age:32})")); - - ResultSet createIndexResult = api.query("CREATE INDEX ON :person(age)"); - Assert.assertFalse(createIndexResult.hasNext()); - Assert.assertEquals(1, createIndexResult.getStatistics().indicesAdded()); - - ResultSet failCreateIndexResult = api.query("CREATE INDEX ON :person(age1)"); - Assert.assertFalse(failCreateIndexResult.hasNext()); - Assert.assertNull(failCreateIndexResult.getStatistics().getStringValue(Label.INDICES_ADDED)); - Assert.assertEquals(0, failCreateIndexResult.getStatistics().indicesAdded()); + Assert.assertNotNull(api.query("CREATE (:person{name:'roi',age:32})")); + + ResultSet createIndexResult = api.query("CREATE INDEX ON :person(age)"); + Assert.assertFalse(createIndexResult.hasNext()); + Assert.assertEquals(1, createIndexResult.getStatistics().indicesAdded()); + + ResultSet failCreateIndexResult = api.query("CREATE INDEX ON :person(age1)"); + Assert.assertFalse(failCreateIndexResult.hasNext()); + Assert.assertNull(failCreateIndexResult.getStatistics().getStringValue(Label.INDICES_ADDED)); + Assert.assertEquals(0, failCreateIndexResult.getStatistics().indicesAdded()); } + @Test + public void testHeader(){ + + Assert.assertNotNull(api.query("CREATE (:person{name:'roi',age:32})")); + Assert.assertNotNull(api.query("CREATE (:person{name:'amit',age:30})")); + Assert.assertNotNull(api.query("MATCH (a:person), (b:person) WHERE (a.name = 'roi' AND b.name='amit') CREATE (a)-[:knows]->(a)")); + + ResultSet queryResult = api.query("MATCH (a:person)-[r:knows]->(b:person) RETURN a,r, a.age"); + + Assert.assertNotNull(queryResult.getHeader()); + Header header = queryResult.getHeader(); + + List schemaNames = header.getSchemaNames(); + List schemaTypes = header.getSchemaTypes(); + + Assert.assertNotNull(schemaNames); + Assert.assertNotNull(schemaTypes); + + Assert.assertEquals(3, schemaNames.size()); + Assert.assertEquals(3, schemaTypes.size()); + + Assert.assertEquals("a", schemaNames.get(0)); + Assert.assertEquals("r", schemaNames.get(1)); + Assert.assertEquals("a.age", schemaNames.get(2)); + + Assert.assertEquals(COLUMN_NODE, schemaTypes.get(0)); + Assert.assertEquals(COLUMN_RELATION, schemaTypes.get(1)); + Assert.assertEquals(COLUMN_SCALAR, schemaTypes.get(2)); + + } @Test - public void testQuery(){ - - // Create both source and destination nodes - Assert.assertNotNull(api.query("CREATE (:qhuman{name:%s,age:%d})","roi", 32)); - Assert.assertNotNull(api.query("CREATE (:qhuman{name:'amit',age:30})")); - - // Connect source and destination nodes. - Assert.assertNotNull(api.query("MATCH (a:qhuman), (b:qhuman) WHERE (a.name = 'roi' AND b.name='amit') CREATE (a)-[:knows]->(b)")); - - // Query - ResultSet resultSet = api.query("MATCH (a:qhuman)-[:knows]->(:qhuman) RETURN a"); - - Assert.assertEquals(Arrays.asList("a.age", "a.name"), resultSet.getHeader()); - Assert.assertEquals("[a.age, a.name]\n[[32, roi]]\n" + resultSet.getStatistics(), resultSet.toString()); - - Assert.assertTrue(resultSet.hasNext()); - Assert.assertEquals(0, resultSet.getStatistics().nodesCreated()); - Assert.assertEquals(0, resultSet.getStatistics().nodesDeleted()); - Assert.assertEquals(0, resultSet.getStatistics().labelsAdded()); - Assert.assertEquals(0, resultSet.getStatistics().propertiesSet()); - Assert.assertEquals(0, resultSet.getStatistics().relationshipsCreated()); - Assert.assertEquals(0, resultSet.getStatistics().relationshipsDeleted()); - Assert.assertNotNull(resultSet.getStatistics().getStringValue(Label.QUERY_INTERNAL_EXECUTION_TIME)); - - Record record = resultSet.next(); - Assert.assertEquals( Arrays.asList("a.age", "a.name"), record.keys()); - Assert.assertEquals( Arrays.asList(32L, "roi"), record.values()); - Assert.assertTrue(record.containsKey("a.name")); - Assert.assertEquals( 2, record.size()); - Assert.assertEquals( "[32, roi]", record.toString()); - - Assert.assertEquals( "roi", record.getString(1)); - Assert.assertEquals( "32", record.getString(0)); - Assert.assertEquals( 32L, ((Long)record.getValue(0)).longValue()); - Assert.assertEquals( 32L, ((Long)record.getValue("a.age")).longValue()); + public void testRecord(){ + String name = "roi"; + int age = 32; + double doubleValue = 3.14; + boolean boolValue = true; + + String place = "TLV"; + int since = 2000; + + + + Property nameProperty = new Property("name", ResultSet.ResultSetScalarTypes.PROPERTY_STRING, name); + Property ageProperty = new Property("age", ResultSet.ResultSetScalarTypes.PROPERTY_INTEGER, age); + Property doubleProperty = new Property("doubleValue", ResultSet.ResultSetScalarTypes.PROPERTY_DOUBLE, doubleValue); + Property trueBooleanProperty = new Property("boolValue", ResultSet.ResultSetScalarTypes.PROPERTY_BOOLEAN, true); + Property falseBooleanProperty = new Property("boolValue", ResultSet.ResultSetScalarTypes.PROPERTY_BOOLEAN, false); + Property nullProperty = new Property("nullValue", ResultSet.ResultSetScalarTypes.PROPERTY_NULL, null); + + Property placeProperty = new Property("place", ResultSet.ResultSetScalarTypes.PROPERTY_STRING, place); + Property sinceProperty = new Property("since", ResultSet.ResultSetScalarTypes.PROPERTY_INTEGER, since); + + Node expectedNode = new Node(); + expectedNode.setId(0); + expectedNode.addLabel("person"); + expectedNode.addProperty(nameProperty); + expectedNode.addProperty(ageProperty); + expectedNode.addProperty(doubleProperty); + expectedNode.addProperty(trueBooleanProperty); + expectedNode.addProperty(nullProperty); + + Edge expectedEdge = new Edge(); + expectedEdge.setId(0); + expectedEdge.setSource(0); + expectedEdge.setDestination(1); + expectedEdge.setRelationshipType("knows"); + expectedEdge.addProperty(placeProperty); + expectedEdge.addProperty(sinceProperty); + expectedEdge.addProperty(doubleProperty); + expectedEdge.addProperty(falseBooleanProperty); + expectedEdge.addProperty(nullProperty); + + + + Assert.assertNotNull(api.query("CREATE (:person{name:%s',age:%d, doubleValue:%f, boolValue:%b, nullValue:null})", name, age, doubleValue, boolValue)); + Assert.assertNotNull(api.query("CREATE (:person{name:'amit',age:30})")); + Assert.assertNotNull(api.query("MATCH (a:person), (b:person) WHERE (a.name = 'roi' AND b.name='amit') " + + "CREATE (a)-[:knows{place:'TLV', since:2000,doubleValue:3.14, boolValue:false, nullValue:null}]->(b)")); + + ResultSet resultSet = api.query("MATCH (a:person)-[r:knows]->(b:person) RETURN a,r, " + + "a.name, a.age, a.doubleValue, a.boolValue, a.nullValue, " + + "r.place, r.since, r.doubleValue, r.boolValue, r.nullValue"); + Assert.assertNotNull(resultSet); + + + Assert.assertEquals(0, resultSet.getStatistics().nodesCreated()); + Assert.assertEquals(0, resultSet.getStatistics().nodesDeleted()); + Assert.assertEquals(0, resultSet.getStatistics().labelsAdded()); + Assert.assertEquals(0, resultSet.getStatistics().propertiesSet()); + Assert.assertEquals(0, resultSet.getStatistics().relationshipsCreated()); + Assert.assertEquals(0, resultSet.getStatistics().relationshipsDeleted()); + Assert.assertNotNull(resultSet.getStatistics().getStringValue(Label.QUERY_INTERNAL_EXECUTION_TIME)); + + + Assert.assertEquals(1, resultSet.size()); + Assert.assertTrue(resultSet.hasNext()); + Record record = resultSet.next(); + Assert.assertFalse(resultSet.hasNext()); + + Node node = record.getValue(0); + Assert.assertNotNull(node); + + Assert.assertEquals(expectedNode, node); + + node = record.getValue("a"); + Assert.assertEquals(expectedNode, node); + + Edge edge = record.getValue(1); + Assert.assertNotNull(edge); + Assert.assertEquals(expectedEdge, edge); + + edge = record.getValue("r"); + Assert.assertEquals(expectedEdge, edge); + + Assert.assertEquals(Arrays.asList("a", "r", "a.name", "a.age", "a.doubleValue", "a.boolValue", "a.nullValue", + "r.place", "r.since", "r.doubleValue", "r.boolValue", "r.nullValue"), record.keys()); + + Assert.assertEquals(Arrays.asList(expectedNode, expectedEdge, + name, age, doubleValue, true, null, + place, since, doubleValue, false, null), + record.values()); + + + Assert.assertEquals( "roi", record.getString(2)); + Assert.assertEquals( "32", record.getString(3)); + Assert.assertEquals( 32L, ((Integer)(record.getValue(3))).longValue()); + Assert.assertEquals( 32L, ((Integer)record.getValue("a.age")).longValue()); Assert.assertEquals( "roi", record.getString("a.name")); - Assert.assertEquals( "32", record.getString("a.age")); + Assert.assertEquals( "32", record.getString("a.age")); + } - + + + @Test - public void testEscapedQuery(){ - Assert.assertNotNull(api.query("CREATE (:escaped{s1:%s,s2:%s})","S\"\'", "S\\'\\\"")); + public void testEscapedQuery() { + Assert.assertNotNull(api.query("CREATE (:escaped{s1:%s,s2:%s})", "S\"\'", "S\\'\\\"")); Assert.assertNotNull(api.query("MATCH (n) where n.s1=%s and n.s2=%s RETURN n", "S\"\'", "S\\'\\\"")); Assert.assertNotNull(api.query("MATCH (n) where n.s1='S\"\\'' RETURN n")); } From 615db353f95da8acbfe06cd07925eb3cf9714a0a Mon Sep 17 00:00:00 2001 From: DvirDukhan Date: Mon, 6 May 2019 12:01:26 +0300 Subject: [PATCH 12/27] moved to version 2.0.0-SNAPSHOT at pom.xml --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 511fd50..1bd6487 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.redislabs jredisgraph - 1.0.7-SNAPSHOT + 2.0.0-SNAPSHOT JRedisGraph Official client for Redis-Graph From 6330e6b7ba3524aafd0678f3adc67bf5ae80586d Mon Sep 17 00:00:00 2001 From: DvirDukhan Date: Wed, 8 May 2019 16:32:37 +0300 Subject: [PATCH 13/27] fixed according to pull request comments --- .../java/com/redislabs/redisgraph/Header.java | 3 + .../redislabs/redisgraph/RedisGraphAPI.java | 84 ++++++++++--------- .../java/com/redislabs/redisgraph/Utils.java | 6 +- .../com/redislabs/redisgraph/impl/Edge.java | 6 +- .../redisgraph/impl/GraphEntity.java | 6 +- .../redislabs/redisgraph/impl/HeaderImpl.java | 2 +- .../com/redislabs/redisgraph/impl/Node.java | 2 +- .../redislabs/redisgraph/impl/Property.java | 6 +- .../redisgraph/impl/ResultSetImpl.java | 18 ++-- .../redisgraph/RedisGraphAPITest.java | 1 - 10 files changed, 73 insertions(+), 61 deletions(-) diff --git a/src/main/java/com/redislabs/redisgraph/Header.java b/src/main/java/com/redislabs/redisgraph/Header.java index 09a882a..43cf8b3 100644 --- a/src/main/java/com/redislabs/redisgraph/Header.java +++ b/src/main/java/com/redislabs/redisgraph/Header.java @@ -2,6 +2,9 @@ import java.util.List; +/** + * Query response header interface. Represents the response schame (column names and types) + */ public interface Header { diff --git a/src/main/java/com/redislabs/redisgraph/RedisGraphAPI.java b/src/main/java/com/redislabs/redisgraph/RedisGraphAPI.java index 4be4284..be3a8a4 100644 --- a/src/main/java/com/redislabs/redisgraph/RedisGraphAPI.java +++ b/src/main/java/com/redislabs/redisgraph/RedisGraphAPI.java @@ -1,6 +1,7 @@ package com.redislabs.redisgraph; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; @@ -22,11 +23,13 @@ public class RedisGraphAPI { private final Pool client; private final String graphId; - private List labels = new ArrayList<>(); - private List relationshipTypes = new ArrayList<>(); - private List propertyNames = new ArrayList<>(); + private final List labels = new ArrayList<>(); + private final List relationshipTypes = new ArrayList<>(); + private final List propertyNames = new ArrayList<>(); + + private static final Map apiMap = new ConcurrentHashMap<>(); + - private static RedisGraphAPI redisGraphAPI; @@ -67,7 +70,8 @@ public RedisGraphAPI(String graphId, String host, int port) { public RedisGraphAPI(String graphId, Pool jedis) { this.graphId = graphId; this.client = jedis; - redisGraphAPI = this; + apiMap.put(graphId, this); + } @@ -79,7 +83,6 @@ public RedisGraphAPI(String graphId, Pool jedis) { * @return a result set */ public ResultSet query(String query, Object ...args) { - StringBuilder sb = new StringBuilder(); if(args.length > 0) { for(int i=0; i largs = new ArrayList<>(Arrays.asList(args)); - largs.add("--COMPACT"); - String[] t = new String[largs.size()]; - binaryClient.sendCommand(provider, largs.toArray(t)); + String[] t = new String[args.length +1]; + System.arraycopy(args, 0 , t, 0, args.length); + t[args.length]="--COMPACT"; + binaryClient.sendCommand(provider, t); return binaryClient; } @@ -174,24 +177,19 @@ public ResultSet callProcedure(String procedure, List args ){ */ public String getLabel(int index){ if (index >= labels.size()){ - labels = getLabels(); + getLabels(); } return labels.get(index); } /** * - * @return list of all the node labels in the graph + * gets a list of all the node labels in the graph */ - private List getLabels() { - ResultSet resultSet = callProcedure("db.labels"); - ArrayList labels = new ArrayList<>(); - while (resultSet.hasNext()){ - Record record = resultSet.next(); - labels.add(record.getString(0)); + private void getLabels() { + getProcedureInfo(labels, "db.labels"); + - } - return labels; } @@ -202,7 +200,7 @@ private List getLabels() { */ public String getRelationshipType(int index){ if (index >= relationshipTypes.size()){ - relationshipTypes = getRelationshipTypes(); + getRelationshipTypes(); } return relationshipTypes.get(index); } @@ -210,17 +208,11 @@ public String getRelationshipType(int index){ /** * - * @return a list of the edge relationship types in the graph + * gets a list of the edge relationship types in the graph */ - private List getRelationshipTypes() { - ResultSet resultSet = callProcedure("db.relationshipTypes"); - ArrayList relationshipTypes = new ArrayList<>(); - while (resultSet.hasNext()){ - Record record = resultSet.next(); - relationshipTypes.add(record.getString(0)); + private void getRelationshipTypes() { + getProcedureInfo(relationshipTypes, "db.relationshipTypes" ); - } - return relationshipTypes; } @@ -231,7 +223,7 @@ private List getRelationshipTypes() { */ public String getPropertyName(int index){ if (index >= propertyNames.size()){ - propertyNames = getPropertyNames(); + getPropertyNames(); } return propertyNames.get(index); } @@ -239,19 +231,31 @@ public String getPropertyName(int index){ /** * - * @return a list of all the property names in the graph + * gets a list of all the property names in the graph */ - private List getPropertyNames() { - ResultSet resultSet = callProcedure("db.propertyKeys"); - ArrayList propertyNames = new ArrayList<>(); + private void getPropertyNames() { + getProcedureInfo(propertyNames, "db.propertyKeys"); + + + } + + /** + * Calls procedure in the graph and returns its data + * @param list a list for storing the data + * @param procedure to call in order to get the data + */ + private void getProcedureInfo(List list, String procedure){ + ResultSet resultSet = callProcedure(procedure); + list.clear(); while (resultSet.hasNext()){ Record record = resultSet.next(); - propertyNames.add(record.getString(0)); + list.add(record.getString(0)); } - return propertyNames; + } + /** * Invoke a stored procedure * @param procedure @@ -277,7 +281,7 @@ public ResultSet callProcedure(String procedure, List args , Map propertyMap = new HashMap<>(); + protected int id; + protected final Map propertyMap = new HashMap<>(); //setters & getters @@ -58,7 +58,7 @@ public void addProperty(String name, ResultSetScalarTypes type, Object value){ public void addProperty (Property property){ - propertyMap.put(property.name, property); + propertyMap.put(property.getName(), property); } /** diff --git a/src/main/java/com/redislabs/redisgraph/impl/HeaderImpl.java b/src/main/java/com/redislabs/redisgraph/impl/HeaderImpl.java index 57de55b..2a7a40f 100644 --- a/src/main/java/com/redislabs/redisgraph/impl/HeaderImpl.java +++ b/src/main/java/com/redislabs/redisgraph/impl/HeaderImpl.java @@ -60,7 +60,7 @@ private void buildSchema() { for (List tuple : this.raw) { //get type - ResultSetColumnTypes type = ResultSetColumnTypes.values()[(int) (long) tuple.get(0)]; + ResultSetColumnTypes type = ResultSetColumnTypes.values()[((Long) tuple.get(0)).intValue()]; //get text String text = SafeEncoder.encode((byte[]) tuple.get(1)); if (type != null) { diff --git a/src/main/java/com/redislabs/redisgraph/impl/Node.java b/src/main/java/com/redislabs/redisgraph/impl/Node.java index 738b92d..80b316f 100644 --- a/src/main/java/com/redislabs/redisgraph/impl/Node.java +++ b/src/main/java/com/redislabs/redisgraph/impl/Node.java @@ -10,7 +10,7 @@ public class Node extends GraphEntity { //members - List labels = new ArrayList<>(); + final private List labels = new ArrayList<>(); /** * @param label - a label to be add diff --git a/src/main/java/com/redislabs/redisgraph/impl/Property.java b/src/main/java/com/redislabs/redisgraph/impl/Property.java index 58a8bd2..7eee7db 100644 --- a/src/main/java/com/redislabs/redisgraph/impl/Property.java +++ b/src/main/java/com/redislabs/redisgraph/impl/Property.java @@ -10,9 +10,9 @@ public class Property { //members - String name; - ResultSet.ResultSetScalarTypes type; - Object value; + private String name; + private ResultSet.ResultSetScalarTypes type; + private Object value; /** diff --git a/src/main/java/com/redislabs/redisgraph/impl/ResultSetImpl.java b/src/main/java/com/redislabs/redisgraph/impl/ResultSetImpl.java index 0c0b201..2444147 100644 --- a/src/main/java/com/redislabs/redisgraph/impl/ResultSetImpl.java +++ b/src/main/java/com/redislabs/redisgraph/impl/ResultSetImpl.java @@ -11,20 +11,22 @@ public class ResultSetImpl implements ResultSet { - Header header = new HeaderImpl(new ArrayList<>()); - Statistics statistics = new StatisticsImpl(new ArrayList<>()); + private Header header = new HeaderImpl(new ArrayList<>()); + private Statistics statistics = new StatisticsImpl(new ArrayList<>()); private final List results = new ArrayList<>(); private int position = 0; + private final String graphId; /** * * @param rawResponse the raw representation of response is at most 3 lists of objects. * The last list is the statistics list. + * @param graphId, the graph ID */ - public ResultSetImpl(List rawResponse){ - + public ResultSetImpl(List rawResponse, String graphId){ + this.graphId = graphId; if(rawResponse.size() != 3){ parseStatistics(rawResponse.get(rawResponse.size()-1)); @@ -50,7 +52,7 @@ private void parseResult(List> rawResultSet) { //go over each raw result for (List row : rawResultSet) { - List parsedRow = new ArrayList<>(); + List parsedRow = new ArrayList<>(row.size()); //go over each object in the result for (int i = 0; i < row.size(); i++) { //get raw representation of the object @@ -123,7 +125,7 @@ private Node deserializeNode(List rawNodeData) { deserializeGraphEntityId(node, rawNodeData.get(0)); List labelsIndices = (List) rawNodeData.get(1); for (long labelIndex : labelsIndices) { - String label = RedisGraphAPI.getInstance().getLabel((int) labelIndex); + String label = RedisGraphAPI.getInstance(graphId).getLabel((int) labelIndex); node.addLabel(label); } deserializeGraphEntityProperties(node, (List>) rawNodeData.get(2)); @@ -155,7 +157,7 @@ private Edge deserializeEdge(List rawEdgeData) { Edge edge = new Edge(); deserializeGraphEntityId(edge, rawEdgeData.get(0)); - String relationshipType = RedisGraphAPI.getInstance().getRelationshipType((int) (long) rawEdgeData.get(1)); + String relationshipType = RedisGraphAPI.getInstance(graphId).getRelationshipType(((Long) rawEdgeData.get(1)).intValue()); edge.setRelationshipType(relationshipType); edge.setSource((int) (long) rawEdgeData.get(2)); @@ -179,7 +181,7 @@ void deserializeGraphEntityProperties(GraphEntity entity, List> raw for (List rawProperty : rawProperties) { Property property = new Property(); - property.setName(RedisGraphAPI.getInstance().getPropertyName((int) (long) rawProperty.get(0))); + property.setName(RedisGraphAPI.getInstance(graphId).getPropertyName( ((Long) rawProperty.get(0)).intValue())); //trimmed for getting to value using deserializeScalar List propertyScalar = rawProperty.subList(1, rawProperty.size()); diff --git a/src/test/java/com/redislabs/redisgraph/RedisGraphAPITest.java b/src/test/java/com/redislabs/redisgraph/RedisGraphAPITest.java index cb94641..403e5bc 100644 --- a/src/test/java/com/redislabs/redisgraph/RedisGraphAPITest.java +++ b/src/test/java/com/redislabs/redisgraph/RedisGraphAPITest.java @@ -59,7 +59,6 @@ public void testCreateLabeledNode() { // Create a node with a label ResultSet resultSet = api.query("CREATE (:human{name:'danny',age:12})"); Assert.assertFalse(resultSet.hasNext()); -// Assert.assertEquals("1", resultSet.getStatistics().getStringValue(Label.NODES_CREATED)); Assert.assertEquals("2", resultSet.getStatistics().getStringValue(Label.PROPERTIES_SET)); Assert.assertNotNull(resultSet.getStatistics().getStringValue(Label.QUERY_INTERNAL_EXECUTION_TIME)); From 40be547dfa9e277795bb29dee5b03abfe79ccde1 Mon Sep 17 00:00:00 2001 From: DvirDukhan Date: Sun, 12 May 2019 10:49:11 +0300 Subject: [PATCH 14/27] changed circle-ci to work with 2.0-edge docker image --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index cfa036b..f31cb1f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -8,7 +8,7 @@ jobs: docker: - image: circleci/openjdk:8u171-jdk - - image: redislabs/redisgraph:edge + - image: redislabs/redisgraph:2.0-edge port: 6379:6379 working_directory: ~/repo From e1e9bfd990d21cb49447110216adf67fbc96c43e Mon Sep 17 00:00:00 2001 From: DvirDukhan Date: Sun, 12 May 2019 14:00:03 +0300 Subject: [PATCH 15/27] result set impl is now holding a local ref to redis graph api --- .../redislabs/redisgraph/RedisGraphAPI.java | 27 ++++--------------- .../redisgraph/impl/ResultSetImpl.java | 15 +++++------ 2 files changed, 12 insertions(+), 30 deletions(-) diff --git a/src/main/java/com/redislabs/redisgraph/RedisGraphAPI.java b/src/main/java/com/redislabs/redisgraph/RedisGraphAPI.java index be3a8a4..2761c0d 100644 --- a/src/main/java/com/redislabs/redisgraph/RedisGraphAPI.java +++ b/src/main/java/com/redislabs/redisgraph/RedisGraphAPI.java @@ -27,12 +27,6 @@ public class RedisGraphAPI { private final List relationshipTypes = new ArrayList<>(); private final List propertyNames = new ArrayList<>(); - private static final Map apiMap = new ConcurrentHashMap<>(); - - - - - private static final CharSequenceTranslator ESCAPE_CHYPER; static { final Map escapeJavaMap = new HashMap<>(); @@ -70,9 +64,6 @@ public RedisGraphAPI(String graphId, String host, int port) { public RedisGraphAPI(String graphId, Pool jedis) { this.graphId = graphId; this.client = jedis; - apiMap.put(graphId, this); - - } /** @@ -93,7 +84,7 @@ public ResultSet query(String query, Object ...args) { } try (Jedis conn = getConnection()) { - return new ResultSetImpl(sendCompactCommand(conn, Command.QUERY, graphId, query).getObjectMultiBulkReply(), graphId); + return new ResultSetImpl(sendCompactCommand(conn, Command.QUERY, graphId, query).getObjectMultiBulkReply(), this); } } @@ -104,6 +95,10 @@ public ResultSet query(String query, Object ...args) { * @return delete running time statistics */ public String deleteGraph() { + //clear local state + labels.clear(); + propertyNames.clear(); + relationshipTypes.clear(); try (Jedis conn = getConnection()) { return sendCommand(conn, Command.DELETE, graphId).getBulkReply(); } @@ -166,7 +161,6 @@ public ResultSet callProcedure(String procedure ){ public ResultSet callProcedure(String procedure, List args ){ return callProcedure(procedure, args, new HashMap<>()); - } @@ -250,9 +244,7 @@ private void getProcedureInfo(List list, String procedure){ while (resultSet.hasNext()){ Record record = resultSet.next(); list.add(record.getString(0)); - } - } @@ -273,15 +265,6 @@ public ResultSet callProcedure(String procedure, List args , Map results = new ArrayList<>(); private int position = 0; - private final String graphId; + private final RedisGraphAPI redisGraphAPI; /** * * @param rawResponse the raw representation of response is at most 3 lists of objects. * The last list is the statistics list. - * @param graphId, the graph ID + * @param redisGraphAPI, the graph api */ - public ResultSetImpl(List rawResponse, String graphId){ - this.graphId = graphId; + public ResultSetImpl(List rawResponse, RedisGraphAPI redisGraphAPI){ + this.redisGraphAPI = redisGraphAPI; if(rawResponse.size() != 3){ parseStatistics(rawResponse.get(rawResponse.size()-1)); @@ -78,7 +78,6 @@ private void parseResult(List> rawResultSet) { Record record = new RecordImpl(header.getSchemaNames(), parsedRow); results.add(record); - } } @@ -125,7 +124,7 @@ private Node deserializeNode(List rawNodeData) { deserializeGraphEntityId(node, rawNodeData.get(0)); List labelsIndices = (List) rawNodeData.get(1); for (long labelIndex : labelsIndices) { - String label = RedisGraphAPI.getInstance(graphId).getLabel((int) labelIndex); + String label = redisGraphAPI.getLabel((int) labelIndex); node.addLabel(label); } deserializeGraphEntityProperties(node, (List>) rawNodeData.get(2)); @@ -157,7 +156,7 @@ private Edge deserializeEdge(List rawEdgeData) { Edge edge = new Edge(); deserializeGraphEntityId(edge, rawEdgeData.get(0)); - String relationshipType = RedisGraphAPI.getInstance(graphId).getRelationshipType(((Long) rawEdgeData.get(1)).intValue()); + String relationshipType = redisGraphAPI.getRelationshipType(((Long) rawEdgeData.get(1)).intValue()); edge.setRelationshipType(relationshipType); edge.setSource((int) (long) rawEdgeData.get(2)); @@ -181,7 +180,7 @@ void deserializeGraphEntityProperties(GraphEntity entity, List> raw for (List rawProperty : rawProperties) { Property property = new Property(); - property.setName(RedisGraphAPI.getInstance(graphId).getPropertyName( ((Long) rawProperty.get(0)).intValue())); + property.setName(redisGraphAPI.getPropertyName( ((Long) rawProperty.get(0)).intValue())); //trimmed for getting to value using deserializeScalar List propertyScalar = rawProperty.subList(1, rawProperty.size()); From 06059bfec2c73021dc230e23f37a32cdbd8cf857 Mon Sep 17 00:00:00 2001 From: DvirDukhan Date: Tue, 14 May 2019 11:26:59 +0300 Subject: [PATCH 16/27] Modified implementation to work in multi threaded mode --- .../com/redislabs/redisgraph/RedisGraph.java | 190 ++++++++++++ .../redislabs/redisgraph/RedisGraphAPI.java | 270 ------------------ .../redislabs/redisgraph/impl/GraphCache.java | 50 ++++ .../redisgraph/impl/GraphCacheList.java | 67 +++++ .../redisgraph/impl/ResultSetImpl.java | 62 ++-- .../redisgraph/RedisGraphAPITest.java | 73 ++--- 6 files changed, 373 insertions(+), 339 deletions(-) create mode 100644 src/main/java/com/redislabs/redisgraph/RedisGraph.java delete mode 100644 src/main/java/com/redislabs/redisgraph/RedisGraphAPI.java create mode 100644 src/main/java/com/redislabs/redisgraph/impl/GraphCache.java create mode 100644 src/main/java/com/redislabs/redisgraph/impl/GraphCacheList.java diff --git a/src/main/java/com/redislabs/redisgraph/RedisGraph.java b/src/main/java/com/redislabs/redisgraph/RedisGraph.java new file mode 100644 index 0000000..c8ae8d6 --- /dev/null +++ b/src/main/java/com/redislabs/redisgraph/RedisGraph.java @@ -0,0 +1,190 @@ +package com.redislabs.redisgraph; + +import com.redislabs.redisgraph.impl.GraphCache; +import com.redislabs.redisgraph.impl.ResultSetImpl; +import org.apache.commons.text.translate.AggregateTranslator; +import org.apache.commons.text.translate.CharSequenceTranslator; +import org.apache.commons.text.translate.LookupTranslator; +import redis.clients.jedis.BinaryClient; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPool; +import redis.clients.jedis.commands.ProtocolCommand; +import redis.clients.jedis.util.Pool; + +import java.io.Closeable; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + + +/** + * + */ +public class RedisGraph implements Closeable { + + + + private final Pool client; + private final Map graphCaches = new ConcurrentHashMap<>(); + + + + private static final CharSequenceTranslator ESCAPE_CHYPER; + static { + final Map escapeJavaMap = new HashMap<>(); + escapeJavaMap.put("\'", "\\'"); + escapeJavaMap.put("\"", "\\\""); + ESCAPE_CHYPER = new AggregateTranslator(new LookupTranslator(Collections.unmodifiableMap(escapeJavaMap))); + } + + /** + * Creates a client running on the local machine + + */ + public RedisGraph() { + this("localhost", 6379); + } + + /** + * Creates a client running on the specific host/post + * + * @param host Redis host + * @param port Redis port + */ + public RedisGraph(String host, int port) { + this( new JedisPool(host, port)); + } + + /** + * Creates a client using provided Jedis pool + * + * @param jedis bring your own Jedis pool + */ + public RedisGraph( Pool jedis) { + + this.client = jedis; + } + + @Override + public void close(){ + this.client.close(); + } + + + /** + * Execute a Cypher query with arguments + * + * @param graphId a graph to perform the query on + * @param query Cypher query + * @param args + * @return a result set + */ + public ResultSet query(String graphId, String query, Object ...args) { + if(args.length > 0) { + for(int i=0; i(), new HashMap<>()); + } + + + /** + * Invokes stored procedure with arguments + * @param graphId a graph to perform the query on + * @param procedure procedure name to invoke + * @param args procedure arguments + * @return result set with the procedure data + */ + public ResultSet callProcedure(String graphId, String procedure, List args ){ + return callProcedure(graphId, procedure, args, new HashMap<>()); + } + + + /** + * Deletes the entire graph + * + * @return delete running time statistics + */ + public String deleteGraph(String graphId) { + //clear local state + graphCaches.remove(graphId); + try (Jedis conn = getConnection()) { + return sendCommand(conn, Command.DELETE, graphId).getBulkReply(); + } + + } + + + /** + * Sends command - will be replaced with sendCompactCommand once graph.delete support --compact flag + * @param conn - connection + * @param provider - command type + * @param args - command arguments + * @return + */ + private BinaryClient sendCommand(Jedis conn, ProtocolCommand provider, String ...args) { + BinaryClient binaryClient = conn.getClient(); + binaryClient.sendCommand(provider, args); + return binaryClient; + } + + + /** + * Sends the command with --COMPACT flag + * @param conn - connection + * @param provider - command type + * @param args - command arguments + * @return + */ + private BinaryClient sendCompactCommand(Jedis conn, ProtocolCommand provider, String ...args) { + BinaryClient binaryClient = conn.getClient(); + String[] t = new String[args.length +1]; + System.arraycopy(args, 0 , t, 0, args.length); + t[args.length]="--COMPACT"; + binaryClient.sendCommand(provider, t); + return binaryClient; + } + + private Jedis getConnection() { + return this.client.getResource(); + } + + + /** + * Invoke a stored procedure + * @param graphId a graph to perform the query on + * @param procedure - procedure to execute + * @param args - procedure arguments + * @param kwargs + * @return + */ + public ResultSet callProcedure(String graphId, String procedure, List args , Map> kwargs ){ + + args = args.stream().map( s -> Utils.quoteString(s)).collect(Collectors.toList()); + StringBuilder q = new StringBuilder(); + q.append(String.format("CALL %s(%s)", procedure, String.join(",", args))); + List y = kwargs.getOrDefault("y", null); + if(y != null){ + q.append(String.join(",", y)); + } + return query(graphId, q.toString()); + } +} diff --git a/src/main/java/com/redislabs/redisgraph/RedisGraphAPI.java b/src/main/java/com/redislabs/redisgraph/RedisGraphAPI.java deleted file mode 100644 index 2761c0d..0000000 --- a/src/main/java/com/redislabs/redisgraph/RedisGraphAPI.java +++ /dev/null @@ -1,270 +0,0 @@ -package com.redislabs.redisgraph; - -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.stream.Collectors; - - -import com.redislabs.redisgraph.impl.ResultSetImpl; -import org.apache.commons.text.translate.AggregateTranslator; -import org.apache.commons.text.translate.CharSequenceTranslator; -import org.apache.commons.text.translate.LookupTranslator; - -import redis.clients.jedis.BinaryClient; -import redis.clients.jedis.Jedis; -import redis.clients.jedis.JedisPool; -import redis.clients.jedis.commands.ProtocolCommand; -import redis.clients.jedis.util.Pool; - -/** - * RedisGraph client - */ -public class RedisGraphAPI { - - private final Pool client; - private final String graphId; - private final List labels = new ArrayList<>(); - private final List relationshipTypes = new ArrayList<>(); - private final List propertyNames = new ArrayList<>(); - - private static final CharSequenceTranslator ESCAPE_CHYPER; - static { - final Map escapeJavaMap = new HashMap<>(); - escapeJavaMap.put("\'", "\\'"); - escapeJavaMap.put("\"", "\\\""); - ESCAPE_CHYPER = new AggregateTranslator(new LookupTranslator(Collections.unmodifiableMap(escapeJavaMap))); - } - - /** - * Creates a client to a specific graph running on the local machine - * - * @param graphId the graph id - */ - public RedisGraphAPI(String graphId) { - this(graphId, "localhost", 6379); - } - - /** - * Creates a client to a specific graph running on the specific host/post - * - * @param graphId the graph id - * @param host Redis host - * @param port Redis port - */ - public RedisGraphAPI(String graphId, String host, int port) { - this(graphId, new JedisPool(host, port)); - } - - /** - * Creates a client to a specific graph using provided Jedis pool - * - * @param graphId the graph id - * @param jedis bring your own Jedis pool - */ - public RedisGraphAPI(String graphId, Pool jedis) { - this.graphId = graphId; - this.client = jedis; - } - - /** - * Execute a Cypher query with arguments - * - * @param query Cypher query - * @param args - * @return a result set - */ - public ResultSet query(String query, Object ...args) { - if(args.length > 0) { - for(int i=0; i(), new HashMap<>()); - - - } - - - /** - * Invokes stored procedure with arguments - * @param procedure - * @param args - * @return - */ - public ResultSet callProcedure(String procedure, List args ){ - return callProcedure(procedure, args, new HashMap<>()); - - } - - - /** - * - * @param index - index of label - * @return requested label - */ - public String getLabel(int index){ - if (index >= labels.size()){ - getLabels(); - } - return labels.get(index); - } - - /** - * - * gets a list of all the node labels in the graph - */ - private void getLabels() { - getProcedureInfo(labels, "db.labels"); - - - } - - - /** - * - * @param index index of the relationship type - * @return requested relationship type - */ - public String getRelationshipType(int index){ - if (index >= relationshipTypes.size()){ - getRelationshipTypes(); - } - return relationshipTypes.get(index); - } - - - /** - * - * gets a list of the edge relationship types in the graph - */ - private void getRelationshipTypes() { - getProcedureInfo(relationshipTypes, "db.relationshipTypes" ); - - } - - - /** - * - * @param index index of property name - * @return requested property - */ - public String getPropertyName(int index){ - if (index >= propertyNames.size()){ - getPropertyNames(); - } - return propertyNames.get(index); - } - - - /** - * - * gets a list of all the property names in the graph - */ - private void getPropertyNames() { - getProcedureInfo(propertyNames, "db.propertyKeys"); - - - } - - /** - * Calls procedure in the graph and returns its data - * @param list a list for storing the data - * @param procedure to call in order to get the data - */ - private void getProcedureInfo(List list, String procedure){ - ResultSet resultSet = callProcedure(procedure); - list.clear(); - while (resultSet.hasNext()){ - Record record = resultSet.next(); - list.add(record.getString(0)); - } - } - - - /** - * Invoke a stored procedure - * @param procedure - * @param args - * @param kwargs - * @return - */ - public ResultSet callProcedure(String procedure, List args , Map> kwargs ){ - - args = args.stream().map( s -> Utils.quoteString(s)).collect(Collectors.toList()); - StringBuilder q = new StringBuilder(); - q.append(String.format("CALL %s(%s)", procedure, String.join(",", args))); - List y = kwargs.getOrDefault("y", null); - if(y != null){ - q.append(String.join(",", y)); - } - return query(q.toString()); - } - -} diff --git a/src/main/java/com/redislabs/redisgraph/impl/GraphCache.java b/src/main/java/com/redislabs/redisgraph/impl/GraphCache.java new file mode 100644 index 0000000..9901455 --- /dev/null +++ b/src/main/java/com/redislabs/redisgraph/impl/GraphCache.java @@ -0,0 +1,50 @@ +package com.redislabs.redisgraph.impl; + +import com.redislabs.redisgraph.RedisGraph; + +/** + * A class to store a local cache in the client, for a specific graph. + * Holds the labels, property names and relationship types + */ +public class GraphCache { + + GraphCacheList labels; + GraphCacheList propertyNames; + GraphCacheList relationshipTypes; + + /** + * + * @param graphId - graph Id + * @param redisGraph - a client to use in the cache, for re-validate it by calling procedures + */ + public GraphCache(String graphId, RedisGraph redisGraph) { + this.labels = new GraphCacheList(graphId, "db.labels", redisGraph); + this.propertyNames = new GraphCacheList(graphId, "db.propertyKeys", redisGraph); + this.relationshipTypes = new GraphCacheList(graphId, "db.relationshipTypes", redisGraph); + } + + /** + * @param index - index of label + * @return requested label + */ + public String getLabel(int index) { + return labels.getCachedData(index); + } + + /** + * @param index index of the relationship type + * @return requested relationship type + */ + public String getRelationshipType(int index) { + return relationshipTypes.getCachedData(index); + } + + /** + * @param index index of property name + * @return requested property + */ + public String getPropertyName(int index) { + + return propertyNames.getCachedData(index); + } +} diff --git a/src/main/java/com/redislabs/redisgraph/impl/GraphCacheList.java b/src/main/java/com/redislabs/redisgraph/impl/GraphCacheList.java new file mode 100644 index 0000000..70a3b8b --- /dev/null +++ b/src/main/java/com/redislabs/redisgraph/impl/GraphCacheList.java @@ -0,0 +1,67 @@ +package com.redislabs.redisgraph.impl; + +import com.redislabs.redisgraph.Record; +import com.redislabs.redisgraph.RedisGraph; +import com.redislabs.redisgraph.ResultSet; + +import java.util.ArrayList; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +/** + * Represents a local cache of list of strings. Holds data from a specific procedure, for a specific graph. + */ +public class GraphCacheList extends ArrayList { + + private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + private final ReentrantReadWriteLock.ReadLock readLock = lock.readLock(); + private final ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock(); + private final String graphId; + private final String procedure; + private final RedisGraph redisGraph; + + /** + * + * @param graphId - graph id + * @param procedure - exact procedure command + * @param redisGraph - a client to use in the cache, for re-validate it by calling procedures + */ + public GraphCacheList(String graphId, String procedure, RedisGraph redisGraph) { + this.graphId = graphId; + this.procedure = procedure; + this.redisGraph = redisGraph; + } + + + /** + * A method to return a cached item if it is in the cache, or re-validate the cache if its invalidated + * @param index index of data item + * @return The string value of the specific procedure response, at the given index. + */ + public String getCachedData(int index) { + if (index >= this.size()) { + writeLock.lock(); + //check again + if (index >= this.size()) { + getProcedureInfo(); + } + writeLock.unlock(); + } + readLock.lock(); + String s = this.get(index); + readLock.unlock(); + return s; + + } + + /** + * Auxiliary method to parse a procedure result set and refresh the cache + */ + private void getProcedureInfo() { + ResultSet resultSet = redisGraph.callProcedure(graphId, procedure); + this.clear(); + while (resultSet.hasNext()) { + Record record = resultSet.next(); + this.add(record.getString(0)); + } + } +} diff --git a/src/main/java/com/redislabs/redisgraph/impl/ResultSetImpl.java b/src/main/java/com/redislabs/redisgraph/impl/ResultSetImpl.java index 028eea2..5b5d3bc 100644 --- a/src/main/java/com/redislabs/redisgraph/impl/ResultSetImpl.java +++ b/src/main/java/com/redislabs/redisgraph/impl/ResultSetImpl.java @@ -1,48 +1,47 @@ package com.redislabs.redisgraph.impl; +import com.redislabs.redisgraph.Header; +import com.redislabs.redisgraph.Record; +import com.redislabs.redisgraph.ResultSet; +import com.redislabs.redisgraph.Statistics; +import redis.clients.jedis.util.SafeEncoder; + import java.util.ArrayList; import java.util.List; import java.util.NoSuchElementException; import java.util.Objects; -import com.redislabs.redisgraph.*; - -import redis.clients.jedis.util.SafeEncoder; - public class ResultSetImpl implements ResultSet { - private Header header = new HeaderImpl(new ArrayList<>()); - private Statistics statistics = new StatisticsImpl(new ArrayList<>()); + private Header header = new HeaderImpl(new ArrayList<>()); + private Statistics statistics = new StatisticsImpl(new ArrayList<>()); private final List results = new ArrayList<>(); private int position = 0; - private final RedisGraphAPI redisGraphAPI; + private final GraphCache graphCache; /** - * * @param rawResponse the raw representation of response is at most 3 lists of objects. * The last list is the statistics list. - * @param redisGraphAPI, the graph api + * @param graphCache, the graph local cache */ - public ResultSetImpl(List rawResponse, RedisGraphAPI redisGraphAPI){ - this.redisGraphAPI = redisGraphAPI; - if(rawResponse.size() != 3){ + public ResultSetImpl(List rawResponse, GraphCache graphCache) { + this.graphCache = graphCache; + if (rawResponse.size() != 3) { - parseStatistics(rawResponse.get(rawResponse.size()-1)); + parseStatistics(rawResponse.get(rawResponse.size() - 1)); - } - else{ + } else { - parseHeader((List>)rawResponse.get(0)); - parseResult((List>)rawResponse.get(1)); - parseStatistics((List)rawResponse.get(2)); + parseHeader((List>) rawResponse.get(0)); + parseResult((List>) rawResponse.get(1)); + parseStatistics((List) rawResponse.get(2)); } } /** - * * @param rawResultSet - raw result set representation */ private void parseResult(List> rawResultSet) { @@ -77,27 +76,22 @@ private void parseResult(List> rawResultSet) { //create new record from deserialized objects Record record = new RecordImpl(header.getSchemaNames(), parsedRow); results.add(record); - } - } - } /** - * * @param rawStatistics raw statistics representation */ - private void parseStatistics(Object rawStatistics){ - statistics = new StatisticsImpl((List)rawStatistics); + private void parseStatistics(Object rawStatistics) { + statistics = new StatisticsImpl((List) rawStatistics); } /** - * * @param rawHeader raw header representation */ - private void parseHeader(List> rawHeader){ + private void parseHeader(List> rawHeader) { header = new HeaderImpl(rawHeader); } @@ -107,7 +101,7 @@ public Statistics getStatistics() { } @Override - public Header getHeader(){ + public Header getHeader() { return header; } @@ -124,7 +118,7 @@ private Node deserializeNode(List rawNodeData) { deserializeGraphEntityId(node, rawNodeData.get(0)); List labelsIndices = (List) rawNodeData.get(1); for (long labelIndex : labelsIndices) { - String label = redisGraphAPI.getLabel((int) labelIndex); + String label = graphCache.getLabel((int) labelIndex); node.addLabel(label); } deserializeGraphEntityProperties(node, (List>) rawNodeData.get(2)); @@ -156,7 +150,7 @@ private Edge deserializeEdge(List rawEdgeData) { Edge edge = new Edge(); deserializeGraphEntityId(edge, rawEdgeData.get(0)); - String relationshipType = redisGraphAPI.getRelationshipType(((Long) rawEdgeData.get(1)).intValue()); + String relationshipType = graphCache.getRelationshipType(((Long) rawEdgeData.get(1)).intValue()); edge.setRelationshipType(relationshipType); edge.setSource((int) (long) rawEdgeData.get(2)); @@ -180,7 +174,7 @@ void deserializeGraphEntityProperties(GraphEntity entity, List> raw for (List rawProperty : rawProperties) { Property property = new Property(); - property.setName(redisGraphAPI.getPropertyName( ((Long) rawProperty.get(0)).intValue())); + property.setName(graphCache.getPropertyName(((Long) rawProperty.get(0)).intValue())); //trimmed for getting to value using deserializeScalar List propertyScalar = rawProperty.subList(1, rawProperty.size()); @@ -204,9 +198,9 @@ private Object deserializeScalar(List rawScalarData) { case PROPERTY_NULL: return null; case PROPERTY_BOOLEAN: - return Boolean.parseBoolean(SafeEncoder.encode((byte[])obj)); + return Boolean.parseBoolean(SafeEncoder.encode((byte[]) obj)); case PROPERTY_DOUBLE: - return Double.parseDouble(SafeEncoder.encode((byte[])obj)); + return Double.parseDouble(SafeEncoder.encode((byte[]) obj)); case PROPERTY_INTEGER: return (Integer) ((Long) obj).intValue(); case PROPERTY_STRING: @@ -224,7 +218,7 @@ private Object deserializeScalar(List rawScalarData) { * @return scalar type */ private ResultSetScalarTypes getScalarTypeFromObject(Object rawScalarType) { - return ResultSetScalarTypes.values()[(int) (long) rawScalarType]; + return ResultSetScalarTypes.values()[((Long) rawScalarType).intValue()]; } @Override diff --git a/src/test/java/com/redislabs/redisgraph/RedisGraphAPITest.java b/src/test/java/com/redislabs/redisgraph/RedisGraphAPITest.java index 403e5bc..d37cf01 100644 --- a/src/test/java/com/redislabs/redisgraph/RedisGraphAPITest.java +++ b/src/test/java/com/redislabs/redisgraph/RedisGraphAPITest.java @@ -8,6 +8,7 @@ import com.redislabs.redisgraph.impl.Edge; import com.redislabs.redisgraph.impl.Node; import com.redislabs.redisgraph.impl.Property; +import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -17,24 +18,26 @@ import static com.redislabs.redisgraph.Header.ResultSetColumnTypes.*; public class RedisGraphAPITest { - RedisGraphAPI api; + RedisGraph api; public RedisGraphAPITest() { - api = new RedisGraphAPI("social"); - - /* Dummy call to generate graph so the first deleteGraph() won't fail */ - api.query("CREATE ({name:'roi',age:32})"); } @Before + public void createApi(){ + api = new RedisGraph(); + } + @After public void deleteGraph() { - api.deleteGraph(); + + api.deleteGraph("social"); + api.close(); } @Test public void testCreateNode() { // Create a node - ResultSet resultSet = api.query("CREATE ({name:'roi',age:32})"); + ResultSet resultSet = api.query("social", "CREATE ({name:'roi',age:32})"); Assert.assertEquals(1, resultSet.getStatistics().nodesCreated()); @@ -57,7 +60,7 @@ public void testCreateNode() { @Test public void testCreateLabeledNode() { // Create a node with a label - ResultSet resultSet = api.query("CREATE (:human{name:'danny',age:12})"); + ResultSet resultSet = api.query("social", "CREATE (:human{name:'danny',age:12})"); Assert.assertFalse(resultSet.hasNext()); Assert.assertEquals("1", resultSet.getStatistics().getStringValue(Label.NODES_CREATED)); Assert.assertEquals("2", resultSet.getStatistics().getStringValue(Label.PROPERTIES_SET)); @@ -67,11 +70,11 @@ public void testCreateLabeledNode() { @Test public void testConnectNodes() { // Create both source and destination nodes - Assert.assertNotNull(api.query("CREATE (:person{name:'roi',age:32})")); - Assert.assertNotNull(api.query("CREATE (:person{name:'amit',age:30})")); + Assert.assertNotNull(api.query("social", "CREATE (:person{name:'roi',age:32})")); + Assert.assertNotNull(api.query("social", "CREATE (:person{name:'amit',age:30})")); // Connect source and destination nodes. - ResultSet resultSet = api.query("MATCH (a:person), (b:person) WHERE (a.name = 'roi' AND b.name='amit') CREATE (a)-[:knows]->(a)"); + ResultSet resultSet = api.query("social", "MATCH (a:person), (b:person) WHERE (a.name = 'roi' AND b.name='amit') CREATE (a)-[:knows]->(a)"); Assert.assertFalse(resultSet.hasNext()); Assert.assertNull(resultSet.getStatistics().getStringValue(Label.NODES_CREATED)); @@ -83,9 +86,9 @@ public void testConnectNodes() { @Test public void testDeleteNodes(){ - Assert.assertNotNull(api.query("CREATE (:person{name:'roi',age:32})")); - Assert.assertNotNull(api.query("CREATE (:person{name:'amit',age:30})")); - ResultSet deleteResult = api.query("MATCH (a:person) WHERE (a.name = 'roi') DELETE a"); + Assert.assertNotNull(api.query("social", "CREATE (:person{name:'roi',age:32})")); + Assert.assertNotNull(api.query("social", "CREATE (:person{name:'amit',age:30})")); + ResultSet deleteResult = api.query("social", "MATCH (a:person) WHERE (a.name = 'roi') DELETE a"); Assert.assertFalse(deleteResult.hasNext()); Assert.assertNull(deleteResult.getStatistics().getStringValue(Label.NODES_CREATED)); @@ -96,9 +99,9 @@ public void testDeleteNodes(){ Assert.assertEquals(1, deleteResult.getStatistics().nodesDeleted()); Assert.assertNotNull(deleteResult.getStatistics().getStringValue(Label.QUERY_INTERNAL_EXECUTION_TIME)); - Assert.assertNotNull(api.query("CREATE (:person{name:'roi',age:32})")); - Assert.assertNotNull(api.query("MATCH (a:person), (b:person) WHERE (a.name = 'roi' AND b.name='amit') CREATE (a)-[:knows]->(a)")); - deleteResult = api.query("MATCH (a:person) WHERE (a.name = 'roi') DELETE a"); + Assert.assertNotNull(api.query("social", "CREATE (:person{name:'roi',age:32})")); + Assert.assertNotNull(api.query("social", "MATCH (a:person), (b:person) WHERE (a.name = 'roi' AND b.name='amit') CREATE (a)-[:knows]->(a)")); + deleteResult = api.query("social", "MATCH (a:person) WHERE (a.name = 'roi') DELETE a"); Assert.assertFalse(deleteResult.hasNext()); Assert.assertNull(deleteResult.getStatistics().getStringValue(Label.NODES_CREATED)); @@ -117,10 +120,10 @@ public void testDeleteNodes(){ @Test public void testDeleteRelationship(){ - Assert.assertNotNull(api.query("CREATE (:person{name:'roi',age:32})")); - Assert.assertNotNull(api.query("CREATE (:person{name:'amit',age:30})")); - Assert.assertNotNull(api.query("MATCH (a:person), (b:person) WHERE (a.name = 'roi' AND b.name='amit') CREATE (a)-[:knows]->(a)")); - ResultSet deleteResult = api.query("MATCH (a:person)-[e]->() WHERE (a.name = 'roi') DELETE e"); + Assert.assertNotNull(api.query("social", "CREATE (:person{name:'roi',age:32})")); + Assert.assertNotNull(api.query("social", "CREATE (:person{name:'amit',age:30})")); + Assert.assertNotNull(api.query("social", "MATCH (a:person), (b:person) WHERE (a.name = 'roi' AND b.name='amit') CREATE (a)-[:knows]->(a)")); + ResultSet deleteResult = api.query("social", "MATCH (a:person)-[e]->() WHERE (a.name = 'roi') DELETE e"); Assert.assertFalse(deleteResult.hasNext()); Assert.assertNull(deleteResult.getStatistics().getStringValue(Label.NODES_CREATED)); @@ -138,13 +141,13 @@ public void testDeleteRelationship(){ @Test public void testIndex() { // Create both source and destination nodes - Assert.assertNotNull(api.query("CREATE (:person{name:'roi',age:32})")); + Assert.assertNotNull(api.query("social", "CREATE (:person{name:'roi',age:32})")); - ResultSet createIndexResult = api.query("CREATE INDEX ON :person(age)"); + ResultSet createIndexResult = api.query("social", "CREATE INDEX ON :person(age)"); Assert.assertFalse(createIndexResult.hasNext()); Assert.assertEquals(1, createIndexResult.getStatistics().indicesAdded()); - ResultSet failCreateIndexResult = api.query("CREATE INDEX ON :person(age1)"); + ResultSet failCreateIndexResult = api.query("social", "CREATE INDEX ON :person(age1)"); Assert.assertFalse(failCreateIndexResult.hasNext()); Assert.assertNull(failCreateIndexResult.getStatistics().getStringValue(Label.INDICES_ADDED)); Assert.assertEquals(0, failCreateIndexResult.getStatistics().indicesAdded()); @@ -153,11 +156,11 @@ public void testIndex() { @Test public void testHeader(){ - Assert.assertNotNull(api.query("CREATE (:person{name:'roi',age:32})")); - Assert.assertNotNull(api.query("CREATE (:person{name:'amit',age:30})")); - Assert.assertNotNull(api.query("MATCH (a:person), (b:person) WHERE (a.name = 'roi' AND b.name='amit') CREATE (a)-[:knows]->(a)")); + Assert.assertNotNull(api.query("social", "CREATE (:person{name:'roi',age:32})")); + Assert.assertNotNull(api.query("social", "CREATE (:person{name:'amit',age:30})")); + Assert.assertNotNull(api.query("social", "MATCH (a:person), (b:person) WHERE (a.name = 'roi' AND b.name='amit') CREATE (a)-[:knows]->(a)")); - ResultSet queryResult = api.query("MATCH (a:person)-[r:knows]->(b:person) RETURN a,r, a.age"); + ResultSet queryResult = api.query("social", "MATCH (a:person)-[r:knows]->(b:person) RETURN a,r, a.age"); Assert.assertNotNull(queryResult.getHeader()); Header header = queryResult.getHeader(); @@ -225,12 +228,12 @@ public void testRecord(){ - Assert.assertNotNull(api.query("CREATE (:person{name:%s',age:%d, doubleValue:%f, boolValue:%b, nullValue:null})", name, age, doubleValue, boolValue)); - Assert.assertNotNull(api.query("CREATE (:person{name:'amit',age:30})")); - Assert.assertNotNull(api.query("MATCH (a:person), (b:person) WHERE (a.name = 'roi' AND b.name='amit') " + + Assert.assertNotNull(api.query("social", "CREATE (:person{name:%s',age:%d, doubleValue:%f, boolValue:%b, nullValue:null})", name, age, doubleValue, boolValue)); + Assert.assertNotNull(api.query("social", "CREATE (:person{name:'amit',age:30})")); + Assert.assertNotNull(api.query("social", "MATCH (a:person), (b:person) WHERE (a.name = 'roi' AND b.name='amit') " + "CREATE (a)-[:knows{place:'TLV', since:2000,doubleValue:3.14, boolValue:false, nullValue:null}]->(b)")); - ResultSet resultSet = api.query("MATCH (a:person)-[r:knows]->(b:person) RETURN a,r, " + + ResultSet resultSet = api.query("social", "MATCH (a:person)-[r:knows]->(b:person) RETURN a,r, " + "a.name, a.age, a.doubleValue, a.boolValue, a.nullValue, " + "r.place, r.since, r.doubleValue, r.boolValue, r.nullValue"); Assert.assertNotNull(resultSet); @@ -287,8 +290,8 @@ public void testRecord(){ @Test public void testEscapedQuery() { - Assert.assertNotNull(api.query("CREATE (:escaped{s1:%s,s2:%s})", "S\"\'", "S\\'\\\"")); - Assert.assertNotNull(api.query("MATCH (n) where n.s1=%s and n.s2=%s RETURN n", "S\"\'", "S\\'\\\"")); - Assert.assertNotNull(api.query("MATCH (n) where n.s1='S\"\\'' RETURN n")); + Assert.assertNotNull(api.query("social", "CREATE (:escaped{s1:%s,s2:%s})", "S\"\'", "S\\'\\\"")); + Assert.assertNotNull(api.query("social", "MATCH (n) where n.s1=%s and n.s2=%s RETURN n", "S\"\'", "S\\'\\\"")); + Assert.assertNotNull(api.query("social", "MATCH (n) where n.s1='S\"\\'' RETURN n")); } } From 36cf62620f5ad6d5273262e6cada56a217cdf829 Mon Sep 17 00:00:00 2001 From: DvirDukhan Date: Wed, 15 May 2019 10:51:02 +0300 Subject: [PATCH 17/27] added connection closing and added multi threaded test --- .../com/redislabs/redisgraph/RedisGraph.java | 4 +- .../redisgraph/RedisGraphAPITest.java | 63 +++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/redislabs/redisgraph/RedisGraph.java b/src/main/java/com/redislabs/redisgraph/RedisGraph.java index c8ae8d6..89aa7da 100644 --- a/src/main/java/com/redislabs/redisgraph/RedisGraph.java +++ b/src/main/java/com/redislabs/redisgraph/RedisGraph.java @@ -91,7 +91,9 @@ public ResultSet query(String graphId, String query, Object ...args) { graphCaches.putIfAbsent(graphId, new GraphCache(graphId, this)); try (Jedis conn = getConnection()) { - return new ResultSetImpl(sendCompactCommand(conn, Command.QUERY, graphId, query).getObjectMultiBulkReply(), graphCaches.get(graphId)); + List rawResponsw = sendCompactCommand(conn, Command.QUERY, graphId, query).getObjectMultiBulkReply(); + conn.close(); + return new ResultSetImpl(rawResponsw, graphCaches.get(graphId)); } } diff --git a/src/test/java/com/redislabs/redisgraph/RedisGraphAPITest.java b/src/test/java/com/redislabs/redisgraph/RedisGraphAPITest.java index d37cf01..10ef524 100644 --- a/src/test/java/com/redislabs/redisgraph/RedisGraphAPITest.java +++ b/src/test/java/com/redislabs/redisgraph/RedisGraphAPITest.java @@ -4,6 +4,8 @@ import java.util.Arrays; import java.util.List; import java.util.NoSuchElementException; +import java.util.stream.Collectors; +import java.util.stream.IntStream; import com.redislabs.redisgraph.impl.Edge; import com.redislabs.redisgraph.impl.Node; @@ -287,6 +289,67 @@ public void testRecord(){ } + @Test + public void testMultiThread(){ + + Assert.assertNotNull(api.query("social", "CREATE (:person{name:'roi',age:32})")); + Assert.assertNotNull(api.query("social", "CREATE (:person{name:'amit',age:30})")); + Assert.assertNotNull(api.query("social", "MATCH (a:person), (b:person) WHERE (a.name = 'roi' AND b.name='amit') CREATE (a)-[:knows]->(b)")); + + + List resultSets = IntStream.range(0,16).parallel(). + mapToObj(i-> api.query("social", "MATCH (a:person)-[r:knows]->(b:person) RETURN a,r, a.age")). + collect(Collectors.toList()); + + Property nameProperty = new Property("name", ResultSet.ResultSetScalarTypes.PROPERTY_STRING, "roi"); + Property ageProperty = new Property("age", ResultSet.ResultSetScalarTypes.PROPERTY_INTEGER, 32); + + Node expectedNode = new Node(); + expectedNode.setId(0); + expectedNode.addLabel("person"); + expectedNode.addProperty(nameProperty); + expectedNode.addProperty(ageProperty); + + + Edge expectedEdge = new Edge(); + expectedEdge.setId(0); + expectedEdge.setSource(0); + expectedEdge.setDestination(1); + expectedEdge.setRelationshipType("knows"); + + + for (ResultSet resultSet : resultSets){ + Assert.assertNotNull(resultSet.getHeader()); + Header header = resultSet.getHeader(); + + List schemaNames = header.getSchemaNames(); + List schemaTypes = header.getSchemaTypes(); + + Assert.assertNotNull(schemaNames); + Assert.assertNotNull(schemaTypes); + + Assert.assertEquals(3, schemaNames.size()); + Assert.assertEquals(3, schemaTypes.size()); + + Assert.assertEquals("a", schemaNames.get(0)); + Assert.assertEquals("r", schemaNames.get(1)); + Assert.assertEquals("a.age", schemaNames.get(2)); + + Assert.assertEquals(COLUMN_NODE, schemaTypes.get(0)); + Assert.assertEquals(COLUMN_RELATION, schemaTypes.get(1)); + Assert.assertEquals(COLUMN_SCALAR, schemaTypes.get(2)); + + Assert.assertEquals(1, resultSet.size()); + Assert.assertTrue(resultSet.hasNext()); + Record record = resultSet.next(); + Assert.assertFalse(resultSet.hasNext()); + + + Assert.assertEquals(Arrays.asList("a", "r", "a.age"), record.keys()); + + Assert.assertEquals(Arrays.asList(expectedNode, expectedEdge, 32), record.values()); + } + } @Test public void testEscapedQuery() { From 4cc3a88e41f75615fd2b2290aaa036561eb06748 Mon Sep 17 00:00:00 2001 From: DvirDukhan Date: Thu, 16 May 2019 14:02:31 +0300 Subject: [PATCH 18/27] changed pull request comments --- .../com/redislabs/redisgraph/RedisGraph.java | 18 +++++----- .../redislabs/redisgraph/impl/GraphCache.java | 6 ++-- .../redisgraph/impl/GraphCacheList.java | 35 +++++++++++-------- .../redisgraph/impl/StatisticsImpl.java | 8 ++--- 4 files changed, 33 insertions(+), 34 deletions(-) diff --git a/src/main/java/com/redislabs/redisgraph/RedisGraph.java b/src/main/java/com/redislabs/redisgraph/RedisGraph.java index 89aa7da..3c28563 100644 --- a/src/main/java/com/redislabs/redisgraph/RedisGraph.java +++ b/src/main/java/com/redislabs/redisgraph/RedisGraph.java @@ -157,12 +157,10 @@ private BinaryClient sendCommand(Jedis conn, ProtocolCommand provider, String .. * @return */ private BinaryClient sendCompactCommand(Jedis conn, ProtocolCommand provider, String ...args) { - BinaryClient binaryClient = conn.getClient(); String[] t = new String[args.length +1]; System.arraycopy(args, 0 , t, 0, args.length); t[args.length]="--COMPACT"; - binaryClient.sendCommand(provider, t); - return binaryClient; + return sendCommand(conn, provider, t); } private Jedis getConnection() { @@ -175,18 +173,18 @@ private Jedis getConnection() { * @param graphId a graph to perform the query on * @param procedure - procedure to execute * @param args - procedure arguments - * @param kwargs + * @param kwargs - procedure output arguments * @return */ public ResultSet callProcedure(String graphId, String procedure, List args , Map> kwargs ){ args = args.stream().map( s -> Utils.quoteString(s)).collect(Collectors.toList()); - StringBuilder q = new StringBuilder(); - q.append(String.format("CALL %s(%s)", procedure, String.join(",", args))); - List y = kwargs.getOrDefault("y", null); - if(y != null){ - q.append(String.join(",", y)); + StringBuilder queryString = new StringBuilder(); + queryString.append(String.format("CALL %s(%s)", procedure, String.join(",", args))); + List kwargsList = kwargs.getOrDefault("y", null); + if(kwargsList != null){ + queryString.append(String.join(",", kwargsList)); } - return query(graphId, q.toString()); + return query(graphId, queryString.toString()); } } diff --git a/src/main/java/com/redislabs/redisgraph/impl/GraphCache.java b/src/main/java/com/redislabs/redisgraph/impl/GraphCache.java index 9901455..15239b3 100644 --- a/src/main/java/com/redislabs/redisgraph/impl/GraphCache.java +++ b/src/main/java/com/redislabs/redisgraph/impl/GraphCache.java @@ -8,9 +8,9 @@ */ public class GraphCache { - GraphCacheList labels; - GraphCacheList propertyNames; - GraphCacheList relationshipTypes; + private final GraphCacheList labels; + private final GraphCacheList propertyNames; + private final GraphCacheList relationshipTypes; /** * diff --git a/src/main/java/com/redislabs/redisgraph/impl/GraphCacheList.java b/src/main/java/com/redislabs/redisgraph/impl/GraphCacheList.java index 70a3b8b..a430252 100644 --- a/src/main/java/com/redislabs/redisgraph/impl/GraphCacheList.java +++ b/src/main/java/com/redislabs/redisgraph/impl/GraphCacheList.java @@ -5,19 +5,22 @@ import com.redislabs.redisgraph.ResultSet; import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * Represents a local cache of list of strings. Holds data from a specific procedure, for a specific graph. */ -public class GraphCacheList extends ArrayList { +public class GraphCacheList { - private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); - private final ReentrantReadWriteLock.ReadLock readLock = lock.readLock(); - private final ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock(); + private Object mutex = new Object(); private final String graphId; private final String procedure; private final RedisGraph redisGraph; + List data = new CopyOnWriteArrayList<>(); + + /** * @@ -38,17 +41,14 @@ public GraphCacheList(String graphId, String procedure, RedisGraph redisGraph) { * @return The string value of the specific procedure response, at the given index. */ public String getCachedData(int index) { - if (index >= this.size()) { - writeLock.lock(); - //check again - if (index >= this.size()) { - getProcedureInfo(); + if (index >= data.size()) { + synchronized (mutex){ + if (index >= data.size()) { + getProcedureInfo(); + } } - writeLock.unlock(); } - readLock.lock(); - String s = this.get(index); - readLock.unlock(); + String s = data.get(index); return s; } @@ -58,10 +58,15 @@ public String getCachedData(int index) { */ private void getProcedureInfo() { ResultSet resultSet = redisGraph.callProcedure(graphId, procedure); - this.clear(); + List newData = new ArrayList<>(); + int i = data.size(); while (resultSet.hasNext()) { Record record = resultSet.next(); - this.add(record.getString(0)); + if(i >= data.size()){ + newData.add(record.getString(0)); + } + i++; } + data.addAll(newData); } } diff --git a/src/main/java/com/redislabs/redisgraph/impl/StatisticsImpl.java b/src/main/java/com/redislabs/redisgraph/impl/StatisticsImpl.java index b8a738a..68887e1 100644 --- a/src/main/java/com/redislabs/redisgraph/impl/StatisticsImpl.java +++ b/src/main/java/com/redislabs/redisgraph/impl/StatisticsImpl.java @@ -29,10 +29,6 @@ public class StatisticsImpl implements Statistics { } - public List getRaw() { - return raw; - } - /** * * @param label the requested statistic label as key @@ -138,13 +134,13 @@ public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof StatisticsImpl)) return false; StatisticsImpl that = (StatisticsImpl) o; - return Objects.equals(getRaw(), that.getRaw()) && + return Objects.equals(raw, raw) && Objects.equals(getStatistics(), that.getStatistics()); } @Override public int hashCode() { - return Objects.hash(getRaw(), getStatistics()); + return Objects.hash(raw, getStatistics()); } @Override From d87acb7b30ef228631da9180b2f963d61dc955d0 Mon Sep 17 00:00:00 2001 From: DvirDukhan Date: Thu, 16 May 2019 14:14:49 +0300 Subject: [PATCH 19/27] changed pull request comments --- .../redisgraph/impl/ResultSetImpl.java | 39 ++++++++++++------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/src/main/java/com/redislabs/redisgraph/impl/ResultSetImpl.java b/src/main/java/com/redislabs/redisgraph/impl/ResultSetImpl.java index 5b5d3bc..5134dd7 100644 --- a/src/main/java/com/redislabs/redisgraph/impl/ResultSetImpl.java +++ b/src/main/java/com/redislabs/redisgraph/impl/ResultSetImpl.java @@ -13,10 +13,9 @@ public class ResultSetImpl implements ResultSet { - private Header header = new HeaderImpl(new ArrayList<>()); - private Statistics statistics = new StatisticsImpl(new ArrayList<>()); - - private final List results = new ArrayList<>(); + private final Header header; + private final Statistics statistics; + private final List results ; private int position = 0; private final GraphCache graphCache; @@ -30,23 +29,28 @@ public ResultSetImpl(List rawResponse, GraphCache graphCache) { this.graphCache = graphCache; if (rawResponse.size() != 3) { - parseStatistics(rawResponse.get(rawResponse.size() - 1)); + header = parseHeader(new ArrayList<>()); + results = new ArrayList<>(); + statistics = parseStatistics(rawResponse.get(rawResponse.size() - 1)); } else { - parseHeader((List>) rawResponse.get(0)); - parseResult((List>) rawResponse.get(1)); - parseStatistics((List) rawResponse.get(2)); + header = parseHeader((List>) rawResponse.get(0)); + results = parseResult((List>) rawResponse.get(1)); + statistics = parseStatistics((List) rawResponse.get(2)); } } /** + * * @param rawResultSet - raw result set representation + * @return parsed result set */ - private void parseResult(List> rawResultSet) { + private List parseResult(List> rawResultSet) { + List results = new ArrayList<>(); if (rawResultSet == null || rawResultSet.isEmpty()) { - return; + return results; } else { //go over each raw result for (List row : rawResultSet) { @@ -78,21 +82,26 @@ private void parseResult(List> rawResultSet) { results.add(record); } } + return results; } /** + * * @param rawStatistics raw statistics representation + * @return parsed statistics */ - private void parseStatistics(Object rawStatistics) { - statistics = new StatisticsImpl((List) rawStatistics); + private StatisticsImpl parseStatistics(Object rawStatistics) { + return new StatisticsImpl((List) rawStatistics); } /** - * @param rawHeader raw header representation + * + * @param rawHeader - raw header representation + * @return parsed header */ - private void parseHeader(List> rawHeader) { - header = new HeaderImpl(rawHeader); + private HeaderImpl parseHeader(List> rawHeader) { + return new HeaderImpl(rawHeader); } @Override From fcc9a5ebd0e966a3ffbe22a4744a0dbfd27a5f09 Mon Sep 17 00:00:00 2001 From: Guy Korland Date: Thu, 16 May 2019 17:09:31 +0300 Subject: [PATCH 20/27] Update GraphCacheList.java --- src/main/java/com/redislabs/redisgraph/impl/GraphCacheList.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/redislabs/redisgraph/impl/GraphCacheList.java b/src/main/java/com/redislabs/redisgraph/impl/GraphCacheList.java index a430252..cd5f9a6 100644 --- a/src/main/java/com/redislabs/redisgraph/impl/GraphCacheList.java +++ b/src/main/java/com/redislabs/redisgraph/impl/GraphCacheList.java @@ -18,7 +18,7 @@ public class GraphCacheList { private final String graphId; private final String procedure; private final RedisGraph redisGraph; - List data = new CopyOnWriteArrayList<>(); + private final List data = new CopyOnWriteArrayList<>(); From 584dcad455f80ab3b77a703cfbdb6c1cedba85c9 Mon Sep 17 00:00:00 2001 From: DvirDukhan Date: Thu, 16 May 2019 18:49:47 +0300 Subject: [PATCH 21/27] fixed a bug --- src/main/java/com/redislabs/redisgraph/impl/GraphCacheList.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/redislabs/redisgraph/impl/GraphCacheList.java b/src/main/java/com/redislabs/redisgraph/impl/GraphCacheList.java index a430252..25cb660 100644 --- a/src/main/java/com/redislabs/redisgraph/impl/GraphCacheList.java +++ b/src/main/java/com/redislabs/redisgraph/impl/GraphCacheList.java @@ -59,7 +59,7 @@ public String getCachedData(int index) { private void getProcedureInfo() { ResultSet resultSet = redisGraph.callProcedure(graphId, procedure); List newData = new ArrayList<>(); - int i = data.size(); + int i = 0; while (resultSet.hasNext()) { Record record = resultSet.next(); if(i >= data.size()){ From e6ceab195073762cb5cfee2bd5a1934512b353ac Mon Sep 17 00:00:00 2001 From: DvirDukhan Date: Sun, 19 May 2019 10:23:15 +0300 Subject: [PATCH 22/27] bug fix tests --- .../redisgraph/RedisGraphAPITest.java | 141 +++++++++++++++++- 1 file changed, 133 insertions(+), 8 deletions(-) diff --git a/src/test/java/com/redislabs/redisgraph/RedisGraphAPITest.java b/src/test/java/com/redislabs/redisgraph/RedisGraphAPITest.java index 10ef524..f263053 100644 --- a/src/test/java/com/redislabs/redisgraph/RedisGraphAPITest.java +++ b/src/test/java/com/redislabs/redisgraph/RedisGraphAPITest.java @@ -303,6 +303,7 @@ public void testMultiThread(){ Property nameProperty = new Property("name", ResultSet.ResultSetScalarTypes.PROPERTY_STRING, "roi"); Property ageProperty = new Property("age", ResultSet.ResultSetScalarTypes.PROPERTY_INTEGER, 32); + Property lastNameProperty =new Property("lastName", ResultSet.ResultSetScalarTypes.PROPERTY_STRING, "a"); Node expectedNode = new Node(); expectedNode.setId(0); @@ -321,36 +322,160 @@ public void testMultiThread(){ for (ResultSet resultSet : resultSets){ Assert.assertNotNull(resultSet.getHeader()); Header header = resultSet.getHeader(); - List schemaNames = header.getSchemaNames(); List schemaTypes = header.getSchemaTypes(); - Assert.assertNotNull(schemaNames); Assert.assertNotNull(schemaTypes); - Assert.assertEquals(3, schemaNames.size()); Assert.assertEquals(3, schemaTypes.size()); - Assert.assertEquals("a", schemaNames.get(0)); Assert.assertEquals("r", schemaNames.get(1)); Assert.assertEquals("a.age", schemaNames.get(2)); - Assert.assertEquals(COLUMN_NODE, schemaTypes.get(0)); Assert.assertEquals(COLUMN_RELATION, schemaTypes.get(1)); Assert.assertEquals(COLUMN_SCALAR, schemaTypes.get(2)); - Assert.assertEquals(1, resultSet.size()); Assert.assertTrue(resultSet.hasNext()); Record record = resultSet.next(); Assert.assertFalse(resultSet.hasNext()); + Assert.assertEquals(Arrays.asList("a", "r", "a.age"), record.keys()); + Assert.assertEquals(Arrays.asList(expectedNode, expectedEdge, 32), record.values()); + } + //test for update in local cache + expectedNode.removeProperty("name"); + expectedNode.removeProperty("age"); + expectedNode.addProperty(lastNameProperty); + expectedNode.removeLabel("person"); + expectedNode.addLabel("worker"); + expectedNode.setId(2); - Assert.assertEquals(Arrays.asList("a", "r", "a.age"), record.keys()); - Assert.assertEquals(Arrays.asList(expectedNode, expectedEdge, 32), record.values()); + expectedEdge.setRelationshipType("worksWith"); + expectedEdge.setSource(2); + expectedEdge.setDestination(3); + expectedEdge.setId(1); + + Assert.assertNotNull(api.query("social", "CREATE (:worker{lastName:'a'})")); + Assert.assertNotNull(api.query("social", "CREATE (:worker{lastName:'b'})")); + Assert.assertNotNull(api.query("social", "MATCH (a:worker), (b:worker) WHERE (a.lastName = 'a' AND b.lastName='b') CREATE (a)-[:worksWith]->(b)")); + + resultSets = IntStream.range(0,16).parallel(). + mapToObj(i-> api.query("social", "MATCH (a:worker)-[r:worksWith]->(b:worker) RETURN a,r")). + collect(Collectors.toList()); + + for (ResultSet resultSet : resultSets){ + Assert.assertNotNull(resultSet.getHeader()); + Header header = resultSet.getHeader(); + List schemaNames = header.getSchemaNames(); + List schemaTypes = header.getSchemaTypes(); + Assert.assertNotNull(schemaNames); + Assert.assertNotNull(schemaTypes); + Assert.assertEquals(2, schemaNames.size()); + Assert.assertEquals(2, schemaTypes.size()); + Assert.assertEquals("a", schemaNames.get(0)); + Assert.assertEquals("r", schemaNames.get(1)); + Assert.assertEquals(COLUMN_NODE, schemaTypes.get(0)); + Assert.assertEquals(COLUMN_RELATION, schemaTypes.get(1)); + Assert.assertEquals(1, resultSet.size()); + Assert.assertTrue(resultSet.hasNext()); + Record record = resultSet.next(); + Assert.assertFalse(resultSet.hasNext()); + Assert.assertEquals(Arrays.asList("a", "r"), record.keys()); + Assert.assertEquals(Arrays.asList(expectedNode, expectedEdge), record.values()); } } + + @Test + public void testAdditionToProcedures(){ + + Assert.assertNotNull(api.query("social", "CREATE (:person{name:'roi',age:32})")); + Assert.assertNotNull(api.query("social", "CREATE (:person{name:'amit',age:30})")); + Assert.assertNotNull(api.query("social", "MATCH (a:person), (b:person) WHERE (a.name = 'roi' AND b.name='amit') CREATE (a)-[:knows]->(b)")); + + + List resultSets = IntStream.range(0,16).parallel(). + mapToObj(i-> api.query("social", "MATCH (a:person)-[r:knows]->(b:person) RETURN a,r")). + collect(Collectors.toList()); + + //expected objects init + Property nameProperty = new Property("name", ResultSet.ResultSetScalarTypes.PROPERTY_STRING, "roi"); + Property ageProperty = new Property("age", ResultSet.ResultSetScalarTypes.PROPERTY_INTEGER, 32); + Property lastNameProperty =new Property("lastName", ResultSet.ResultSetScalarTypes.PROPERTY_STRING, "a"); + + Node expectedNode = new Node(); + expectedNode.setId(0); + expectedNode.addLabel("person"); + expectedNode.addProperty(nameProperty); + expectedNode.addProperty(ageProperty); + + + Edge expectedEdge = new Edge(); + expectedEdge.setId(0); + expectedEdge.setSource(0); + expectedEdge.setDestination(1); + expectedEdge.setRelationshipType("knows"); + + + ResultSet resultSet = api.query("social", "MATCH (a:person)-[r:knows]->(b:person) RETURN a,r"); + Assert.assertNotNull(resultSet.getHeader()); + Header header = resultSet.getHeader(); + List schemaNames = header.getSchemaNames(); + List schemaTypes = header.getSchemaTypes(); + Assert.assertNotNull(schemaNames); + Assert.assertNotNull(schemaTypes); + Assert.assertEquals(2, schemaNames.size()); + Assert.assertEquals(2, schemaTypes.size()); + Assert.assertEquals("a", schemaNames.get(0)); + Assert.assertEquals("r", schemaNames.get(1)); + Assert.assertEquals(COLUMN_NODE, schemaTypes.get(0)); + Assert.assertEquals(COLUMN_RELATION, schemaTypes.get(1)); + Assert.assertEquals(1, resultSet.size()); + Assert.assertTrue(resultSet.hasNext()); + Record record = resultSet.next(); + Assert.assertFalse(resultSet.hasNext()); + Assert.assertEquals(Arrays.asList("a", "r"), record.keys()); + Assert.assertEquals(Arrays.asList(expectedNode, expectedEdge), record.values()); + + //test for local cache updates + + expectedNode.removeProperty("name"); + expectedNode.removeProperty("age"); + expectedNode.addProperty(lastNameProperty); + expectedNode.removeLabel("person"); + expectedNode.addLabel("worker"); + expectedNode.setId(2); + expectedEdge.setRelationshipType("worksWith"); + expectedEdge.setSource(2); + expectedEdge.setDestination(3); + expectedEdge.setId(1); + Assert.assertNotNull(api.query("social", "CREATE (:worker{lastName:'a'})")); + Assert.assertNotNull(api.query("social", "CREATE (:worker{lastName:'b'})")); + Assert.assertNotNull(api.query("social", "MATCH (a:worker), (b:worker) WHERE (a.lastName = 'a' AND b.lastName='b') CREATE (a)-[:worksWith]->(b)")); + resultSet = api.query("social", "MATCH (a:worker)-[r:worksWith]->(b:worker) RETURN a,r"); + Assert.assertNotNull(resultSet.getHeader()); + header = resultSet.getHeader(); + schemaNames = header.getSchemaNames(); + schemaTypes = header.getSchemaTypes(); + Assert.assertNotNull(schemaNames); + Assert.assertNotNull(schemaTypes); + Assert.assertEquals(2, schemaNames.size()); + Assert.assertEquals(2, schemaTypes.size()); + Assert.assertEquals("a", schemaNames.get(0)); + Assert.assertEquals("r", schemaNames.get(1)); + Assert.assertEquals(COLUMN_NODE, schemaTypes.get(0)); + Assert.assertEquals(COLUMN_RELATION, schemaTypes.get(1)); + Assert.assertEquals(1, resultSet.size()); + Assert.assertTrue(resultSet.hasNext()); + record = resultSet.next(); + Assert.assertFalse(resultSet.hasNext()); + Assert.assertEquals(Arrays.asList("a", "r"), record.keys()); + Assert.assertEquals(Arrays.asList(expectedNode, expectedEdge), record.values()); + + } + + @Test public void testEscapedQuery() { Assert.assertNotNull(api.query("social", "CREATE (:escaped{s1:%s,s2:%s})", "S\"\'", "S\\'\\\"")); From 3bf901f6bade2e169549d2469cfc4bbb5ce3f2af Mon Sep 17 00:00:00 2001 From: DvirDukhan Date: Thu, 23 May 2019 11:45:22 +0300 Subject: [PATCH 23/27] testing redisconf branch --- .circleci/config.yml | 2 +- .../com/redislabs/redisgraph/RedisGraph.java | 4 +++ .../redisgraph/impl/ResultSetImpl.java | 3 ++- .../redisgraph/RedisGraphAPITest.java | 26 ++++++++++++++----- 4 files changed, 26 insertions(+), 9 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index f31cb1f..54a0bd0 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -44,7 +44,7 @@ jobs: - run: bash <(curl -s https://codecov.io/bash) -t ${CODECOV_TOKEN} - - run: mvn -s .circleci.settings.xml -DskipTests deploy + - run: mvn -s .circleci.settings.xml -DskipTests deploy -P release workflows: version: 2 diff --git a/src/main/java/com/redislabs/redisgraph/RedisGraph.java b/src/main/java/com/redislabs/redisgraph/RedisGraph.java index 3c28563..bf8c9e1 100644 --- a/src/main/java/com/redislabs/redisgraph/RedisGraph.java +++ b/src/main/java/com/redislabs/redisgraph/RedisGraph.java @@ -95,6 +95,10 @@ public ResultSet query(String graphId, String query, Object ...args) { conn.close(); return new ResultSetImpl(rawResponsw, graphCaches.get(graphId)); } + catch (Exception e){ +// e.printStackTrace(); + return new ResultSetImpl(new ArrayList(), graphCaches.get(graphId)); + } } /** diff --git a/src/main/java/com/redislabs/redisgraph/impl/ResultSetImpl.java b/src/main/java/com/redislabs/redisgraph/impl/ResultSetImpl.java index 5134dd7..252636a 100644 --- a/src/main/java/com/redislabs/redisgraph/impl/ResultSetImpl.java +++ b/src/main/java/com/redislabs/redisgraph/impl/ResultSetImpl.java @@ -31,7 +31,8 @@ public ResultSetImpl(List rawResponse, GraphCache graphCache) { header = parseHeader(new ArrayList<>()); results = new ArrayList<>(); - statistics = parseStatistics(rawResponse.get(rawResponse.size() - 1)); + statistics = rawResponse.size()> 0 ? parseStatistics(rawResponse.get(rawResponse.size() - 1)) : + parseStatistics(new ArrayList()); } else { diff --git a/src/test/java/com/redislabs/redisgraph/RedisGraphAPITest.java b/src/test/java/com/redislabs/redisgraph/RedisGraphAPITest.java index f263053..e695e2e 100644 --- a/src/test/java/com/redislabs/redisgraph/RedisGraphAPITest.java +++ b/src/test/java/com/redislabs/redisgraph/RedisGraphAPITest.java @@ -36,6 +36,8 @@ public void deleteGraph() { api.close(); } + + @Test public void testCreateNode() { // Create a node @@ -110,7 +112,7 @@ public void testDeleteNodes(){ Assert.assertNull(deleteResult.getStatistics().getStringValue(Label.PROPERTIES_SET)); Assert.assertNull(deleteResult.getStatistics().getStringValue(Label.NODES_CREATED)); Assert.assertNull(deleteResult.getStatistics().getStringValue(Label.RELATIONSHIPS_CREATED)); - Assert.assertNull(deleteResult.getStatistics().getStringValue(Label.RELATIONSHIPS_DELETED)); + Assert.assertEquals(1, deleteResult.getStatistics().relationshipsDeleted()); Assert.assertEquals(1, deleteResult.getStatistics().nodesDeleted()); Assert.assertNotNull(deleteResult.getStatistics().getStringValue(Label.QUERY_INTERNAL_EXECUTION_TIME)); @@ -290,14 +292,24 @@ public void testRecord(){ @Test - public void testMultiThread(){ + public void tinyTestMultiThread(){ + ResultSet resultSet = api.query("social", "CREATE ({name:'roi',age:32})"); + api.query("social", "MATCH (a:person) RETURN a"); + for (int i =0; i < 10000; i++){ + List resultSets = IntStream.range(0,10).parallel(). + mapToObj(j-> api.query("social", "MATCH (a:person) RETURN a")). + collect(Collectors.toList()); - Assert.assertNotNull(api.query("social", "CREATE (:person{name:'roi',age:32})")); - Assert.assertNotNull(api.query("social", "CREATE (:person{name:'amit',age:30})")); - Assert.assertNotNull(api.query("social", "MATCH (a:person), (b:person) WHERE (a.name = 'roi' AND b.name='amit') CREATE (a)-[:knows]->(b)")); + } + } - List resultSets = IntStream.range(0,16).parallel(). + @Test + public void testMultiThread(){ + + Assert.assertNotNull(api.query("social", "CREATE (:person {name:'roi', age:32})-[:knows]->(:person {name:'amit',age:30}) ")); + + List resultSets = IntStream.range(0,10).parallel(). mapToObj(i-> api.query("social", "MATCH (a:person)-[r:knows]->(b:person) RETURN a,r, a.age")). collect(Collectors.toList()); @@ -360,7 +372,7 @@ public void testMultiThread(){ Assert.assertNotNull(api.query("social", "CREATE (:worker{lastName:'b'})")); Assert.assertNotNull(api.query("social", "MATCH (a:worker), (b:worker) WHERE (a.lastName = 'a' AND b.lastName='b') CREATE (a)-[:worksWith]->(b)")); - resultSets = IntStream.range(0,16).parallel(). + resultSets = IntStream.range(0,10).parallel(). mapToObj(i-> api.query("social", "MATCH (a:worker)-[r:worksWith]->(b:worker) RETURN a,r")). collect(Collectors.toList()); From ca8d0017add6e3ed13764bf5a9e5310fb0a53804 Mon Sep 17 00:00:00 2001 From: DvirDukhan Date: Sun, 26 May 2019 17:39:47 +0300 Subject: [PATCH 24/27] removed Jedis try resource block - move to try catch --- .../com/redislabs/redisgraph/RedisGraph.java | 22 ++++++++++++------- .../redisgraph/RedisGraphAPITest.java | 9 ++++---- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/redislabs/redisgraph/RedisGraph.java b/src/main/java/com/redislabs/redisgraph/RedisGraph.java index bf8c9e1..9655a97 100644 --- a/src/main/java/com/redislabs/redisgraph/RedisGraph.java +++ b/src/main/java/com/redislabs/redisgraph/RedisGraph.java @@ -89,16 +89,22 @@ public ResultSet query(String graphId, String query, Object ...args) { query = String.format(query, args); } graphCaches.putIfAbsent(graphId, new GraphCache(graphId, this)); - - try (Jedis conn = getConnection()) { - List rawResponsw = sendCompactCommand(conn, Command.QUERY, graphId, query).getObjectMultiBulkReply(); + Jedis conn = null; + List rawResponse = new ArrayList(); + try { + conn = getConnection(); + rawResponse= sendCompactCommand(conn, Command.QUERY, graphId, query).getObjectMultiBulkReply(); conn.close(); - return new ResultSetImpl(rawResponsw, graphCaches.get(graphId)); - } - catch (Exception e){ -// e.printStackTrace(); - return new ResultSetImpl(new ArrayList(), graphCaches.get(graphId)); + + } catch (Exception e) { + e.printStackTrace(); + if(conn != null && conn.isConnected()){ + conn.close(); + throw e; + } } + return new ResultSetImpl(rawResponse, graphCaches.get(graphId)); + } /** diff --git a/src/test/java/com/redislabs/redisgraph/RedisGraphAPITest.java b/src/test/java/com/redislabs/redisgraph/RedisGraphAPITest.java index e695e2e..8d71363 100644 --- a/src/test/java/com/redislabs/redisgraph/RedisGraphAPITest.java +++ b/src/test/java/com/redislabs/redisgraph/RedisGraphAPITest.java @@ -296,8 +296,9 @@ public void tinyTestMultiThread(){ ResultSet resultSet = api.query("social", "CREATE ({name:'roi',age:32})"); api.query("social", "MATCH (a:person) RETURN a"); for (int i =0; i < 10000; i++){ - List resultSets = IntStream.range(0,10).parallel(). - mapToObj(j-> api.query("social", "MATCH (a:person) RETURN a")). + List resultSets = IntStream.range(0,16).parallel(). + mapToObj( + j-> api.query("social", "MATCH (a:person) RETURN a")). collect(Collectors.toList()); } @@ -309,7 +310,7 @@ public void testMultiThread(){ Assert.assertNotNull(api.query("social", "CREATE (:person {name:'roi', age:32})-[:knows]->(:person {name:'amit',age:30}) ")); - List resultSets = IntStream.range(0,10).parallel(). + List resultSets = IntStream.range(0,16).parallel(). mapToObj(i-> api.query("social", "MATCH (a:person)-[r:knows]->(b:person) RETURN a,r, a.age")). collect(Collectors.toList()); @@ -372,7 +373,7 @@ public void testMultiThread(){ Assert.assertNotNull(api.query("social", "CREATE (:worker{lastName:'b'})")); Assert.assertNotNull(api.query("social", "MATCH (a:worker), (b:worker) WHERE (a.lastName = 'a' AND b.lastName='b') CREATE (a)-[:worksWith]->(b)")); - resultSets = IntStream.range(0,10).parallel(). + resultSets = IntStream.range(0,16).parallel(). mapToObj(i-> api.query("social", "MATCH (a:worker)-[r:worksWith]->(b:worker) RETURN a,r")). collect(Collectors.toList()); From 659f7c4c78859d917d61b378100aaf3b7c0d0644 Mon Sep 17 00:00:00 2001 From: DvirDukhan Date: Tue, 28 May 2019 14:56:56 +0300 Subject: [PATCH 25/27] changed try resource block --- .../java/com/redislabs/redisgraph/RedisGraph.java | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/src/main/java/com/redislabs/redisgraph/RedisGraph.java b/src/main/java/com/redislabs/redisgraph/RedisGraph.java index 9655a97..c9155aa 100644 --- a/src/main/java/com/redislabs/redisgraph/RedisGraph.java +++ b/src/main/java/com/redislabs/redisgraph/RedisGraph.java @@ -89,19 +89,9 @@ public ResultSet query(String graphId, String query, Object ...args) { query = String.format(query, args); } graphCaches.putIfAbsent(graphId, new GraphCache(graphId, this)); - Jedis conn = null; List rawResponse = new ArrayList(); - try { - conn = getConnection(); + try(Jedis conn = getConnection();){ rawResponse= sendCompactCommand(conn, Command.QUERY, graphId, query).getObjectMultiBulkReply(); - conn.close(); - - } catch (Exception e) { - e.printStackTrace(); - if(conn != null && conn.isConnected()){ - conn.close(); - throw e; - } } return new ResultSetImpl(rawResponse, graphCaches.get(graphId)); From 3ea0b34ba67e4fc195ac67889d640875e695fa7f Mon Sep 17 00:00:00 2001 From: DvirDukhan Date: Tue, 28 May 2019 14:59:46 +0300 Subject: [PATCH 26/27] changed try resource block (again) --- src/main/java/com/redislabs/redisgraph/RedisGraph.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/redislabs/redisgraph/RedisGraph.java b/src/main/java/com/redislabs/redisgraph/RedisGraph.java index c9155aa..5542538 100644 --- a/src/main/java/com/redislabs/redisgraph/RedisGraph.java +++ b/src/main/java/com/redislabs/redisgraph/RedisGraph.java @@ -89,8 +89,8 @@ public ResultSet query(String graphId, String query, Object ...args) { query = String.format(query, args); } graphCaches.putIfAbsent(graphId, new GraphCache(graphId, this)); - List rawResponse = new ArrayList(); - try(Jedis conn = getConnection();){ + List rawResponse = null; + try(Jedis conn = getConnection()){ rawResponse= sendCompactCommand(conn, Command.QUERY, graphId, query).getObjectMultiBulkReply(); } return new ResultSetImpl(rawResponse, graphCaches.get(graphId)); From f3ee260b7eb9d61152b0cc724e1e710c8bc1120b Mon Sep 17 00:00:00 2001 From: DvirDukhan Date: Tue, 28 May 2019 15:26:37 +0300 Subject: [PATCH 27/27] removed release flag from circleci config.yaml --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 54a0bd0..abebfaf 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -44,7 +44,7 @@ jobs: - run: bash <(curl -s https://codecov.io/bash) -t ${CODECOV_TOKEN} - - run: mvn -s .circleci.settings.xml -DskipTests deploy -P release + - run: mvn -s .circleci.settings.xml -DskipTests deploy workflows: version: 2