Skip to content

Commit 4c6ec81

Browse files
committed
fix for LPG serialisation issue on multival properties
1 parent d206b09 commit 4c6ec81

File tree

4 files changed

+123
-14
lines changed

4 files changed

+123
-14
lines changed

src/main/java/n10s/rdf/export/LPGToRDFProcesssor.java

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import org.eclipse.rdf4j.model.vocabulary.RDFS;
1313
import org.neo4j.graphdb.*;
1414

15+
import java.time.LocalDateTime;
1516
import java.util.*;
1617
import java.util.stream.Collectors;
1718
import java.util.stream.Stream;
@@ -244,11 +245,36 @@ protected Set<Statement> processNode(Node node, Map<Long, IRI> ontologyEntities
244245
IRI predicate = (exportMappings.containsKey(key) ? vf.createIRI(exportMappings.get(key)) :
245246
vf.createIRI(BASE_SCH_NS, key));
246247
Object propertyValueObject = allProperties.get(key);
247-
if (propertyValueObject instanceof Object[]) {
248-
for (Object o : (Object[]) propertyValueObject) {
249-
statements.add(vf.createStatement(subject, predicate,
250-
createTypedLiteral(o)));
248+
if (propertyValueObject instanceof long[]) {
249+
for (int i = 0; i < ((long[]) propertyValueObject).length; i++) {
250+
Literal object = createTypedLiteral(((long[]) propertyValueObject)[i]);
251+
statements.add(
252+
vf.createStatement(subject, predicate, object));
251253
}
254+
} else if (propertyValueObject instanceof double[]) {
255+
for (int i = 0; i < ((double[]) propertyValueObject).length; i++) {
256+
Literal object = createTypedLiteral(((double[]) propertyValueObject)[i]);
257+
statements.add(
258+
vf.createStatement(subject, predicate, object));
259+
}
260+
} else if (propertyValueObject instanceof boolean[]) {
261+
for (int i = 0; i < ((boolean[]) propertyValueObject).length; i++) {
262+
Literal object = createTypedLiteral(((boolean[]) propertyValueObject)[i]);
263+
statements.add(
264+
vf.createStatement(subject, predicate, object));
265+
}
266+
} else if (propertyValueObject instanceof LocalDateTime[]) {
267+
for (int i = 0; i < ((LocalDateTime[]) propertyValueObject).length; i++) {
268+
Literal object = createTypedLiteral(((LocalDateTime[]) propertyValueObject)[i]);
269+
statements.add(
270+
vf.createStatement(subject, predicate, object));
271+
}
272+
} else if (propertyValueObject instanceof Object[]) {
273+
for (Object o : (Object[]) propertyValueObject) {
274+
statements.add(vf.createStatement(subject, predicate,
275+
createTypedLiteral(o)));
276+
}
277+
252278
} else {
253279
statements.add(vf.createStatement(subject, predicate,
254280
createTypedLiteral(propertyValueObject)));

src/main/java/n10s/rdf/export/RDFExportProcedures.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package n10s.rdf.export;
22

3+
import static n10s.graphconfig.GraphConfig.GRAPHCONF_VOC_URI_IGNORE;
4+
import static n10s.graphconfig.GraphConfig.GRAPHCONF_VOC_URI_MAP;
35
import static n10s.mapping.MappingUtils.getExportMappingsFromDB;
46

57
import java.util.HashMap;
@@ -44,7 +46,8 @@ public Stream<StreamedStatement> cypher(@Name("cypher") String cypher,
4446

4547
boolean rdfstar = props.containsKey("includeRelProperties") && props.get("includeRelProperties").equals(true);
4648
GraphConfig gc = getGraphConfig(tx);
47-
if (gc == null) {
49+
if (gc == null || gc.getHandleVocabUris() == GRAPHCONF_VOC_URI_IGNORE
50+
|| gc.getHandleVocabUris() == GRAPHCONF_VOC_URI_MAP) {
4851
proc = new LPGToRDFProcesssor(db, tx, gc,
4952
getExportMappingsFromDB(db), props.containsKey("mappedElemsOnly") &&
5053
props.get("mappedElemsOnly").equals(true), rdfstar);
@@ -81,8 +84,8 @@ public Stream<StreamedStatement> spo(@Name("subject") String subject,
8184
boolean rdfstar = props.containsKey("includeRelProperties") && props.get("includeRelProperties").equals(true);
8285

8386
GraphConfig gc = getGraphConfig(tx);
84-
if (gc == null) {
85-
// throw new UnsupportedOperationException("method not currently implemented for non-RDF graphs");
87+
if (gc == null || gc.getHandleVocabUris() == GRAPHCONF_VOC_URI_IGNORE
88+
|| gc.getHandleVocabUris() == GRAPHCONF_VOC_URI_MAP) {
8689
proc = new LPGToRDFProcesssor(db, tx, gc,
8790
getExportMappingsFromDB(db), props.containsKey("mappedElemsOnly") &&
8891
props.get("mappedElemsOnly").equals(true), rdfstar);

src/test/java/n10s/endpoint/RDFEndpointTest.java

Lines changed: 78 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
package n10s.endpoint;
22

33
import static n10s.graphconfig.Params.PREFIX_SEPARATOR;
4-
import static org.junit.Assert.assertEquals;
5-
import static org.junit.Assert.assertNull;
6-
import static org.junit.Assert.assertTrue;
4+
import static org.junit.Assert.*;
75
import static org.neo4j.internal.helpers.collection.Iterators.count;
86

97
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -14,9 +12,8 @@
1412
import java.nio.charset.StandardCharsets;
1513
import java.nio.file.Files;
1614
import java.nio.file.Paths;
17-
import java.util.HashMap;
18-
import java.util.Map;
19-
import java.util.Set;
15+
import java.util.*;
16+
2017
import n10s.ModelTestUtils;
2118
import n10s.graphconfig.GraphConfigProcedures;
2219
import n10s.mapping.MappingUtils;
@@ -26,8 +23,11 @@
2623
import n10s.quadrdf.load.QuadRDFLoadProcedures;
2724
import n10s.rdf.RDFProcedures;
2825
import n10s.rdf.delete.RDFDeleteProcedures;
26+
import n10s.rdf.export.ExportProcessor;
27+
import n10s.rdf.export.RDFExportProcedures;
2928
import n10s.rdf.load.RDFLoadProcedures;
3029
import n10s.validation.ValidationProcedures;
30+
import org.eclipse.rdf4j.model.vocabulary.RDF;
3131
import org.eclipse.rdf4j.rio.RDFFormat;
3232
import org.junit.Rule;
3333
import org.junit.Test;
@@ -60,7 +60,8 @@ public class RDFEndpointTest {
6060
.withProcedure(RDFDeleteProcedures.class)
6161
.withProcedure(OntoLoadProcedures.class)
6262
.withProcedure(NsPrefixDefProcedures.class)
63-
.withProcedure(ValidationProcedures.class);
63+
.withProcedure(ValidationProcedures.class)
64+
.withProcedure(RDFExportProcedures.class);
6465

6566
@Rule
6667
public Neo4jRule temp = new Neo4jRule().withProcedure(RDFLoadProcedures.class)
@@ -2441,4 +2442,74 @@ public void testCypherOnQuadRDFAfterDeleteRDFBNodes() throws Exception {
24412442

24422443
}
24432444

2445+
2446+
@Test
2447+
public void testTicket13061() throws Exception {
2448+
// Given
2449+
final GraphDatabaseService graphDatabaseService = neo4j.defaultDatabaseService();
2450+
//create constraint
2451+
try (Transaction tx = graphDatabaseService.beginTx()) {
2452+
tx.execute("CREATE CONSTRAINT n10s_unique_uri "
2453+
+ "ON (r:Resource) ASSERT r.uri IS UNIQUE");
2454+
tx.commit();
2455+
}
2456+
//create graph config and import RDF
2457+
try (Transaction tx = graphDatabaseService.beginTx()) {
2458+
tx.execute("CALL n10s.graphconfig.init(" +
2459+
"{ handleVocabUris: 'MAP', handleMultival: 'ARRAY', keepCustomDataTypes: true, keepLangTag: true})");
2460+
Result importResult = tx.execute("CALL n10s.rdf.import.fetch('" +
2461+
RDFEndpointTest.class.getClassLoader().getResource("data13061.trig")
2462+
.toURI() + "','TriG',{})");
2463+
2464+
tx.commit();
2465+
} catch (Exception e){
2466+
fail("exception raised on rdf.import");
2467+
}
2468+
// check data is correctly loaded
2469+
Long id;
2470+
try (Transaction tx = graphDatabaseService.beginTx()) {
2471+
Result result = tx.execute("match (n:ConceptScheme) return properties(n) as n, size((n)--()) as deg");
2472+
Map<String, Object> next = result.next();
2473+
Map<String,Object> n = (Map<String,Object>)next.get("n");
2474+
long[] tcVals = (long[])n.get("topConcepts");
2475+
assertEquals(3L, tcVals.length);
2476+
long[] expected = new long[]{0, 3, 5};
2477+
assertTrue(Arrays.equals(tcVals, expected));
2478+
assertEquals(1L, next.get("deg"));
2479+
2480+
2481+
Result res
2482+
= tx
2483+
.execute(" CALL n10s.rdf.export.cypher(' match(n:ConceptScheme) return n ', {}) ");
2484+
assertTrue(res.hasNext());
2485+
while(res.hasNext()){
2486+
Map<String, Object> triple = res.next();
2487+
assertTrue(triple.get("subject").equals("http://data.elsevier.com/vocabulary/OmniScience"));
2488+
List<String> expectedList = new ArrayList<String>();
2489+
expectedList.add("0");
2490+
expectedList.add("3");
2491+
expectedList.add("5");
2492+
assertTrue((triple.get("predicate").equals(RDF.TYPE.stringValue()) &&
2493+
triple.get("object").equals("neo4j://graph.schema#ConceptScheme"))
2494+
|| (triple.get("predicate").equals("neo4j://graph.schema#topConcepts") &&
2495+
expectedList.contains(triple.get("object"))) &&
2496+
triple.get("isLiteral").equals(true) && triple.get("literalType").equals("http://www.w3.org/2001/XMLSchema#long"));
2497+
}
2498+
}
2499+
2500+
Map<String, Object> map = new HashMap<>();
2501+
map.put("cypher", "match(n:ConceptScheme) return n");
2502+
2503+
Response response = HTTP.withHeaders("Accept", "text/plain").POST(
2504+
HTTP.GET(neo4j.httpURI().resolve("rdf").toString()).location() + "neo4j/cypher", map);
2505+
2506+
assertEquals(200, response.status());
2507+
String expected = "<http://data.elsevier.com/vocabulary/OmniScience> <neo4j://graph.schema#topConcepts> \"5\"^^<http://www.w3.org/2001/XMLSchema#long> .\n" +
2508+
"<http://data.elsevier.com/vocabulary/OmniScience> <neo4j://graph.schema#topConcepts> \"3\"^^<http://www.w3.org/2001/XMLSchema#long> .\n" +
2509+
"<http://data.elsevier.com/vocabulary/OmniScience> <neo4j://graph.schema#topConcepts> \"0\"^^<http://www.w3.org/2001/XMLSchema#long> .\n" +
2510+
"<http://data.elsevier.com/vocabulary/OmniScience> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <neo4j://graph.schema#ConceptScheme> .\n";
2511+
assertTrue(ModelTestUtils
2512+
.compareModels(expected, RDFFormat.NTRIPLES, response.rawContent(), RDFFormat.TURTLE));
2513+
}
2514+
24442515
}

src/test/resources/data13061.trig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
2+
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
3+
@prefix skos: <http://www.w3.org/2004/02/skos/core#> .
4+
5+
<http://data.elsevier.com/vocabulary/OmniScience> {
6+
<http://data.elsevier.com/vocabulary/OmniScience> a skos:ConceptScheme;
7+
skos:hasTopConcept <http://data.elsevier.com/vocabulary/OmniScience/Concept-189723269>;
8+
<https://data.elsevier.com/schema/evo/topConcepts> 0, 3, 5 .
9+
}

0 commit comments

Comments
 (0)