diff --git a/common/src/main/java/apoc/export/util/MetaInformation.java b/common/src/main/java/apoc/export/util/MetaInformation.java index dd6131f61..072c3e740 100644 --- a/common/src/main/java/apoc/export/util/MetaInformation.java +++ b/common/src/main/java/apoc/export/util/MetaInformation.java @@ -133,8 +133,9 @@ public static String typeFor(Class value, Set allowed) { case NULL: return null; case INTEGER: - case FLOAT: return "integer".equals(name) || !isAllowed ? "int" : name; + case FLOAT: + return !isAllowed ? "float" : name; default: return isAllowed ? name : "string"; // We manage all other data types as strings } diff --git a/core/src/main/java/apoc/export/csv/CsvFormat.java b/core/src/main/java/apoc/export/csv/CsvFormat.java index 27b85eeb3..f92bb51c5 100644 --- a/core/src/main/java/apoc/export/csv/CsvFormat.java +++ b/core/src/main/java/apoc/export/csv/CsvFormat.java @@ -172,6 +172,8 @@ public void writeAll(InternalTransaction threadBoundTx, SubGraph graph, Reporter final var relPropTypes = collectPropTypesForRelationships(graph, db, config); final var nodeHeader = generateHeader(nodePropTypes, config.useTypes(), NODE_HEADER_FIXED_COLUMNS); final var relHeader = generateHeader(relPropTypes, config.useTypes(), REL_HEADER_FIXED_COLUMNS); + final var nodePropNames = nodePropTypes.keySet().stream().sorted().toList(); + final var relPropNames = relPropTypes.keySet().stream().sorted().toList(); final var header = new ArrayList<>(nodeHeader); header.addAll(relHeader); out.writeNext(header.toArray(String[]::new), applyQuotesToAll); @@ -182,7 +184,7 @@ public void writeAll(InternalTransaction threadBoundTx, SubGraph graph, Reporter graph, out, reporter, - nodeHeader.subList(NODE_HEADER_FIXED_COLUMNS.length, nodeHeader.size()), + nodePropNames, cols, config.getBatchSize(), config.shouldDifferentiateNulls()); @@ -191,7 +193,7 @@ public void writeAll(InternalTransaction threadBoundTx, SubGraph graph, Reporter graph, out, reporter, - relHeader.subList(REL_HEADER_FIXED_COLUMNS.length, relHeader.size()), + relPropNames, cols, nodeHeader.size(), config.getBatchSize(), @@ -350,7 +352,7 @@ private void writeNodes( SubGraph graph, CSVWriter out, Reporter reporter, - List header, + List nodePropTypes, int cols, int batchSize, boolean keepNulls) { @@ -359,7 +361,7 @@ private void writeNodes( for (Node node : graph.getNodes()) { row[0] = String.valueOf(getNodeId(threadBoundTx, node.getElementId())); row[1] = getLabelsString(node); - collectProps(header, node, reporter, row, 2, keepNulls); + collectProps(nodePropTypes, node, reporter, row, 2, keepNulls); out.writeNext(row, applyQuotesToAll); nodes++; if (batchSize == -1 || nodes % batchSize == 0) { @@ -392,7 +394,7 @@ private void writeRels( SubGraph graph, CSVWriter out, Reporter reporter, - List relHeader, + List relPropNames, int cols, int offset, int batchSize, @@ -405,7 +407,7 @@ private void writeRels( row[offset + 1] = String.valueOf(getNodeId(threadBoundTx, rel.getEndNode().getElementId())); row[offset + 2] = rel.getType().name(); - collectProps(relHeader, rel, reporter, row, 3 + offset, keepNull); + collectProps(relPropNames, rel, reporter, row, 3 + offset, keepNull); out.writeNext(row, applyQuotesToAll); rels++; if (batchSize == -1 || rels % batchSize == 0) { diff --git a/core/src/test/java/apoc/export/csv/ExportCsvTest.java b/core/src/test/java/apoc/export/csv/ExportCsvTest.java index f39db1c75..926f42267 100644 --- a/core/src/test/java/apoc/export/csv/ExportCsvTest.java +++ b/core/src/test/java/apoc/export/csv/ExportCsvTest.java @@ -490,6 +490,36 @@ public void testExportInvalidQuoteValue() { } } + @Test + public void textExportWithTypes() { + db.executeTransactionally( + "CREATE (n:TestNode) SET n = {valFloat:toFloat(123), name:'NodeName', valInt:5, dateVal: date('2024-11-01')};"); + TestUtil.testCall( + db, + """ + CALL apoc.graph.fromCypher("MATCH (n:TestNode) RETURN n", {}, 'TestNode.csv',{}) YIELD graph + CALL apoc.export.csv.graph(graph, null, {stream:true, useTypes:true}) YIELD properties, rows, data + return properties, rows, data; + """, + (r) -> { + String data = (String) r.get("data"); + // FLOAT value + assertTrue(data.contains("valFloat:float")); + assertTrue(data.contains("123.0")); + // INT value + assertTrue(data.contains("valInt:int")); + assertTrue(data.contains("5")); + // STRING value and unknown types to csv export + assertTrue(data.contains("name")); + assertTrue(data.contains("NodeName")); + assertTrue(data.contains("dateVal")); + assertTrue(data.contains("2024-11-01")); + }); + + final String deleteQuery = "MATCH (n:TestNode) DETACH DELETE n"; + db.executeTransactionally(deleteQuery); + } + @Test public void testExportAllCsvCompressed() { final CompressionAlgo compressionAlgo = DEFLATE;