Skip to content

Commit

Permalink
fixes neo4j-contrib#1319: apoc.meta.data() reports wrong property typ…
Browse files Browse the repository at this point in the history
…e information
  • Loading branch information
conker84 committed Oct 30, 2019
1 parent c426bb2 commit dfcaba0
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 16 deletions.
38 changes: 27 additions & 11 deletions src/main/java/apoc/meta/Meta.java
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ public static class MetaResult {
public List<String> other = new ArrayList<>();
public List<String> otherLabels = new ArrayList<>();
public String elementType;
public List<String> types = new ArrayList<>();

public MetaResult addLabel(String label) {
this.otherLabels.add(label);
Expand Down Expand Up @@ -167,7 +168,17 @@ public MetaResult other(List<String> labels) {
}

public MetaResult type(String type) {
this.type = type;
if (type == null || type.equals(this.type)) {
return this;
}
if (!this.types.contains(type)) {
this.types.add(type);
}
if (!type.equals(this.type) && this.type != null) {
this.type = Types.STRING.toString();
} else {
this.type = type;
}
return this;
}

Expand Down Expand Up @@ -426,7 +437,7 @@ public Stream<MapResult> schema(@Name(value = "config",defaultValue = "{}") Map<
return Stream.of(new MapResult(nodes));
}

private Map<String, Map<String, MetaResult>> collectMetaData (MetaConfig config) {
private Map<String, Map<String, MetaResult>> collectMetaData(MetaConfig config) {
Map<String,Map<String,MetaResult>> metaData = new LinkedHashMap<>(100);
Schema schema = db.schema();

Expand Down Expand Up @@ -505,7 +516,11 @@ private Map<String, Object> collectNodesMetaData(MetaStats metaStats, Map<String
labels = metaResult.otherLabels;
if (!metaResult.type.equals("RELATIONSHIP")) { // NODE PROPERTY
entityProperties.put(entityDataKey,
MapUtil.map("type", metaResult.type, "indexed", metaResult.index, "unique", metaResult.unique, "existence", metaResult.existence));
MapUtil.map("type", metaResult.type,
"indexed", metaResult.index,
"unique", metaResult.unique,
"existence", metaResult.existence,
"types", metaResult.types));
} else {
entityRelationships.put(metaResult.property,
MapUtil.map("direction", "out", "count", metaResult.rightCount, "labels", metaResult.other,
Expand Down Expand Up @@ -577,7 +592,8 @@ private Map<String, Object> collectRelationshipsMetaData(MetaStats metaStats, Ma
entityProperties.put(entityDataKey, MapUtil.map(
"type", metaResult.type,
"array", metaResult.array,
"existence", metaResult.existence));
"existence", metaResult.existence,
"types", metaResult.types));
}
}
if (isRelationship) {
Expand All @@ -592,11 +608,9 @@ private Map<String, Object> collectRelationshipsMetaData(MetaStats metaStats, Ma

private void addProperties(Map<String, MetaResult> properties, String labelName, Iterable<ConstraintDefinition> constraints, Set<String> indexed, PropertyContainer pc, Node node) {
for (String prop : pc.getPropertyKeys()) {
if (properties.containsKey(prop)) continue;
MetaResult res = metaResultForProp(pc, labelName, prop);
res.elementType(Types.of(pc).name());
MetaResult res = properties.computeIfAbsent(prop, (key) -> metaResultForProp(pc, labelName, prop));
setMetaResultPropertyType(res, pc.getProperty(prop));
addSchemaInfo(res, prop, constraints, indexed, node);
properties.put(prop,res);
}
}

Expand Down Expand Up @@ -659,13 +673,15 @@ private void addSchemaInfo(MetaResult res, String prop, Iterable<ConstraintDefin

private MetaResult metaResultForProp(PropertyContainer pc, String labelName, String prop) {
MetaResult res = new MetaResult(labelName, prop);
Object value = pc.getProperty(prop);
res.type(Types.of(value).name());
res.elementType(Types.of(pc).name());
return res;
}

private void setMetaResultPropertyType(MetaResult res, Object value) {
res.type(Types.of(value).name());
if (value.getClass().isArray()) {
res.array = true;
}
return res;
}

private List<String> toStrings(Iterable<Label> labels) {
Expand Down
86 changes: 81 additions & 5 deletions src/test/java/apoc/meta/MetaTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,19 @@

import java.time.Clock;
import java.time.LocalDate;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static apoc.util.MapUtil.map;
import static apoc.util.TestUtil.testCall;
import static apoc.util.TestUtil.testResult;
import static java.util.Arrays.asList;
import static java.util.Collections.singletonMap;
import static org.junit.Assert.assertEquals;
Expand Down Expand Up @@ -230,7 +236,7 @@ public void testMetaSchema() {
db.execute("CREATE (:Person:Actor:Director {name:'Tom', born:'05-06-1956', dead:false})-[:ACTED_IN {roles:'Forrest'}]->(:Movie {title:'Forrest Gump'})").close();
testCall(db, "CALL apoc.meta.schema()",
(row) -> {
List<String> emprtyList = new ArrayList<String>();
List<String> emptyList = new ArrayList<String>();
List<String> fullList = Arrays.asList("Actor","Director");

Map<String, Object> o = (Map<String, Object>) row.get("value");
Expand All @@ -242,9 +248,10 @@ public void testMetaSchema() {
assertNotNull(movie);
assertEquals("node", movie.get("type"));
assertEquals(1L, movie.get("count"));
assertEquals(emprtyList, movie.get("labels"));
assertEquals(4, movieTitleProperties.size());
assertEquals(emptyList, movie.get("labels"));
assertEquals(5, movieTitleProperties.size());
assertEquals("STRING", movieTitleProperties.get("type"));
assertEquals(Arrays.asList("STRING"), movieTitleProperties.get("types"));
assertEquals(true, movieTitleProperties.get("indexed"));
assertEquals(false, movieTitleProperties.get("unique"));
Map<String, Object> movieRel = (Map<String, Object>) movie.get("relationships");
Expand All @@ -268,14 +275,14 @@ public void testMetaSchema() {
assertNotNull(actor);
assertEquals("node", actor.get("type"));
assertEquals(1L, actor.get("count"));
assertEquals(emprtyList, actor.get("labels"));
assertEquals(emptyList, actor.get("labels"));

Map<String, Object> director = (Map<String, Object>) o.get("Director");
Map<String, Object> directorProperties = (Map<String, Object>) director.get("properties");
assertNotNull(director);
assertEquals("node", director.get("type"));
assertEquals(1L, director.get("count"));
assertEquals(emprtyList, director.get("labels"));
assertEquals(emptyList, director.get("labels"));
assertEquals(3, directorProperties.size());

Map<String, Object> actedIn = (Map<String, Object>) o.get("ACTED_IN");
Expand All @@ -284,6 +291,7 @@ public void testMetaSchema() {
assertNotNull(actedIn);
assertEquals("relationship", actedIn.get("type"));
assertEquals("STRING", actedInRoleProperty.get("type"));
assertEquals(Arrays.asList("STRING"), actedInRoleProperty.get("types"));
assertEquals(false, actedInRoleProperty.get("array"));
assertEquals(false, actedInRoleProperty.get("existence"));
});
Expand Down Expand Up @@ -652,4 +660,72 @@ public void testMetaGraphExtraRelsWithSample() throws Exception {
});
}

@Test
public void testMetaDataShouldReportArrayOfTypes() {
// given
db.execute("create (:A { x: 1 }), (:A { x: 'Foo' });").close();
db.execute("create (:B { x: 'Foo' }), (:B { x: 'Foo1' }), (:B { x: 1 });").close();
db.execute("create (:C { x: 1 }), (:C { x: 1 }), (:C { x: 1, y: 'Y' });").close();
Map<String, Object> expected = map("A.x", new AbstractMap.SimpleEntry<>("STRING", new HashSet<>(Arrays.asList("INTEGER", "STRING"))),
"B.x", new AbstractMap.SimpleEntry<>("STRING", new HashSet<>(Arrays.asList("INTEGER", "STRING"))),
"C.x", new AbstractMap.SimpleEntry<>("INTEGER", new HashSet<>(Arrays.asList("INTEGER"))),
"C.y", new AbstractMap.SimpleEntry<>("STRING", new HashSet<>(Arrays.asList("STRING"))));

// when
testResult(db, "CALL apoc.meta.data({sample: -1})",
(result) -> {
// then
Map<String, Object> actual = result.stream()
.collect(Collectors.toMap(e -> e.get("label") + "." + e.get("property"),
e -> new AbstractMap.SimpleEntry<>(e.get("type"), new HashSet<>((List<String>) e.get("types"))),
(u,v) -> { throw new IllegalStateException(String.format("Duplicate key %s", u)); },
LinkedHashMap::new));
assertEquals(expected, actual);
});
}

@Test
public void testMetaSchemaShouldReportTypes() {
// given
db.execute("CREATE (:Person {name:'Tom', age: 35})-[:KNOWS {since: 1993}]->(:Person {name: 'John', age: '35'})").close();
db.execute("CREATE (:Person {name:'Andrea'})-[:KNOWS {since: '2014'}]->(:Person {name: 'Michel'})").close();

// when
testCall(db, "CALL apoc.meta.schema({sample: -1})",
(row) -> {
// then
Map<String, Object> schema = (Map<String, Object>) row.get("value");
assertEquals(2, schema.size());

// Nodes
Map<String, Object> person = (Map<String, Object>) schema.get("Person");
assertEquals("node", person.get("type"));
assertEquals(4L, person.get("count"));
assertEquals(Collections.emptyList(), person.get("labels"));

Map<String, Object> personProperties = (Map<String, Object>) person.get("properties");

Map<String, Object> personName = (Map<String, Object>) personProperties.get("name");
assertEquals(5, personName.size());
assertEquals("STRING", personName.get("type"));
assertEquals(Arrays.asList("STRING"), personName.get("types"));

Map<String, Object> personAge = (Map<String, Object>) personProperties.get("age");
assertEquals(5, personAge.size());
assertEquals("STRING", personAge.get("type"));
assertEquals(new HashSet<>(Arrays.asList("STRING", "INTEGER")), new HashSet<>((List<String>) personAge.get("types")));

// Rels
Map<String, Object> knows = (Map<String, Object>) schema.get("KNOWS");
assertEquals("relationship", knows.get("type"));
assertEquals(2L, knows.get("count"));

Map<String, Object> knowsProperties = (Map<String, Object>) knows.get("properties");
Map<String, Object> knowsSince = (Map<String, Object>) knowsProperties.get("since");
assertEquals(4, knowsSince.size());
assertEquals("STRING", knowsSince.get("type"));
assertEquals(new HashSet<>(Arrays.asList("STRING", "INTEGER")), new HashSet<>((List<String>) personAge.get("types")));
});
}

}

0 comments on commit dfcaba0

Please sign in to comment.