diff --git a/src/main/java/com/redislabs/redisgraph/ResultSet.java b/src/main/java/com/redislabs/redisgraph/ResultSet.java index 251a4d0..a901594 100644 --- a/src/main/java/com/redislabs/redisgraph/ResultSet.java +++ b/src/main/java/com/redislabs/redisgraph/ResultSet.java @@ -7,15 +7,6 @@ */ public interface ResultSet extends Iterator { - enum ResultSetScalarTypes { - PROPERTY_UNKNOWN, - PROPERTY_NULL, - PROPERTY_STRING, - PROPERTY_INTEGER, - PROPERTY_BOOLEAN, - PROPERTY_DOUBLE, - } - int size(); Statistics getStatistics(); diff --git a/src/main/java/com/redislabs/redisgraph/graph_entities/GraphEntity.java b/src/main/java/com/redislabs/redisgraph/graph_entities/GraphEntity.java index e913921..99120ba 100644 --- a/src/main/java/com/redislabs/redisgraph/graph_entities/GraphEntity.java +++ b/src/main/java/com/redislabs/redisgraph/graph_entities/GraphEntity.java @@ -1,8 +1,5 @@ package com.redislabs.redisgraph.graph_entities; - -import com.redislabs.redisgraph.ResultSet.ResultSetScalarTypes; - import java.util.*; @@ -42,12 +39,11 @@ public void setId(int 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){ + public void addProperty(String name, Object value){ - addProperty(new Property(name, type, value)); + addProperty(new Property(name, value)); } diff --git a/src/main/java/com/redislabs/redisgraph/graph_entities/Property.java b/src/main/java/com/redislabs/redisgraph/graph_entities/Property.java index 9a897f8..c007555 100644 --- a/src/main/java/com/redislabs/redisgraph/graph_entities/Property.java +++ b/src/main/java/com/redislabs/redisgraph/graph_entities/Property.java @@ -7,12 +7,12 @@ /** * A Graph entity property. Has a name, type, and value */ -public class Property { +public class Property { //members private String name; - private ResultSet.ResultSetScalarTypes type; - private Object value; + + private T value; /** @@ -26,15 +26,14 @@ public Property() { * Parameterized constructor * * @param name - * @param type * @param value */ - public Property(String name, ResultSet.ResultSetScalarTypes type, Object value) { + public Property(String name, T value) { this.name = name; - this.type = type; this.value = value; } + //getters & setters /** @@ -51,25 +50,11 @@ 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() { + public T getValue() { return value; } @@ -77,7 +62,7 @@ public Object getValue() { /** * @param value property value to be set */ - public void setValue(Object value) { + public void setValue(T value) { this.value = value; } @@ -88,13 +73,12 @@ public boolean equals(Object o) { 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); + return Objects.hash(name, value); } /** @@ -105,7 +89,6 @@ public int hashCode() { 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(); diff --git a/src/main/java/com/redislabs/redisgraph/impl/resultset/ResultSetImpl.java b/src/main/java/com/redislabs/redisgraph/impl/resultset/ResultSetImpl.java index fd208b6..6fa57fb 100644 --- a/src/main/java/com/redislabs/redisgraph/impl/resultset/ResultSetImpl.java +++ b/src/main/java/com/redislabs/redisgraph/impl/resultset/ResultSetImpl.java @@ -1,6 +1,10 @@ package com.redislabs.redisgraph.impl.resultset; -import com.redislabs.redisgraph.*; +import com.redislabs.redisgraph.Header; +import com.redislabs.redisgraph.Record; +import com.redislabs.redisgraph.RedisGraph; +import com.redislabs.redisgraph.ResultSet; +import com.redislabs.redisgraph.Statistics; import com.redislabs.redisgraph.graph_entities.Edge; import com.redislabs.redisgraph.graph_entities.GraphEntity; import com.redislabs.redisgraph.graph_entities.Node; @@ -199,7 +203,6 @@ private void deserializeGraphEntityProperties(GraphEntity entity, List propertyScalar = rawProperty.subList(1, rawProperty.size()); - property.setType(getScalarTypeFromObject(propertyScalar.get(0))); property.setValue(deserializeScalar(propertyScalar)); entity.addProperty(property); @@ -213,33 +216,49 @@ private void deserializeGraphEntityProperties(GraphEntity entity, List rawScalarData) { - ResultSetScalarTypes type = getScalarTypeFromObject(rawScalarData.get(0)); + ResultSetScalarTypes type = getValueTypeFromObject(rawScalarData.get(0)); + Object obj = rawScalarData.get(1); switch (type) { - case PROPERTY_NULL: + case VALUE_NULL: return null; - case PROPERTY_BOOLEAN: + case VALUE_BOOLEAN: return Boolean.parseBoolean(SafeEncoder.encode((byte[]) obj)); - case PROPERTY_DOUBLE: + case VALUE_DOUBLE: return Double.parseDouble(SafeEncoder.encode((byte[]) obj)); - case PROPERTY_INTEGER: + case VALUE_INTEGER: return ((Long) obj).intValue(); - case PROPERTY_STRING: + case VALUE_STRING: return SafeEncoder.encode((byte[]) obj); - case PROPERTY_UNKNOWN: + case VALUE_ARRAY: + return deserializeArray(obj); + case VALUE_NODE: + return deserializeNode((List) obj); + case VALUE_EDGE: + return deserializeEdge((List) obj); + case VALUE_UNKNOWN: default: return obj; } } + private List deserializeArray(Object rawScalarData) { + List> array = (List>) rawScalarData; + List res = new ArrayList<>(array.size()); + for (List arrayValue : array) { + res.add(deserializeScalar(arrayValue)); + } + return res; + } + /** * Auxiliary function to retrieve scalar types * * @param rawScalarType * @return scalar type */ - private ResultSetScalarTypes getScalarTypeFromObject(Object rawScalarType) { - return ResultSetScalarTypes.values()[((Long) rawScalarType).intValue()]; + private ResultSetScalarTypes getValueTypeFromObject(Object rawScalarType) { + return ResultSetScalarTypes.getValue(((Long) rawScalarType).intValue()); } @Override diff --git a/src/main/java/com/redislabs/redisgraph/impl/resultset/ResultSetScalarTypes.java b/src/main/java/com/redislabs/redisgraph/impl/resultset/ResultSetScalarTypes.java new file mode 100644 index 0000000..5e3e193 --- /dev/null +++ b/src/main/java/com/redislabs/redisgraph/impl/resultset/ResultSetScalarTypes.java @@ -0,0 +1,24 @@ +package com.redislabs.redisgraph.impl.resultset; + +import redis.clients.jedis.exceptions.JedisDataException; + +enum ResultSetScalarTypes { + VALUE_UNKNOWN, + VALUE_NULL, + VALUE_STRING, + VALUE_INTEGER, + VALUE_BOOLEAN, + VALUE_DOUBLE, + VALUE_ARRAY, + VALUE_EDGE, + VALUE_NODE; + + + static ResultSetScalarTypes[] values = values(); + + public static ResultSetScalarTypes getValue(int index) { + if (index < 0 || index > values.length) throw new JedisDataException("Unrecognized response type"); + return values[index]; + } + +} diff --git a/src/test/java/com/redislabs/redisgraph/RedisGraphAPITest.java b/src/test/java/com/redislabs/redisgraph/RedisGraphAPITest.java index 79e313d..e2be9e4 100644 --- a/src/test/java/com/redislabs/redisgraph/RedisGraphAPITest.java +++ b/src/test/java/com/redislabs/redisgraph/RedisGraphAPITest.java @@ -203,15 +203,15 @@ public void testRecord(){ - 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 nameProperty = new Property("name", name); + Property ageProperty = new Property("age", age); + Property doubleProperty = new Property("doubleValue", doubleValue); + Property trueBooleanProperty = new Property("boolValue", true); + Property falseBooleanProperty = new Property("boolValue", false); + Property nullProperty = new Property("nullValue", null); - Property placeProperty = new Property("place", ResultSet.ResultSetScalarTypes.PROPERTY_STRING, place); - Property sinceProperty = new Property("since", ResultSet.ResultSetScalarTypes.PROPERTY_INTEGER, since); + Property placeProperty = new Property("place", place); + Property sinceProperty = new Property("since", since); Node expectedNode = new Node(); expectedNode.setId(0); @@ -223,11 +223,11 @@ public void testRecord(){ expectedNode.addProperty(nullProperty); Assert.assertEquals( "Node{labels=[person], id=0, " - + "propertyMap={name=Property{name='name', type=PROPERTY_STRING, value=roi}, " - + "boolValue=Property{name='boolValue', type=PROPERTY_BOOLEAN, value=true}, " - + "doubleValue=Property{name='doubleValue', type=PROPERTY_DOUBLE, value=3.14}, " - + "nullValue=Property{name='nullValue', type=PROPERTY_NULL, value=null}, " - + "age=Property{name='age', type=PROPERTY_INTEGER, value=32}}}", expectedNode.toString()); + + "propertyMap={name=Property{name='name', value=roi}, " + + "boolValue=Property{name='boolValue', value=true}, " + + "doubleValue=Property{name='doubleValue', value=3.14}, " + + "nullValue=Property{name='nullValue', value=null}, " + + "age=Property{name='age', value=32}}}", expectedNode.toString()); Edge expectedEdge = new Edge(); expectedEdge.setId(0); @@ -240,11 +240,11 @@ public void testRecord(){ expectedEdge.addProperty(falseBooleanProperty); expectedEdge.addProperty(nullProperty); Assert.assertEquals("Edge{relationshipType='knows', source=0, destination=1, id=0, " - + "propertyMap={boolValue=Property{name='boolValue', type=PROPERTY_BOOLEAN, value=false}, " - + "place=Property{name='place', type=PROPERTY_STRING, value=TLV}, " - + "doubleValue=Property{name='doubleValue', type=PROPERTY_DOUBLE, value=3.14}, " - + "nullValue=Property{name='nullValue', type=PROPERTY_NULL, value=null}, " - + "since=Property{name='since', type=PROPERTY_INTEGER, value=2000}}}", expectedEdge.toString()); + + "propertyMap={boolValue=Property{name='boolValue', value=false}, " + + "place=Property{name='place', value=TLV}, " + + "doubleValue=Property{name='doubleValue', value=3.14}, " + + "nullValue=Property{name='nullValue', value=null}, " + + "since=Property{name='since', value=2000}}}", expectedEdge.toString()); 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})")); @@ -332,9 +332,9 @@ public void testMultiThread(){ 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); - Property lastNameProperty =new Property("lastName", ResultSet.ResultSetScalarTypes.PROPERTY_STRING, "a"); + Property nameProperty = new Property("name", "roi"); + Property ageProperty = new Property("age", 32); + Property lastNameProperty =new Property("lastName", "a"); Node expectedNode = new Node(); expectedNode.setId(0); @@ -426,9 +426,9 @@ public void testAdditionToProcedures(){ Assert.assertNotNull(api.query("social", "MATCH (a:person), (b:person) WHERE (a.name = 'roi' AND b.name='amit') CREATE (a)-[:knows]->(b)")); //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"); + Property nameProperty = new Property("name", "roi"); + Property ageProperty = new Property("age", 32); + Property lastNameProperty =new Property("lastName", "a"); Node expectedNode = new Node(); expectedNode.setId(0); @@ -570,7 +570,7 @@ public void testMultiExec(){ Assert.assertEquals(COLUMN_NODE, schemaTypes.get(0)); - Property nameProperty = new Property("name", ResultSet.ResultSetScalarTypes.PROPERTY_STRING, "a"); + Property nameProperty = new Property("name", "a"); Node expectedNode = new Node(); expectedNode.setId(0); @@ -629,15 +629,15 @@ public void testContextedAPI() { 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 nameProperty = new Property("name", name); + Property ageProperty = new Property("age", age); + Property doubleProperty = new Property("doubleValue", doubleValue); + Property trueBooleanProperty = new Property("boolValue", true); + Property falseBooleanProperty = new Property("boolValue", false); + Property nullProperty = new Property("nullValue", null); - Property placeProperty = new Property("place", ResultSet.ResultSetScalarTypes.PROPERTY_STRING, place); - Property sinceProperty = new Property("since", ResultSet.ResultSetScalarTypes.PROPERTY_INTEGER, since); + Property placeProperty = new Property("place", place); + Property sinceProperty = new Property("since", since); Node expectedNode = new Node(); expectedNode.setId(0); @@ -758,4 +758,127 @@ public void testReadTransactionWatch(){ c1.close(); c2.close(); } + + @Test + public void testArraySupport() { + + Node expectedANode = new Node(); + expectedANode.setId(0); + expectedANode.addLabel("person"); + Property aNameProperty = new Property("name", "a"); + Property aAgeProperty = new Property("age", 32); + Property aListProperty = new Property("array", Arrays.asList(0,1,2)); + expectedANode.addProperty(aNameProperty); + expectedANode.addProperty(aAgeProperty); + expectedANode.addProperty(aListProperty); + + + Node expectedBNode = new Node(); + expectedBNode.setId(1); + expectedBNode.addLabel("person"); + Property bNameProperty = new Property("name", "b"); + Property bAgeProperty = new Property("age", 30); + Property bListProperty = new Property("array", Arrays.asList(3,4,5)); + expectedBNode.addProperty(bNameProperty); + expectedBNode.addProperty(bAgeProperty); + expectedBNode.addProperty(bListProperty); + + + + Assert.assertNotNull(api.query("social", "CREATE (:person{name:'a',age:32,array:[0,1,2]})")); + Assert.assertNotNull(api.query("social", "CREATE (:person{name:'b',age:30,array:[3,4,5]})")); + + + // test array + + ResultSet resultSet = api.query("social", "WITH [0,1,2] as x return x"); + + // check header + Assert.assertNotNull(resultSet.getHeader()); + Header header = resultSet.getHeader(); + + + List schemaNames = header.getSchemaNames(); + List schemaTypes = header.getSchemaTypes(); + + Assert.assertNotNull(schemaNames); + Assert.assertNotNull(schemaTypes); + + Assert.assertEquals(1, schemaNames.size()); + Assert.assertEquals(1, schemaTypes.size()); + + Assert.assertEquals("x", schemaNames.get(0)); + Assert.assertEquals(COLUMN_SCALAR, schemaTypes.get(0)); + + // check record + Assert.assertEquals(1, resultSet.size()); + Assert.assertTrue(resultSet.hasNext()); + Record record = resultSet.next(); + Assert.assertFalse(resultSet.hasNext()); + Assert.assertEquals(Arrays.asList("x"), record.keys()); + + + List x = record.getValue("x"); + Assert.assertEquals(Arrays.asList(0,1,2), x); + + // test collect + resultSet = api.query("social", "MATCH(n) return collect(n) as x"); + + Assert.assertNotNull(resultSet.getHeader()); + header = resultSet.getHeader(); + + + schemaNames = header.getSchemaNames(); + schemaTypes = header.getSchemaTypes(); + + Assert.assertNotNull(schemaNames); + Assert.assertNotNull(schemaTypes); + + Assert.assertEquals(1, schemaNames.size()); + Assert.assertEquals(1, schemaTypes.size()); + + Assert.assertEquals("x", schemaNames.get(0)); + Assert.assertEquals(COLUMN_SCALAR, schemaTypes.get(0)); + + // check record + Assert.assertEquals(1, resultSet.size()); + Assert.assertTrue(resultSet.hasNext()); + record = resultSet.next(); + Assert.assertFalse(resultSet.hasNext()); + Assert.assertEquals(Arrays.asList("x"), record.keys()); + x = record.getValue("x"); + Assert.assertEquals(Arrays.asList(expectedANode, expectedBNode), x); + + + // test unwind + resultSet = api.query("social", "unwind([0,1,2]) as x return x"); + + Assert.assertNotNull(resultSet.getHeader()); + header = resultSet.getHeader(); + + + schemaNames = header.getSchemaNames(); + schemaTypes = header.getSchemaTypes(); + + Assert.assertNotNull(schemaNames); + Assert.assertNotNull(schemaTypes); + + Assert.assertEquals(1, schemaNames.size()); + Assert.assertEquals(1, schemaTypes.size()); + + Assert.assertEquals("x", schemaNames.get(0)); + Assert.assertEquals(COLUMN_SCALAR, schemaTypes.get(0)); + + // check record + Assert.assertEquals(3, resultSet.size()); + + for (int i = 0; i < 3; i++) { + Assert.assertTrue(resultSet.hasNext()); + record = resultSet.next(); + Assert.assertEquals(Arrays.asList("x"), record.keys()); + Assert.assertEquals(i, (int) record.getValue("x")); + + } + + } }