diff --git a/demo/JavaClientDemo/movie b/demo/JavaClientDemo/movie deleted file mode 120000 index 563e4590c6..0000000000 --- a/demo/JavaClientDemo/movie +++ /dev/null @@ -1 +0,0 @@ -../movie \ No newline at end of file diff --git a/demo/JavaClientDemo/pom.xml b/demo/JavaClientDemo/pom.xml deleted file mode 100644 index 7101ac0668..0000000000 --- a/demo/JavaClientDemo/pom.xml +++ /dev/null @@ -1,169 +0,0 @@ - - - - 4.0.0 - - 1.8 - 1.8 - - - - org.sonatype.oss - oss-parent - 7 - - - com.antgroup.tugraph - tugraph-rpc-client-demo - 3.1.0 - - - - - Apache 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt - repo - - - - - - release - - - - org.apache.maven.plugins - maven-gpg-plugin - 1.6 - - - sign-artifacts - verify - - sign - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - 3.1.0 - - - attach-javadoc - compile - - jar - - - - - none - UTF-8 - UTF-8 - UTF-8 - - - - - - - oss - https://oss.sonatype.org/content/repositories/snapshots/ - - - oss - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - - - - - - - - - org.apache.maven.plugins - maven-jar-plugin - - - - com.antgroup.tugraph.demo.TuGraphRpcClientDemo - - - - - - org.apache.maven.plugins - maven-assembly-plugin - 3.3.0 - - - jar-with-dependencies - - - - com.antgroup.tugraph.demo.TuGraphRpcClientDemo - jav - - - - - assemble-all - package - - single - - - - - - - - - - - com.antgroup.tugraph - tugraph-java-rpc-client - 0.0.1 - - - com.alibaba - fastjson - 1.2.83 - - - org.apache.logging.log4j - log4j-slf4j-impl - 2.13.2 - test - - - org.apache.logging.log4j - log4j-core - 2.17.1 - test - - - junit - junit - 4.13.1 - test - - - org.projectlombok - lombok - 1.18.16 - compile - - - org.junit.jupiter - junit-jupiter - RELEASE - compile - - - diff --git a/demo/JavaClientDemo/src/main/java/com/antgroup/tugraph/demo/TuGraphRpcClientDemo.java b/demo/JavaClientDemo/src/main/java/com/antgroup/tugraph/demo/TuGraphRpcClientDemo.java deleted file mode 100644 index 39a01d68ad..0000000000 --- a/demo/JavaClientDemo/src/main/java/com/antgroup/tugraph/demo/TuGraphRpcClientDemo.java +++ /dev/null @@ -1,285 +0,0 @@ -package com.antgroup.tugraph.demo; -import java.io.IOException; -import com.antgroup.tugraph.TuGraphRpcClient; -import org.junit.jupiter.api.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class TuGraphRpcClientDemo { - static Logger log = LoggerFactory.getLogger(TuGraphRpcClientDemo.class); - TuGraphRpcClient client = new TuGraphRpcClient("list://11.166.81.245:19090","admin", "73@TuGraph"); - void deleteAndCreate(String graphName) { - try { - // delete graph - client.callCypher(String.format("CALL dbms.graph.deleteGraph('%s')", graphName), "default", 1000); - } catch (Exception e) { - log.info(e.toString()); - } - // create graph - client.callCypher(String.format("CALL dbms.graph.createGraph('%s', 'this is a demo graph', 20)", graphName), "default", 1000); - } - - @Test - // import data by configuration file - void demo1() throws IOException { - String graphName = "demo1"; - deleteAndCreate(graphName); - // create vertex and edge labels described in 'schema' section of `movie/import_for_java_demo.json` - client.importSchemaFromFile("movie/import_for_java_demo.json", graphName, 1000); - - // get all vertex labels - String res = client.callCypher("CALL db.vertexLabels()", graphName, 1000); - log.info("CALL db.vertexLabels() : " + res); - // get all edge labels - res = client.callCypher("CALL db.edgeLabels()", graphName, 1000); - log.info("CALL db.edgeLabels() : " + res); - - // import vertex and edge data described in 'files' section of `movie/import_for_java_demo.json` - client.importDataFromFile("movie/import_for_java_demo.json",",", true, 4, 0, graphName, 10000); - - // count all vertexs - String vertexCount = client.callCypher("MATCH (n) RETURN count(n)", graphName, 1000); - log.info(vertexCount); - // count all edges - String edgeCount = client.callCypher("MATCH (n)-[r]->(m) RETURN count(r)", graphName, 1000); - log.info(edgeCount); - } - - // - @Test - // import data by data block - void demo2() throws IOException { - String graphName = "demo2"; - deleteAndCreate(graphName); - // create vertex and edge labels described in 'schema' section of `movie/import_for_java_demo.json` - client.importSchemaFromFile("movie/import_for_java_demo.json", graphName, 1000); -/* -{ - "files": [ - { - "format": "CSV", - "label": "person", - "columns": ["id", "name", "born", "poster_image"] - } - ] -} -*/ - String personDesc = "{\n" + - " \"files\": [\n" + - " {\n" + - " \"format\": \"CSV\",\n" + - " \"label\": \"person\",\n" + - " \"columns\": [\"id\", \"name\", \"born\", \"poster_image\"]\n" + - " }\n" + - " ]\n" + - " }"; - -/* -2,Laurence Fishburne,1961,https://image.tmdb.org/t/p/w185/mh0lZ1XsT84FayMNiT6Erh91mVu.jpg -3,Carrie-Anne Moss,1967,https://image.tmdb.org/t/p/w185/8iATAc5z5XOKFFARLsvaawa8MTY.jpg -4,Hugo Weaving,1960,https://image.tmdb.org/t/p/w185/3DKJSeTucd7krnxXkwcir6PgT88.jpg -5,Gloria Foster,1933,https://image.tmdb.org/t/p/w185/ahwiARgfOYctk6sOLBBk5w7cfH5.jpg -6,Joe Pantoliano,1951,https://image.tmdb.org/t/p/w185/zBvDX2HWepvW9im6ikgoyOL2Xj0.jpg -7,Marcus Chong,1967,https://image.tmdb.org/t/p/w185/zYfXjMszFajTb93phn2Fi6LwEGN.jpg -8,Matt Doran,1976,https://image.tmdb.org/t/p/w185/gLpWm3azLiXgDPRWo23AnG5WM7O.jpg -9,Anthony Ray Parker,1958,https://image.tmdb.org/t/p/w185/iMHr0onfM8v4uVdPVnXxXx2xwwN.jpg -10,Keanu Reeves,1964,https://image.tmdb.org/t/p/w185/id1qIb7cZs2eQno90KsKwG8VLGN.jpg -*/ - - String personData = "2,Laurence Fishburne,1961,https://image.tmdb.org/t/p/w185/mh0lZ1XsT84FayMNiT6Erh91mVu.jpg\n" + - "3,Carrie-Anne Moss,1967,https://image.tmdb.org/t/p/w185/8iATAc5z5XOKFFARLsvaawa8MTY.jpg\n" + - "4,Hugo Weaving,1960,https://image.tmdb.org/t/p/w185/3DKJSeTucd7krnxXkwcir6PgT88.jpg\n" + - "5,Gloria Foster,1933,https://image.tmdb.org/t/p/w185/ahwiARgfOYctk6sOLBBk5w7cfH5.jpg\n" + - "6,Joe Pantoliano,1951,https://image.tmdb.org/t/p/w185/zBvDX2HWepvW9im6ikgoyOL2Xj0.jpg\n" + - "7,Marcus Chong,1967,https://image.tmdb.org/t/p/w185/zYfXjMszFajTb93phn2Fi6LwEGN.jpg\n" + - "8,Matt Doran,1976,https://image.tmdb.org/t/p/w185/gLpWm3azLiXgDPRWo23AnG5WM7O.jpg\n" + - "9,Anthony Ray Parker,1958,https://image.tmdb.org/t/p/w185/iMHr0onfM8v4uVdPVnXxXx2xwwN.jpg\n" + - "10,Keanu Reeves,1964,https://image.tmdb.org/t/p/w185/id1qIb7cZs2eQno90KsKwG8VLGN.jpg"; - - // import vertex data of 'person' label - // personData : data block in csv format - // personDesc : describe the detailed format of the data block - client.importDataFromContent(personDesc, personData, ",", true, 4, graphName, 10000); - -/* -{ - "files": [ - { - "format": "CSV", - "label": "movie", - "columns": ["id","title","tagline","summary","poster_image","duration","rated"] - } - ] -} -*/ - String moiveDesc = "{\n" + - " \"files\": [\n" + - " {\n" + - " \"format\": \"CSV\",\n" + - " \"label\": \"movie\",\n" + - " \"columns\": [\"id\",\"title\",\"tagline\",\"summary\",\"poster_image\",\"duration\",\"rated\"]\n" + - " }\n" + - " ]\n" + - "}"; - -/* -82,Pulp Fiction,Just because you are a character doesn't mean you have character.,placeholder text,http://image.tmdb.org/t/p/w185/dM2w364MScsjFf8pfMbaWUcWrR.jpg,154,R -130,Cloud Atlas,Everything is Connected,placeholder text,http://image.tmdb.org/t/p/w185/k9gWDjfXM80iXQLuMvPlZgSFJgR.jpg,172,R -457,The Shawshank Redemption,Fear can hold you prisoner. Hope can set you free.,placeholder text,http://image.tmdb.org/t/p/w185/9O7gLzmreU0nGkIB6K3BsJbzvNv.jpg,142,R -471,The Godfather,An offer you can't refuse.,placeholder text,http://image.tmdb.org/t/p/w185/d4KNaTrltq6bpkFS01pYtyXa09m.jpg,175,R -496,The Godfather: Part II,I don't feel I have to wipe everybody out\ Tom. Just my enemies.,placeholder text,http://image.tmdb.org/t/p/w185/tHbMIIF51rguMNSastqoQwR0sBs.jpg,200,R -517,The Good\ the Bad and the Ugly,For three men the Civil War wasn't hell. It was practice.,placeholder text,http://image.tmdb.org/t/p/w185/8PD1dgf0kQHtRawoSxp1jFemI1q.jpg,161,R -532,The Dark Knight,Why So Serious?,placeholder text,http://image.tmdb.org/t/p/w185/1hRoyzDtpgMU7Dz4JF22RANzQO7.jpg,152,PG-13 -564,The Dark Knight Rises,The Legend Ends,placeholder text,http://image.tmdb.org/t/p/w185/dEYnvnUfXrqvqeRSqvIEtmzhoA8.jpg,165,PG-13 -*/ - String moiveData = "82,Pulp Fiction,Just because you are a character doesn't mean you have character.,placeholder text,http://image.tmdb.org/t/p/w185/dM2w364MScsjFf8pfMbaWUcWrR.jpg,154,R\n" + - "130,Cloud Atlas,Everything is Connected,placeholder text,http://image.tmdb.org/t/p/w185/k9gWDjfXM80iXQLuMvPlZgSFJgR.jpg,172,R\n" + - "457,The Shawshank Redemption,Fear can hold you prisoner. Hope can set you free.,placeholder text,http://image.tmdb.org/t/p/w185/9O7gLzmreU0nGkIB6K3BsJbzvNv.jpg,142,R\n" + - "471,The Godfather,An offer you can't refuse.,placeholder text,http://image.tmdb.org/t/p/w185/d4KNaTrltq6bpkFS01pYtyXa09m.jpg,175,R\n" + - "496,The Godfather: Part II,I don't feel I have to wipe everybody out\\ Tom. Just my enemies.,placeholder text,http://image.tmdb.org/t/p/w185/tHbMIIF51rguMNSastqoQwR0sBs.jpg,200,R\n" + - "517,The Good\\ the Bad and the Ugly,For three men the Civil War wasn't hell. It was practice.,placeholder text,http://image.tmdb.org/t/p/w185/8PD1dgf0kQHtRawoSxp1jFemI1q.jpg,161,R\n" + - "532,The Dark Knight,Why So Serious?,placeholder text,http://image.tmdb.org/t/p/w185/1hRoyzDtpgMU7Dz4JF22RANzQO7.jpg,152,PG-13\n" + - "564,The Dark Knight Rises,The Legend Ends,placeholder text,http://image.tmdb.org/t/p/w185/dEYnvnUfXrqvqeRSqvIEtmzhoA8.jpg,165,PG-13"; - - // import vertex data of 'moive' label - // moiveData : data block in csv format - // moiveDesc : describe the detailed format of the data block - client.importDataFromContent(moiveDesc, moiveData, ",", true, 4, graphName, 10000); - - - -/* -{ - "files": [ - { - "format":"CSV", - "label":"acted_in", - "SRC_ID":"person", - "DST_ID":"movie", - "columns": ["SRC_ID", "DST_ID", "role"] - } - ] -} -*/ - String actedInDesc = "{\n" + - " \"files\": [\n" + - " {\n" + - " \"format\":\"CSV\",\n" + - " \"label\":\"acted_in\",\n" + - " \"SRC_ID\":\"person\",\n" + - " \"DST_ID\":\"movie\",\n" + - " \"columns\": [\"SRC_ID\", \"DST_ID\", \"role\"]\n" + - " }\n" + - " ]\n" + - "}"; - -/* -2,82,Morpheus -2,130,Morpheus -2,457,Morpheus -3,496,Trinity -3,517,Trinity -3,564,Trinity -*/ - - String actedInData = "2,82,Morpheus\n" + - "2,130,Morpheus\n" + - "2,457,Morpheus\n" + - "3,496,Trinity\n" + - "3,517,Trinity\n" + - "3,564,Trinity"; - - // import edge data of 'acted_in' label - // actedInData : data block in csv format - // actedInDesc : describe the detailed format of the data block - client.importDataFromContent(actedInDesc, actedInData, ",", true, 4, graphName, 10000); - - // get all vertex labels - String res = client.callCypher("CALL db.vertexLabels()", graphName, 1000); - log.info("CALL db.vertexLabels() : " + res); - // get all edge labels - res = client.callCypher("CALL db.edgeLabels()", graphName, 1000); - log.info("CALL db.edgeLabels() : " + res); - - // count all vertexs - String vertexCount = client.callCypher("MATCH (n) RETURN count(n)", graphName, 1000); - log.info(vertexCount); - // count all edges - String edgeCount = client.callCypher("MATCH (n)-[r]->(m) RETURN count(r)", graphName, 1000); - log.info(edgeCount); - } - - @Test - // create vertex and edge labels by cypher statements - // create vertex and edge data by cypher statements - void demo3() throws IOException { - String graphName = "demo3"; - deleteAndCreate(graphName); - // create vertex `person` label - client.callCypher("CALL db.createVertexLabel(" + - "'person'," + // vertex name - "'id'," + // primary property - "'id', int32, false," + - "'name', string, false," + - "'born', int32, true," + - "'poster_image', string, true" + - ")", graphName, 1000); - - // create vertex `movie` label - client.callCypher("CALL db.createVertexLabel(" + - "'movie'," + // vertex name - "'id'," + // primary property - "'id',int32, false," + - "'title', string, false," + - "'tagline', string, false," + - "'summary', string, true," + - "'poster_image', string, true," + - "'duration', int32, false," + - "'rated', string, true" + - ")", graphName, 1000); - - // create edge `acted_in` label - client.callCypher(("CALL db.createEdgeLabel(" + - "'acted_in'," + // edge name - "'[]'," + // edge constraints. empty array means no constraints; - "'role', string, false)"), graphName, 1000); - - // doc-zh/3.developer-document/2.cypher.md - - // 2,Laurence Fishburne,1961,https://image.tmdb.org/t/p/w185/mh0lZ1XsT84FayMNiT6Erh91mVu.jpg - // create vertex by cypher - client.callCypher("CREATE (n:person {" + - "id: 2," + - "name: 'Laurence Fishburne'," + - "born: 1961," + - "poster_image: 'https://image.tmdb.org/t/p/w185/mh0lZ1XsT84FayMNiT6Erh91mVu.jpg'})", graphName, 1000); - - // 130,Cloud Atlas,Everything is Connected,placeholder text,http://image.tmdb.org/t/p/w185/k9gWDjfXM80iXQLuMvPlZgSFJgR.jpg,172,R - // create vertex by cypher - client.callCypher("CREATE (n:movie {" + - "id: 130," + - "title: 'Cloud Atlas'," + - "tagline:'Everything is Connected'," + - "summary: 'placeholder text'," + - "poster_image: 'http://image.tmdb.org/t/p/w185/k9gWDjfXM80iXQLuMvPlZgSFJgR.jpg'," + - "duration: 172," + - "rated: 'R'})", graphName, 1000); - - // create edge by cypher - client.callCypher("MATCH (a:person), (b:movie) WHERE a.id = 2 AND b.id = 130 CREATE (a)-[r:acted_in {role: 'Morpheus'}]->(b)", graphName, 1000); - - // get all vertex labels - String res = client.callCypher("CALL db.vertexLabels()", graphName, 1000); - log.info("CALL db.vertexLabels() : " + res); - // get all edge labels - res = client.callCypher("CALL db.edgeLabels()", graphName, 1000); - log.info("CALL db.edgeLabels() : " + res); - - // count all vertexs - String vertexCount = client.callCypher("MATCH (n) RETURN count(n)", graphName, 1000); - log.info(vertexCount); - // count all edges - String edgeCount = client.callCypher("MATCH (n)-[r]->(m) RETURN count(r)", graphName, 1000); - log.info(edgeCount); - } -} diff --git a/demo/JavaClientDemo/src/main/resources/log4j.properties b/demo/JavaClientDemo/src/main/resources/log4j.properties deleted file mode 100644 index a44181f7ef..0000000000 --- a/demo/JavaClientDemo/src/main/resources/log4j.properties +++ /dev/null @@ -1,7 +0,0 @@ -# -log4j.rootLogger = info,stdout,D,E - -log4j.appender.stdout = org.apache.log4j.ConsoleAppender -log4j.appender.stdout.Target = System.out -log4j.appender.stdout.layout = org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS}ms, [%t]:%m%n \ No newline at end of file diff --git a/demo/TuGraphOGMDemo/pom.xml b/demo/TuGraphOGMDemo/pom.xml deleted file mode 100644 index 5953df46e3..0000000000 --- a/demo/TuGraphOGMDemo/pom.xml +++ /dev/null @@ -1,93 +0,0 @@ - - - 4.0.0 - - org.example - TuGpraphOgmTest - 1.0-SNAPSHOT - - - 11 - 11 - - - - - org.neo4j - neo4j-ogm-api - 0.1.0-SNAPSHOT - - - - org.neo4j - neo4j-ogm-core - 0.1.0-SNAPSHOT - - - - org.neo4j - tugraph-rpc-driver - 0.1.0-SNAPSHOT - - - - org.assertj - assertj-core - compile - 3.20.2 - - - - org.apache.logging.log4j - log4j-slf4j-impl - 2.13.2 - test - - - org.apache.logging.log4j - log4j-core - 2.19.0 - test - - - - - - - org.apache.maven.plugins - maven-shade-plugin - 3.1.1 - - - package - - shade - - - - - - *:* - - META-INF/*.SF - META-INF/*.DSA - META-INF/*.RSA - - - - - - test.TestBase - - - false - - - - - - - diff --git a/demo/TuGraphOGMDemo/src/main/java/entity/Actor.java b/demo/TuGraphOGMDemo/src/main/java/entity/Actor.java deleted file mode 100644 index 09408bce7c..0000000000 --- a/demo/TuGraphOGMDemo/src/main/java/entity/Actor.java +++ /dev/null @@ -1,54 +0,0 @@ -package entity; - -import java.util.HashSet; -import java.util.Set; -import org.neo4j.ogm.annotation.Id; -import org.neo4j.ogm.annotation.NodeEntity; -import org.neo4j.ogm.annotation.Relationship; - -@NodeEntity -public class Actor { - - @Id - private Long id; - private String name; - - @Relationship(type = "ACTS_IN", direction = Relationship.Direction.OUTGOING) - private Set movies = new HashSet<>(); - - public Actor() { - } - - public Actor(String name) { - this.name = name; - } - - public void actsIn(Movie movie) { - movies.add(movie); - movie.getActors().add(this); - } - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public Set getMovies() { - return movies; - } - - public void setMovies(Set movies) { - this.movies = movies; - } -} diff --git a/demo/TuGraphOGMDemo/src/main/java/entity/Movie.java b/demo/TuGraphOGMDemo/src/main/java/entity/Movie.java deleted file mode 100644 index 06126ddb83..0000000000 --- a/demo/TuGraphOGMDemo/src/main/java/entity/Movie.java +++ /dev/null @@ -1,57 +0,0 @@ -package entity; - -import java.util.HashSet; -import java.util.Set; -import org.neo4j.ogm.annotation.Id; -import org.neo4j.ogm.annotation.NodeEntity; -import org.neo4j.ogm.annotation.Relationship; - -@NodeEntity -public class Movie { - - @Id - private Long id; - private String title; - private int released; - - @Relationship(type = "ACTS_IN", direction = Relationship.Direction.INCOMING) - Set actors = new HashSet<>(); - - public Movie() { - } - - public Movie(String title, int year) { - this.title = title; - this.released = year; - } - - public Long getId() { - return id; - } - - - public String getTitle() { - return title; - } - - public void setTitle(String title) { - this.title = title; - } - - public int getReleased() { - return released; - } - - public void setReleased(int released) { - this.released = released; - } - - public Set getActors() { - return actors; - } - - public void setActors(Set actors) { - this.actors = actors; - } - -} \ No newline at end of file diff --git a/demo/TuGraphOGMDemo/src/main/java/test/Client.java b/demo/TuGraphOGMDemo/src/main/java/test/Client.java deleted file mode 100644 index abd7dd9dfc..0000000000 --- a/demo/TuGraphOGMDemo/src/main/java/test/Client.java +++ /dev/null @@ -1,23 +0,0 @@ -package test; - -import org.neo4j.ogm.config.Configuration; -import org.neo4j.ogm.driver.Driver; -import org.neo4j.ogm.drivers.rpc.driver.RpcDriver; - -public class Client { - private static Configuration.Builder baseConfigurationBuilder; - - protected static Driver getDriver(String[] args) { - String databaseUri = "list://" + args[0]; - String username = args[1]; - String password = args[2]; - - Driver driver = new RpcDriver(); - baseConfigurationBuilder = new Configuration.Builder() - .uri(databaseUri) - .verifyConnection(true) - .credentials(username, password); - driver.configure(baseConfigurationBuilder.build()); - return driver; - } -} diff --git a/demo/TuGraphOGMDemo/src/main/java/test/TestBase.java b/demo/TuGraphOGMDemo/src/main/java/test/TestBase.java deleted file mode 100644 index 0357544f09..0000000000 --- a/demo/TuGraphOGMDemo/src/main/java/test/TestBase.java +++ /dev/null @@ -1,174 +0,0 @@ -package test; - -import com.alibaba.fastjson.JSONObject; -import entity.Actor; -import entity.Movie; -import org.neo4j.ogm.cypher.ComparisonOperator; -import org.neo4j.ogm.model.QueryStatistics; -import org.neo4j.ogm.model.Result; -import org.neo4j.ogm.session.Session; -import org.neo4j.ogm.session.SessionFactory; -import org.neo4j.ogm.cypher.Filter; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.*; - -import static java.util.Collections.emptyMap; -import static org.assertj.core.api.Assertions.*; - - -public class TestBase extends Client{ - private static final Logger log = LoggerFactory.getLogger(TestBase.class); - private static SessionFactory sessionFactory; - private static Session session; - - - public static void main(String[] args) { - if (args.length != 3) { - log.info("java -jar target/TuGpraphOgmTest-1.0-SNAPSHOT.jar [host:port] [user] [password]"); - return; - } - sessionFactory = new SessionFactory(getDriver(args), "entity"); - session = sessionFactory.openSession(); - - testCreate(); - testQuery(); - testUpdate(); - testDelete(); - } - - private static void testDelete() { - log.info("----------------testDelete--------------------"); - // Test1 CREATE -> DELETE - Actor a1 = new Actor(); - Actor a2 = new Actor(); - a1.setName("ado"); - a2.setName("abo"); - session.save(a1); - session.save(a2); - Collection savedActors = session.loadAll(Actor.class); - assertThat(savedActors).hasSize(4); - - List actorList = new ArrayList<>(); - actorList.add(a1); - actorList.add(a2); - session.delete(actorList); - Collection delActors = session.loadAll(Actor.class); - assertThat(delActors).hasSize(2); - - // Test2 MATCH -> DELETE - Result result = session.query("MATCH (n)-[r]->(n2) DELETE r", emptyMap()); - QueryStatistics statistics = result.queryStatistics(); - log.info("deleted " + statistics.getRelationshipsDeleted() + " edges"); - assertThat(statistics.getRelationshipsDeleted()).isEqualTo(3); - - // Test3 CREATE -> MATCH -> DELETE - Movie movie = new Movie("Speed", 2019); - Actor alice = new Actor("Alice Neeves"); - alice.actsIn(movie); - session.save(movie); - Movie m1 = session.load(Movie.class, movie.getId()); - session.delete(m1); - session.delete(alice); - Collection ms = session.loadAll(Movie.class); - assertThat(ms).hasSize(3); - - // Test4 DELETE -> LOADALL - session.deleteAll(Actor.class); - Collection actors = session.loadAll(Actor.class); - assertThat(actors).hasSize(0); - - // Test5 DELETE - session.purgeDatabase(); - Collection movies = session.loadAll(Movie.class); - assertThat(movies).hasSize(0); - } - - private static void testQuery() { - log.info("----------------testQuery--------------------"); - // Test1 LOADALL - Collection movies = session.loadAll(Movie.class); - assertThat(movies).hasSize(3); - List moviesList = new ArrayList<>(movies); - for (int i = 0; i < moviesList.size(); i++) { - log.info("Movie" + i + ": " + moviesList.get(i).getTitle() - + ", released in " + moviesList.get(i).getReleased()); - } - - //Test2 LOADALL(Filter) - Collection moviesFilter = session.loadAll(Movie.class, new Filter("released", ComparisonOperator.LESS_THAN, 1995)); - assertThat(moviesFilter).hasSize(2); - - //Test3 MATCH - HashMap parameters = new HashMap<>(); - parameters.put("title", "The Matrix"); - Iterable actual = session - .query(Movie.class, "MATCH (m:Movie{title: $title}) RETURN m", parameters); - assertThat(actual.iterator().next().getTitle()).isEqualTo("The Matrix"); - - // Test4 - HashMap parameters1 = new HashMap<>(); - parameters1.put("title", "The Matrix"); - Integer counts = session - .queryForObject(Integer.class, "MATCH (n:Movie{title: $title})-[r]-(m:Actor) RETURN COUNT(m) AS counts", parameters1); - assertThat(counts).isEqualTo(2); - - // Test5 - Result result6 = session.query("MATCH p = (m)-[rel]-(n) return p", Collections.emptyMap()); - assertThat(result6.queryResults()).hasSize(6); - } - - private static void testCreate() { - log.info("----------------testCreate--------------------"); - // Test1 CREATE -> LOAD - session.query("CALL db.createVertexLabel('Movie', 'title', 'title', STRING, false, 'released', INT32, true)", emptyMap()); - session.query("CALL db.createVertexLabel('Actor', 'name', 'name', STRING, false)", emptyMap()); - session.query("CALL db.createEdgeLabel('ACTS_IN', '[]')", emptyMap()); - session.query("CALL db.createVertexLabel('Director', 'name', 'name', STRING, false, 'age', INT16, true)", emptyMap()); - session.query("CALL db.createEdgeLabel('DIRECT', '[]')", emptyMap()); - Movie movie1 = new Movie("Jokes", 1990); - session.save(movie1); - Movie m1 = session.load(Movie.class, movie1.getId()); - assertThat(movie1.getId()).isEqualTo(m1.getId()); - assertThat(movie1.getTitle()).isEqualTo(m1.getTitle()); - - // Test2 CREATE -> LOAD - Movie movie = new Movie("The Matrix", 1999); - Actor keanu = new Actor("Keanu Reeves"); - keanu.actsIn(movie); - Actor carrie = new Actor("Carrie-Ann Moss"); - carrie.actsIn(movie); - session.save(movie); - Movie matrix = session.load(Movie.class, movie.getId()); - for(Actor actor : matrix.getActors()) { - log.info("Actor: " + actor.getName()); - } - assertThat(matrix.getActors()).hasSize(2); - - // Test3 CREATE -> MATCH - Result createResult = session.query("CREATE (n:Movie{title:\"The Shawshank Redemption\", released:1994})<-[r:DIRECT]-(n2:Director{name:\"Frank Darabont\", age:63})", emptyMap()); - QueryStatistics statistics = createResult.queryStatistics(); - assertThat(statistics.getNodesCreated()).isEqualTo(2); - assertThat(statistics.getRelationshipsCreated()).isEqualTo(1); - Result result = session.query("MATCH (m)-[r:DIRECT]->(n) return m,r,n", Collections.emptyMap()); - JSONObject r = (JSONObject) result.queryResults().iterator().next().get("r"); - for (String key : r.keySet()) { - log.info(key + ": " + r.get(key)); - } - assertThat(r).hasSize(6); - } - - private static void testUpdate() { - log.info("----------------testUpdate--------------------"); - // Test1: MATCH -> UPDATE -> LOAD - HashMap parameters = new HashMap<>(); - parameters.put("name", "Keanu Reeves"); - Actor actor = session.queryForObject(Actor.class, - "MATCH (actor:Actor{name:$name}) RETURN actor", parameters); - actor.setName("NOBU Reeves"); - session.save(actor); - Actor newactor = session.load(Actor.class, actor.getId()); - assertThat(newactor.getName()).isEqualTo("NOBU Reeves"); - } -} diff --git a/docs/en-US/source/5.developer-manual/5.ecosystem-tools/3.tugraph-explorer.md b/docs/en-US/source/5.developer-manual/5.ecosystem-tools/3.tugraph-explorer.md index 8b1476bb58..46afb76206 100644 --- a/docs/en-US/source/5.developer-manual/5.ecosystem-tools/3.tugraph-explorer.md +++ b/docs/en-US/source/5.developer-manual/5.ecosystem-tools/3.tugraph-explorer.md @@ -1,145 +1,3 @@ # TuGraph Explorer -TuGraph Explorer is strongly dependent on TuGraph, so before starting Explorer, we need to start TuGraph first. - - -## 1.Introduction - -TuGraph is a graph database independently developed by Ant Group, which provides graph database engine and graph analysis engine. Its main features are large data storage and computation, and it also supports efficient online transaction processing (OLTP) and Online analysis processing (OLAP). - - -## 2.Install TuGraph - -> Refer to the official documentation () for more information. - -TuGraph needs to be installed via Docker Image, follow these steps to install it locally: - -- install local Docker environment: reference[official documentation](https://docs.docker.com/get-started/); - -```shell -$ sudo docker --version -``` - -If the above command can print the docker version number successfully, it indicates that the docker environment has been installed. - -- To download TuGraph images:[Download TuGraph Image](https://tugraph-web.oss-cn-beijing.aliyuncs.com/tugraph/tugraph-3.3.0/TuGraph-Image-3.3.0.tar.gz) - -Currently, TuGraph provides an image file based on Ubuntu 16.04 LTS and CenterOS 7.3. The image file is a compressed file named lgraph_x.y.z.ar, where x.y.z is the version number of TuGraph. - -- Load the TuGraph image: - -```shell -// lgraph_lastest.tar.gz 是 TuGraph 镜像文件名 -$ docker import lgraph_lastest.tar.gz - -// After the loading is complete, a message is displayed indicating that the image has been loaded -``` - -- Start Docker - -```shell -$ docker run -d -v {host_data_dir}:/mnt -p 7090:7090 -it reg.docker.alibaba-inc.com/tugraph/tugraph:x.y.z -$ docker exec -it {container_id} bash - -// host_data_dir = /Users/moyee/tugraph -// container_id = xxx -$ docker run -d -v /Users/moyee/tugraph:/mnt -p 7090:7090 -it reg.docker.alibaba-inc.com/tugraph/tugraph:3.1.1 -$ docker exec -it xxx bash - -``` - -Parameter Description - -- -v volume mapping -- {host_data_dir} is a directory where the user wants to store data, such as/home/user1/workspace -- -p The function of Docker is port mapping. The example maps Docker's port 7090 to the local port 7090 -- {container_id} is the container id of Docker, which can be obtained through docker ps - - - -## 3.TuGraph operation - - - -### 3.1.Start TuGraph Service - -```shell -$ lgraph_server --config ~/demo/movie/lgraph.json -``` - -- lgraph.json is the configuration file for TuGraph - - - -### 3.2.TuGraph Browser Query - -TuGraph Browser Is a visual query tool provided by TuGraph. Users can open the browser, type {IP}:{Port}, enter the default username by 'admin', password by '73@TuGraph' to complete the login. Enter the TuGraph Query page after successful login.
![image.png](https://tugraph-web-static.oss-cn-beijing.aliyuncs.com/%E6%96%87%E6%A1%A3/2.Operating/7.tugraph-browser-query-01.png) - - -## 4.Introduction to TuGraph Explorer - -TuGraph Explorer is a GraphInsight based visual graph analysis platform that provides complete graph exploration and analysis capabilities to help users gain valuable insights from massive graph data. - - - -## 5.Start TuGraph Explorer - -Once TuGraph is installed successfully, you can start installing TuGraph Explorer. - -- Load TuGraph Explorer image: - -```shell -// lgraph_lastest.tar.gz TuGraph image file name -$ docker import tugraph_explore.tar.gz - -// After the loading is complete, a message is displayed indicating that the image has been loaded -``` - -- Start Docker - -```shell -$ docker run -d -p 7091:7091 -it reg.docker.alibaba-inc.com/tugraph-explore:1.0.1 -$ docker exec -it {container_id} bash -``` - -Parameter Description: - -- -p The function of Docker is port mapping. In this example, Docker port 7091 is mapped to local port 7091 -- {container_id} is the id of a Docker container, which can be obtained through docker ps - -- Start TuGraph Explorer - -```shell -$ cd /usr/src/tugraphexplore -$ npm run dev -- -p 7091 -``` - -After the TuGraph Explorer service started, it can be accessed through `**http://localhost:7091/tugraph/explore.html**` ,If everything is normal, you will see the following page.![image.png](https://tugraph-web-static.oss-cn-beijing.aliyuncs.com/tugraph-expolore/tugraph-explore-index.png) - -## 6.Connect TuGraph - -Once TuGraph Explorer is up, the first step is to connect to the TuGraph database. Click the "Connect" button to bring up the page for connecting to the Graph database, as shown in the image below. -![image.png](https://gw.alipayobjects.com/mdn/rms_fa12c2/afts/img/A*JEUKRYMH--4AAAAAAAAAAAAAARQnAQ) - -To connect to TuGraph data, we need to provide the following information: - -- Graph database account -- Graph database password -- Address of the graph database: The format is ip:port - -**The IP address needs to be the container IP address, which can be queried by running the following command**。 - -``` -$ docker run -d -v /Users/xx/tugraph:/mnt -p 7090:7090 -it reg.docker.alibaba-inc.com/tugraph/tugraph:3.3.0 -$ docker exec -it 8408b49033bc1698(TuGraph container) bash -$ cat /etc/hosts -127.0.0.1 localhost -::1 localhost ip6-localhost ip6-loopback -fe00::0 ip6-localnet -ff00::0 ip6-mcastprefix -ff02::1 ip6-allnodes -ff02::2 ip6-allrouters -172.17.0.4 8408b543243bc69 -``` - -As shown above, the address to connect the graph database should be filled in:**172.17.0.4:7090**。 +> TuGraph Explorer has merged to [TuGraph Browser](../../4.user-guide/1.tugraph-browser.md) \ No newline at end of file diff --git a/docs/zh-CN/source/5.developer-manual/5.ecosystem-tools/3.tugraph-explorer.md b/docs/zh-CN/source/5.developer-manual/5.ecosystem-tools/3.tugraph-explorer.md index f34f415719..5e31c46502 100644 --- a/docs/zh-CN/source/5.developer-manual/5.ecosystem-tools/3.tugraph-explorer.md +++ b/docs/zh-CN/source/5.developer-manual/5.ecosystem-tools/3.tugraph-explorer.md @@ -1,69 +1,3 @@ # TuGraph-Explorer -> TuGraph Explorer 强依赖 TuGraph,因此,在启动 Explorer 之前,我们先需要先启动 TuGraph。 - -## 1.TuGraph Explorer 简介 - -TuGraph Explorer 是基于 GraphInsight 构建的图可视分析平台,提供了完整的图探索分析能力,能够帮助用户从海量的图数据中洞察出有价值的信息。TuGraph Explorer 当前提供了Docker部署方式。 - -## 2.启动 TuGraph Explorer - -TuGraph 安装成功以后,就可以开始安装 TuGraph Explorer。 - -- 加载 TuGraph Explorer 镜像: - -```shell -// lgraph_lastest.tar.gz 是 TuGraph 镜像文件名 -$ docker import tugraph_explore.tar.gz - -// 加载完毕后, 提示已加载镜像 -``` - -- 启动 Docker - -```shell -$ docker run -d -p 7091:7091 -it reg.docker.alibaba-inc.com/tugraph-explore:1.0.1 -$ docker exec -it {container_id} bash -``` - -参数说明: - -- -p 的作用是端口映射,示例中将 Docker 的 7091 端口映射到本地的 7091 端口 -- {container_id} 是 Docker 的 container id,可以通过 docker ps 获得 - -- 启动 TuGraph Explorer - -```shell -$ cd /usr/src/tugraphexplore -$ npm run dev -- -p 7091 -``` - -TuGraph Explorer 服务启动起来以后,通过 **http://localhost:7091/tugraph/explore.html** 就可以访问了,如果一切正常,就会看到如下页面。![image.png](https://tugraph-web-static.oss-cn-beijing.aliyuncs.com/tugraph-expolore/tugraph-explore-index.png) - -## 3.连接 TuGraph - -TuGraph Explorer 启动起来以后,第一步就是需要连接 TuGraph 数据库。点击「连接」按钮,弹出连接图数据库的页面,如下图所示。 -![image.png](https://gw.alipayobjects.com/mdn/rms_fa12c2/afts/img/A*JEUKRYMH--4AAAAAAAAAAAAAARQnAQ) - -连接 TuGraph 数据,我们需要提供以下信息: - -- 图数据库的账号 -- 图数据库的密码 -- 图数据库的地址:格式为 ip:port - -**地址需要填写容器 IP,可以通过如下指令查看**。 - -``` -$ docker run -d -v /Users/xx/tugraph:/mnt -p 7090:7090 -it reg.docker.alibaba-inc.com/tugraph/tugraph:3.3.0 -$ docker exec -it 8408b49033bc1698(TuGraph 的容器) bash -$ cat /etc/hosts -127.0.0.1 localhost -::1 localhost ip6-localhost ip6-loopback -fe00::0 ip6-localnet -ff00::0 ip6-mcastprefix -ff02::1 ip6-allnodes -ff02::2 ip6-allrouters -172.17.0.4 8408b543243bc69 -``` - -如上所示,连接图数据库的地址应该填写:**172.17.0.4:7090**。 +> TuGraph Explorer 已经合并到 [TuGraph Browser](../../4.user-guide/1.tugraph-browser.md) diff --git a/include/fma-common/logging.h b/include/fma-common/logging.h index 4ca2a4d78d..26f215f20f 100644 --- a/include/fma-common/logging.h +++ b/include/fma-common/logging.h @@ -15,6 +15,7 @@ namespace fma_common { #define LOG() FMA_LOG() #define WARN() FMA_WARN() #define ERR() FMA_ERR() +#define FATAL() FMA_FATAL() #define CHECK(pred) FMA_CHECK(pred) #define CHECK_EQ(a, b) FMA_CHECK_EQ(a, b) #define CHECK_NEQ(a, b) FMA_CHECK_NEQ(a, b) diff --git a/include/lgraph/lgraph_types.h b/include/lgraph/lgraph_types.h index 7ccc9da6e2..2a335d8c6b 100644 --- a/include/lgraph/lgraph_types.h +++ b/include/lgraph/lgraph_types.h @@ -39,6 +39,7 @@ enum class AccessLevel { FULL = 3 }; +[[maybe_unused]] inline static std::string to_string(const AccessLevel& v) { switch (v) { case AccessLevel::NONE: return "NONE"; @@ -57,6 +58,7 @@ enum class FieldAccessLevel { WRITE = 2 }; +[[maybe_unused]] inline static std::string to_string(const FieldAccessLevel& v) { switch (v) { case FieldAccessLevel::NONE: return "NONE"; @@ -71,6 +73,7 @@ enum class GraphQueryType { GQL = 1 }; +[[maybe_unused]] inline static std::string to_string(const GraphQueryType& v) { switch (v) { case GraphQueryType::CYPHER: return "CYPHER"; @@ -106,6 +109,23 @@ struct EdgeOptions : LabelOptions { // edge temporal field, edge will be stored in the order of this field // Default: empty std::string temporal_field; + // order of edge temporal field + // Default: ASC + enum class TemporalFieldOrder { + ASC = 0, + DESC = 1, + } temporal_field_order = TemporalFieldOrder::ASC; + + inline static std::string to_string(const TemporalFieldOrder& v) { + switch (v) { + case TemporalFieldOrder::ASC: + return "ASC"; + case TemporalFieldOrder::DESC: + return "DESC"; + default: + throw std::runtime_error("Unknown TemporalFieldOrder"); + } + } EdgeOptions() = default; explicit EdgeOptions(const EdgeConstraints& edge_constraints) @@ -123,8 +143,8 @@ struct EdgeOptions : LabelOptions { constraints = "[" + constraints + "]"; return "detach_property: " + std::to_string(detach_property) + - ", edge_constraints: " + constraints + - ", temporal_field: " + temporal_field; + ", edge_constraints: " + constraints + ", temporal_field: " + temporal_field + + ", temporal_field_order: " + to_string(temporal_field_order); } void clear() { detach_property = false; diff --git a/src/core/data_type.h b/src/core/data_type.h index 82321be8c7..957e4fbcd8 100644 --- a/src/core/data_type.h +++ b/src/core/data_type.h @@ -46,6 +46,8 @@ typedef lgraph_api::EdgeConstraints EdgeConstraints; typedef lgraph_api::LabelOptions LabelOptions; typedef lgraph_api::VertexOptions VertexOptions; typedef lgraph_api::EdgeOptions EdgeOptions; +typedef lgraph_api::EdgeOptions::TemporalFieldOrder TemporalFieldOrder; + typedef int64_t VertexId; typedef int64_t EdgeId; diff --git a/src/core/lightning_graph.cpp b/src/core/lightning_graph.cpp index b6501e1878..2e1d24eef6 100644 --- a/src/core/lightning_graph.cpp +++ b/src/core/lightning_graph.cpp @@ -118,9 +118,9 @@ size_t LightningGraph::GetNumVertices() { template inline void CheckIsValidLabelFieldName(const std::string& lof) { - if (lof.empty() || lof.size() > 64) { + if (lof.empty() || lof.size() > 255) { throw InputError(std::string((IS_LABEL ? "Label" : "Field name")) + - " is invalid: must be between 1 and 64 bytes."); + " is invalid: must be between 1 and 255 bytes."); } if (fma_common::TextParserUtils::IsDigits(lof.front())) { throw InputError(std::string((IS_LABEL ? "Label" : "Field name")) + diff --git a/src/core/schema.cpp b/src/core/schema.cpp index b820454e9d..464f0526d1 100644 --- a/src/core/schema.cpp +++ b/src/core/schema.cpp @@ -414,6 +414,7 @@ void Schema::ClearFields() { void Schema::SetSchema(bool is_vertex, size_t n_fields, const FieldSpec* fields, const std::string& primary, const std::string& temporal, + const TemporalFieldOrder& temporal_order, const EdgeConstraints& edge_constraints) { if (_F_UNLIKELY(n_fields > _detail::MAX_NUM_FIELDS)) throw TooManyFieldsException(); fields_.clear(); @@ -433,6 +434,7 @@ void Schema::SetSchema(bool is_vertex, size_t n_fields, const FieldSpec* fields, is_vertex_ = is_vertex; primary_field_ = primary; temporal_field_ = temporal; + temporal_order_ = temporal_order; edge_constraints_ = edge_constraints; RefreshLayout(); } diff --git a/src/core/schema.h b/src/core/schema.h index 47cd88cc3a..69131df700 100644 --- a/src/core/schema.h +++ b/src/core/schema.h @@ -77,6 +77,7 @@ class Schema { std::string primary_field_{}; // temporal_field_ should be int64. std::string temporal_field_{}; + TemporalFieldOrder temporal_order_{}; // When Schema is VERTEX type, `edge_constraints_` is always empty. EdgeConstraints edge_constraints_; std::unordered_set fulltext_fields_; @@ -141,11 +142,14 @@ class Schema { */ void SetSchema(bool is_vertex, size_t n_fields, const FieldSpec* fields, const std::string& primary, const std::string& temporal, + const TemporalFieldOrder& temporal_order, const EdgeConstraints& edge_constraints); void SetSchema(bool is_vertex, const std::vector& fields, const std::string& primary, - const std::string& temporal, const EdgeConstraints& edge_constraints) { - SetSchema(is_vertex, fields.size(), fields.data(), primary, temporal, edge_constraints); + const std::string& temporal, const TemporalFieldOrder& temporal_order, + const EdgeConstraints& edge_constraints) { + SetSchema(is_vertex, fields.size(), fields.data(), primary, temporal, temporal_order, + edge_constraints); } void SetEdgeConstraintsLids(std::unordered_map> lids) { @@ -175,6 +179,7 @@ class Schema { const std::string& GetPrimaryField() const { return primary_field_; } bool DetachProperty() const { return detach_property_; } bool HasTemporalField() const { return !temporal_field_.empty(); } + TemporalFieldOrder GetTemporalOrder() const { return temporal_order_; } const EdgeConstraints& GetEdgeConstraints() const { return edge_constraints_; } void SetEdgeConstraints(const EdgeConstraints& edge_constraints) { edge_constraints_ = edge_constraints; @@ -476,13 +481,17 @@ class Schema { s = BinaryRead(buf, temporal_field_); if (!s) return 0; bytes_read += s; + s = BinaryRead(buf, temporal_order_); + if (!s) return 0; + bytes_read += s; s = BinaryRead(buf, edge_constraints_); if (!s) return 0; bytes_read += s; s = BinaryRead(buf, detach_property_); if (!s) return 0; bytes_read += s; - SetSchema(is_vertex_, fds, primary_field_, temporal_field_, edge_constraints_); + SetSchema(is_vertex_, fds, primary_field_, temporal_field_, temporal_order_, + edge_constraints_); return bytes_read; } @@ -492,7 +501,8 @@ class Schema { BinaryWrite(buf, label_in_record_) + BinaryWrite(buf, deleted_) + BinaryWrite(buf, GetFieldSpecs()) + BinaryWrite(buf, is_vertex_) + BinaryWrite(buf, primary_field_) + BinaryWrite(buf, temporal_field_) + - BinaryWrite(buf, edge_constraints_) + BinaryWrite(buf, detach_property_); + BinaryWrite(buf, temporal_order_) + BinaryWrite(buf, edge_constraints_) + + BinaryWrite(buf, detach_property_); } std::string ToString() const { return fma_common::ToString(GetFieldSpecsAsMap()); } diff --git a/src/core/schema_manager.h b/src/core/schema_manager.h index f4090fd55e..c423994f49 100644 --- a/src/core/schema_manager.h +++ b/src/core/schema_manager.h @@ -204,6 +204,7 @@ class SchemaManager { ls->SetLabelId((LabelId)(schemas_.size() - 1)); } std::string primary, temporal; + TemporalFieldOrder temporal_order; if (is_vertex) { primary = dynamic_cast(options).primary_field; } @@ -212,8 +213,10 @@ class SchemaManager { edge_constraints = dynamic_cast(options).edge_constraints; primary = dynamic_cast(options).temporal_field; temporal = dynamic_cast(options).temporal_field; + temporal_order = dynamic_cast(options).temporal_field_order; } - ls->SetSchema(is_vertex, n_fields, fields, primary, temporal, edge_constraints); + ls->SetSchema(is_vertex, n_fields, fields, primary, temporal, temporal_order, + edge_constraints); ls->SetLabel(label); ls->SetDetachProperty(options.detach_property); name_to_idx_.emplace_hint(it, label, ls->GetLabelId()); diff --git a/src/core/transaction.cpp b/src/core/transaction.cpp index a20db56802..30061dac4c 100644 --- a/src/core/transaction.cpp +++ b/src/core/transaction.cpp @@ -13,6 +13,7 @@ */ #include "core/blob_manager.h" +#include "core/data_type.h" #include "core/field_data_helper.h" #include "core/index_manager.h" #include "core/lightning_graph.h" @@ -1197,13 +1198,25 @@ Transaction::AddVertex(const LabelT& label, size_t n_fields, const FieldT* field return newvid; } -TemporalId Transaction::ParseTemporalId(const FieldData& fd) { return fd.AsInt64(); } +TemporalId Transaction::ParseTemporalId(const FieldData& fd, + const TemporalFieldOrder& temporal_order) { + if (temporal_order == TemporalFieldOrder::ASC) { + return fd.AsInt64(); + } else { + return std::numeric_limits::max() ^ fd.AsInt64(); + } +} -TemporalId Transaction::ParseTemporalId(const std::string& str) { +TemporalId Transaction::ParseTemporalId(const std::string& str, + const TemporalFieldOrder& temporal_order) { TemporalId tid = 0; if (fma_common::TextParserUtils::ParseT(str, tid) != str.size()) throw InputError(FMA_FMT("Incorrect tid format: {}", str)); - return tid; + if (temporal_order == TemporalFieldOrder::ASC) { + return tid; + } else { + return std::numeric_limits::max() ^ tid; + } } /** @@ -1240,7 +1253,7 @@ Transaction::AddEdge(VertexId src, VertexId dst, const LabelT& label, size_t n_f if (schema->HasTemporalField()) { int pos = schema->GetTemporalPos(n_fields, fields); if (pos != -1) { - tid = ParseTemporalId(values[pos]); + tid = ParseTemporalId(values[pos], schema->GetTemporalOrder()); } } const auto& constraints = schema->GetEdgeConstraintsLids(); @@ -1275,7 +1288,7 @@ Transaction::UpsertEdge(VertexId src, VertexId dst, const LabelT& label, size_t // NOTE: if one edge has primary id, different primary id will be insert rather than update. int pos = schema->GetTemporalPos(n_fields, fields); if (pos != -1) { - tid = ParseTemporalId(values[pos]); + tid = ParseTemporalId(values[pos], schema->GetTemporalOrder()); } } graph::OutEdgeIterator it = diff --git a/src/core/transaction.h b/src/core/transaction.h index 93a64ccf94..56ed80df11 100644 --- a/src/core/transaction.h +++ b/src/core/transaction.h @@ -1061,6 +1061,12 @@ class Transaction { void GetStartAndEndVid(VertexId& start, VertexId& end); + static TemporalId ParseTemporalId(const FieldData& fd, + const TemporalFieldOrder& temporal_order); + + static TemporalId ParseTemporalId(const std::string& fd, + const TemporalFieldOrder& temporal_order); + private: void CloseAllIterators() { /** NOTE: it->Close() will remove the it from iterators_. So we @@ -1077,9 +1083,6 @@ class Transaction { EdgeIndex* GetEdgeIndex(const std::string& label, const std::string& field); EdgeIndex* GetEdgeIndex(size_t label, size_t field); - TemporalId ParseTemporalId(const FieldData& fd); - TemporalId ParseTemporalId(const std::string& fd); - void EnterTxn(); void LeaveTxn(); diff --git a/src/cypher/execution_plan/execution_plan.cpp b/src/cypher/execution_plan/execution_plan.cpp index aceec6ec0d..2c6627268e 100644 --- a/src/cypher/execution_plan/execution_plan.cpp +++ b/src/cypher/execution_plan/execution_plan.cpp @@ -27,7 +27,7 @@ #include "monitor/memory_monitor_allocator.h" #include "optimization/pass_manager.h" #include "procedure/procedure.h" -#include "validation/check_graph.h" +#include "cypher/execution_plan/validation/graph_name_checker.h" #include "cypher/execution_plan/execution_plan.h" namespace cypher { @@ -1278,13 +1278,14 @@ void ExecutionPlan::Build(const std::vector &stmt, parser::Cmd } // Optimize the operations in the ExecutionPlan. // TODO(seijiang): split context-free optimizations & context-dependent ones - PassManager pass_manager(Root(), ctx); + PassManager pass_manager(_root, ctx); pass_manager.ExecutePasses(); } -void ExecutionPlan::Validate(cypher::RTContext *ctx_) { - CheckGraphVisitor check_graph(ctx_); - check_graph.Visit(*_root); +void ExecutionPlan::Validate(cypher::RTContext *ctx) { + // todo(kehuang): Add validation manager here. + GraphNameChecker checker(_root, ctx); + checker.Execute(); } ExecutionPlan::~ExecutionPlan() { diff --git a/src/cypher/execution_plan/optimization/opt_rewrite_with_schema_inference.h b/src/cypher/execution_plan/optimization/opt_rewrite_with_schema_inference.h index 36df1e2f63..2bcc34145d 100644 --- a/src/cypher/execution_plan/optimization/opt_rewrite_with_schema_inference.h +++ b/src/cypher/execution_plan/optimization/opt_rewrite_with_schema_inference.h @@ -17,6 +17,14 @@ // #pragma once +#include "db/galaxy.h" +#include "cypher/execution_plan/ops/op_expand_all.h" +#include "cypher/execution_plan/ops/op_all_node_scan.h" +#include "cypher/execution_plan/ops/op_all_node_scan_dynamic.h" +#include "cypher/execution_plan/ops/op_node_index_seek.h" +#include "cypher/execution_plan/ops/op_node_index_seek_dynamic.h" +#include "cypher/execution_plan/ops/op_node_by_label_scan.h" +#include "cypher/execution_plan/ops/op_node_by_label_scan_dynamic.h" #include "cypher/execution_plan/optimization/opt_pass.h" #include "cypher/execution_plan/optimization/rewrite/schema_rewrite.h" @@ -230,19 +238,16 @@ class OptRewriteWithSchemaInference : public OptPass { bool Gate() override { return true; } int Execute(OpBase *root) override { - const lgraph::SchemaInfo *schema_info; if (_ctx->graph_.empty()) { - _ctx->ac_db_.reset(nullptr); - schema_info = nullptr; - } else { - _ctx->ac_db_ = std::make_unique( - _ctx->galaxy_->OpenGraph(_ctx->user_, _ctx->graph_)); - lgraph_api::GraphDB db(_ctx->ac_db_.get(), true); - _ctx->txn_ = std::make_unique(db.CreateReadTxn()); - schema_info = &_ctx->txn_->GetTxn()->GetSchemaInfo(); + // Skip optimization when the graph name is empty. + return 0; } + _ctx->ac_db_ = std::make_unique( + _ctx->galaxy_->OpenGraph(_ctx->user_, _ctx->graph_)); + lgraph_api::GraphDB db(_ctx->ac_db_.get(), true); + _ctx->txn_ = std::make_unique(db.CreateReadTxn()); + const lgraph::SchemaInfo *schema_info = &_ctx->txn_->GetTxn()->GetSchemaInfo(); _ctx->txn_.reset(nullptr); - // _ctx->ac_db_.reset(nullptr); _RewriteWithSchemaInference(root, schema_info); return 0; } diff --git a/src/cypher/execution_plan/validation/check_graph.h b/src/cypher/execution_plan/validation/graph_name_checker.h similarity index 76% rename from src/cypher/execution_plan/validation/check_graph.h rename to src/cypher/execution_plan/validation/graph_name_checker.h index c5138f5520..b71adb12c7 100644 --- a/src/cypher/execution_plan/validation/check_graph.h +++ b/src/cypher/execution_plan/validation/graph_name_checker.h @@ -14,12 +14,15 @@ #pragma once +#include "cypher/execution_plan/ops/ops.h" +#include "cypher/execution_plan/validation/validation_pass.h" +#include "cypher/execution_plan/runtime_context.h" #include "cypher/execution_plan/visitor/visitor.h" namespace cypher { -class CheckGraphVisitor : public Visitor { +class GraphNameCheckerVisitor : public Visitor { public: - explicit CheckGraphVisitor(cypher::RTContext *ctx) : ctx_(ctx) {} + explicit GraphNameCheckerVisitor(cypher::RTContext *ctx) : ctx_(ctx) {} void Visit(const OpBase &op) override { std::vector ops = {&op}; @@ -28,8 +31,9 @@ class CheckGraphVisitor : public Visitor { ops.insert(ops.end(), op_->children.begin(), op_->children.end()); op_->Accept(this); } - if (!has_standalone_call && ctx_->graph_.empty()) - throw lgraph::CypherException("graph name cannot be empty"); + if (!has_standalone_call && ctx_->graph_.empty()) { + throw lgraph::CypherException("Graph name cannot be empty."); + } } void Visit(const Aggregate &op) override{}; @@ -72,4 +76,23 @@ class CheckGraphVisitor : public Visitor { cypher::RTContext *ctx_ = nullptr; bool has_standalone_call = false; }; + +class GraphNameChecker : public ValidationPass { + public: + GraphNameChecker(OpBase *root, cypher::RTContext *ctx) + : ValidationPass(typeid(GraphNameChecker).name()), root_(root), ctx_(ctx) {} + + bool Gate() override { return true; } + + int Execute() override { + GraphNameCheckerVisitor visitor(ctx_); + visitor.Visit(*root_); + return 0; + } + + private: + OpBase *root_; + cypher::RTContext *ctx_; +}; + } // namespace cypher diff --git a/src/cypher/execution_plan/validation/validation_pass.h b/src/cypher/execution_plan/validation/validation_pass.h new file mode 100644 index 0000000000..76466940d7 --- /dev/null +++ b/src/cypher/execution_plan/validation/validation_pass.h @@ -0,0 +1,38 @@ +/** + * Copyright 2023 AntGroup CO., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + */ + +#pragma once + +#include "cypher/execution_plan/ops/op.h" + +namespace cypher { + +class ValidationPass { + public: + std::string name_; + + explicit ValidationPass(const std::string &name) : name_(name) {} + + virtual ~ValidationPass() {} + + /* This pass and all sub-passes are executed only if the function returns + * true. The default implementation returns true. */ + virtual bool Gate() = 0; + + /* This is the code to run. If this is not overridden, then there should + * be sub-passes otherwise this pass does nothing. */ + virtual int Execute() = 0; +}; + +} // namespace cypher diff --git a/src/db/graph_manager.cpp b/src/db/graph_manager.cpp index d9abb1a886..c628093b85 100644 --- a/src/db/graph_manager.cpp +++ b/src/db/graph_manager.cpp @@ -175,8 +175,10 @@ std::map lgraph::GraphManager::ListGraphs() const lgraph::ScopedRef lgraph::GraphManager::GetGraphRef( const std::string& graph) const { + if (graph.empty()) throw InputError("Graph name cannot be empty."); auto it = graphs_.find(graph); - if (it == graphs_.end()) throw InputError("No such graph."); + if (it == graphs_.end()) + throw InputError(fma_common::StringFormatter::Format("No such graph: {}", graph)); return it->second.GetScopedRef(); } diff --git a/src/import/import_config_parser.h b/src/import/import_config_parser.h index f8b8e5ead2..5fc34616c1 100644 --- a/src/import/import_config_parser.h +++ b/src/import/import_config_parser.h @@ -20,6 +20,7 @@ #include "fma-common/fma_stream.h" #include "lgraph/lgraph_types.h" +#include "core/data_type.h" #include "core/field_data_helper.h" #include "core/lightning_graph.h" #include "import/import_exception.h" @@ -54,7 +55,9 @@ enum KeyWord { SKIP, HEADER, OPTIONAL_, // OPTIONAL is a macro in windows SDK - FORMAT + FORMAT, + ASC, // TemporalFieldOrder::ASC + DESC // TemporalFieldOrder::DESC }; class KeyWordFunc { @@ -83,6 +86,8 @@ class KeyWordFunc { {KeyWord::HEADER, "HEADER"}, {KeyWord::OPTIONAL_, "OPTIONAL"}, {KeyWord::FORMAT, "FORMAT"}, + {KeyWord::ASC, "ASC"}, + {KeyWord::DESC, "DESC"}, }; return m; } @@ -164,6 +169,17 @@ class KeyWordFunc { return ft; } + static TemporalFieldOrder GetTemporalFieldOrderFromStr(const std::string& s) { + KeyWord kw = GetKeyWordFromStr(s); + if (kw == KeyWord::ASC) { + return TemporalFieldOrder::ASC; + } else if (kw == KeyWord::DESC) { + return TemporalFieldOrder::DESC; + } else { + throw std::runtime_error(FMA_FMT("keyword [{}] is not a TemporalFieldOrder", s)); + } + } + static const std::string& GetStrFromKeyWord(const KeyWord& kw) { return KeyWordToStrMap().at(kw); } @@ -182,6 +198,7 @@ struct ColumnSpec { bool global = false; bool primary = false; bool temporal = false; + TemporalFieldOrder temporal_order = TemporalFieldOrder::ASC; bool fulltext = false; inline bool CheckValid() const { @@ -207,10 +224,13 @@ struct ColumnSpec { global(false), primary(false), temporal(false), + temporal_order(TemporalFieldOrder::ASC), fulltext(false) {} ColumnSpec(std::string name_, FieldType type_, bool is_id_, bool optional_ = false, bool index_ = false, bool unique_ = false, bool global_ = false, - bool temporal_ = false, bool fulltext_ = false) + bool temporal_ = false, + TemporalFieldOrder temporal_order_ = TemporalFieldOrder::ASC, + bool fulltext_ = false) : name(std::move(name_)), type(type_), optional(optional_), @@ -219,6 +239,7 @@ struct ColumnSpec { global(global_), primary(is_id_), temporal(temporal_), + temporal_order(temporal_order_), fulltext(fulltext_) {} bool operator==(const ColumnSpec& rhs) const { @@ -342,7 +363,6 @@ struct LabelDesc { return ret; } - std::vector GetFieldSpecs(std::vector& names) const { std::vector fs; for (const auto& n : names) { @@ -812,6 +832,12 @@ class ImportConfParser { } if (s.contains("temporal") && cs.name == s["temporal"]) { cs.temporal = true; + if (s.contains("temporal_order")) { + cs.temporal_order = + KeyWordFunc::GetTemporalFieldOrderFromStr(s["temporal_order"]); + } else { + cs.temporal_order = TemporalFieldOrder::ASC; + } } if (p.contains("index")) { cs.index = p["index"]; diff --git a/src/import/import_online.cpp b/src/import/import_online.cpp index 45c4631465..f8de22fdd4 100644 --- a/src/import/import_online.cpp +++ b/src/import/import_online.cpp @@ -422,8 +422,11 @@ std::string lgraph::import_v2::ImportOnline::HandleOnlineSchema(std::string&& de } else { auto eo = std::make_unique(); eo->edge_constraints = v.edge_constraints; - if (v.HasTemporalField()) - eo->temporal_field = v.GetTemporalField().name; + if (v.HasTemporalField()) { + auto tf = v.GetTemporalField(); + eo->temporal_field = tf.name; + eo->temporal_field_order = tf.temporal_order; + } options = std::move(eo); } options->detach_property = v.detach_property; diff --git a/src/import/import_v2.cpp b/src/import/import_v2.cpp index 48ca5fc4ed..d898f978ef 100644 --- a/src/import/import_v2.cpp +++ b/src/import/import_v2.cpp @@ -18,6 +18,8 @@ #include "import/import_v2.h" #include "import/blob_writer.h" +using lgraph::import::LabelGraph; + lgraph::import_v2::Importer::Importer(const Config& config) : config_(config), intermediate_file_(config.intermediate_dir, config.intermediate_buf_size), @@ -106,8 +108,11 @@ void lgraph::import_v2::Importer::DoImportOffline() { options = std::move(vo); } else { auto eo = std::make_unique(); - if (v.HasTemporalField()) - eo->temporal_field = v.GetTemporalField().name; + if (v.HasTemporalField()) { + auto tf = v.GetTemporalField(); + eo->temporal_field = tf.name; + eo->temporal_field_order = tf.temporal_order; + } eo->edge_constraints = v.edge_constraints; options = std::move(eo); } @@ -518,9 +523,12 @@ void lgraph::import_v2::Importer::LoadEdgeFiles(LightningGraph* db, std::string size_t second_id_pos = std::max(src_id_pos, dst_id_pos); size_t label_id; bool has_tid = ld.HasTemporalField(); + TemporalFieldOrder tid_order = TemporalFieldOrder::ASC; int primary_id_pos = -1; if (has_tid) { - primary_id_pos = (int)fd.FindIdxExcludeSkip(ld.GetTemporalField().name); + auto tf = ld.GetTemporalField(); + tid_order = tf.temporal_order; + primary_id_pos = (int)fd.FindIdxExcludeSkip(tf.name); } std::vector field_ids; Schema schema; @@ -563,7 +571,9 @@ void lgraph::import_v2::Importer::LoadEdgeFiles(LightningGraph* db, std::string const FieldData& src_id = edge[src_id_pos]; const FieldData& dst_id = edge[dst_id_pos]; TemporalId tid = 0; - if (has_tid) tid = edge[primary_id_pos].AsInt64(); + if (has_tid) { + tid = Transaction::ParseTemporalId(edge[primary_id_pos], tid_order); + } VidType src_vid, dst_vid; bool resolve_success = true; // try to translate src and dst id into vid diff --git a/src/import/import_v2.h b/src/import/import_v2.h index bb3ad8769f..829bd7a335 100644 --- a/src/import/import_v2.h +++ b/src/import/import_v2.h @@ -28,7 +28,6 @@ class HaStateMachine; namespace import_v2 { class ImportOnline; -using lgraph::import::LabelGraph; using lgraph::import::PlanExecutor; class Importer { diff --git a/src/import/import_v3.cpp b/src/import/import_v3.cpp index ae8d7787bc..0357702d43 100644 --- a/src/import/import_v3.cpp +++ b/src/import/import_v3.cpp @@ -163,8 +163,11 @@ void Importer::DoImportOffline() { options = std::move(vo); } else { auto eo = std::make_unique(); - if (v.HasTemporalField()) - eo->temporal_field = v.GetTemporalField().name; + if (v.HasTemporalField()) { + auto tf = v.GetTemporalField(); + eo->temporal_field = tf.name; + eo->temporal_field_order = tf.temporal_order; + } eo->edge_constraints = v.edge_constraints; options = std::move(eo); } @@ -566,6 +569,7 @@ void Importer::EdgeDataToSST() { LabelId label_id = 0; int16_t tid_pos = 0; bool has_tid = false; + TemporalFieldOrder tid_order = TemporalFieldOrder::ASC; LabelId src_label_id = 0; LabelId dst_label_id = 0; Schema* schema = nullptr; @@ -638,11 +642,14 @@ void Importer::EdgeDataToSST() { file.n_header_line, config_.continue_on_error, config_.quiet ? 0 : 100)); } bool has_tid = false; + TemporalFieldOrder tid_order = TemporalFieldOrder::ASC; int16_t tid_pos = -1; - if (schemaDesc_.FindEdgeLabel(file.label).HasTemporalField()) { + auto cs = schemaDesc_.FindEdgeLabel(file.label); + if (cs.HasTemporalField()) { has_tid = true; - tid_pos = (int16_t)file.FindIdxExcludeSkip( - schemaDesc_.FindEdgeLabel(file.label).GetTemporalField().name); + auto tf = cs.GetTemporalField(); + tid_order = tf.temporal_order; + tid_pos = (int16_t)file.FindIdxExcludeSkip(tf.name); } std::vector> block; @@ -660,6 +667,7 @@ void Importer::EdgeDataToSST() { edgeDataBlock->dst_id_pos = dst_id_pos; edgeDataBlock->label_id = boost::endian::native_to_big(label_id); edgeDataBlock->has_tid = has_tid; + edgeDataBlock->tid_order = tid_order; edgeDataBlock->tid_pos = tid_pos; edgeDataBlock->src_label_id = boost::endian::native_to_big(src_label_id); edgeDataBlock->dst_label_id = boost::endian::native_to_big(dst_label_id); @@ -738,8 +746,11 @@ void Importer::EdgeDataToSST() { } const FieldData& src_fd = line[edgeDataBlock->src_id_pos]; const FieldData& dst_fd = line[edgeDataBlock->dst_id_pos]; - int64_t tid = edgeDataBlock->has_tid - ? line[edgeDataBlock->tid_pos].AsInt64() : 0; + int64_t tid = + edgeDataBlock->has_tid + ? Transaction::ParseTemporalId(line[edgeDataBlock->tid_pos], + edgeDataBlock->tid_order) + : 0; k.clear(); k.append((const char*)&edgeDataBlock->src_label_id, sizeof(edgeDataBlock->src_label_id)); diff --git a/src/lucene/pom.xml b/src/lucene/pom.xml index 43ec6b5789..457020df66 100644 --- a/src/lucene/pom.xml +++ b/src/lucene/pom.xml @@ -23,12 +23,6 @@ lucene-analyzers-common 8.11.1 - - junit - junit - 4.13.1 - test - commons-io commons-io diff --git a/src/lucene/src/test/java/LuceneTest.java b/src/lucene/src/test/java/LuceneTest.java index e991232906..c7bbc4fec1 100644 --- a/src/lucene/src/test/java/LuceneTest.java +++ b/src/lucene/src/test/java/LuceneTest.java @@ -1,27 +1,23 @@ import org.apache.lucene.queryparser.classic.ParseException; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; import java.io.IOException; public class LuceneTest { - private Lucene lucene; - @Before - public void init() throws IOException { - lucene = new Lucene("test","StandardAnalyzer", 10, 1); - lucene.clear(); - } + private static class InnerLucene extends Lucene{ + public InnerLucene() throws IOException { + super("test", "StandardAnalyzer", 10, 1); + super.clear(); + } - @After - public void close() throws IOException { - lucene.close(); + @Override + protected void finalize() throws Throwable { + super.close(); + } } - @Test - public void testAddVertex() throws IOException, ParseException { + public static void testAddVertex() throws IOException, ParseException { + InnerLucene lucene = new InnerLucene(); int labelId = 10; for (int i = 0; i < 10; i++) { String[] keys = {"field_1"}; @@ -30,15 +26,15 @@ public void testAddVertex() throws IOException, ParseException { } lucene.maybeRefresh(); ScoreVid[] ids = lucene.queryVertex(labelId, "field_1:field_1_5", 100); - Assert.assertTrue(ids.length == 1); - Assert.assertTrue(ids[0].vid == 5); + assert ids.length == 1; + assert ids[0].vid == 5; ids = lucene.queryVertex(labelId, "field_1_5", 100); - Assert.assertTrue(ids.length == 0); + assert ids.length == 0; } - @Test - public void testDeleteVertex() throws IOException, ParseException { + public static void testDeleteVertex() throws IOException, ParseException { + InnerLucene lucene = new InnerLucene(); int labelId1 = 10; for (int i = 0; i < 10; i++) { String[] keys = {"field_1"}; @@ -48,12 +44,12 @@ public void testDeleteVertex() throws IOException, ParseException { lucene.deleteVertex(5); lucene.maybeRefresh(); ScoreVid[] ids = lucene.queryVertex(labelId1, "field_1:field_1_5", 100); - Assert.assertTrue(ids.length == 0); + assert ids.length == 0; lucene.deleteVertex(20); lucene.maybeRefresh(); ids = lucene.queryVertex(labelId1, "field_1:field_1_*", 100); - Assert.assertTrue(ids.length == 9); + assert ids.length == 9; int labelId2 = 20; for (int i = 10; i < 20; i++) { @@ -63,20 +59,20 @@ public void testDeleteVertex() throws IOException, ParseException { } lucene.maybeRefresh(); ids = lucene.queryVertex(labelId2, "field_2:field_*", 100); - Assert.assertTrue(ids.length == 10); + assert ids.length == 10; ids = lucene.queryVertex(labelId1, "field_1:field_*", 100); - Assert.assertTrue(ids.length == 9); + assert ids.length == 9; lucene.deleteLabel(true, labelId1); lucene.maybeRefresh(); ids = lucene.queryVertex(labelId2, "field_2:field_*", 100); - Assert.assertTrue(ids.length == 10); + assert ids.length == 10; ids = lucene.queryVertex(labelId1, "field_1:field_*", 100); - Assert.assertTrue(ids.length == 0); + assert ids.length == 0; } - @Test - public void testAddEdge() throws IOException, ParseException { + public static void testAddEdge() throws IOException, ParseException { + InnerLucene lucene = new InnerLucene(); int labelId = 11; for (int i = 0; i < 10; i++) { String[] keys = {"field_1"}; @@ -85,15 +81,15 @@ public void testAddEdge() throws IOException, ParseException { } lucene.maybeRefresh(); ScoreEdgeUid[] ids = lucene.queryEdge(labelId, "field_1:field_1_5", 100); - Assert.assertTrue(ids.length == 1); - Assert.assertTrue(ids[0].srcId == 5); - Assert.assertTrue(ids[0].destId == 6); - Assert.assertTrue(ids[0].labelId == labelId); - Assert.assertTrue(ids[0].edgeId == 5); + assert ids.length == 1; + assert ids[0].srcId == 5; + assert ids[0].destId == 6; + assert ids[0].labelId == labelId; + assert ids[0].edgeId == 5; } - @Test - public void testDeleteEdge() throws IOException, ParseException { + public static void testDeleteEdge() throws IOException, ParseException { + InnerLucene lucene = new InnerLucene(); int labelId = 11; for (int i = 0; i < 10; i++) { String[] keys = {"field_1"}; @@ -103,6 +99,13 @@ public void testDeleteEdge() throws IOException, ParseException { lucene.deleteEdge(5, 6, labelId, 5); lucene.maybeRefresh(); ScoreEdgeUid[] ids = lucene.queryEdge(labelId, "field_1:field_1_5", 100); - Assert.assertTrue(ids.length == 0); + assert ids.length == 0; + } + + public static void main(String[] args) throws IOException, ParseException { + testAddVertex(); + testDeleteVertex(); + testAddEdge(); + testDeleteEdge(); } } \ No newline at end of file diff --git a/src/lucene/tugraph_lucene-1.0-SNAPSHOT.jar b/src/lucene/tugraph_lucene-1.0-SNAPSHOT.jar index 78773a6b74..6fe6944041 100644 Binary files a/src/lucene/tugraph_lucene-1.0-SNAPSHOT.jar and b/src/lucene/tugraph_lucene-1.0-SNAPSHOT.jar differ diff --git a/test/graph_factory.h b/test/graph_factory.h index 032d5b2216..3f7011aaa1 100644 --- a/test/graph_factory.h +++ b/test/graph_factory.h @@ -542,13 +542,13 @@ R"( } static void create_mini_finbench(const std::string& dir = "./lgraph_db") { - lgraph::import_v2::Importer::Config config; + lgraph::import_v3::Importer::Config config; config.config_file = "../../test/resource/data/mini_finbench/mini_finbench.json"; config.db_dir = dir; config.delete_if_exists = true; config.graph = "default"; config.delimiter = "|"; - lgraph::import_v2::Importer importer(config); + lgraph::import_v3::Importer importer(config); importer.DoImportOffline(); } diff --git a/test/resource/cases/finbench/cypher/base.result b/test/resource/cases/finbench/cypher/base.result new file mode 100644 index 0000000000..21c4955844 --- /dev/null +++ b/test/resource/cases/finbench/cypher/base.result @@ -0,0 +1,20 @@ +# temporal order: DESC +MATCH (n:Account) +WITH n LIMIT 1 +MATCH (n)-[e:withdraw]->(m:Account) +WHERE e.timestamp > 0 +RETURN n.id, e.timestamp, type(e), m.id +LIMIT 3; +[{"e.timestamp":100,"m.id":3,"n.id":0,"type(e)":"withdraw"},{"e.timestamp":95,"m.id":3,"n.id":0,"type(e)":"withdraw"},{"e.timestamp":91,"m.id":12,"n.id":0,"type(e)":"withdraw"}] + +MATCH (n:Account{id:0}), (m:Account{id:3}) +CREATE (n)-[:withdraw{timestamp:97, amount:0.0}]->(m); +[{"":"created 0 vertices, created 1 edges."}] + +MATCH (n:Account) +WITH n LIMIT 1 +MATCH (n)-[e:withdraw]->(m:Account) +WHERE e.timestamp > 0 +RETURN n.id, e.timestamp, type(e), m.id +LIMIT 4; +[{"e.timestamp":100,"m.id":3,"n.id":0,"type(e)":"withdraw"},{"e.timestamp":97,"m.id":3,"n.id":0,"type(e)":"withdraw"},{"e.timestamp":95,"m.id":3,"n.id":0,"type(e)":"withdraw"},{"e.timestamp":91,"m.id":12,"n.id":0,"type(e)":"withdraw"}] diff --git a/test/resource/cases/finbench/cypher/base.test b/test/resource/cases/finbench/cypher/base.test new file mode 100644 index 0000000000..a944ec64cc --- /dev/null +++ b/test/resource/cases/finbench/cypher/base.test @@ -0,0 +1,17 @@ +# temporal order: DESC +MATCH (n:Account) +WITH n LIMIT 1 +MATCH (n)-[e:withdraw]->(m:Account) +WHERE e.timestamp > 0 +RETURN n.id, e.timestamp, type(e), m.id +LIMIT 3; + +MATCH (n:Account{id:0}), (m:Account{id:3}) +CREATE (n)-[:withdraw{timestamp:97, amount:0.0}]->(m); + +MATCH (n:Account) +WITH n LIMIT 1 +MATCH (n)-[e:withdraw]->(m:Account) +WHERE e.timestamp > 0 +RETURN n.id, e.timestamp, type(e), m.id +LIMIT 4; \ No newline at end of file diff --git a/test/resource/data/mini_finbench/mini_finbench.json b/test/resource/data/mini_finbench/mini_finbench.json index 17d6aeb7b0..d73f165e6a 100644 --- a/test/resource/data/mini_finbench/mini_finbench.json +++ b/test/resource/data/mini_finbench/mini_finbench.json @@ -54,8 +54,8 @@ { "label" : "transfer", "type" : "EDGE", - "tid" : "timestamp", - "tid_order" : "desc", + "temporal" : "timestamp", + "temporal_order" : "DESC", "properties" : [ {"name" : "timestamp", "type":"INT64"}, {"name" : "amount", "type":"DOUBLE"} @@ -64,8 +64,8 @@ { "label" : "withdraw", "type" : "EDGE", - "tid" : "timestamp", - "tid_order" : "desc", + "temporal" : "timestamp", + "temporal_order" : "DESC", "properties" : [ {"name" : "timestamp", "type":"INT64"}, {"name" : "amount", "type":"DOUBLE"} @@ -74,8 +74,8 @@ { "label" : "repay", "type" : "EDGE", - "tid" : "timestamp", - "tid_order" : "desc", + "temporal" : "timestamp", + "temporal_order" : "DESC", "properties" : [ {"name" : "timestamp", "type":"INT64"}, {"name" : "amount", "type":"DOUBLE"} @@ -84,8 +84,8 @@ { "label" : "deposit", "type" : "EDGE", - "tid" : "timestamp", - "tid_order" : "desc", + "temporal" : "timestamp", + "temporal_order" : "DESC", "properties" : [ {"name" : "timestamp", "type":"INT64"}, {"name" : "amount", "type":"DOUBLE"} @@ -94,8 +94,8 @@ { "label" : "signIn", "type" : "EDGE", - "tid" : "timestamp", - "tid_order" : "desc", + "temporal" : "timestamp", + "temporal_order" : "DESC", "properties" : [ {"name" : "timestamp", "type":"INT64"} ] @@ -103,8 +103,8 @@ { "label" : "invest", "type" : "EDGE", - "tid" : "timestamp", - "tid_order" : "desc", + "temporal" : "timestamp", + "temporal_order" : "DESC", "properties" : [ {"name" : "timestamp", "type":"INT64"}, {"name" : "ratio", "type":"DOUBLE"} @@ -113,8 +113,8 @@ { "label" : "apply", "type" : "EDGE", - "tid" : "timestamp", - "tid_order" : "desc", + "temporal" : "timestamp", + "temporal_order" : "DESC", "properties" : [ {"name" : "timestamp", "type":"INT64"} ] @@ -122,8 +122,8 @@ { "label" : "guarantee", "type" : "EDGE", - "tid" : "timestamp", - "tid_order" : "desc", + "temporal" : "timestamp", + "temporal_order" : "DESC", "properties" : [ {"name" : "timestamp", "type":"INT64"} ] diff --git a/test/test_cypher.cpp b/test/test_cypher.cpp index 7c64517abe..d28a9e5188 100644 --- a/test/test_cypher.cpp +++ b/test/test_cypher.cpp @@ -2199,15 +2199,16 @@ int test_fix_crash_issues(cypher::RTContext *ctx) { "(m:Person {name:'Liam Neeson'}), " "(o:Person {name:'Liam Neeson'}) " "WHERE custom.myadd('asd')='1' RETURN 1"); - // issue #312 + // issue #312 & #463 auto graph = ctx->graph_; ctx->graph_ = ""; expected_exception_any(ctx, "MATCH (n) RETURN n LIMIT 5"); expected_exception_any(ctx, "CALL db.vertexLabels"); expected_exception_any(ctx, "CALL db.warmup"); eval_script(ctx, "CALL dbms.graph.listGraphs()"); + expected_exception_any(ctx, "MATCH p=(n)-[e]->(m) RETURN p LIMIT 5"); ctx->graph_ = graph; - // issue #312 end + // issue #312 & #464 end return 0; } diff --git a/test/test_detach_property.cpp b/test/test_detach_property.cpp index ec35f9100c..2741fe5a20 100644 --- a/test/test_detach_property.cpp +++ b/test/test_detach_property.cpp @@ -53,7 +53,8 @@ TEST_F(TestDetachProperty, normal) { e_options.detach_property = true; e_options.edge_constraints = {{"v1", "v2"}}; UT_EXPECT_EQ(e_options.to_string(), - "detach_property: 1, edge_constraints: [v1 -> v2], temporal_field: "); + "detach_property: 1, edge_constraints: [v1 -> v2], temporal_field: , " + "temporal_field_order: ASC"); ret = graph.AddEdgeLabel("e1", std::vector( {{"weight", FieldType::INT16, false}}), e_options); @@ -226,7 +227,8 @@ TEST_F(TestDetachProperty, reopen) { e_options.detach_property = true; e_options.edge_constraints = {{"v1", "v2"}}; UT_EXPECT_EQ(e_options.to_string(), - "detach_property: 1, edge_constraints: [v1 -> v2], temporal_field: "); + "detach_property: 1, edge_constraints: [v1 -> v2], temporal_field: , " + "temporal_field_order: ASC"); ret = graph.AddEdgeLabel( "e1", std::vector({{"weight", FieldType::INT16, false}}), e_options); UT_EXPECT_TRUE(ret); diff --git a/test/test_import_online.cpp b/test/test_import_online.cpp index 41899463c8..7296956e25 100644 --- a/test/test_import_online.cpp +++ b/test/test_import_online.cpp @@ -1434,6 +1434,9 @@ Liam Neeson,Batman Begins,Henri Ducard, 298 web::json::value json_val = web::json::value::parse(str); UT_EXPECT_EQ(json_val.size() == 0, true); ret = client.ImportSchemaFromContent(str, sImportContent["schema"]); + if (!ret) { + UT_ERR() << "ImportSchemaFromContent failed, error: " << str; + } UT_EXPECT_TRUE(ret); ret = client.ImportDataFromContent(str, sImportContent["person_desc"], sImportContent["person"], ","); diff --git a/test/test_lgraph.cpp b/test/test_lgraph.cpp index 85c073ef0f..ab1d8ee1f9 100644 --- a/test/test_lgraph.cpp +++ b/test/test_lgraph.cpp @@ -216,8 +216,12 @@ TEST_F(TestLGraph, LGraph) { UT_EXPECT_ANY_THROW(db.AddLabel( "", std::vector({{"id", FieldType::STRING, false}}), true, VertexOptions("id"))); + // label name + db.AddLabel(std::string(255, 'e'), + std::vector({{"id", FieldType::STRING, false}}), + true, VertexOptions("id")); // long label name - UT_EXPECT_ANY_THROW(db.AddLabel(std::string(65, 'a'), + UT_EXPECT_ANY_THROW(db.AddLabel(std::string(256, 'a'), std::vector({{"id", FieldType::STRING, false}}), true, VertexOptions("id"))); // strange label names @@ -243,11 +247,16 @@ TEST_F(TestLGraph, LGraph) { db.AddLabel("field_name_start_with_digit", std::vector{FieldSpec("10234_kkk", FieldType::STRING, false)}, true, VertexOptions("10234_kkk"))); + // field name ok + db.AddLabel( + "field_name_not_long", + std::vector{FieldSpec(std::string(255, 'a'), FieldType::STRING, false)}, + true, VertexOptions(std::string(255, 'a'))); // field name too long UT_EXPECT_ANY_THROW(db.AddLabel( "field_name_too_long", - std::vector{FieldSpec(std::string(65, 'a'), FieldType::STRING, false)}, true, - VertexOptions(std::string(65, 'a')))); + std::vector{FieldSpec(std::string(256, 'a'), FieldType::STRING, false)}, + true, VertexOptions(std::string(256, 'a')))); // invalid character in field name for (auto& str : std::vector{"#", "!", "+", "-", "*", "/"}) UT_EXPECT_ANY_THROW(db.AddLabel( @@ -385,8 +394,8 @@ TEST_F(TestLGraph, LGraph) { txn.Commit(); UT_LOG() << "Vertex added: " << nv; } catch (std::exception& e) { - UT_EXPECT_TRUE(false); ERR() << "Error occurred: " << e.what(); + UT_EXPECT_TRUE(false); } // check vertex data integrity @@ -409,8 +418,8 @@ TEST_F(TestLGraph, LGraph) { } txn.Abort(); } catch (std::exception& e) { - UT_EXPECT_TRUE(false); ERR() << "Error occurred: " << e.what(); + UT_EXPECT_TRUE(false); } // add edges @@ -466,8 +475,8 @@ TEST_F(TestLGraph, LGraph) { } UT_LOG() << "Edges added: " << nedges; } catch (std::exception& e) { - UT_EXPECT_TRUE(false); ERR() << "Error occurred: " << e.what(); + UT_EXPECT_TRUE(false); } // check edges @@ -492,8 +501,8 @@ TEST_F(TestLGraph, LGraph) { } DumpGraph(txn); } catch (std::exception& e) { - UT_EXPECT_TRUE(false); ERR() << "Error occurred: " << e.what(); + UT_EXPECT_TRUE(false); } // Test AddEdge2 @@ -503,6 +512,7 @@ TEST_F(TestLGraph, LGraph) { {"weight", FieldType::FLOAT, false}}; EdgeOptions options; options.temporal_field = "ts"; + options.temporal_field_order = TemporalFieldOrder::ASC; ASSERT(db.AddLabel("e2", e2_fds, false, options)); auto txn = db.CreateWriteTxn(); VertexId src = AddVertex(txn, "v10", "10"); @@ -551,6 +561,7 @@ TEST_F(TestLGraph, LGraph) { } } catch (std::exception& e) { ERR() << "Error occurred: " << e.what(); + UT_EXPECT_TRUE(false); } try { @@ -567,8 +578,8 @@ TEST_F(TestLGraph, LGraph) { } DumpGraph(txn); } catch (std::exception& e) { - UT_EXPECT_TRUE(false); ERR() << "Error occurred: " << e.what(); + UT_EXPECT_TRUE(false); } std::set> updated_edges; @@ -612,8 +623,8 @@ TEST_F(TestLGraph, LGraph) { auto txn = db.CreateReadTxn(); DumpGraph(txn); } catch (std::exception& e) { - UT_EXPECT_TRUE(false); ERR() << "Error occurred: " << e.what(); + UT_EXPECT_TRUE(false); } try { @@ -681,8 +692,8 @@ TEST_F(TestLGraph, LGraph) { } txn.Commit(); } catch (std::exception& e) { - UT_EXPECT_TRUE(false); ERR() << "Error occurred: " << e.what(); + UT_EXPECT_TRUE(false); } try { @@ -729,8 +740,8 @@ TEST_F(TestLGraph, LGraph) { } UT_EXPECT_EQ(ne, 0); } catch (std::exception& e) { - UT_EXPECT_TRUE(false); ERR() << "Error occurred: " << e.what(); + UT_EXPECT_TRUE(false); } UT_LOG() << "test delete all index"; { diff --git a/test/test_lgraph_api.cpp b/test/test_lgraph_api.cpp index 3b7a95ac63..415309e1aa 100644 --- a/test/test_lgraph_api.cpp +++ b/test/test_lgraph_api.cpp @@ -15,6 +15,7 @@ #include #include +#include "core/data_type.h" #include "fma-common/configuration.h" #include "fma-common/logging.h" #include "fma-common/utils.h" @@ -552,6 +553,7 @@ TEST_F(TestLGraphApi, LGraphApi) { FMA_LOG() << "Testing edges with tid"; EdgeOptions options; options.temporal_field = "ts"; + options.temporal_field_order = lgraph::TemporalFieldOrder::ASC; db.AddEdgeLabel("et", std::vector{ lgraph_api::FieldSpec("ts", FieldType::INT64, false), diff --git a/test/test_schema.cpp b/test/test_schema.cpp index 0f14144e44..f012abc392 100644 --- a/test/test_schema.cpp +++ b/test/test_schema.cpp @@ -32,7 +32,7 @@ static Schema ConstructSimpleSchema() { FieldSpec("string", FieldType::STRING, true), FieldSpec("blob", FieldType::BLOB, true), FieldSpec("date", FieldType::DATE, false)}), - "int16", "", {}); + "int16", "", {}, {}); return s; } @@ -79,16 +79,16 @@ TEST_F(TestSchema, SetSchema) { s.SetSchema(true, std::vector({FieldSpec("int16", FieldType::INT16, true), FieldSpec("int16", FieldType::INT16, true)}), - "int16", "", {}), + "int16", "", {}, {}), lgraph::FieldAlreadyExistsException); UT_EXPECT_THROW( s.SetSchema(true, std::vector({FieldSpec("int16", FieldType::NUL, true)}), - "int16", "", {}), + "int16", "", {}, {}), lgraph::FieldCannotBeNullTypeException); std::vector fs; for (size_t i = 0; i < _detail::MAX_NUM_FIELDS + 1; i++) fs.emplace_back(UT_FMT("f_{}", i), FieldType::INT16, true); - UT_EXPECT_THROW(s.SetSchema(true, fs, "f_0", "", {}), lgraph::TooManyFieldsException); + UT_EXPECT_THROW(s.SetSchema(true, fs, "f_0", "", {}, {}), lgraph::TooManyFieldsException); } TEST_F(TestSchema, HasBlob) { @@ -96,8 +96,8 @@ TEST_F(TestSchema, HasBlob) { UT_EXPECT_TRUE(s.HasBlob()); Schema s2 = s; UT_EXPECT_TRUE(s2.HasBlob()); - s.SetSchema(true, - std::vector({FieldSpec("f", FieldType::INT16, true)}), "f", "", {}); + s.SetSchema(true, std::vector({FieldSpec("f", FieldType::INT16, true)}), "f", "", {}, + {}); UT_EXPECT_TRUE(!s.HasBlob()); s = s2; UT_EXPECT_TRUE(s.HasBlob()); @@ -146,11 +146,11 @@ TEST_F(TestSchema, DumpRecord) { FieldSpec fd_4("addr", FieldType::STRING, true); FieldSpec fd_5("float", FieldType::DOUBLE, true); std::vector fds{fd_0, fd_1, fd_2, fd_3, fd_4, fd_5}; - schema.SetSchema(true, fds, "uid", "", {}); - schema_1.SetSchema(true, fds, "uid", "", {}); + schema.SetSchema(true, fds, "uid", "", {}, {}); + schema_1.SetSchema(true, fds, "uid", "", {}, {}); UT_EXPECT_EQ(schema.GetNumFields(), 6); UT_LOG() << "size of schema:" << schema.GetNumFields(); - schema.SetSchema(true, fds, "uid", "", {}); + schema.SetSchema(true, fds, "uid", "", {}, {}); Value va_tmp = schema.CreateEmptyRecord(); UT_EXPECT_THROW(schema_1.SetField(va_tmp, (std::string) "name", FieldData()), lgraph::FieldCannotBeSetNullException); diff --git a/test/test_schema_change.cpp b/test/test_schema_change.cpp index 90152879b9..bbe34e9bd3 100644 --- a/test/test_schema_change.cpp +++ b/test/test_schema_change.cpp @@ -65,6 +65,7 @@ static void CreateSampleDB(const std::string& dir, bool detach_property) { lg.BlockingAddIndex("person", "age", false, true); EdgeOptions options; options.temporal_field = "ts"; + options.temporal_field_order = lgraph::TemporalFieldOrder::ASC; options.detach_property = detach_property; UT_EXPECT_TRUE(lg.AddLabel("knows", std::vector({FieldSpec("weight", FieldType::FLOAT, true), @@ -138,7 +139,7 @@ TEST_P(TestSchemaChange, ModifyFields) { FieldSpec("name1", FieldType::STRING, true), FieldSpec("name2", FieldType::STRING, true), FieldSpec("blob", FieldType::BLOB, true), FieldSpec("age", FieldType::FLOAT, false)}), - "id", "", {}); + "id", "", {}, {}); std::map fields = s1.GetFieldSpecsAsMap(); { Schema s2(s1); @@ -574,7 +575,7 @@ TEST_P(TestSchemaChange, DelLabel) { FieldSpec("name2", FieldType::STRING, true), FieldSpec("blob", FieldType::BLOB, true), FieldSpec("age", FieldType::FLOAT, false)}), - "id", "", {}); + "id", "", {}, {}); UT_EXPECT_THROW( s1.AddFields(std::vector({FieldSpec("SKIP", FieldType::STRING, false)})), lgraph::InputError); @@ -596,7 +597,7 @@ TEST_P(TestSchemaChange, DelLabel) { FieldSpec("name2", FieldType::STRING, true), FieldSpec("blob", FieldType::BLOB, true), FieldSpec("age", FieldType::FLOAT, false)}), - "", "id", {}); + "", "id", {}, {}); UT_EXPECT_THROW(s.DelFields(std::vector{"id"}), FieldCannotBeDeletedException); s.AddFields({FieldSpec("telphone", FieldType::STRING, false)}); UT_EXPECT_EQ(s.HasTemporalField(), true);