Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package apoc.neo4j.docker;

import apoc.util.Neo4jContainerExtension;
import apoc.util.TestContainerUtil;
import org.junit.*;
import org.neo4j.driver.Session;
import org.neo4j.driver.summary.ResultSummary;

import java.util.List;
import java.util.Map;

import static apoc.util.TestContainerUtil.createEnterpriseDB;
import static apoc.util.TestContainerUtil.testResult;

public class ImportExportEnterpriseTest {
private static Neo4jContainerExtension neo4jContainer;
private static Session session;

@BeforeClass
public static void beforeAll() throws InterruptedException {
neo4jContainer = createEnterpriseDB(List.of(TestContainerUtil.ApocPackage.EXTENDED, TestContainerUtil.ApocPackage.CORE), true)
.withNeo4jConfig("apoc.import.file.enabled", "true")
.withNeo4jConfig("apoc.export.file.enabled", "true")
.withNeo4jConfig("internal.cypher.enable_vector_type", "true");
neo4jContainer.start();
session = neo4jContainer.getSession();



}

@AfterClass
public static void afterAll() {
neo4jContainer.close();
}

@Before
public void before() {
var vectorTypes = List.of("INT64", "INT32", "INT16", "INT8", "FLOAT64", "FLOAT32");
for (String type : vectorTypes) {
session.executeWrite(
tx -> tx.run("CYPHER 25 CREATE (:VectorFoo { z: VECTOR([1, 2, 3], 3, %s) });".formatted(type)).consume()
);
}
}

@After
public void after() {
session.executeWrite(tx -> tx.run("MATCH (n:VectorFoo) DETACH DELETE n").consume());
}

@Test
public void testParquet() {
ResultSummary resultSummary = session.executeWrite(tx -> tx.run("CALL apoc.export.parquet.all('test.parquet')").consume());
System.out.println("resultSummary = " + resultSummary);

ResultSummary resultSummary1 = session.executeWrite(tx -> tx.run("CALL apoc.import.parquet('test.parquet')").consume());
System.out.println("resultSummary1 = " + resultSummary1);

ResultSummary resultSummary2 = session.executeWrite(tx -> tx.run("CALL apoc.load.parquet('test.parquet')").consume());
System.out.println("resultSummary2 = " + resultSummary2);

testResult(session, "CALL apoc.load.parquet('test.parquet')", r -> {
Map<String, Object> next = r.next();
System.out.println("next = " + next);

});

// todo - wait for next driver versions?
// --- session.run("MATCH (n) RETURN n").list()
/*
org.neo4j.driver.internal.util.ErrorUtil$InternalExceptionCause
detailMessage = "Struct tag: 0x56 representing type VECTOR is not supported for this protocol version"
cause = {Neo4jException@7483} "org.neo4j.driver.exceptions.Neo4jException: 22NBD: Unsupported struct tag: 0x56."
*/

session.run("MATCH (n) RETURN n").list();

System.out.println("resultSummary2 = " + resultSummary2);
}

@Test
public void testXls() {
// TODO
}

@Test
public void testArrow() {
// TODO - import and export, since load that is in LoadArrowExtended
}

@Test
public void testCsv() {
// TODO - test just the load, if the export is feasible, otherwise write a new issue or so
}
}
2 changes: 1 addition & 1 deletion extended/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ dependencies {
// They need to be provided either through the database or in an extra .jar
compileOnly group: 'org.neo4j', name: 'neo4j', version: neo4jVersionEffective
// same version as the one included in neo4j `lib`
compileOnly group: 'org.neo4j.driver', name: 'neo4j-java-driver', version: '5.28.7'
compileOnly group: 'org.neo4j.driver', name: 'neo4j-java-driver', version: '6.0.0-beta01'

compileOnly group: 'org.apache.poi', name: 'poi', version: '5.1.0', {
exclude group: 'org.apache.commons', module: 'commons-collections4'
Expand Down
3 changes: 3 additions & 0 deletions extended/src/main/java/apoc/load/xls/LoadXls.java
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,9 @@ private Object convertType(Object value) {
return dateParse(value.toString(), OffsetTime.class, dateParse);
case DURATION:
return durationParse(value.toString());
// TODO - vector
case VECTOR:
return null;
default:
return value;
}
Expand Down
2 changes: 2 additions & 0 deletions extended/src/main/java/apoc/util/ExtendedUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ public static String dateFormat( TemporalAccessor value, String format){
public static double doubleValue( Entity pc, String prop, Number defaultValue) {
return Util.toDouble(pc.getProperty(prop, defaultValue));
}

// TODO - vector parse??

public static Duration durationParse(String value) {
return Duration.parse(value);
Expand Down
3 changes: 3 additions & 0 deletions extended/src/test/java/apoc/ComparePerformancesTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@

@Ignore("This test compare import/export procedures performances, we ignore it since it's slow and just log the times spent")
public class ComparePerformancesTest {

// TODO - roundrip with these kind of procedures and vectors

private static final File directory = new File("target/import");
static { //noinspection ResultOfMethodCallIgnored
directory.mkdirs();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import apoc.meta.Meta;
import apoc.meta.MetaRestricted;
import apoc.util.TestUtil;
import apoc.util.Util;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.ClassRule;
Expand All @@ -14,13 +15,17 @@

import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static apoc.ApocConfig.APOC_EXPORT_FILE_ENABLED;
import static apoc.ApocConfig.APOC_IMPORT_FILE_ENABLED;
import static apoc.ApocConfig.apocConfig;
import static apoc.export.arrow.ArrowTestUtil.ARROW_BASE_FOLDER;
import static apoc.export.arrow.ArrowTestUtil.testImportCommon;
import static org.neo4j.configuration.SettingImpl.newBuilder;
import static org.neo4j.configuration.SettingValueParsers.BOOL;

public class ImportArrowExtendedTest {
private static File directory = new File(ARROW_BASE_FOLDER);
Expand All @@ -29,13 +34,45 @@ public class ImportArrowExtendedTest {
}

private final Map<String, Object> MAPPING_ALL = Map.of("mapping",
Map.of("bffSince", "Duration", "place", "Point", "listInt", "LongArray", "born", "LocalDateTime")
Util.map("bffSince", "Duration",
"place", "Point",
"listInt", "LongArray",
"born", "LocalDateTime",
"INTEGER64", "vector",
"INTEGER32", "vector",
"INTEGER16", "vector",
"INTEGER8", "vector",
"FLOAT64", "vector",
"FLOAT32", "vector"
)
);

@ClassRule
public static DbmsRule db = new ImpermanentDbmsRule()

.withSetting(
GraphDatabaseSettings.procedure_unrestricted,
List.of(
"apoc.meta.nodes.count",
"apoc.meta.stats",
"apoc.meta.data",
"apoc.meta.schema",
"apoc.meta.nodeTypeProperties",
"apoc.meta.relTypeProperties",
"apoc.meta.graph",
"apoc.meta.graph.of",
"apoc.meta.graphSample",
"apoc.meta.subGraph"))
.withSetting(GraphDatabaseInternalSettings.cypher_enable_vector_type, true)
.withSetting(
newBuilder("internal.dbms.debug.track_cursor_close", BOOL, false)
.build(),
false)
.withSetting(
newBuilder("internal.dbms.debug.trace_cursors", BOOL, false).build(), false)
.withSetting(GraphDatabaseInternalSettings.cypher_enable_vector_type, true)
.withSetting(GraphDatabaseInternalSettings.enable_experimental_cypher_versions, true)
.withSetting(GraphDatabaseSettings.load_csv_file_url_root, directory.toPath().toAbsolutePath());
.withSetting(GraphDatabaseSettings.load_csv_file_url_root, directory.toPath().toAbsolutePath());



Expand All @@ -48,8 +85,17 @@ public static void beforeClass() {
public void before() {
db.executeTransactionally("MATCH (n) DETACH DELETE n");

var vectorTypes1 = List.of("INT64", "INT32", "INT16", "INT8", "FLOAT64", "FLOAT32");
for (String type : vectorTypes1) {
db.executeTransactionally("CYPHER 25 CREATE (:Foo { z: VECTOR([1, 2, 3], 3, %s) });".formatted(type));
}

db.executeTransactionally("CREATE (f:User {name:'Adam',age:42,male:true,kids:['Sam','Anna','Grace'], born:localdatetime('2015-05-18T19:32:24.000'), place:point({latitude: 13.1, longitude: 33.46789, height: 100.0})})-[:KNOWS {since: 1993, bffSince: duration('P5M1.5D')}]->(b:User {name:'Jim',age:42})");
db.executeTransactionally("CREATE (:Another {foo:1, listInt: [1,2]}), (:Another {bar:'Sam'})");
// var vectorTypes = List.of("INTEGER64", "INTEGER32", "INTEGER16", "INTEGER8", "FLOAT64","FLOAT32");
var vectorTypes = List.of("INT64", "INT32", "INT16", "INT8", "FLOAT64", "FLOAT32");
var types = vectorTypes.stream().map(i -> "%1$s: VECTOR([1,2,3], 3, %1$s)".formatted(i)).collect(Collectors.joining(","));
db.executeTransactionally("CYPHER 25 CREATE (:Vectors {%s})".formatted(types));

apocConfig().setProperty(APOC_IMPORT_FILE_ENABLED, true);
apocConfig().setProperty(APOC_EXPORT_FILE_ENABLED, true);
Expand Down
Loading
Loading