├── .circleci.settings.xml ├── .deepsource.toml ├── .github ├── dependabot.yml ├── release-drafter-config.yml └── workflows │ ├── codeql.yml │ ├── release-drafter.yml │ └── version-and-release.yml ├── .gitignore ├── LICENSE ├── README.md ├── pom.xml └── src ├── main └── java │ ├── META-INF │ └── MANIFEST.MF │ └── com │ └── redislabs │ └── redisgraph │ ├── Header.java │ ├── Record.java │ ├── RedisGraph.java │ ├── RedisGraphContext.java │ ├── RedisGraphContextGenerator.java │ ├── RedisGraphPipeline.java │ ├── RedisGraphTransaction.java │ ├── ResultSet.java │ ├── Statistics.java │ ├── exceptions │ └── JRedisGraphException.java │ ├── graph_entities │ ├── Edge.java │ ├── GraphEntity.java │ ├── Node.java │ ├── Path.java │ ├── Point.java │ └── Property.java │ └── impl │ ├── Utils.java │ ├── api │ ├── AbstractRedisGraph.java │ ├── ContextedRedisGraph.java │ ├── RedisGraph.java │ ├── RedisGraphCacheHolder.java │ ├── RedisGraphCommand.java │ ├── RedisGraphPipeline.java │ └── RedisGraphTransaction.java │ ├── graph_cache │ ├── GraphCache.java │ ├── GraphCacheList.java │ └── RedisGraphCaches.java │ └── resultset │ ├── HeaderImpl.java │ ├── RecordImpl.java │ ├── ResultSetImpl.java │ ├── ResultSetScalarTypes.java │ └── StatisticsImpl.java └── test └── java └── com └── redislabs └── redisgraph ├── InstantiationTest.java ├── IterableTest.java ├── PipelineTest.java ├── RedisGraphAPITest.java ├── TransactionTest.java ├── exceptions └── JRedisGraphErrorTest.java ├── graph_entities └── PathTest.java ├── impl └── UtilsTest.java └── test └── utils ├── PathBuilder.java └── PathBuilderTest.java /.circleci.settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ossrh 5 | ${env.OSSH_USERNAME} 6 | ${env.OSSH_PASSWORD} 7 | 8 | 9 | gpg.passphrase 10 | ${env.GPG_PASSPHRASE} 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.deepsource.toml: -------------------------------------------------------------------------------- 1 | version = 1 2 | 3 | [[analyzers]] 4 | name = "java" 5 | enabled = true 6 | 7 | [analyzers.meta] 8 | runtime_version = "8" 9 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "weekly" 12 | -------------------------------------------------------------------------------- /.github/release-drafter-config.yml: -------------------------------------------------------------------------------- 1 | name-template: 'Version $NEXT_PATCH_VERSION' 2 | tag-template: 'v$NEXT_PATCH_VERSION' 3 | categories: 4 | - title: 'Features' 5 | labels: 6 | - 'feature' 7 | - 'enhancement' 8 | - title: 'Bug Fixes' 9 | labels: 10 | - 'fix' 11 | - 'bugfix' 12 | - 'bug' 13 | - title: 'Maintenance' 14 | label: 'chore' 15 | change-template: '- $TITLE (#$NUMBER)' 16 | exclude-labels: 17 | - 'skip-changelog' 18 | template: | 19 | ## Changes 20 | 21 | $CHANGES 22 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | schedule: 9 | - cron: "56 20 * * 3" 10 | 11 | jobs: 12 | analyze: 13 | name: Analyze 14 | runs-on: ubuntu-latest 15 | permissions: 16 | actions: read 17 | contents: read 18 | security-events: write 19 | 20 | strategy: 21 | fail-fast: false 22 | matrix: 23 | language: [ java ] 24 | 25 | steps: 26 | - name: Checkout 27 | uses: actions/checkout@v3 28 | 29 | - name: Initialize CodeQL 30 | uses: github/codeql-action/init@v2 31 | with: 32 | languages: ${{ matrix.language }} 33 | queries: +security-and-quality 34 | 35 | - name: Autobuild 36 | uses: github/codeql-action/autobuild@v2 37 | 38 | - name: Perform CodeQL Analysis 39 | uses: github/codeql-action/analyze@v2 40 | with: 41 | category: "/language:${{ matrix.language }}" 42 | -------------------------------------------------------------------------------- /.github/workflows/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name: Release Drafter 2 | 3 | on: 4 | push: 5 | # branches to consider in the event; optional, defaults to all 6 | branches: 7 | - master 8 | 9 | jobs: 10 | update_release_draft: 11 | runs-on: ubuntu-latest 12 | steps: 13 | # Drafts your next Release notes as Pull Requests are merged into "master" 14 | - uses: release-drafter/release-drafter@v5 15 | with: 16 | # (Optional) specify config name to use, relative to .github/. Default: release-drafter.yml 17 | config-name: release-drafter-config.yml 18 | env: 19 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 20 | -------------------------------------------------------------------------------- /.github/workflows/version-and-release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - uses: actions/checkout@v2 13 | 14 | - name: get version from tag 15 | id: get_version 16 | run: | 17 | realversion="${GITHUB_REF/refs\/tags\//}" 18 | realversion="${realversion//v/}" 19 | echo "::set-output name=VERSION::$realversion" 20 | 21 | - name: Set up publishing to maven central 22 | uses: actions/setup-java@v2 23 | with: 24 | java-version: '8' 25 | distribution: 'adopt' 26 | server-id: ossrh 27 | server-username: MAVEN_USERNAME 28 | server-password: MAVEN_PASSWORD 29 | 30 | - name: mvn versions 31 | run: mvn versions:set -DnewVersion=${{ steps.get_version.outputs.VERSION }} 32 | 33 | - name: Install gpg key 34 | run: | 35 | cat <(echo -e "${{ secrets.OSSH_GPG_SECRET_KEY }}") | gpg --batch --import 36 | gpg --list-secret-keys --keyid-format LONG 37 | 38 | - name: Publish 39 | run: | 40 | mvn --no-transfer-progress \ 41 | --batch-mode \ 42 | -Dgpg.passphrase='${{ secrets.OSSH_GPG_SECRET_KEY_PASSWORD }}' \ 43 | -DskipTests deploy -P release 44 | env: 45 | MAVEN_USERNAME: ${{secrets.OSSH_USERNAME}} 46 | MAVEN_PASSWORD: ${{secrets.OSSH_TOKEN}} 47 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | /bin/ 4 | /target/ 5 | /test-output/ 6 | 7 | 8 | # Log file 9 | *.log 10 | 11 | # BlueJ files 12 | *.ctxt 13 | 14 | # Mobile Tools for Java (J2ME) 15 | .mtj.tmp/ 16 | 17 | # Package Files # 18 | *.jar 19 | *.war 20 | *.ear 21 | *.zip 22 | *.tar.gz 23 | *.rar 24 | 25 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 26 | hs_err_pid* 27 | 28 | #eclispe 29 | .classpath 30 | .project 31 | /.settings/ 32 | 33 | #intelij 34 | .idea -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2018, Redis Labs 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![license](https://img.shields.io/github/license/RedisGraph/JRedisGraph.svg)](https://github.com/RedisGraph/JRedisGraph/blob/master/LICENSE) 2 | [![GitHub issues](https://img.shields.io/github/release/RedisGraph/JRedisGraph.svg)](https://github.com/RedisGraph/JRedisGraph/releases/latest) 3 | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.redislabs/jredisgraph/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.redislabs/jredisgraph) 4 | [![Javadocs](https://www.javadoc.io/badge/com.redislabs/jredisgraph.svg)](https://www.javadoc.io/doc/com.redislabs/jredisgraph) 5 | [![Codecov](https://codecov.io/gh/RedisGraph/JRedisGraph/branch/master/graph/badge.svg)](https://codecov.io/gh/RedisGraph/JRedisGraph) 6 | [![Known Vulnerabilities](https://snyk.io/test/github/RedisGraph/JRedisGraph/badge.svg?targetFile=pom.xml)](https://snyk.io/test/github/RedisGraph/JRedisGraph?targetFile=pom.xml) 7 | 8 | # JRedisGraph 9 | [![Forum](https://img.shields.io/badge/Forum-RedisGraph-blue)](https://forum.redislabs.com/c/modules/redisgraph) 10 | [![Discord](https://img.shields.io/discord/697882427875393627?style=flat-square)](https://discord.gg/gWBRT6P) 11 | 12 | RedisGraph Java client 13 | 14 | ## Deprecation notice 15 | 16 | As of [Jedis](https://github.com/redis/jedis) version 4.2.0, this library is deprecated. Its features have been merged into Jedis. Please either install it from [maven](https://mvnrepository.com/artifact/redis.clients/jedis) or [the repo](https://github.com/redis/jedis). 17 | 18 | ### Official Releases 19 | 20 | ```xml 21 | 22 | 23 | com.redislabs 24 | jredisgraph 25 | 2.5.1 26 | 27 | 28 | ``` 29 | 30 | ### Snapshots 31 | ```xml 32 | 33 | 34 | snapshots-repo 35 | https://oss.sonatype.org/content/repositories/snapshots 36 | 37 | 38 | ``` 39 | 40 | and 41 | 42 | ```xml 43 | 44 | 45 | com.redislabs 46 | jredisgraph 47 | 2.6.0-SNAPSHOT 48 | 49 | 50 | ``` 51 | 52 | ## Example: Using the Java Client 53 | ```java 54 | package com.redislabs.redisgraph; 55 | 56 | import com.redislabs.redisgraph.graph_entities.Edge; 57 | import com.redislabs.redisgraph.graph_entities.Node; 58 | import com.redislabs.redisgraph.graph_entities.Path; 59 | import com.redislabs.redisgraph.impl.api.RedisGraph; 60 | 61 | import java.util.List; 62 | 63 | public class RedisGraphExample { 64 | public static void main(String[] args) { 65 | // general context api. Not bound to graph key or connection 66 | RedisGraph graph = new RedisGraph(); 67 | 68 | Map params = new HashMap<>(); 69 | params.put("age", 30); 70 | params.put("name", "amit"); 71 | 72 | // send queries to a specific graph called "social" 73 | graph.query("social","CREATE (:person{name:'roi',age:32})"); 74 | graph.query("social","CREATE (:person{name:$name,age:$age})", params); 75 | graph.query("social","MATCH (a:person), (b:person) WHERE (a.name = 'roi' AND b.name='amit') CREATE (a)-[:knows]->(b)"); 76 | 77 | ResultSet resultSet = graph.query("social", "MATCH (a:person)-[r:knows]->(b:person) RETURN a, r, b"); 78 | while(resultSet.hasNext()) { 79 | Record record = resultSet.next(); 80 | // get values 81 | Node a = record.getValue("a"); 82 | Edge r = record.getValue("r"); 83 | 84 | //print record 85 | System.out.println(record.toString()); 86 | } 87 | 88 | resultSet = graph.query("social", "MATCH p = (:person)-[:knows]->(:person) RETURN p"); 89 | while(resultSet.hasNext()) { 90 | Record record = resultSet.next(); 91 | Path p = record.getValue("p"); 92 | 93 | // More path API at Javadoc. 94 | System.out.println(p.nodeCount()); 95 | } 96 | 97 | // delete graph 98 | graph.deleteGraph("social"); 99 | 100 | // get connection context - closable object 101 | try(RedisGraphContext context = graph.getContext()) { 102 | context.query("contextSocial","CREATE (:person{name:'roi',age:32})"); 103 | context.query("social","CREATE (:person{name:$name,age:$age})", params); 104 | context.query("contextSocial", "MATCH (a:person), (b:person) WHERE (a.name = 'roi' AND b.name='amit') CREATE (a)-[:knows]->(b)"); 105 | // WATCH/MULTI/EXEC 106 | context.watch("contextSocial"); 107 | RedisGraphTransaction t = context.multi(); 108 | t.query("contextSocial", "MATCH (a:person)-[r:knows]->(b:person{name:$name,age:$age}) RETURN a, r, b", params); 109 | // support for Redis/Jedis native commands in transaction 110 | t.set("x", "1"); 111 | t.get("x"); 112 | // get multi/exec results 113 | List execResults = t.exec(); 114 | System.out.println(execResults.toString()); 115 | 116 | context.deleteGraph("contextSocial"); 117 | } 118 | } 119 | } 120 | 121 | ``` 122 | 123 | ## License 124 | [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2FRedisGraph%2FJRedisGraph.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2FRedisGraph%2FJRedisGraph?ref=badge_large) 125 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.redislabs 8 | jredisgraph 9 | 2.6.0-SNAPSHOT 10 | 11 | JRedisGraph 12 | Official client for Redis-Graph 13 | https://redisgraph.io 14 | 15 | 16 | RedisLabs 17 | www.redislabs.com 18 | 19 | 20 | CircleCI 21 | https://circleci.com/gh/RedisGraph/JRedisGraph 22 | 23 | 24 | https://github.com/RedisGraph/JRedisGraph/issues 25 | Github 26 | 27 | 28 | https://github.com/RedisGraph/JRedisGraph 29 | scm:git:git://github.com/RedisGraph/JRedisGraph.git 30 | scm:git:git@github.com:RedisGraph/JRedisGraph.git 31 | 32 | 33 | 34 | Roi Lipman 35 | Redis Labs, Inc. 36 | 37 | 38 | Guy Korland 39 | Redis Labs, Inc. 40 | 41 | 42 | 43 | 44 | BSD 3 Clause 45 | https://opensource.org/licenses/BSD-3-Clause 46 | repo 47 | 48 | 49 | 50 | 51 | 52 | redis.clients 53 | jedis 54 | 3.9.0 55 | 56 | 57 | org.apache.commons 58 | commons-text 59 | 1.10.0 60 | 61 | 62 | junit 63 | junit 64 | 4.13.2 65 | test 66 | 67 | 68 | org.slf4j 69 | slf4j-simple 70 | 1.6.6 71 | test 72 | 73 | 74 | org.junit.jupiter 75 | junit-jupiter 76 | 5.7.1 77 | test 78 | 79 | 80 | nl.jqno.equalsverifier 81 | equalsverifier 82 | 3.1.10 83 | test 84 | 85 | 86 | 87 | 8 88 | 8 89 | 8 90 | 8 91 | 92 | 93 | 94 | 95 | ossrh 96 | https://oss.sonatype.org/content/repositories/snapshots 97 | 98 | 99 | ossrh 100 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 101 | 102 | 103 | 104 | 105 | 106 | org.jacoco 107 | jacoco-maven-plugin 108 | 0.8.5 109 | 110 | 111 | 112 | prepare-agent 113 | 114 | 115 | 116 | report 117 | test 118 | 119 | report 120 | 121 | 122 | 123 | 124 | 125 | org.sonatype.plugins 126 | nexus-staging-maven-plugin 127 | 1.6.7 128 | true 129 | 130 | ossrh 131 | https://oss.sonatype.org/ 132 | true 133 | 134 | 135 | 136 | 137 | 138 | 139 | release 140 | 141 | 142 | 143 | 144 | org.apache.maven.plugins 145 | maven-source-plugin 146 | 2.2.1 147 | 148 | 149 | attach-sources 150 | 151 | jar-no-fork 152 | 153 | 154 | 155 | 156 | 157 | org.apache.maven.plugins 158 | maven-javadoc-plugin 159 | 2.9.1 160 | 161 | 162 | attach-javadocs 163 | 164 | jar 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | org.apache.maven.plugins 173 | maven-gpg-plugin 174 | 3.0.1 175 | 176 | 177 | --pinentry-mode 178 | loopback 179 | 180 | 181 | 182 | 183 | sign-artifacts 184 | verify 185 | 186 | sign 187 | 188 | 189 | 190 | 191 | 192 | 193 | org.apache.maven.plugins 194 | maven-pmd-plugin 195 | 3.12.0 196 | 197 | 198 | 199 | 200 | 201 | 202 | -------------------------------------------------------------------------------- /src/main/java/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Main-Class: com.redislabs.redisgraph.RedisGraph 3 | 4 | -------------------------------------------------------------------------------- /src/main/java/com/redislabs/redisgraph/Header.java: -------------------------------------------------------------------------------- 1 | package com.redislabs.redisgraph; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * Query response header interface. Represents the response schema (column names and types) 7 | */ 8 | public interface Header { 9 | 10 | 11 | enum ResultSetColumnTypes { 12 | COLUMN_UNKNOWN, 13 | COLUMN_SCALAR, 14 | COLUMN_NODE, 15 | COLUMN_RELATION 16 | 17 | } 18 | 19 | 20 | List getSchemaNames(); 21 | 22 | List getSchemaTypes(); 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/redislabs/redisgraph/Record.java: -------------------------------------------------------------------------------- 1 | package com.redislabs.redisgraph; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * Container for RedisGraph result values. 7 | * 8 | * List records are returned from RedisGraph statement execution, contained within a ResultSet. 9 | */ 10 | public interface Record { 11 | 12 | /** 13 | * The value at the given field index 14 | * 15 | * @param index field index 16 | * 17 | * @return the value 18 | */ 19 | T getValue(int index); 20 | 21 | /** 22 | * The value at the given field 23 | * 24 | * @param key header key 25 | * 26 | * @return the value 27 | */ 28 | T getValue(String key); 29 | 30 | /** 31 | * The value at the given field index (represented as String) 32 | * 33 | * @param index 34 | * @return string representation of the value 35 | */ 36 | String getString(int index); 37 | 38 | /** 39 | * The value at the given field (represented as String) 40 | * 41 | * @param key header key 42 | * 43 | * @return string representation of the value 44 | */ 45 | String getString(String key); 46 | 47 | /** 48 | * The keys of the record 49 | * 50 | * @return list of the record key 51 | */ 52 | List keys(); 53 | 54 | /** 55 | * The values of the record 56 | * 57 | * @return list of the record values 58 | */ 59 | List values(); 60 | 61 | /** 62 | * Check if the record header contains the given key 63 | * 64 | * @param key header key 65 | * 66 | * @return true if the the key exists 67 | */ 68 | boolean containsKey(String key); 69 | 70 | /** 71 | * The number of fields in this record 72 | * 73 | * @return the number of fields 74 | */ 75 | int size(); 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/com/redislabs/redisgraph/RedisGraph.java: -------------------------------------------------------------------------------- 1 | package com.redislabs.redisgraph; 2 | 3 | import java.io.Closeable; 4 | import java.util.List; 5 | import java.util.Map; 6 | 7 | import redis.clients.jedis.Jedis; 8 | 9 | public interface RedisGraph extends Closeable { 10 | 11 | public static RedisGraph with(Jedis jedis) { 12 | return new com.redislabs.redisgraph.impl.api.RedisGraph(jedis); 13 | } 14 | 15 | /** 16 | * Execute a Cypher query. 17 | * @param graphId a graph to perform the query on 18 | * @param query Cypher query 19 | * @return a result set 20 | */ 21 | ResultSet query(String graphId, String query); 22 | 23 | /** 24 | * Execute a Cypher read-only query. 25 | * @param graphId a graph to perform the query on 26 | * @param query Cypher query 27 | * @return a result set 28 | */ 29 | ResultSet readOnlyQuery(String graphId, String query); 30 | 31 | /** 32 | * Execute a Cypher query with timeout. 33 | * @param graphId a graph to perform the query on 34 | * @param query Cypher query 35 | * @param timeout 36 | * @return a result set 37 | */ 38 | ResultSet query(String graphId, String query, long timeout); 39 | 40 | /** 41 | * Execute a Cypher read-only query with timeout. 42 | * @param graphId a graph to perform the query on 43 | * @param query Cypher query 44 | * @param timeout 45 | * @return a result set 46 | */ 47 | ResultSet readOnlyQuery(String graphId, String query, long timeout); 48 | 49 | 50 | /** 51 | * Execute a Cypher query with arguments 52 | * @param graphId a graph to perform the query on 53 | * @param query Cypher query 54 | * @param args 55 | * @return a result set 56 | * @deprecated use {@link #query(String, String, Map)} instead. 57 | */ 58 | @Deprecated 59 | ResultSet query(String graphId, String query, Object ...args); 60 | 61 | 62 | /** 63 | * Executes a cypher query with parameters. 64 | * @param graphId a graph to perform the query on. 65 | * @param query Cypher query. 66 | * @param params parameters map. 67 | * @return a result set. 68 | */ 69 | ResultSet query(String graphId, String query, Map params); 70 | 71 | /** 72 | * Executes a cypher read-only query with parameters. 73 | * @param graphId a graph to perform the query on. 74 | * @param query Cypher query. 75 | * @param params parameters map. 76 | * @return a result set. 77 | */ 78 | ResultSet readOnlyQuery(String graphId, String query, Map params); 79 | 80 | /** 81 | * Executes a cypher query with parameters and timeout. 82 | * @param graphId a graph to perform the query on. 83 | * @param query Cypher query. 84 | * @param params parameters map. 85 | * @param timeout 86 | * @return a result set. 87 | */ 88 | ResultSet query(String graphId, String query, Map params, long timeout); 89 | 90 | /** 91 | * Executes a cypher read-only query with parameters and timeout. 92 | * @param graphId a graph to perform the query on. 93 | * @param query Cypher query. 94 | * @param params parameters map. 95 | * @param timeout 96 | * @return a result set. 97 | */ 98 | ResultSet readOnlyQuery(String graphId, String query, Map params, long timeout); 99 | 100 | /** 101 | * Invokes stored procedures without arguments 102 | * @param graphId a graph to perform the query on 103 | * @param procedure procedure name to invoke 104 | * @return result set with the procedure data 105 | */ 106 | ResultSet callProcedure(String graphId, String procedure); 107 | 108 | /** 109 | * Invokes stored procedure with arguments 110 | * @param graphId a graph to perform the query on 111 | * @param procedure procedure name to invoke 112 | * @param args procedure arguments 113 | * @return result set with the procedure data 114 | */ 115 | ResultSet callProcedure(String graphId, String procedure, List args); 116 | 117 | /** 118 | * Invoke a stored procedure 119 | * @param graphId a graph to perform the query on 120 | * @param procedure - procedure to execute 121 | * @param args - procedure arguments 122 | * @param kwargs - procedure output arguments 123 | * @return result set with the procedure data 124 | */ 125 | ResultSet callProcedure(String graphId, String procedure, List args , Map> kwargs); 126 | 127 | /** 128 | * Deletes the entire graph 129 | * @param graphId graph to delete 130 | * @return delete running time statistics 131 | */ 132 | String deleteGraph(String graphId); 133 | 134 | @Override 135 | void close(); 136 | } 137 | -------------------------------------------------------------------------------- /src/main/java/com/redislabs/redisgraph/RedisGraphContext.java: -------------------------------------------------------------------------------- 1 | package com.redislabs.redisgraph; 2 | 3 | import redis.clients.jedis.Jedis; 4 | 5 | public interface RedisGraphContext extends RedisGraph { 6 | 7 | 8 | /** 9 | * Returns implementing class connection context 10 | * @return Jedis connection 11 | */ 12 | Jedis getConnectionContext(); 13 | 14 | /** 15 | * Returns a Redis transactional object, over the connection context, with graph API capabilities 16 | * @return Redis transactional object, over the connection context, with graph API capabilities 17 | */ 18 | RedisGraphTransaction multi(); 19 | 20 | /** 21 | * Returns a Redis pipeline object, over the connection context, with graph API capabilities 22 | * @return Redis pipeline object, over the connection context, with graph API capabilities 23 | */ 24 | RedisGraphPipeline pipelined(); 25 | 26 | /** 27 | * Perform watch over given Redis keys 28 | * @param keys 29 | * @return "OK" 30 | */ 31 | String watch(String... keys); 32 | 33 | /** 34 | * Removes watch from all keys 35 | * @return 36 | */ 37 | String unwatch(); 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/redislabs/redisgraph/RedisGraphContextGenerator.java: -------------------------------------------------------------------------------- 1 | package com.redislabs.redisgraph; 2 | 3 | public interface RedisGraphContextGenerator extends RedisGraph { 4 | 5 | /** 6 | * Generate a connection bounded api 7 | * @return a connection bounded api 8 | */ 9 | RedisGraphContext getContext(); 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/redislabs/redisgraph/RedisGraphPipeline.java: -------------------------------------------------------------------------------- 1 | package com.redislabs.redisgraph; 2 | 3 | import redis.clients.jedis.Response; 4 | import redis.clients.jedis.commands.BasicRedisPipeline; 5 | import redis.clients.jedis.commands.BinaryRedisPipeline; 6 | import redis.clients.jedis.commands.BinaryScriptingCommandsPipeline; 7 | import redis.clients.jedis.commands.ClusterPipeline; 8 | import redis.clients.jedis.commands.MultiKeyBinaryRedisPipeline; 9 | import redis.clients.jedis.commands.MultiKeyCommandsPipeline; 10 | import redis.clients.jedis.commands.RedisPipeline; 11 | import redis.clients.jedis.commands.ScriptingCommandsPipeline; 12 | 13 | import java.io.Closeable; 14 | import java.util.List; 15 | import java.util.Map; 16 | 17 | /** 18 | * An interface which aligned to Jedis Pipeline interface 19 | */ 20 | public interface RedisGraphPipeline extends 21 | MultiKeyBinaryRedisPipeline, 22 | MultiKeyCommandsPipeline, ClusterPipeline, 23 | BinaryScriptingCommandsPipeline, ScriptingCommandsPipeline, 24 | BasicRedisPipeline, BinaryRedisPipeline, RedisPipeline, Closeable { 25 | 26 | /** 27 | * Execute a Cypher query. 28 | * @param graphId a graph to perform the query on 29 | * @param query Cypher query 30 | * @return a response which builds the result set with the query answer. 31 | */ 32 | Response query(String graphId, String query); 33 | 34 | /** 35 | * Execute a Cypher read-only query. 36 | * @param graphId a graph to perform the query on 37 | * @param query Cypher query 38 | * @return a response which builds the result set with the query answer. 39 | */ 40 | Response readOnlyQuery(String graphId, String query); 41 | 42 | /** 43 | * Execute a Cypher query with timeout. 44 | * @param graphId a graph to perform the query on 45 | * @param query Cypher query 46 | * @param timeout 47 | * @return a response which builds the result set with the query answer. 48 | */ 49 | Response query(String graphId, String query, long timeout); 50 | 51 | /** 52 | * Execute a Cypher read-only query with timeout. 53 | * @param graphId a graph to perform the query on 54 | * @param query Cypher query 55 | * @param timeout 56 | * @return a response which builds the result set with the query answer. 57 | */ 58 | Response readOnlyQuery(String graphId, String query, long timeout); 59 | 60 | /** 61 | * Executes a cypher query with parameters. 62 | * @param graphId a graph to perform the query on. 63 | * @param query Cypher query. 64 | * @param params parameters map. 65 | * @return a response which builds the result set with the query answer. 66 | */ 67 | Response query(String graphId, String query, Map params); 68 | 69 | /** 70 | * Executes a cypher read-only query with parameters. 71 | * @param graphId a graph to perform the query on. 72 | * @param query Cypher query. 73 | * @param params parameters map. 74 | * @return a response which builds the result set with the query answer. 75 | */ 76 | Response readOnlyQuery(String graphId, String query, Map params); 77 | 78 | /** 79 | * Executes a cypher query with parameters and timeout. 80 | * @param graphId a graph to perform the query on. 81 | * @param query Cypher query. 82 | * @param params parameters map. 83 | * @param timeout 84 | * @return a response which builds the result set with the query answer. 85 | */ 86 | Response query(String graphId, String query, Map params, long timeout); 87 | 88 | /** 89 | * Executes a cypher read-only query with parameters and timeout. 90 | * @param graphId a graph to perform the query on. 91 | * @param query Cypher query. 92 | * @param params parameters map. 93 | * @param timeout 94 | * @return a response which builds the result set with the query answer. 95 | */ 96 | Response readOnlyQuery(String graphId, String query, Map params, long timeout); 97 | 98 | /** 99 | * Invokes stored procedures without arguments 100 | * @param graphId a graph to perform the query on 101 | * @param procedure procedure name to invoke 102 | * @return a response which builds result set with the procedure data 103 | */ 104 | Response callProcedure(String graphId, String procedure); 105 | 106 | /** 107 | * Invokes stored procedure with arguments 108 | * @param graphId a graph to perform the query on 109 | * @param procedure procedure name to invoke 110 | * @param args procedure arguments 111 | * @return a response which builds result set with the procedure data 112 | */ 113 | Response callProcedure(String graphId, String procedure, List args); 114 | 115 | /** 116 | * Invoke a stored procedure 117 | * @param graphId a graph to perform the query on 118 | * @param procedure - procedure to execute 119 | * @param args - procedure arguments 120 | * @param kwargs - procedure output arguments 121 | * @return a response which builds result set with the procedure data 122 | */ 123 | Response callProcedure(String graphId, String procedure, List args , Map> kwargs); 124 | 125 | /** 126 | * Deletes the entire graph 127 | * @param graphId graph to delete 128 | * @return a response which builds the delete running time statistics 129 | */ 130 | Response deleteGraph(String graphId); 131 | 132 | 133 | /** 134 | * Synchronize pipeline by reading all responses. This operation close the pipeline. Whenever 135 | * possible try to avoid using this version and use Pipeline.sync() as it won't go through all the 136 | * responses and generate the right response type (usually it is a waste of time). 137 | * @return A list of all the responses in the order you executed them. 138 | */ 139 | List syncAndReturnAll(); 140 | 141 | /** 142 | * Synchronize pipeline by reading all responses. This operation close the pipeline. In order to 143 | * get return values from pipelined commands, capture the different Response<?> of the 144 | * commands you execute. 145 | */ 146 | public void sync(); 147 | 148 | 149 | /** 150 | * Blocks until all the previous write commands are successfully transferred and acknowledged by 151 | * at least the specified number of replicas. If the timeout, specified in milliseconds, is 152 | * reached, the command returns even if the specified number of replicas were not yet reached. 153 | * @param replicas successfully transferred and acknowledged by at least the specified number of 154 | * replicas 155 | * @param timeout the time to block in milliseconds, a timeout of 0 means to block forever 156 | * @return the number of replicas reached by all the writes performed in the context of the 157 | * current connection 158 | */ 159 | public Response waitReplicas(int replicas, long timeout); 160 | } 161 | -------------------------------------------------------------------------------- /src/main/java/com/redislabs/redisgraph/RedisGraphTransaction.java: -------------------------------------------------------------------------------- 1 | package com.redislabs.redisgraph; 2 | 3 | import redis.clients.jedis.Response; 4 | import redis.clients.jedis.commands.BasicRedisPipeline; 5 | import redis.clients.jedis.commands.BinaryRedisPipeline; 6 | import redis.clients.jedis.commands.BinaryScriptingCommandsPipeline; 7 | import redis.clients.jedis.commands.ClusterPipeline; 8 | import redis.clients.jedis.commands.MultiKeyBinaryRedisPipeline; 9 | import redis.clients.jedis.commands.MultiKeyCommandsPipeline; 10 | import redis.clients.jedis.commands.RedisPipeline; 11 | import redis.clients.jedis.commands.ScriptingCommandsPipeline; 12 | 13 | import java.io.Closeable; 14 | import java.util.List; 15 | import java.util.Map; 16 | 17 | /** 18 | * An interface which aligned to Jedis transactional interface 19 | */ 20 | public interface RedisGraphTransaction extends 21 | MultiKeyBinaryRedisPipeline, 22 | MultiKeyCommandsPipeline, ClusterPipeline, 23 | BinaryScriptingCommandsPipeline, ScriptingCommandsPipeline, 24 | BasicRedisPipeline, BinaryRedisPipeline, RedisPipeline, Closeable { 25 | 26 | /** 27 | * Execute a Cypher query. 28 | * @param graphId a graph to perform the query on 29 | * @param query Cypher query 30 | * @return a response which builds the result set with the query answer. 31 | */ 32 | Response query(String graphId, String query); 33 | 34 | /** 35 | * Execute a Cypher read-only query. 36 | * @param graphId a graph to perform the query on 37 | * @param query Cypher query 38 | * @return a response which builds the result set with the query answer. 39 | */ 40 | Response readOnlyQuery(String graphId, String query); 41 | 42 | /** 43 | * Execute a Cypher query with timeout. 44 | * @param graphId a graph to perform the query on 45 | * @param query Cypher query 46 | * @param timeout 47 | * @return a response which builds the result set with the query answer. 48 | */ 49 | Response query(String graphId, String query, long timeout); 50 | 51 | /** 52 | * Execute a Cypher read-only query with timeout. 53 | * @param graphId a graph to perform the query on 54 | * @param query Cypher query 55 | * @param timeout 56 | * @return a response which builds the result set with the query answer. 57 | */ 58 | Response readOnlyQuery(String graphId, String query, long timeout); 59 | 60 | /** 61 | * Execute a Cypher query with arguments 62 | * @param graphId a graph to perform the query on 63 | * @param query Cypher query 64 | * @param args 65 | * @return a response which builds the result set with the query answer. 66 | * @deprecated use {@link #query(String, String, Map)} instead. 67 | */ 68 | @Deprecated 69 | Response query(String graphId, String query, Object ...args); 70 | 71 | /** 72 | * Executes a cypher query with parameters. 73 | * @param graphId a graph to perform the query on. 74 | * @param query Cypher query. 75 | * @param params parameters map. 76 | * @return a response which builds the result set with the query answer. 77 | */ 78 | Response query(String graphId, String query, Map params); 79 | 80 | /** 81 | * Executes a cypher read-only query with parameters. 82 | * @param graphId a graph to perform the query on. 83 | * @param query Cypher query. 84 | * @param params parameters map. 85 | * @return a response which builds the result set with the query answer. 86 | */ 87 | Response readOnlyQuery(String graphId, String query, Map params); 88 | 89 | /** 90 | * Executes a cypher query with parameters and timeout. 91 | * @param graphId a graph to perform the query on. 92 | * @param query Cypher query. 93 | * @param params parameters map. 94 | * @param timeout 95 | * @return a response which builds the result set with the query answer. 96 | */ 97 | Response query(String graphId, String query, Map params, long timeout); 98 | 99 | /** 100 | * Executes a cypher read-only query with parameters and timeout. 101 | * @param graphId a graph to perform the query on. 102 | * @param query Cypher query. 103 | * @param params parameters map. 104 | * @param timeout 105 | * @return a response which builds the result set with the query answer. 106 | */ 107 | Response readOnlyQuery(String graphId, String query, Map params, long timeout); 108 | 109 | /** 110 | * Invokes stored procedures without arguments 111 | * @param graphId a graph to perform the query on 112 | * @param procedure procedure name to invoke 113 | * @return a response which builds result set with the procedure data 114 | */ 115 | Response callProcedure(String graphId, String procedure); 116 | 117 | /** 118 | * Invokes stored procedure with arguments 119 | * @param graphId a graph to perform the query on 120 | * @param procedure procedure name to invoke 121 | * @param args procedure arguments 122 | * @return a response which builds result set with the procedure data 123 | */ 124 | Response callProcedure(String graphId, String procedure, List args); 125 | 126 | /** 127 | * Invoke a stored procedure 128 | * @param graphId a graph to perform the query on 129 | * @param procedure - procedure to execute 130 | * @param args - procedure arguments 131 | * @param kwargs - procedure output arguments 132 | * @return a response which builds result set with the procedure data 133 | */ 134 | Response callProcedure(String graphId, String procedure, List args , Map> kwargs); 135 | 136 | /** 137 | * Deletes the entire graph 138 | * @param graphId graph to delete 139 | * @return a response which builds the delete running time statistics 140 | */ 141 | Response deleteGraph(String graphId); 142 | 143 | 144 | /** 145 | * executes the transaction 146 | * @return a list of the executed transaction commands answers, in case of successful transaction, null otherwise 147 | */ 148 | List exec(); 149 | 150 | /** 151 | * If object is in transaction mode, 152 | * flushes all previously queued commands in a transaction and restores the connection state to normal 153 | */ 154 | void clear(); 155 | 156 | /** 157 | * 158 | * @return 159 | */ 160 | List> execGetResponse(); 161 | 162 | /** 163 | * Flushes all previously queued commands in a transaction and restores the connection state to normal 164 | */ 165 | String discard(); 166 | } 167 | -------------------------------------------------------------------------------- /src/main/java/com/redislabs/redisgraph/ResultSet.java: -------------------------------------------------------------------------------- 1 | package com.redislabs.redisgraph; 2 | 3 | import java.util.Iterator; 4 | 5 | /** 6 | * Hold a query result 7 | */ 8 | public interface ResultSet extends Iterable, Iterator { 9 | 10 | int size(); 11 | 12 | Statistics getStatistics(); 13 | 14 | Header getHeader(); 15 | 16 | } -------------------------------------------------------------------------------- /src/main/java/com/redislabs/redisgraph/Statistics.java: -------------------------------------------------------------------------------- 1 | package com.redislabs.redisgraph; 2 | 3 | 4 | public interface Statistics { 5 | 6 | /** 7 | * Different Statistics labels 8 | */ 9 | enum Label{ 10 | LABELS_ADDED("Labels added"), 11 | INDICES_ADDED("Indices created"), 12 | INDICES_DELETED("Indices deleted"), 13 | NODES_CREATED("Nodes created"), 14 | NODES_DELETED("Nodes deleted"), 15 | RELATIONSHIPS_DELETED("Relationships deleted"), 16 | PROPERTIES_SET("Properties set"), 17 | RELATIONSHIPS_CREATED("Relationships created"), 18 | CACHED_EXECUTION("Cached execution"), 19 | QUERY_INTERNAL_EXECUTION_TIME("Query internal execution time"); 20 | 21 | private final String text; 22 | 23 | Label(String text) { 24 | this.text = text; 25 | } 26 | 27 | @Override 28 | public String toString() { 29 | return this.text; 30 | } 31 | 32 | /** 33 | * Get a Label by label text 34 | * 35 | * @param value label text 36 | * @return the matching Label 37 | */ 38 | public static Label getEnum(String value) { 39 | for(Label v : values()) { 40 | if(v.toString().equalsIgnoreCase(value)) return v; 41 | } 42 | return null; 43 | } 44 | } 45 | 46 | /** 47 | * Retrieves the relevant statistic 48 | * 49 | * @param label the requested statistic label 50 | * @return a String representation of the specific statistic or null 51 | */ 52 | String getStringValue(Statistics.Label label); 53 | 54 | int nodesCreated(); 55 | 56 | int nodesDeleted(); 57 | 58 | int indicesAdded(); 59 | 60 | int indicesDeleted(); 61 | 62 | int labelsAdded(); 63 | 64 | int relationshipsDeleted(); 65 | 66 | int relationshipsCreated(); 67 | 68 | int propertiesSet(); 69 | 70 | boolean cachedExecution(); 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/com/redislabs/redisgraph/exceptions/JRedisGraphException.java: -------------------------------------------------------------------------------- 1 | package com.redislabs.redisgraph.exceptions; 2 | 3 | import redis.clients.jedis.exceptions.JedisDataException; 4 | 5 | /** 6 | * RedisGraph query evaluation exception. An instance of JRedisGraphException is 7 | * thrown when RedisGraph encounters an error during query evaluation. 8 | */ 9 | public class JRedisGraphException extends JedisDataException { 10 | private static final long serialVersionUID = -476099681322055468L; 11 | 12 | public JRedisGraphException(String message) { 13 | super(message); 14 | } 15 | 16 | public JRedisGraphException(Throwable cause) { 17 | super(cause); 18 | } 19 | 20 | public JRedisGraphException(String message, Throwable cause) { 21 | super(message, cause); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/redislabs/redisgraph/graph_entities/Edge.java: -------------------------------------------------------------------------------- 1 | package com.redislabs.redisgraph.graph_entities; 2 | 3 | import java.util.Objects; 4 | 5 | /** 6 | * A class represent an edge (graph entity). In addition to the base class id and properties, an edge shows its source, 7 | * destination and relationship type 8 | */ 9 | public class Edge extends GraphEntity { 10 | 11 | //members 12 | private String relationshipType; 13 | private long source; 14 | private long destination; 15 | 16 | public Edge() { 17 | super(); 18 | } 19 | 20 | /** 21 | * Use this constructor to reduce memory allocations 22 | * when properties are added to the edge 23 | * @param propertiesCapacity preallocate the capacity for the properties 24 | */ 25 | public Edge(int propertiesCapacity) { 26 | super(propertiesCapacity); 27 | } 28 | //getters & setters 29 | 30 | /** 31 | * @return the edge relationship type 32 | */ 33 | public String getRelationshipType() { 34 | return relationshipType; 35 | } 36 | 37 | /** 38 | * @param relationshipType - the relationship type to be set. 39 | */ 40 | public void setRelationshipType(String relationshipType) { 41 | this.relationshipType = relationshipType; 42 | } 43 | 44 | 45 | /** 46 | * @return The id of the source node 47 | */ 48 | public long getSource() { 49 | return source; 50 | } 51 | 52 | /** 53 | * @param source - The id of the source node to be set 54 | */ 55 | public void setSource(long source) { 56 | this.source = source; 57 | } 58 | 59 | /** 60 | * 61 | * @return the id of the destination node 62 | */ 63 | public long getDestination() { 64 | return destination; 65 | } 66 | 67 | /** 68 | * 69 | * @param destination - The id of the destination node to be set 70 | */ 71 | public void setDestination(long destination) { 72 | this.destination = destination; 73 | } 74 | 75 | 76 | @Override 77 | public boolean equals(Object o) { 78 | if (this == o) return true; 79 | if (!(o instanceof Edge)) return false; 80 | if (!super.equals(o)) return false; 81 | Edge edge = (Edge) o; 82 | return source == edge.source && 83 | destination == edge.destination && 84 | Objects.equals(relationshipType, edge.relationshipType); 85 | } 86 | 87 | @Override 88 | public int hashCode() { 89 | return Objects.hash(super.hashCode(), relationshipType, source, destination); 90 | } 91 | 92 | @Override 93 | public String toString() { 94 | final StringBuilder sb = new StringBuilder("Edge{"); 95 | sb.append("relationshipType='").append(relationshipType).append('\''); 96 | sb.append(", source=").append(source); 97 | sb.append(", destination=").append(destination); 98 | sb.append(", id=").append(id); 99 | sb.append(", propertyMap=").append(propertyMap); 100 | sb.append('}'); 101 | return sb.toString(); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/main/java/com/redislabs/redisgraph/graph_entities/GraphEntity.java: -------------------------------------------------------------------------------- 1 | package com.redislabs.redisgraph.graph_entities; 2 | 3 | import java.util.*; 4 | 5 | 6 | /** 7 | * This is an abstract class for representing a graph entity. 8 | * A graph entity has an id and a set of properties. The properties are mapped and accessed by their names. 9 | */ 10 | public abstract class GraphEntity { 11 | //members 12 | protected long id; 13 | protected final Map> propertyMap; 14 | 15 | public GraphEntity() { 16 | propertyMap = new HashMap<>(); 17 | } 18 | 19 | /** 20 | * Use this constructor to reduce memory allocations 21 | * when properties are added to the edge 22 | * @param propertiesCapacity preallocate the capacity for the properties 23 | */ 24 | public GraphEntity(int propertiesCapacity) { 25 | propertyMap = new HashMap<>(propertiesCapacity); 26 | } 27 | 28 | //setters & getters 29 | 30 | /** 31 | * @return entity id 32 | */ 33 | public long getId() { 34 | return id; 35 | } 36 | 37 | /** 38 | * @param id - entity id to be set 39 | */ 40 | public void setId(long id) { 41 | this.id = id; 42 | } 43 | 44 | 45 | /** 46 | * Adds a property to the entity, by composing name, type and value to a property object 47 | * 48 | * @param name 49 | * @param value 50 | */ 51 | public void addProperty(String name, Object value) { 52 | addProperty(new Property(name, value)); 53 | } 54 | 55 | /** 56 | * @return Entity's property names, as a Set 57 | */ 58 | public Set getEntityPropertyNames() { 59 | return propertyMap.keySet(); 60 | } 61 | 62 | /** 63 | * Add a property to the entity 64 | * 65 | * @param property 66 | */ 67 | public void addProperty(Property property) { 68 | 69 | 70 | propertyMap.put(property.getName(), property); 71 | } 72 | 73 | /** 74 | * @return number of properties 75 | */ 76 | public int getNumberOfProperties() { 77 | return propertyMap.size(); 78 | } 79 | 80 | 81 | /** 82 | * @param propertyName - property name as lookup key (String) 83 | * @return property object, or null if key is not found 84 | */ 85 | public Property getProperty(String propertyName) { 86 | return propertyMap.get(propertyName); 87 | } 88 | 89 | 90 | /** 91 | * @param name - the name of the property to be removed 92 | */ 93 | public void removeProperty(String name) { 94 | propertyMap.remove(name); 95 | } 96 | 97 | @Override 98 | public boolean equals(Object o) { 99 | if (this == o) return true; 100 | if (!(o instanceof GraphEntity)) return false; 101 | GraphEntity that = (GraphEntity) o; 102 | return id == that.id && 103 | Objects.equals(propertyMap, that.propertyMap); 104 | } 105 | 106 | @Override 107 | public int hashCode() { 108 | return Objects.hash(id, propertyMap); 109 | } 110 | 111 | public abstract String toString(); 112 | } 113 | -------------------------------------------------------------------------------- /src/main/java/com/redislabs/redisgraph/graph_entities/Node.java: -------------------------------------------------------------------------------- 1 | package com.redislabs.redisgraph.graph_entities; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.Objects; 6 | 7 | /** 8 | * * A class represent an node (graph entity). In addition to the base class id and properties, a node has labels. 9 | */ 10 | public class Node extends GraphEntity { 11 | 12 | //members 13 | private final List labels; 14 | 15 | public Node() { 16 | super(); 17 | labels = new ArrayList<>(); 18 | } 19 | 20 | /** 21 | * Use this constructor to reduce memory allocations 22 | * when labels or properties are added to the node 23 | * @param labelsCapacity preallocate the capacity for the node labels 24 | * @param propertiesCapacity preallocate the capacity for the properties 25 | */ 26 | public Node(int labelsCapacity, int propertiesCapacity) { 27 | super(propertiesCapacity); 28 | this.labels = new ArrayList<>(labelsCapacity); 29 | } 30 | 31 | 32 | /** 33 | * @param label - a label to be add 34 | */ 35 | public void addLabel(String label) { 36 | labels.add(label); 37 | } 38 | 39 | /** 40 | * @param label - a label to be removed 41 | */ 42 | public void removeLabel(String label) { 43 | labels.remove(label); 44 | } 45 | 46 | /** 47 | * @param index - label index 48 | * @return the property label 49 | * @throws IndexOutOfBoundsException if the index is out of range 50 | * ({@code index < 0 || index >= getNumberOfLabels()}) 51 | */ 52 | public String getLabel(int index){ 53 | return labels.get(index); 54 | } 55 | 56 | /** 57 | * 58 | * @return the number of labels 59 | */ 60 | public int getNumberOfLabels() { 61 | return labels.size(); 62 | } 63 | 64 | @Override 65 | public boolean equals(Object o) { 66 | if (this == o) return true; 67 | if (!(o instanceof Node)) return false; 68 | if (!super.equals(o)) return false; 69 | Node node = (Node) o; 70 | return Objects.equals(labels, node.labels); 71 | } 72 | 73 | @Override 74 | public int hashCode() { 75 | return Objects.hash(super.hashCode(), labels); 76 | } 77 | 78 | @Override 79 | public String toString() { 80 | final StringBuilder sb = new StringBuilder("Node{"); 81 | sb.append("labels=").append(labels); 82 | sb.append(", id=").append(id); 83 | sb.append(", propertyMap=").append(propertyMap); 84 | sb.append('}'); 85 | return sb.toString(); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/com/redislabs/redisgraph/graph_entities/Path.java: -------------------------------------------------------------------------------- 1 | package com.redislabs.redisgraph.graph_entities; 2 | 3 | import java.util.List; 4 | import java.util.Objects; 5 | 6 | /** 7 | * This class represents a path in the graph. 8 | */ 9 | public final class Path { 10 | 11 | private final List nodes; 12 | private final List edges; 13 | 14 | 15 | /** 16 | * Parametrized constructor 17 | * @param nodes - List of nodes. 18 | * @param edges - List of edges. 19 | */ 20 | public Path(List nodes, List edges) { 21 | this.nodes = nodes; 22 | this.edges = edges; 23 | } 24 | 25 | /** 26 | * Returns the nodes of the path. 27 | * @return List of nodes. 28 | */ 29 | public List getNodes() { 30 | return nodes; 31 | } 32 | 33 | /** 34 | * Returns the edges of the path. 35 | * @return List of edges. 36 | */ 37 | public List getEdges() { 38 | return edges; 39 | } 40 | 41 | /** 42 | * Returns the length of the path - number of edges. 43 | * @return Number of edges. 44 | */ 45 | public int length() { 46 | return edges.size(); 47 | } 48 | 49 | /** 50 | * Return the number of nodes in the path. 51 | * @return Number of nodes. 52 | */ 53 | public int nodeCount(){ 54 | return nodes.size(); 55 | } 56 | 57 | /** 58 | * Returns the first node in the path. 59 | * @return First nodes in the path. 60 | * @throws IndexOutOfBoundsException if the path is empty. 61 | */ 62 | public Node firstNode(){ 63 | return nodes.get(0); 64 | } 65 | 66 | /** 67 | * Returns the last node in the path. 68 | * @return Last nodes in the path. 69 | * @throws IndexOutOfBoundsException if the path is empty. 70 | */ 71 | public Node lastNode(){ 72 | return nodes.get(nodes.size() - 1); 73 | } 74 | 75 | /** 76 | * Returns a node with specified index in the path. 77 | * @return Node. 78 | * @throws IndexOutOfBoundsException if the index is out of range 79 | * ({@code index < 0 || index >= nodesCount()}) 80 | */ 81 | public Node getNode(int index){ 82 | return nodes.get(index); 83 | } 84 | 85 | /** 86 | * Returns an edge with specified index in the path. 87 | * @return Edge. 88 | * @throws IndexOutOfBoundsException if the index is out of range 89 | * ({@code index < 0 || index >= length()}) 90 | */ 91 | public Edge getEdge(int index){ 92 | return edges.get(index); 93 | } 94 | 95 | @Override 96 | public boolean equals(Object o) { 97 | if (this == o) return true; 98 | if (o == null || getClass() != o.getClass()) return false; 99 | Path path = (Path) o; 100 | return Objects.equals(nodes, path.nodes) && 101 | Objects.equals(edges, path.edges); 102 | } 103 | 104 | @Override 105 | public int hashCode() { 106 | return Objects.hash(nodes, edges); 107 | } 108 | 109 | @Override 110 | public String toString() { 111 | final StringBuilder sb = new StringBuilder("Path{"); 112 | sb.append("nodes=").append(nodes); 113 | sb.append(", edges=").append(edges); 114 | sb.append('}'); 115 | return sb.toString(); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/main/java/com/redislabs/redisgraph/graph_entities/Point.java: -------------------------------------------------------------------------------- 1 | package com.redislabs.redisgraph.graph_entities; 2 | 3 | import java.util.List; 4 | import java.util.Objects; 5 | 6 | /** 7 | * This class represents a (geographical) point in the graph. 8 | */ 9 | public final class Point { 10 | 11 | private static final double EPSILON = 1e-5; 12 | 13 | private final double latitude; 14 | private final double longitude; 15 | 16 | /** 17 | * @param latitude 18 | * @param longitude 19 | */ 20 | public Point(double latitude, double longitude) { 21 | this.latitude = latitude; 22 | this.longitude = longitude; 23 | } 24 | 25 | /** 26 | * @param values {@code [latitude, longitude]} 27 | */ 28 | public Point(List values) { 29 | if (values == null || values.size() != 2) { 30 | throw new IllegalArgumentException("Point requires two doubles."); 31 | } 32 | this.latitude = values.get(0); 33 | this.longitude = values.get(1); 34 | } 35 | 36 | public double getLatitude() { 37 | return latitude; 38 | } 39 | 40 | public double getLongitude() { 41 | return longitude; 42 | } 43 | 44 | @Override 45 | public boolean equals(Object other) { 46 | if (this == other) return true; 47 | if (!(other instanceof Point)) return false; 48 | Point o = (Point) other; 49 | return Math.abs(latitude - o.latitude) < EPSILON && 50 | Math.abs(longitude - o.longitude) < EPSILON; 51 | } 52 | 53 | @Override 54 | public int hashCode() { 55 | return Objects.hash(latitude, longitude); 56 | } 57 | 58 | @Override 59 | public String toString() { 60 | return "Point{latitude=" + latitude + ", longitude=" + longitude + "}"; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/redislabs/redisgraph/graph_entities/Property.java: -------------------------------------------------------------------------------- 1 | package com.redislabs.redisgraph.graph_entities; 2 | 3 | import java.util.Objects; 4 | 5 | /** 6 | * A Graph entity property. Has a name, type, and value 7 | */ 8 | public class Property { 9 | 10 | //members 11 | private String name; 12 | private T value; 13 | 14 | 15 | /** 16 | * Default constructor 17 | */ 18 | public Property() { 19 | 20 | } 21 | 22 | /** 23 | * Parameterized constructor 24 | * 25 | * @param name 26 | * @param value 27 | */ 28 | public Property(String name, T value) { 29 | this.name = name; 30 | this.value = value; 31 | } 32 | 33 | 34 | //getters & setters 35 | 36 | /** 37 | * @return property name 38 | */ 39 | public String getName() { 40 | return name; 41 | } 42 | 43 | /** 44 | * @param name - property name to be set 45 | */ 46 | public void setName(String name) { 47 | this.name = name; 48 | } 49 | 50 | 51 | /** 52 | * @return property value 53 | */ 54 | public T getValue() { 55 | return value; 56 | } 57 | 58 | 59 | /** 60 | * @param value property value to be set 61 | */ 62 | public void setValue(T value) { 63 | this.value = value; 64 | } 65 | 66 | private boolean valueEquals(Object value1, Object value2) { 67 | if(value1 instanceof Integer) value1 = Long.valueOf(((Integer) value1).longValue()); 68 | if(value2 instanceof Integer) value2 = Long.valueOf(((Integer) value2).longValue()); 69 | return Objects.equals(value1, value2); 70 | } 71 | 72 | @Override 73 | public boolean equals(Object o) { 74 | if (this == o) return true; 75 | if (!(o instanceof Property)) return false; 76 | Property property = (Property) o; 77 | return Objects.equals(name, property.name) && 78 | valueEquals(value, property.value); 79 | } 80 | 81 | @Override 82 | public int hashCode() { 83 | return Objects.hash(name, value); 84 | } 85 | 86 | /** 87 | * Default toString implementation 88 | * @return 89 | */ 90 | @Override 91 | public String toString() { 92 | final StringBuilder sb = new StringBuilder("Property{"); 93 | sb.append("name='").append(name).append('\''); 94 | sb.append(", value=").append(value); 95 | sb.append('}'); 96 | return sb.toString(); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/com/redislabs/redisgraph/impl/Utils.java: -------------------------------------------------------------------------------- 1 | package com.redislabs.redisgraph.impl; 2 | 3 | import org.apache.commons.text.translate.AggregateTranslator; 4 | import org.apache.commons.text.translate.CharSequenceTranslator; 5 | import org.apache.commons.text.translate.LookupTranslator; 6 | 7 | import java.util.*; 8 | import java.util.stream.Collectors; 9 | 10 | /** 11 | * Utilities class 12 | */ 13 | public class Utils { 14 | public static final List DUMMY_LIST = new ArrayList<>(0); 15 | public static final Map> DUMMY_MAP = new HashMap<>(0); 16 | public static final String COMPACT_STRING = "--COMPACT"; 17 | public static final String TIMEOUT_STRING = "TIMEOUT"; 18 | 19 | private static final CharSequenceTranslator ESCAPE_CHYPER; 20 | static { 21 | final Map escapeJavaMap = new HashMap<>(); 22 | escapeJavaMap.put("\'", "\\'"); 23 | escapeJavaMap.put("\"", "\\\""); 24 | ESCAPE_CHYPER = new AggregateTranslator(new LookupTranslator(Collections.unmodifiableMap(escapeJavaMap))); 25 | } 26 | 27 | private Utils() {} 28 | 29 | /** 30 | * 31 | * @param str - a string 32 | * @return the input string surrounded with quotation marks, if needed 33 | */ 34 | private static String quoteString(String str){ 35 | StringBuilder sb = new StringBuilder(str.length()+2); 36 | sb.append('"'); 37 | sb.append(str.replace("\"","\\\"")); 38 | sb.append('"'); 39 | return sb.toString(); 40 | } 41 | 42 | 43 | /** 44 | * Prepare and formats a query and query arguments 45 | * @param query - query 46 | * @param args - query arguments 47 | * @return formatted query 48 | * @deprecated use {@link #prepareQuery(String, Map)} instead. 49 | */ 50 | @Deprecated 51 | public static String prepareQuery(String query, Object ...args){ 52 | if(args.length > 0) { 53 | for(int i=0; i params){ 70 | StringBuilder sb = new StringBuilder("CYPHER "); 71 | for(Map.Entry entry : params.entrySet()) { 72 | String key = entry.getKey(); 73 | Object value = entry.getValue(); 74 | sb.append(key).append('='); 75 | sb.append(valueToString(value)); 76 | sb.append(' '); 77 | } 78 | sb.append(query); 79 | return sb.toString(); 80 | } 81 | 82 | private static String arrayToString(Object[] arr) { 83 | StringBuilder sb = new StringBuilder().append('['); 84 | sb.append(String.join(", ", Arrays.stream(arr).map(Utils::valueToString).collect(Collectors.toList()))); 85 | sb.append(']'); 86 | return sb.toString(); 87 | } 88 | 89 | private static String valueToString(Object value) { 90 | if(value == null) return "null"; 91 | 92 | if(value instanceof String){ 93 | return quoteString((String) value); 94 | } 95 | if(value instanceof Character){ 96 | return quoteString(((Character)value).toString()); 97 | } 98 | 99 | if(value instanceof Object[]){ 100 | return arrayToString((Object[]) value); 101 | 102 | } 103 | if(value instanceof List){ 104 | @SuppressWarnings("unchecked") 105 | List list = (List) value; 106 | return arrayToString(list.toArray()); 107 | } 108 | return value.toString(); 109 | } 110 | 111 | /** 112 | * Prepare and format a procedure call and its arguments 113 | * @param procedure - procedure to invoke 114 | * @param args - procedure arguments 115 | * @param kwargs - procedure output arguments 116 | * @return formatter procedure call 117 | */ 118 | public static String prepareProcedure(String procedure, List args , Map> kwargs){ 119 | args = args.stream().map( Utils::quoteString).collect(Collectors.toList()); 120 | StringBuilder queryStringBuilder = new StringBuilder(); 121 | queryStringBuilder.append("CALL ").append(procedure).append('('); 122 | int i = 0; 123 | for (; i < args.size() - 1; i++) { 124 | queryStringBuilder.append(args.get(i)).append(','); 125 | } 126 | if (i == args.size()-1) { 127 | queryStringBuilder.append(args.get(i)); 128 | } 129 | queryStringBuilder.append(')'); 130 | List kwargsList = kwargs.getOrDefault("y", null); 131 | if(kwargsList != null){ 132 | i = 0; 133 | for (; i < kwargsList.size() - 1; i++) { 134 | queryStringBuilder.append(kwargsList.get(i)).append(','); 135 | 136 | } 137 | queryStringBuilder.append(kwargsList.get(i)); 138 | } 139 | return queryStringBuilder.toString(); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/main/java/com/redislabs/redisgraph/impl/api/AbstractRedisGraph.java: -------------------------------------------------------------------------------- 1 | package com.redislabs.redisgraph.impl.api; 2 | 3 | import com.redislabs.redisgraph.RedisGraph; 4 | import com.redislabs.redisgraph.ResultSet; 5 | import com.redislabs.redisgraph.impl.Utils; 6 | import redis.clients.jedis.Jedis; 7 | 8 | import java.util.List; 9 | import java.util.Map; 10 | 11 | /** 12 | * An abstract class to handle non implementation specific user requests 13 | */ 14 | public abstract class AbstractRedisGraph implements RedisGraph { 15 | 16 | /** 17 | * Inherited classes should return a Jedis connection, with respect to their context 18 | * @return Jedis connection 19 | */ 20 | protected abstract Jedis getConnection(); 21 | 22 | /** 23 | * Sends a query to the redis graph. Implementation and context dependent 24 | * @param graphId graph to be queried 25 | * @param preparedQuery prepared query 26 | * @return Result set 27 | */ 28 | protected abstract ResultSet sendQuery(String graphId, String preparedQuery); 29 | 30 | /** 31 | * Sends a read-only query to the redis graph. Implementation and context dependent 32 | * @param graphId graph to be queried 33 | * @param preparedQuery prepared query 34 | * @return Result set 35 | */ 36 | protected abstract ResultSet sendReadOnlyQuery(String graphId, String preparedQuery); 37 | 38 | /** 39 | * Sends a query to the redis graph.Implementation and context dependent 40 | * @param graphId graph to be queried 41 | * @param preparedQuery prepared query 42 | * @param timeout 43 | * @return Result set 44 | */ 45 | protected abstract ResultSet sendQuery(String graphId, String preparedQuery, long timeout); 46 | 47 | /** 48 | * Sends a read-query to the redis graph.Implementation and context dependent 49 | * @param graphId graph to be queried 50 | * @param preparedQuery prepared query 51 | * @param timeout 52 | * @return Result set 53 | */ 54 | protected abstract ResultSet sendReadOnlyQuery(String graphId, String preparedQuery, long timeout); 55 | 56 | /** 57 | * Execute a Cypher query. 58 | * @param graphId a graph to perform the query on 59 | * @param query Cypher query 60 | * @return a result set 61 | */ 62 | public ResultSet query(String graphId, String query) { 63 | return sendQuery(graphId, query); 64 | } 65 | 66 | /** 67 | * Execute a Cypher read-only query. 68 | * @param graphId a graph to perform the query on 69 | * @param query Cypher query 70 | * @return a result set 71 | */ 72 | public ResultSet readOnlyQuery(String graphId, String query) { 73 | return sendReadOnlyQuery(graphId, query); 74 | } 75 | 76 | /** 77 | * Execute a Cypher query with timeout. 78 | * @param graphId a graph to perform the query on 79 | * @param timeout 80 | * @param query Cypher query 81 | * @return a result set 82 | */ 83 | @Override 84 | public ResultSet query(String graphId, String query, long timeout) { 85 | return sendQuery(graphId, query, timeout); 86 | } 87 | 88 | /** 89 | * Execute a Cypher read-only query with timeout. 90 | * @param graphId a graph to perform the query on 91 | * @param timeout 92 | * @param query Cypher query 93 | * @return a result set 94 | */ 95 | @Override 96 | public ResultSet readOnlyQuery(String graphId, String query, long timeout) { 97 | return sendReadOnlyQuery(graphId, query, timeout); 98 | } 99 | 100 | /** 101 | * Execute a Cypher query with arguments 102 | * @param graphId a graph to perform the query on 103 | * @param query Cypher query 104 | * @param args 105 | * @return a result set 106 | * @deprecated use {@link #query(String, String, Map)} instead. 107 | */ 108 | @Deprecated 109 | public ResultSet query(String graphId, String query, Object ...args) { 110 | String preparedQuery = Utils.prepareQuery(query, args); 111 | return sendQuery(graphId, preparedQuery); 112 | } 113 | 114 | /** 115 | * Executes a cypher query with parameters. 116 | * @param graphId a graph to perform the query on. 117 | * @param query Cypher query. 118 | * @param params parameters map. 119 | * @return a result set. 120 | */ 121 | public ResultSet query(String graphId, String query, Map params) { 122 | String preparedQuery = Utils.prepareQuery(query, params); 123 | return sendQuery(graphId, preparedQuery); 124 | } 125 | 126 | /** 127 | * Executes a cypher read-only query with parameters. 128 | * @param graphId a graph to perform the query on. 129 | * @param query Cypher query. 130 | * @param params parameters map. 131 | * @return a result set. 132 | */ 133 | public ResultSet readOnlyQuery(String graphId, String query, Map params) { 134 | String preparedQuery = Utils.prepareQuery(query, params); 135 | return sendReadOnlyQuery(graphId, preparedQuery); 136 | } 137 | 138 | /** 139 | * Executes a cypher query with parameters and timeout. 140 | * @param graphId a graph to perform the query on. 141 | * @param timeout 142 | * @param query Cypher query. 143 | * @param params parameters map. 144 | * @return a result set. 145 | */ 146 | @Override 147 | public ResultSet query(String graphId, String query, Map params, long timeout) { 148 | String preparedQuery = Utils.prepareQuery(query, params); 149 | return sendQuery(graphId, preparedQuery, timeout); 150 | } 151 | 152 | /** 153 | * Executes a cypher read-only query with parameters and timeout. 154 | * @param graphId a graph to perform the query on. 155 | * @param timeout 156 | * @param query Cypher query. 157 | * @param params parameters map. 158 | * @return a result set. 159 | */ 160 | @Override 161 | public ResultSet readOnlyQuery(String graphId, String query, Map params, long timeout) { 162 | String preparedQuery = Utils.prepareQuery(query, params); 163 | return sendReadOnlyQuery(graphId, preparedQuery, timeout); 164 | } 165 | 166 | public ResultSet callProcedure(String graphId, String procedure){ 167 | return callProcedure(graphId, procedure, Utils.DUMMY_LIST, Utils.DUMMY_MAP); 168 | } 169 | 170 | public ResultSet callProcedure(String graphId, String procedure, List args){ 171 | return callProcedure(graphId, procedure, args, Utils.DUMMY_MAP); 172 | } 173 | 174 | public ResultSet callProcedure(String graphId, String procedure, List args , Map> kwargs){ 175 | 176 | String preparedProcedure = Utils.prepareProcedure(procedure, args, kwargs); 177 | return query(graphId, preparedProcedure); 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /src/main/java/com/redislabs/redisgraph/impl/api/ContextedRedisGraph.java: -------------------------------------------------------------------------------- 1 | package com.redislabs.redisgraph.impl.api; 2 | 3 | import java.util.List; 4 | 5 | import com.redislabs.redisgraph.RedisGraphContext; 6 | import com.redislabs.redisgraph.ResultSet; 7 | import com.redislabs.redisgraph.exceptions.JRedisGraphException; 8 | import com.redislabs.redisgraph.impl.Utils; 9 | import com.redislabs.redisgraph.impl.graph_cache.RedisGraphCaches; 10 | import com.redislabs.redisgraph.impl.resultset.ResultSetImpl; 11 | 12 | import redis.clients.jedis.Client; 13 | import redis.clients.jedis.Jedis; 14 | import redis.clients.jedis.exceptions.JedisDataException; 15 | import redis.clients.jedis.util.SafeEncoder; 16 | 17 | /** 18 | * An implementation of RedisGraphContext. Allows sending RedisGraph and some Redis commands, 19 | * within a specific connection context 20 | */ 21 | public class ContextedRedisGraph extends AbstractRedisGraph implements RedisGraphContext, RedisGraphCacheHolder { 22 | 23 | private final Jedis connectionContext; 24 | private RedisGraphCaches caches; 25 | 26 | /** 27 | * Generates a new instance with a specific Jedis connection 28 | * @param connectionContext 29 | */ 30 | public ContextedRedisGraph(Jedis connectionContext) { 31 | this.connectionContext = connectionContext; 32 | } 33 | 34 | /** 35 | * Overrides the abstract method. Return the instance only connection 36 | * @return 37 | */ 38 | @Override 39 | protected Jedis getConnection() { 40 | return this.connectionContext; 41 | } 42 | 43 | /** 44 | * Sends the query over the instance only connection 45 | * @param graphId graph to be queried 46 | * @param preparedQuery prepared query 47 | * @return Result set with the query answer 48 | */ 49 | @Override 50 | protected ResultSet sendQuery(String graphId, String preparedQuery) { 51 | Jedis conn = getConnection(); 52 | try { 53 | @SuppressWarnings("unchecked") 54 | List rawResponse = (List) conn.sendCommand(RedisGraphCommand.QUERY, graphId, preparedQuery, Utils.COMPACT_STRING); 55 | return new ResultSetImpl(rawResponse, this, caches.getGraphCache(graphId)); 56 | } catch (JRedisGraphException rt) { 57 | throw rt; 58 | } catch (JedisDataException j) { 59 | throw new JRedisGraphException(j); 60 | } 61 | } 62 | 63 | /** 64 | * Sends the read-only query over the instance only connection 65 | * @param graphId graph to be queried 66 | * @param preparedQuery prepared query 67 | * @return Result set with the query answer 68 | */ 69 | @Override 70 | protected ResultSet sendReadOnlyQuery(String graphId, String preparedQuery) { 71 | Jedis conn = getConnection(); 72 | try { 73 | @SuppressWarnings("unchecked") 74 | List rawResponse = (List) conn.sendCommand(RedisGraphCommand.RO_QUERY, graphId, preparedQuery, Utils.COMPACT_STRING); 75 | return new ResultSetImpl(rawResponse, this, caches.getGraphCache(graphId)); 76 | } catch (JRedisGraphException ge) { 77 | throw ge; 78 | } catch (JedisDataException de) { 79 | throw new JRedisGraphException(de); 80 | } 81 | } 82 | 83 | /** 84 | * Sends the query over the instance only connection 85 | * @param graphId graph to be queried 86 | * @param timeout 87 | * @param preparedQuery prepared query 88 | * @return Result set with the query answer 89 | */ 90 | @Override 91 | protected ResultSet sendQuery(String graphId, String preparedQuery, long timeout) { 92 | Jedis conn = getConnection(); 93 | try { 94 | @SuppressWarnings("unchecked") 95 | List rawResponse = (List) conn.sendBlockingCommand(RedisGraphCommand.QUERY, 96 | graphId, preparedQuery, Utils.COMPACT_STRING, Utils.TIMEOUT_STRING, Long.toString(timeout)); 97 | return new ResultSetImpl(rawResponse, this, caches.getGraphCache(graphId)); 98 | } catch (JRedisGraphException rt) { 99 | throw rt; 100 | } catch (JedisDataException j) { 101 | throw new JRedisGraphException(j); 102 | } 103 | } 104 | 105 | /** 106 | * Sends the read-only query over the instance only connection 107 | * @param graphId graph to be queried 108 | * @param timeout 109 | * @param preparedQuery prepared query 110 | * @return Result set with the query answer 111 | */ 112 | @Override 113 | protected ResultSet sendReadOnlyQuery(String graphId, String preparedQuery, long timeout) { 114 | Jedis conn = getConnection(); 115 | try { 116 | @SuppressWarnings("unchecked") 117 | List rawResponse = (List) conn.sendBlockingCommand(RedisGraphCommand.RO_QUERY, 118 | graphId, preparedQuery, Utils.COMPACT_STRING, Utils.TIMEOUT_STRING, Long.toString(timeout)); 119 | return new ResultSetImpl(rawResponse, this, caches.getGraphCache(graphId)); 120 | } catch (JRedisGraphException ge) { 121 | throw ge; 122 | } catch (JedisDataException de) { 123 | throw new JRedisGraphException(de); 124 | } 125 | } 126 | 127 | /** 128 | * @return Returns the instance Jedis connection. 129 | */ 130 | @Override 131 | public Jedis getConnectionContext() { 132 | return this.connectionContext; 133 | } 134 | 135 | /** 136 | * Creates a new RedisGraphTransaction transactional object 137 | * @return new RedisGraphTransaction 138 | */ 139 | @Override 140 | public RedisGraphTransaction multi() { 141 | Jedis jedis = getConnection(); 142 | Client client = jedis.getClient(); 143 | client.multi(); 144 | client.getOne(); 145 | RedisGraphTransaction transaction = new RedisGraphTransaction(client, this); 146 | transaction.setRedisGraphCaches(caches); 147 | return transaction; 148 | } 149 | 150 | /** 151 | * Creates a new RedisGraphPipeline pipeline object 152 | * @return new RedisGraphPipeline 153 | */ 154 | @Override 155 | public RedisGraphPipeline pipelined() { 156 | Jedis jedis = getConnection(); 157 | Client client = jedis.getClient(); 158 | RedisGraphPipeline pipeline = new RedisGraphPipeline(client, this); 159 | pipeline.setRedisGraphCaches(caches); 160 | return pipeline; 161 | } 162 | 163 | /** 164 | * Perfrom watch over given Redis keys 165 | * @param keys 166 | * @return "OK" 167 | */ 168 | @Override 169 | public String watch(String... keys) { 170 | return this.getConnection().watch(keys); 171 | } 172 | 173 | /** 174 | * Removes watch from all keys 175 | * @return 176 | */ 177 | @Override 178 | public String unwatch() { 179 | return this.getConnection().unwatch(); 180 | } 181 | 182 | /** 183 | * Deletes the entire graph 184 | * @param graphId graph to delete 185 | * @return delete running time statistics 186 | */ 187 | @Override 188 | public String deleteGraph(String graphId) { 189 | Jedis conn = getConnection(); 190 | Object response; 191 | try { 192 | response = conn.sendCommand(RedisGraphCommand.DELETE, graphId); 193 | } catch (Exception e) { 194 | conn.close(); 195 | throw e; 196 | } 197 | //clear local state 198 | caches.removeGraphCache(graphId); 199 | return SafeEncoder.encode((byte[]) response); 200 | } 201 | 202 | /** 203 | * closes the Jedis connection 204 | */ 205 | @Override 206 | public void close() { 207 | this.connectionContext.close(); 208 | 209 | } 210 | 211 | @Override 212 | public void setRedisGraphCaches(RedisGraphCaches caches) { 213 | this.caches = caches; 214 | } 215 | 216 | } 217 | -------------------------------------------------------------------------------- /src/main/java/com/redislabs/redisgraph/impl/api/RedisGraph.java: -------------------------------------------------------------------------------- 1 | package com.redislabs.redisgraph.impl.api; 2 | 3 | import com.redislabs.redisgraph.RedisGraphContext; 4 | import com.redislabs.redisgraph.RedisGraphContextGenerator; 5 | import com.redislabs.redisgraph.ResultSet; 6 | import com.redislabs.redisgraph.impl.graph_cache.RedisGraphCaches; 7 | import redis.clients.jedis.Jedis; 8 | import redis.clients.jedis.JedisPool; 9 | import redis.clients.jedis.util.Pool; 10 | import redis.clients.jedis.util.SafeEncoder; 11 | 12 | /** 13 | * 14 | */ 15 | public class RedisGraph extends AbstractRedisGraph implements RedisGraphContextGenerator { 16 | 17 | private final Pool pool; 18 | private final Jedis jedis; 19 | private final RedisGraphCaches caches = new RedisGraphCaches(); 20 | 21 | /** 22 | * Creates a client running on the local machine 23 | 24 | */ 25 | public RedisGraph() { 26 | this("localhost", 6379); 27 | } 28 | 29 | /** 30 | * Creates a client running on the specific host/post 31 | * 32 | * @param host Redis host 33 | * @param port Redis port 34 | */ 35 | public RedisGraph(String host, int port) { 36 | this(new JedisPool(host, port)); 37 | } 38 | 39 | /** 40 | * Creates a client using provided Jedis pool 41 | * 42 | * @param pool bring your own Jedis pool 43 | */ 44 | public RedisGraph(Pool pool) { 45 | this.pool = pool; 46 | this.jedis = null; 47 | } 48 | 49 | public RedisGraph(Jedis jedis) { 50 | this.jedis = jedis; 51 | this.pool = null; 52 | } 53 | 54 | /** 55 | * Overrides the abstract function. Gets and returns a Jedis connection from the Jedis pool 56 | * @return a Jedis connection 57 | */ 58 | @Override 59 | protected Jedis getConnection() { 60 | return jedis != null ? jedis : pool.getResource(); 61 | } 62 | 63 | /** 64 | * Overrides the abstract function. 65 | * Sends the query from any Jedis connection received from the Jedis pool and closes it once done 66 | * @param graphId graph to be queried 67 | * @param preparedQuery prepared query 68 | * @return Result set with the query answer 69 | */ 70 | @Override 71 | protected ResultSet sendQuery(String graphId, String preparedQuery){ 72 | try (ContextedRedisGraph contextedRedisGraph = new ContextedRedisGraph(getConnection())) { 73 | contextedRedisGraph.setRedisGraphCaches(caches); 74 | return contextedRedisGraph.sendQuery(graphId, preparedQuery); 75 | } 76 | } 77 | 78 | /** 79 | * Overrides the abstract function. 80 | * Sends the read-only query from any Jedis connection received from the Jedis pool and closes it once done 81 | * @param graphId graph to be queried 82 | * @param preparedQuery prepared query 83 | * @return Result set with the query answer 84 | */ 85 | @Override 86 | protected ResultSet sendReadOnlyQuery(String graphId, String preparedQuery){ 87 | try (ContextedRedisGraph contextedRedisGraph = new ContextedRedisGraph(getConnection())) { 88 | contextedRedisGraph.setRedisGraphCaches(caches); 89 | return contextedRedisGraph.sendReadOnlyQuery(graphId, preparedQuery); 90 | } 91 | } 92 | 93 | /** 94 | * Overrides the abstract function. 95 | * Sends the query from any Jedis connection received from the Jedis pool and closes it once done 96 | * @param graphId graph to be queried 97 | * @param preparedQuery prepared query 98 | * @param timeout 99 | * @return Result set with the query answer 100 | */ 101 | @Override 102 | protected ResultSet sendQuery(String graphId, String preparedQuery, long timeout){ 103 | try (ContextedRedisGraph contextedRedisGraph = new ContextedRedisGraph(getConnection())) { 104 | contextedRedisGraph.setRedisGraphCaches(caches); 105 | return contextedRedisGraph.sendQuery(graphId, preparedQuery, timeout); 106 | } 107 | } 108 | 109 | /** 110 | * Overrides the abstract function. 111 | * Sends the read-only query from any Jedis connection received from the Jedis pool and closes it once done 112 | * @param graphId graph to be queried 113 | * @param preparedQuery prepared query 114 | * @param timeout 115 | * @return Result set with the query answer 116 | */ 117 | @Override 118 | protected ResultSet sendReadOnlyQuery(String graphId, String preparedQuery, long timeout){ 119 | try (ContextedRedisGraph contextedRedisGraph = new ContextedRedisGraph(getConnection())) { 120 | contextedRedisGraph.setRedisGraphCaches(caches); 121 | return contextedRedisGraph.sendReadOnlyQuery(graphId, preparedQuery, timeout); 122 | } 123 | } 124 | 125 | /** 126 | * Closes the Jedis pool 127 | */ 128 | @Override 129 | public void close() { 130 | if (pool != null) { 131 | pool.close(); 132 | } 133 | if (jedis != null) { 134 | jedis.close(); 135 | } 136 | } 137 | 138 | /** 139 | * Deletes the entire graph 140 | * @param graphId graph to delete 141 | * @return delete running time statistics 142 | */ 143 | @Override 144 | public String deleteGraph(String graphId) { 145 | try (Jedis conn = getConnection()) { 146 | Object response = conn.sendCommand(RedisGraphCommand.DELETE, graphId); 147 | //clear local state 148 | caches.removeGraphCache(graphId); 149 | return SafeEncoder.encode((byte[]) response); 150 | } 151 | } 152 | 153 | /** 154 | * Returns a new ContextedRedisGraph bounded to a Jedis connection from the Jedis pool 155 | * @return ContextedRedisGraph 156 | */ 157 | @Override 158 | public RedisGraphContext getContext() { 159 | ContextedRedisGraph contextedRedisGraph = new ContextedRedisGraph(getConnection()); 160 | contextedRedisGraph.setRedisGraphCaches(this.caches); 161 | return contextedRedisGraph; 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /src/main/java/com/redislabs/redisgraph/impl/api/RedisGraphCacheHolder.java: -------------------------------------------------------------------------------- 1 | package com.redislabs.redisgraph.impl.api; 2 | 3 | import com.redislabs.redisgraph.impl.graph_cache.RedisGraphCaches; 4 | 5 | public interface RedisGraphCacheHolder { 6 | 7 | void setRedisGraphCaches(RedisGraphCaches caches); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/redislabs/redisgraph/impl/api/RedisGraphCommand.java: -------------------------------------------------------------------------------- 1 | package com.redislabs.redisgraph.impl.api; 2 | import redis.clients.jedis.util.SafeEncoder; 3 | import redis.clients.jedis.commands.ProtocolCommand; 4 | 5 | /** 6 | * 7 | * 8 | * 9 | */ 10 | public enum RedisGraphCommand implements ProtocolCommand { 11 | QUERY("graph.QUERY"), 12 | RO_QUERY("graph.RO_QUERY"), 13 | DELETE("graph.DELETE"); 14 | 15 | private final byte[] raw; 16 | 17 | RedisGraphCommand(String alt) { 18 | raw = SafeEncoder.encode(alt); 19 | } 20 | 21 | public byte[] getRaw() { 22 | return raw; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/redislabs/redisgraph/impl/api/RedisGraphPipeline.java: -------------------------------------------------------------------------------- 1 | package com.redislabs.redisgraph.impl.api; 2 | 3 | import com.redislabs.redisgraph.RedisGraph; 4 | import com.redislabs.redisgraph.ResultSet; 5 | import com.redislabs.redisgraph.impl.Utils; 6 | import com.redislabs.redisgraph.impl.graph_cache.RedisGraphCaches; 7 | import com.redislabs.redisgraph.impl.resultset.ResultSetImpl; 8 | import redis.clients.jedis.Builder; 9 | import redis.clients.jedis.BuilderFactory; 10 | import redis.clients.jedis.Client; 11 | import redis.clients.jedis.Pipeline; 12 | import redis.clients.jedis.Response; 13 | 14 | import java.util.List; 15 | import java.util.Map; 16 | 17 | /** 18 | * This class is extending Jedis Pipeline 19 | */ 20 | public class RedisGraphPipeline extends Pipeline implements com.redislabs.redisgraph.RedisGraphPipeline, RedisGraphCacheHolder { 21 | 22 | private final RedisGraph redisGraph; 23 | private RedisGraphCaches caches; 24 | 25 | 26 | public RedisGraphPipeline(Client client, RedisGraph redisGraph){ 27 | super.setClient(client); 28 | this.redisGraph = redisGraph; 29 | } 30 | 31 | /** 32 | * Execute a Cypher query. 33 | * @param graphId a graph to perform the query on 34 | * @param query Cypher query 35 | * @return a response which builds the result set with the query answer. 36 | */ 37 | @Override 38 | public Response query(String graphId, String query) { 39 | client.sendCommand(RedisGraphCommand.QUERY, graphId, query, Utils.COMPACT_STRING); 40 | return getResponse(new Builder() { 41 | @SuppressWarnings("unchecked") 42 | @Override 43 | public ResultSet build(Object o) { 44 | return new ResultSetImpl((List) o, redisGraph, caches.getGraphCache(graphId)); 45 | } 46 | }); 47 | } 48 | 49 | /** 50 | * Execute a Cypher read-oly query. 51 | * @param graphId a graph to perform the query on 52 | * @param query Cypher query 53 | * @return a response which builds the result set with the query answer. 54 | */ 55 | @Override 56 | public Response readOnlyQuery(String graphId, String query) { 57 | client.sendCommand(RedisGraphCommand.RO_QUERY, graphId, query, Utils.COMPACT_STRING); 58 | return getResponse(new Builder() { 59 | @SuppressWarnings("unchecked") 60 | @Override 61 | public ResultSet build(Object o) { 62 | return new ResultSetImpl((List) o, redisGraph, caches.getGraphCache(graphId)); 63 | } 64 | }); 65 | } 66 | 67 | /** 68 | * Execute a Cypher query with timeout. 69 | * 70 | * NOTE: timeout is simply sent to DB. Socket timeout will not be changed. 71 | * @param graphId a graph to perform the query on 72 | * @param query Cypher query 73 | * @param timeout 74 | * @return a response which builds the result set with the query answer. 75 | */ 76 | @Override 77 | public Response query(String graphId, String query, long timeout) { 78 | client.sendCommand(RedisGraphCommand.QUERY, graphId, query, Utils.COMPACT_STRING, Utils.TIMEOUT_STRING, 79 | Long.toString(timeout)); 80 | return getResponse(new Builder() { 81 | @SuppressWarnings("unchecked") 82 | @Override 83 | public ResultSet build(Object o) { 84 | return new ResultSetImpl((List) o, redisGraph, caches.getGraphCache(graphId)); 85 | } 86 | }); 87 | } 88 | 89 | /** 90 | * Execute a Cypher read-only query with timeout. 91 | * 92 | * NOTE: timeout is simply sent to DB. Socket timeout will not be changed. 93 | * @param graphId a graph to perform the query on 94 | * @param query Cypher query 95 | * @param timeout 96 | * @return a response which builds the result set with the query answer. 97 | */ 98 | @Override 99 | public Response readOnlyQuery(String graphId, String query, long timeout) { 100 | client.sendCommand(RedisGraphCommand.RO_QUERY, graphId, query, Utils.COMPACT_STRING, Utils.TIMEOUT_STRING, 101 | Long.toString(timeout)); 102 | return getResponse(new Builder() { 103 | @SuppressWarnings("unchecked") 104 | @Override 105 | public ResultSet build(Object o) { 106 | return new ResultSetImpl((List) o, redisGraph, caches.getGraphCache(graphId)); 107 | } 108 | }); 109 | } 110 | 111 | /** 112 | * Executes a cypher query with parameters. 113 | * @param graphId a graph to perform the query on. 114 | * @param query Cypher query. 115 | * @param params parameters map. 116 | * @return a response which builds the result set with the query answer. 117 | */ 118 | @Override 119 | public Response query(String graphId, String query, Map params) { 120 | String preparedQuery = Utils.prepareQuery(query, params); 121 | client.sendCommand(RedisGraphCommand.QUERY, graphId, preparedQuery, Utils.COMPACT_STRING); 122 | return getResponse(new Builder() { 123 | @SuppressWarnings("unchecked") 124 | @Override 125 | public ResultSet build(Object o) { 126 | return new ResultSetImpl((List) o, redisGraph, caches.getGraphCache(graphId)); 127 | } 128 | }); 129 | } 130 | 131 | /** 132 | * Executes a cypher read-only query with parameters. 133 | * @param graphId a graph to perform the query on. 134 | * @param query Cypher query. 135 | * @param params parameters map. 136 | * @return a response which builds the result set with the query answer. 137 | */ 138 | @Override 139 | public Response readOnlyQuery(String graphId, String query, Map params) { 140 | String preparedQuery = Utils.prepareQuery(query, params); 141 | client.sendCommand(RedisGraphCommand.RO_QUERY, graphId, preparedQuery, Utils.COMPACT_STRING); 142 | return getResponse(new Builder() { 143 | @SuppressWarnings("unchecked") 144 | @Override 145 | public ResultSet build(Object o) { 146 | return new ResultSetImpl((List) o, redisGraph, caches.getGraphCache(graphId)); 147 | } 148 | }); 149 | } 150 | 151 | /** 152 | * Executes a cypher query with parameters and timeout. 153 | * 154 | * NOTE: timeout is simply sent to DB. Socket timeout will not be changed. 155 | * timeout. 156 | * @param graphId a graph to perform the query on. 157 | * @param query Cypher query. 158 | * @param params parameters map. 159 | * @param timeout 160 | * @return a response which builds the result set with the query answer. 161 | */ 162 | @Override 163 | public Response query(String graphId, String query, Map params, long timeout) { 164 | String preparedQuery = Utils.prepareQuery(query, params); 165 | client.sendCommand(RedisGraphCommand.QUERY, graphId, preparedQuery, Utils.COMPACT_STRING, Utils.TIMEOUT_STRING, 166 | Long.toString(timeout)); 167 | return getResponse(new Builder() { 168 | @SuppressWarnings("unchecked") 169 | @Override 170 | public ResultSet build(Object o) { 171 | return new ResultSetImpl((List) o, redisGraph, caches.getGraphCache(graphId)); 172 | } 173 | }); 174 | } 175 | 176 | /** 177 | * Executes a cypher read-only query with parameters and timeout. 178 | * 179 | * NOTE: timeout is simply sent to DB. Socket timeout will not be changed. 180 | * timeout. 181 | * @param graphId a graph to perform the query on. 182 | * @param query Cypher query. 183 | * @param params parameters map. 184 | * @param timeout 185 | * @return a response which builds the result set with the query answer. 186 | */ 187 | @Override 188 | public Response readOnlyQuery(String graphId, String query, Map params, long timeout) { 189 | String preparedQuery = Utils.prepareQuery(query, params); 190 | client.sendCommand(RedisGraphCommand.RO_QUERY, graphId, preparedQuery, Utils.COMPACT_STRING, 191 | Utils.TIMEOUT_STRING, 192 | Long.toString(timeout)); 193 | return getResponse(new Builder() { 194 | @SuppressWarnings("unchecked") 195 | @Override 196 | public ResultSet build(Object o) { 197 | return new ResultSetImpl((List) o, redisGraph, caches.getGraphCache(graphId)); 198 | } 199 | }); 200 | } 201 | 202 | /** 203 | * Invokes stored procedures without arguments 204 | * @param graphId a graph to perform the query on 205 | * @param procedure procedure name to invoke 206 | * @return response with result set with the procedure data 207 | */ 208 | public Response callProcedure(String graphId, String procedure){ 209 | return callProcedure(graphId, procedure, Utils.DUMMY_LIST, Utils.DUMMY_MAP); 210 | } 211 | 212 | /** 213 | * Invokes stored procedure with arguments 214 | * @param graphId a graph to perform the query on 215 | * @param procedure procedure name to invoke 216 | * @param args procedure arguments 217 | * @return response with result set with the procedure data 218 | */ 219 | public Response callProcedure(String graphId, String procedure, List args ){ 220 | return callProcedure(graphId, procedure, args, Utils.DUMMY_MAP); 221 | } 222 | 223 | 224 | /** 225 | * Invoke a stored procedure 226 | * @param graphId a graph to perform the query on 227 | * @param procedure - procedure to execute 228 | * @param args - procedure arguments 229 | * @param kwargs - procedure output arguments 230 | * @return response with result set with the procedure data 231 | */ 232 | public Response callProcedure(String graphId, String procedure, List args, 233 | Map> kwargs) { 234 | String preparedProcedure = Utils.prepareProcedure(procedure, args, kwargs); 235 | return query(graphId, preparedProcedure); 236 | } 237 | 238 | 239 | /** 240 | * Deletes the entire graph 241 | * @param graphId graph to delete 242 | * @return response with the deletion running time statistics 243 | */ 244 | public Response deleteGraph(String graphId){ 245 | 246 | client.sendCommand(RedisGraphCommand.DELETE, graphId); 247 | Response response = getResponse(BuilderFactory.STRING); 248 | caches.removeGraphCache(graphId); 249 | return response; 250 | } 251 | 252 | @Override 253 | public void setRedisGraphCaches(RedisGraphCaches caches) { 254 | this.caches = caches; 255 | } 256 | } 257 | -------------------------------------------------------------------------------- /src/main/java/com/redislabs/redisgraph/impl/api/RedisGraphTransaction.java: -------------------------------------------------------------------------------- 1 | package com.redislabs.redisgraph.impl.api; 2 | 3 | import com.redislabs.redisgraph.RedisGraph; 4 | import com.redislabs.redisgraph.ResultSet; 5 | import com.redislabs.redisgraph.impl.Utils; 6 | import com.redislabs.redisgraph.impl.graph_cache.RedisGraphCaches; 7 | import com.redislabs.redisgraph.impl.resultset.ResultSetImpl; 8 | import redis.clients.jedis.Builder; 9 | import redis.clients.jedis.BuilderFactory; 10 | import redis.clients.jedis.Client; 11 | import redis.clients.jedis.Response; 12 | import redis.clients.jedis.Transaction; 13 | 14 | import java.util.List; 15 | import java.util.Map; 16 | 17 | /** 18 | * This class is extending Jedis Transaction 19 | */ 20 | public class RedisGraphTransaction extends Transaction 21 | implements com.redislabs.redisgraph.RedisGraphTransaction, RedisGraphCacheHolder { 22 | 23 | private final RedisGraph redisGraph; 24 | private RedisGraphCaches caches; 25 | 26 | public RedisGraphTransaction(Client client, RedisGraph redisGraph) { 27 | // init as in Jedis 28 | super(client); 29 | 30 | this.redisGraph = redisGraph; 31 | } 32 | 33 | /** 34 | * Execute a Cypher query. 35 | * @param graphId a graph to perform the query on 36 | * @param query Cypher query 37 | * @return a response which builds the result set with the query answer. 38 | */ 39 | @Override 40 | public Response query(String graphId, String query) { 41 | client.sendCommand(RedisGraphCommand.QUERY, graphId, query, Utils.COMPACT_STRING); 42 | return getResponse(new Builder() { 43 | @SuppressWarnings("unchecked") 44 | @Override 45 | public ResultSet build(Object o) { 46 | return new ResultSetImpl((List) o, redisGraph, caches.getGraphCache(graphId)); 47 | } 48 | }); 49 | } 50 | 51 | /** 52 | * Execute a Cypher read-oly query. 53 | * @param graphId a graph to perform the query on 54 | * @param query Cypher query 55 | * @return a response which builds the result set with the query answer. 56 | */ 57 | @Override 58 | public Response readOnlyQuery(String graphId, String query) { 59 | client.sendCommand(RedisGraphCommand.RO_QUERY, graphId, query, Utils.COMPACT_STRING); 60 | return getResponse(new Builder() { 61 | @SuppressWarnings("unchecked") 62 | @Override 63 | public ResultSet build(Object o) { 64 | return new ResultSetImpl((List) o, redisGraph, caches.getGraphCache(graphId)); 65 | } 66 | }); 67 | } 68 | 69 | /** 70 | * Execute a Cypher query with timeout. 71 | * 72 | * NOTE: timeout is simply sent to DB. Socket timeout will not be changed. 73 | * @param graphId a graph to perform the query on 74 | * @param query Cypher query 75 | * @param timeout 76 | * @return a response which builds the result set with the query answer. 77 | */ 78 | @Override 79 | public Response query(String graphId, String query, long timeout) { 80 | client.sendCommand(RedisGraphCommand.QUERY, graphId, query, Utils.COMPACT_STRING, Utils.TIMEOUT_STRING, 81 | Long.toString(timeout)); 82 | return getResponse(new Builder() { 83 | @SuppressWarnings("unchecked") 84 | @Override 85 | public ResultSet build(Object o) { 86 | return new ResultSetImpl((List) o, redisGraph, caches.getGraphCache(graphId)); 87 | } 88 | }); 89 | } 90 | 91 | /** 92 | * Execute a Cypher read-only query with timeout. 93 | * 94 | * NOTE: timeout is simply sent to DB. Socket timeout will not be changed. 95 | * @param graphId a graph to perform the query on 96 | * @param query Cypher query 97 | * @param timeout 98 | * @return a response which builds the result set with the query answer. 99 | */ 100 | @Override 101 | public Response readOnlyQuery(String graphId, String query, long timeout) { 102 | client.sendCommand(RedisGraphCommand.RO_QUERY, graphId, query, Utils.COMPACT_STRING, Utils.TIMEOUT_STRING, 103 | Long.toString(timeout)); 104 | return getResponse(new Builder() { 105 | @SuppressWarnings("unchecked") 106 | @Override 107 | public ResultSet build(Object o) { 108 | return new ResultSetImpl((List) o, redisGraph, caches.getGraphCache(graphId)); 109 | } 110 | }); 111 | } 112 | 113 | /** 114 | * Execute a Cypher query with arguments 115 | * 116 | * @param graphId a graph to perform the query on 117 | * @param query Cypher query 118 | * @param args 119 | * @return response with a result set 120 | * @deprecated use {@link #query(String, String, Map)} instead. 121 | */ 122 | @Deprecated 123 | @Override 124 | public Response query(String graphId, String query, Object... args) { 125 | String preparedQuery = Utils.prepareQuery(query, args); 126 | client.sendCommand(RedisGraphCommand.QUERY, graphId, preparedQuery, Utils.COMPACT_STRING); 127 | return getResponse(new Builder() { 128 | @SuppressWarnings("unchecked") 129 | @Override 130 | public ResultSet build(Object o) { 131 | return new ResultSetImpl((List) o, redisGraph, caches.getGraphCache(graphId)); 132 | } 133 | }); 134 | } 135 | 136 | /** 137 | * Executes a cypher query with parameters. 138 | * @param graphId a graph to perform the query on. 139 | * @param query Cypher query. 140 | * @param params parameters map. 141 | * @return a response which builds the result set with the query answer. 142 | */ 143 | @Override 144 | public Response query(String graphId, String query, Map params) { 145 | String preparedQuery = Utils.prepareQuery(query, params); 146 | client.sendCommand(RedisGraphCommand.QUERY, graphId, preparedQuery, Utils.COMPACT_STRING); 147 | return getResponse(new Builder() { 148 | @SuppressWarnings("unchecked") 149 | @Override 150 | public ResultSet build(Object o) { 151 | return new ResultSetImpl((List) o, redisGraph, caches.getGraphCache(graphId)); 152 | } 153 | }); 154 | } 155 | 156 | /** 157 | * Executes a cypher read-only query with parameters. 158 | * @param graphId a graph to perform the query on. 159 | * @param query Cypher query. 160 | * @param params parameters map. 161 | * @return a response which builds the result set with the query answer. 162 | */ 163 | @Override 164 | public Response readOnlyQuery(String graphId, String query, Map params) { 165 | String preparedQuery = Utils.prepareQuery(query, params); 166 | client.sendCommand(RedisGraphCommand.RO_QUERY, graphId, preparedQuery, Utils.COMPACT_STRING); 167 | return getResponse(new Builder() { 168 | @SuppressWarnings("unchecked") 169 | @Override 170 | public ResultSet build(Object o) { 171 | return new ResultSetImpl((List) o, redisGraph, caches.getGraphCache(graphId)); 172 | } 173 | }); 174 | } 175 | 176 | /** 177 | * Executes a cypher query with parameters and timeout. 178 | * 179 | * NOTE: timeout is simply sent to DB. Socket timeout will not be changed. 180 | * @param graphId a graph to perform the query on. 181 | * @param query Cypher query. 182 | * @param params parameters map. 183 | * @param timeout 184 | * @return a response which builds the result set with the query answer. 185 | */ 186 | @Override 187 | public Response query(String graphId, String query, Map params, long timeout) { 188 | String preparedQuery = Utils.prepareQuery(query, params); 189 | client.sendCommand(RedisGraphCommand.QUERY, graphId, preparedQuery, Utils.COMPACT_STRING, Utils.TIMEOUT_STRING, 190 | Long.toString(timeout)); 191 | return getResponse(new Builder() { 192 | @SuppressWarnings("unchecked") 193 | @Override 194 | public ResultSet build(Object o) { 195 | return new ResultSetImpl((List) o, redisGraph, caches.getGraphCache(graphId)); 196 | } 197 | }); 198 | } 199 | 200 | /** 201 | * Executes a cypher read-only query with parameters and timeout. 202 | * 203 | * NOTE: timeout is simply sent to DB. Socket timeout will not be changed. 204 | * @param graphId a graph to perform the query on. 205 | * @param query Cypher query. 206 | * @param params parameters map. 207 | * @param timeout 208 | * @return a response which builds the result set with the query answer. 209 | */ 210 | @Override 211 | public Response readOnlyQuery(String graphId, String query, Map params, long timeout) { 212 | String preparedQuery = Utils.prepareQuery(query, params); 213 | client.sendCommand(RedisGraphCommand.RO_QUERY, graphId, preparedQuery, Utils.COMPACT_STRING, 214 | Utils.TIMEOUT_STRING, Long.toString(timeout)); 215 | return getResponse(new Builder() { 216 | @SuppressWarnings("unchecked") 217 | @Override 218 | public ResultSet build(Object o) { 219 | return new ResultSetImpl((List) o, redisGraph, caches.getGraphCache(graphId)); 220 | } 221 | }); 222 | } 223 | 224 | /** 225 | * Invokes stored procedures without arguments, in multi/exec context 226 | * @param graphId a graph to perform the query on 227 | * @param procedure procedure name to invoke 228 | * @return response with result set with the procedure data 229 | */ 230 | public Response callProcedure(String graphId, String procedure) { 231 | return callProcedure(graphId, procedure, Utils.DUMMY_LIST, Utils.DUMMY_MAP); 232 | } 233 | 234 | /** 235 | * Invokes stored procedure with arguments, in multi/exec context 236 | * @param graphId a graph to perform the query on 237 | * @param procedure procedure name to invoke 238 | * @param args procedure arguments 239 | * @return response with result set with the procedure data 240 | */ 241 | public Response callProcedure(String graphId, String procedure, List args) { 242 | return callProcedure(graphId, procedure, args, Utils.DUMMY_MAP); 243 | } 244 | 245 | /** 246 | * Invoke a stored procedure, in multi/exec context 247 | * @param graphId a graph to perform the query on 248 | * @param procedure - procedure to execute 249 | * @param args - procedure arguments 250 | * @param kwargs - procedure output arguments 251 | * @return response with result set with the procedure data 252 | */ 253 | public Response callProcedure(String graphId, String procedure, List args, 254 | Map> kwargs) { 255 | String preparedProcedure = Utils.prepareProcedure(procedure, args, kwargs); 256 | return query(graphId, preparedProcedure); 257 | } 258 | 259 | /** 260 | * Deletes the entire graph, in multi/exec context 261 | * @param graphId graph to delete 262 | * @return response with the deletion running time statistics 263 | */ 264 | public Response deleteGraph(String graphId) { 265 | client.sendCommand(RedisGraphCommand.DELETE, graphId); 266 | Response response = getResponse(BuilderFactory.STRING); 267 | caches.removeGraphCache(graphId); 268 | return response; 269 | } 270 | 271 | @Override 272 | public void setRedisGraphCaches(RedisGraphCaches caches) { 273 | this.caches = caches; 274 | } 275 | 276 | } 277 | -------------------------------------------------------------------------------- /src/main/java/com/redislabs/redisgraph/impl/graph_cache/GraphCache.java: -------------------------------------------------------------------------------- 1 | package com.redislabs.redisgraph.impl.graph_cache; 2 | 3 | import com.redislabs.redisgraph.RedisGraph; 4 | 5 | /** 6 | * A class to store a local cache in the client, for a specific graph. 7 | * Holds the labels, property names and relationship types 8 | */ 9 | public class GraphCache { 10 | 11 | private final GraphCacheList labels; 12 | private final GraphCacheList propertyNames; 13 | private final GraphCacheList relationshipTypes; 14 | 15 | /** 16 | * 17 | * @param graphId - graph Id 18 | */ 19 | public GraphCache(String graphId) { 20 | this.labels = new GraphCacheList(graphId, "db.labels"); 21 | this.propertyNames = new GraphCacheList(graphId, "db.propertyKeys"); 22 | this.relationshipTypes = new GraphCacheList(graphId, "db.relationshipTypes"); 23 | } 24 | 25 | /** 26 | * @param index - index of label 27 | * @return requested label 28 | */ 29 | public String getLabel(int index, RedisGraph redisGraph) { 30 | return labels.getCachedData(index, redisGraph); 31 | } 32 | 33 | /** 34 | * @param index index of the relationship type 35 | * @return requested relationship type 36 | */ 37 | public String getRelationshipType(int index, RedisGraph redisGraph) { 38 | return relationshipTypes.getCachedData(index, redisGraph); 39 | } 40 | 41 | /** 42 | * @param index index of property name 43 | * @return requested property 44 | */ 45 | public String getPropertyName(int index, RedisGraph redisGraph) { 46 | 47 | return propertyNames.getCachedData(index, redisGraph); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/redislabs/redisgraph/impl/graph_cache/GraphCacheList.java: -------------------------------------------------------------------------------- 1 | package com.redislabs.redisgraph.impl.graph_cache; 2 | 3 | import com.redislabs.redisgraph.Record; 4 | import com.redislabs.redisgraph.RedisGraph; 5 | import com.redislabs.redisgraph.ResultSet; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | import java.util.concurrent.CopyOnWriteArrayList; 10 | 11 | 12 | /** 13 | * Represents a local cache of list of strings. Holds data from a specific procedure, for a specific graph. 14 | */ 15 | class GraphCacheList { 16 | 17 | private final String graphId; 18 | private final String procedure; 19 | private final List data = new CopyOnWriteArrayList<>(); 20 | 21 | /** 22 | * 23 | * @param graphId - graph id 24 | * @param procedure - exact procedure command 25 | */ 26 | public GraphCacheList(String graphId, String procedure) { 27 | this.graphId = graphId; 28 | this.procedure = procedure; 29 | } 30 | 31 | 32 | /** 33 | * A method to return a cached item if it is in the cache, or re-validate the cache if its invalidated 34 | * @param index index of data item 35 | * @return The string value of the specific procedure response, at the given index. 36 | */ 37 | public String getCachedData(int index, RedisGraph redisGraph) { 38 | if (index >= data.size()) { 39 | synchronized (data){ 40 | if (index >= data.size()) { 41 | getProcedureInfo(redisGraph); 42 | } 43 | } 44 | } 45 | return data.get(index); 46 | 47 | } 48 | 49 | /** 50 | * Auxiliary method to parse a procedure result set and refresh the cache 51 | */ 52 | private void getProcedureInfo(RedisGraph redisGraph) { 53 | ResultSet resultSet = redisGraph.callProcedure(graphId, procedure); 54 | List newData = new ArrayList<>(); 55 | int i = 0; 56 | while (resultSet.hasNext()) { 57 | Record record = resultSet.next(); 58 | if(i >= data.size()){ 59 | newData.add(record.getString(0)); 60 | } 61 | i++; 62 | } 63 | data.addAll(newData); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/com/redislabs/redisgraph/impl/graph_cache/RedisGraphCaches.java: -------------------------------------------------------------------------------- 1 | package com.redislabs.redisgraph.impl.graph_cache; 2 | 3 | import com.redislabs.redisgraph.RedisGraph; 4 | 5 | import java.util.Map; 6 | import java.util.concurrent.ConcurrentHashMap; 7 | 8 | public class RedisGraphCaches { 9 | 10 | private final Map graphCaches = new ConcurrentHashMap<>(); 11 | 12 | public GraphCache getGraphCache(String graphId){ 13 | if (!graphCaches.containsKey(graphId)){ 14 | graphCaches.putIfAbsent(graphId, new GraphCache(graphId)); 15 | } 16 | return graphCaches.get(graphId); 17 | } 18 | 19 | /** 20 | * Returns a String which represents the name of the label mapped to the label id 21 | * @param graphId graph to perform the query 22 | * @param index label index 23 | * @param redisGraph RedisGraphAPI implementation 24 | * @return label name 25 | */ 26 | public String getLabel(String graphId, int index, RedisGraph redisGraph) { 27 | return getGraphCache(graphId).getLabel(index, redisGraph); 28 | } 29 | 30 | /** 31 | * Returns a String which represents the name of the relationship mapped to the label id 32 | * @param graphId graph to perform the query 33 | * @param index relationship index 34 | * @param redisGraph RedisGraphAPI implementation 35 | * @return relationship name 36 | */ 37 | public String getRelationshipType(String graphId, int index, RedisGraph redisGraph){ 38 | return getGraphCache(graphId).getRelationshipType(index, redisGraph); 39 | } 40 | 41 | /** 42 | * Returns a String which represents the name of the property mapped to the label id 43 | * @param graphId graph to perform the query 44 | * @param index property index 45 | * @param redisGraph RedisGraphAPI implementation 46 | * @return property name 47 | */ 48 | public String getPropertyName(String graphId, int index, RedisGraph redisGraph){ 49 | return getGraphCache(graphId).getPropertyName(index, redisGraph); 50 | } 51 | 52 | /** 53 | * Removes a graph meta data cache 54 | * @param graphId 55 | */ 56 | public void removeGraphCache(String graphId){ 57 | graphCaches.remove(graphId); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/redislabs/redisgraph/impl/resultset/HeaderImpl.java: -------------------------------------------------------------------------------- 1 | package com.redislabs.redisgraph.impl.resultset; 2 | 3 | import com.redislabs.redisgraph.Header; 4 | import redis.clients.jedis.util.SafeEncoder; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | import java.util.Objects; 9 | 10 | /** 11 | * Query result header interface implementation 12 | */ 13 | public class HeaderImpl implements Header { 14 | 15 | //members 16 | private final List> raw; 17 | private final List schemaTypes = new ArrayList<>(); 18 | private final List schemaNames = new ArrayList<>(); 19 | 20 | 21 | /** 22 | * Parameterized constructor 23 | * A raw representation of a header (query response schema) is a list. 24 | * Each entry in the list is a tuple (list of size 2). 25 | * tuple[0] represents the type of the column, and tuple[1] represents the name of the column. 26 | * 27 | * @param raw - raw representation of a header 28 | */ 29 | public HeaderImpl(List> raw) { 30 | this.raw = raw; 31 | } 32 | 33 | 34 | /** 35 | * @return a list of column names, ordered by they appearance in the query 36 | */ 37 | @Override 38 | public List getSchemaNames() { 39 | if (schemaNames.isEmpty()) { 40 | buildSchema(); 41 | } 42 | return schemaNames; 43 | } 44 | 45 | /** 46 | * @return a list of column types, ordered by they appearance in the query 47 | */ 48 | @Override 49 | public List getSchemaTypes() { 50 | if (schemaTypes.isEmpty()) { 51 | buildSchema(); 52 | } 53 | return schemaTypes; 54 | } 55 | 56 | /** 57 | * Extracts schema names and types from the raw representation 58 | */ 59 | private void buildSchema() { 60 | for (List tuple : this.raw) { 61 | 62 | //get type 63 | ResultSetColumnTypes type = ResultSetColumnTypes.values()[((Long) tuple.get(0)).intValue()]; 64 | //get text 65 | String text = SafeEncoder.encode((byte[]) tuple.get(1)); 66 | if (type != null) { 67 | schemaTypes.add(type); 68 | schemaNames.add(text); 69 | } 70 | } 71 | } 72 | 73 | @Override 74 | public boolean equals(Object o) { 75 | if (this == o) return true; 76 | if (!(o instanceof HeaderImpl)) return false; 77 | HeaderImpl header = (HeaderImpl) o; 78 | return Objects.equals(getSchemaTypes(), header.getSchemaTypes()) && 79 | Objects.equals(getSchemaNames(), header.getSchemaNames()); 80 | } 81 | 82 | @Override 83 | public int hashCode() { 84 | return Objects.hash(getSchemaTypes(), getSchemaNames()); 85 | } 86 | 87 | @Override 88 | public String toString() { 89 | final StringBuilder sb = new StringBuilder("HeaderImpl{"); 90 | sb.append("schemaTypes=").append(schemaTypes); 91 | sb.append(", schemaNames=").append(schemaNames); 92 | sb.append('}'); 93 | return sb.toString(); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/com/redislabs/redisgraph/impl/resultset/RecordImpl.java: -------------------------------------------------------------------------------- 1 | package com.redislabs.redisgraph.impl.resultset; 2 | 3 | import java.util.List; 4 | import java.util.Objects; 5 | 6 | import com.redislabs.redisgraph.Record; 7 | 8 | public class RecordImpl implements Record { 9 | 10 | private final List header; 11 | private final List values; 12 | 13 | public RecordImpl(List header, List values){ 14 | this.header=header; 15 | this.values = values; 16 | } 17 | 18 | @Override 19 | public T getValue(int index) { 20 | return (T)this.values.get(index); 21 | } 22 | 23 | @Override 24 | public T getValue(String key) { 25 | return getValue(this.header.indexOf(key)); 26 | } 27 | 28 | @Override 29 | public String getString(int index) { 30 | return this.values.get(index).toString(); 31 | } 32 | 33 | @Override 34 | public String getString(String key) { 35 | return getString(this.header.indexOf(key)); 36 | } 37 | 38 | @Override 39 | public List keys() { 40 | return header; 41 | } 42 | 43 | @Override 44 | public List values() { 45 | return this.values; 46 | } 47 | 48 | @Override 49 | public boolean containsKey(String key) { 50 | return this.header.contains(key); 51 | } 52 | 53 | @Override 54 | public int size() { 55 | return this.header.size(); 56 | } 57 | 58 | @Override 59 | public boolean equals(Object o) { 60 | if (this == o) return true; 61 | if (!(o instanceof RecordImpl)) return false; 62 | RecordImpl record = (RecordImpl) o; 63 | return Objects.equals(header, record.header) && 64 | Objects.equals(values, record.values); 65 | } 66 | 67 | @Override 68 | public int hashCode() { 69 | return Objects.hash(header, values); 70 | } 71 | 72 | @Override 73 | public String toString() { 74 | final StringBuilder sb = new StringBuilder("Record{"); 75 | sb.append("values=").append(values); 76 | sb.append('}'); 77 | return sb.toString(); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/com/redislabs/redisgraph/impl/resultset/ResultSetImpl.java: -------------------------------------------------------------------------------- 1 | package com.redislabs.redisgraph.impl.resultset; 2 | 3 | import com.redislabs.redisgraph.Header; 4 | import com.redislabs.redisgraph.Record; 5 | import com.redislabs.redisgraph.RedisGraph; 6 | import com.redislabs.redisgraph.ResultSet; 7 | import com.redislabs.redisgraph.Statistics; 8 | import com.redislabs.redisgraph.exceptions.JRedisGraphException; 9 | import com.redislabs.redisgraph.graph_entities.*; 10 | import com.redislabs.redisgraph.impl.graph_cache.GraphCache; 11 | import redis.clients.jedis.BuilderFactory; 12 | import redis.clients.jedis.util.SafeEncoder; 13 | import redis.clients.jedis.exceptions.JedisDataException; 14 | 15 | import java.util.*; 16 | 17 | public class ResultSetImpl implements ResultSet { 18 | 19 | private final Header header; 20 | private final Statistics statistics; 21 | private final List results; 22 | 23 | private int position = 0; 24 | private final RedisGraph redisGraph; 25 | private final GraphCache cache; 26 | 27 | /** 28 | * @param rawResponse the raw representation of response is at most 3 lists of 29 | * objects. The last list is the statistics list. 30 | * @param redisGraph the graph connection 31 | * @param cache the graph local cache 32 | */ 33 | @SuppressWarnings("unchecked") 34 | public ResultSetImpl(List rawResponse, RedisGraph redisGraph, GraphCache cache) { 35 | this.redisGraph = redisGraph; 36 | this.cache = cache; 37 | 38 | // If a run-time error occurred, the last member of the rawResponse will be a 39 | // JedisDataException. 40 | if (rawResponse.get(rawResponse.size() - 1) instanceof JedisDataException) { 41 | 42 | throw new JRedisGraphException((Throwable) rawResponse.get(rawResponse.size() - 1)); 43 | } 44 | 45 | if (rawResponse.size() != 3) { 46 | 47 | header = parseHeader(new ArrayList<>()); 48 | results = new ArrayList<>(); 49 | statistics = rawResponse.isEmpty() ? parseStatistics(new ArrayList()) 50 | : parseStatistics(rawResponse.get(rawResponse.size() - 1)); 51 | 52 | } else { 53 | 54 | header = parseHeader((List>) rawResponse.get(0)); 55 | results = parseResult((List>) rawResponse.get(1)); 56 | statistics = parseStatistics(rawResponse.get(2)); 57 | } 58 | } 59 | 60 | /** 61 | * @param rawResultSet - raw result set representation 62 | * @return parsed result set 63 | */ 64 | @SuppressWarnings("unchecked") 65 | private List parseResult(List> rawResultSet) { 66 | if (rawResultSet == null || rawResultSet.isEmpty()) { 67 | return new ArrayList<>(0); 68 | } 69 | 70 | List results = new ArrayList<>(rawResultSet.size()); 71 | // go over each raw result 72 | for (List row : rawResultSet) { 73 | 74 | List parsedRow = new ArrayList<>(row.size()); 75 | // go over each object in the result 76 | for (int i = 0; i < row.size(); i++) { 77 | // get raw representation of the object 78 | List obj = (List) row.get(i); 79 | // get object type 80 | Header.ResultSetColumnTypes objType = header.getSchemaTypes().get(i); 81 | // deserialize according to type and 82 | switch (objType) { 83 | case COLUMN_NODE: 84 | parsedRow.add(deserializeNode(obj)); 85 | break; 86 | case COLUMN_RELATION: 87 | parsedRow.add(deserializeEdge(obj)); 88 | break; 89 | case COLUMN_SCALAR: 90 | parsedRow.add(deserializeScalar(obj)); 91 | break; 92 | default: 93 | parsedRow.add(null); 94 | break; 95 | } 96 | } 97 | 98 | // create new record from deserialized objects 99 | Record record = new RecordImpl(header.getSchemaNames(), parsedRow); 100 | results.add(record); 101 | } 102 | 103 | return results; 104 | } 105 | 106 | /** 107 | * @param rawStatistics raw statistics representation 108 | * @return parsed statistics 109 | */ 110 | @SuppressWarnings("unchecked") 111 | private StatisticsImpl parseStatistics(Object rawStatistics) { 112 | return new StatisticsImpl((List) rawStatistics); 113 | } 114 | 115 | /** 116 | * @param rawHeader - raw header representation 117 | * @return parsed header 118 | */ 119 | private HeaderImpl parseHeader(List> rawHeader) { 120 | return new HeaderImpl(rawHeader); 121 | } 122 | 123 | @Override 124 | public Statistics getStatistics() { 125 | return statistics; 126 | } 127 | 128 | @Override 129 | public Header getHeader() { 130 | return header; 131 | } 132 | 133 | /** 134 | * @param rawNodeData - raw node object in the form of list of object 135 | * rawNodeData.get(0) - id (long) rawNodeData.get(1) - a list 136 | * y which contains the labels of this node. Each entry is a 137 | * label id from the type of long rawNodeData.get(2) - a list 138 | * which contains the properties of the node. 139 | * @return Node object 140 | */ 141 | @SuppressWarnings("unchecked") 142 | private Node deserializeNode(List rawNodeData) { 143 | 144 | List labelsIndices = (List) rawNodeData.get(1); 145 | List> rawProperties = (List>) rawNodeData.get(2); 146 | 147 | Node node = new Node(labelsIndices.size(), rawProperties.size()); 148 | deserializeGraphEntityId(node, (Long) rawNodeData.get(0)); 149 | 150 | for (Long labelIndex : labelsIndices) { 151 | String label = cache.getLabel(labelIndex.intValue(), redisGraph); 152 | node.addLabel(label); 153 | } 154 | 155 | deserializeGraphEntityProperties(node, rawProperties); 156 | 157 | return node; 158 | } 159 | 160 | /** 161 | * @param graphEntity graph entity 162 | * @param id entity id to be set to the graph entity 163 | */ 164 | private void deserializeGraphEntityId(GraphEntity graphEntity, long id) { 165 | graphEntity.setId(id); 166 | } 167 | 168 | /** 169 | * @param rawEdgeData - a list of objects rawEdgeData[0] - edge id 170 | * rawEdgeData[1] - edge relationship type rawEdgeData[2] - 171 | * edge source rawEdgeData[3] - edge destination 172 | * rawEdgeData[4] - edge properties 173 | * @return Edge object 174 | */ 175 | @SuppressWarnings("unchecked") 176 | private Edge deserializeEdge(List rawEdgeData) { 177 | 178 | List> rawProperties = (List>) rawEdgeData.get(4); 179 | 180 | Edge edge = new Edge(rawProperties.size()); 181 | deserializeGraphEntityId(edge, (Long) rawEdgeData.get(0)); 182 | 183 | String relationshipType = cache.getRelationshipType(((Long) rawEdgeData.get(1)).intValue(), redisGraph); 184 | edge.setRelationshipType(relationshipType); 185 | 186 | edge.setSource((long) rawEdgeData.get(2)); 187 | edge.setDestination((long) rawEdgeData.get(3)); 188 | 189 | deserializeGraphEntityProperties(edge, rawProperties); 190 | 191 | return edge; 192 | } 193 | 194 | /** 195 | * @param entity graph entity for adding the properties to 196 | * @param rawProperties raw representation of a list of graph entity properties. 197 | * Each entry is a list (rawProperty) is a raw 198 | * representation of property, as follows: 199 | * rawProperty.get(0) - property key rawProperty.get(1) - 200 | * property type rawProperty.get(2) - property value 201 | */ 202 | private void deserializeGraphEntityProperties(GraphEntity entity, List> rawProperties) { 203 | 204 | for (List rawProperty : rawProperties) { 205 | Property property = new Property<>(); 206 | property.setName(cache.getPropertyName(((Long) rawProperty.get(0)).intValue(), redisGraph)); 207 | 208 | // trimmed for getting to value using deserializeScalar 209 | List propertyScalar = rawProperty.subList(1, rawProperty.size()); 210 | property.setValue(deserializeScalar(propertyScalar)); 211 | 212 | entity.addProperty(property); 213 | 214 | } 215 | 216 | } 217 | 218 | /** 219 | * @param rawScalarData - a list of object. list[0] is the scalar type, list[1] 220 | * is the scalar value 221 | * @return value of the specific scalar type 222 | */ 223 | @SuppressWarnings("unchecked") 224 | private Object deserializeScalar(List rawScalarData) { 225 | ResultSetScalarTypes type = getValueTypeFromObject(rawScalarData.get(0)); 226 | 227 | Object obj = rawScalarData.get(1); 228 | switch (type) { 229 | case VALUE_NULL: 230 | return null; 231 | case VALUE_BOOLEAN: 232 | return Boolean.parseBoolean(SafeEncoder.encode((byte[]) obj)); 233 | case VALUE_DOUBLE: 234 | return Double.parseDouble(SafeEncoder.encode((byte[]) obj)); 235 | case VALUE_INTEGER: 236 | return (Long) obj; 237 | case VALUE_STRING: 238 | return SafeEncoder.encode((byte[]) obj); 239 | case VALUE_ARRAY: 240 | return deserializeArray(obj); 241 | case VALUE_NODE: 242 | return deserializeNode((List) obj); 243 | case VALUE_EDGE: 244 | return deserializeEdge((List) obj); 245 | case VALUE_PATH: 246 | return deserializePath(obj); 247 | case VALUE_MAP: 248 | return deserializeMap(obj); 249 | case VALUE_POINT: 250 | return deserializePoint(obj); 251 | case VALUE_UNKNOWN: 252 | default: 253 | return obj; 254 | } 255 | } 256 | 257 | private Object deserializePoint(Object rawScalarData) { 258 | return new Point(BuilderFactory.DOUBLE_LIST.build(rawScalarData)); 259 | } 260 | 261 | @SuppressWarnings("unchecked") 262 | private Map deserializeMap(Object rawScalarData) { 263 | List keyTypeValueEntries = (List) rawScalarData; 264 | 265 | int size = keyTypeValueEntries.size(); 266 | Map map = new HashMap<>(size >> 1); // set the capacity to half of the list 267 | 268 | for (int i = 0; i < size; i += 2) { 269 | String key = SafeEncoder.encode((byte[]) keyTypeValueEntries.get(i)); 270 | Object value = deserializeScalar((List) keyTypeValueEntries.get(i + 1)); 271 | map.put(key, value); 272 | } 273 | return map; 274 | } 275 | 276 | @SuppressWarnings("unchecked") 277 | private Path deserializePath(Object rawScalarData) { 278 | List> array = (List>) rawScalarData; 279 | List nodes = (List) deserializeScalar(array.get(0)); 280 | List edges = (List) deserializeScalar(array.get(1)); 281 | return new Path(nodes, edges); 282 | } 283 | 284 | @SuppressWarnings("unchecked") 285 | private List deserializeArray(Object rawScalarData) { 286 | List> array = (List>) rawScalarData; 287 | List res = new ArrayList<>(array.size()); 288 | for (List arrayValue : array) { 289 | res.add(deserializeScalar(arrayValue)); 290 | } 291 | return res; 292 | } 293 | 294 | /** 295 | * Auxiliary function to retrieve scalar types 296 | * 297 | * @param rawScalarType 298 | * @return scalar type 299 | */ 300 | private ResultSetScalarTypes getValueTypeFromObject(Object rawScalarType) { 301 | return ResultSetScalarTypes.getValue(((Long) rawScalarType).intValue()); 302 | } 303 | 304 | @Override 305 | @Deprecated 306 | public boolean hasNext() { 307 | return position < results.size(); 308 | } 309 | 310 | @Override 311 | @Deprecated 312 | public Record next() { 313 | if (!hasNext()) 314 | throw new NoSuchElementException(); 315 | return results.get(position++); 316 | } 317 | 318 | @Override 319 | public int size() { 320 | return results.size(); 321 | } 322 | 323 | @Override 324 | public boolean equals(Object o) { 325 | if (this == o) 326 | return true; 327 | if (!(o instanceof ResultSetImpl)) 328 | return false; 329 | ResultSetImpl resultSet = (ResultSetImpl) o; 330 | return Objects.equals(getHeader(), resultSet.getHeader()) 331 | && Objects.equals(getStatistics(), resultSet.getStatistics()) 332 | && Objects.equals(results, resultSet.results); 333 | } 334 | 335 | @Override 336 | public int hashCode() { 337 | return Objects.hash(getHeader(), getStatistics(), results); 338 | } 339 | 340 | @Override 341 | public String toString() { 342 | final StringBuilder sb = new StringBuilder("ResultSetImpl{"); 343 | sb.append("header=").append(header); 344 | sb.append(", statistics=").append(statistics); 345 | sb.append(", results=").append(results); 346 | sb.append('}'); 347 | return sb.toString(); 348 | } 349 | 350 | @Override 351 | public Iterator iterator() { 352 | // TODO Auto-generated method stub 353 | return results.iterator(); 354 | } 355 | } 356 | -------------------------------------------------------------------------------- /src/main/java/com/redislabs/redisgraph/impl/resultset/ResultSetScalarTypes.java: -------------------------------------------------------------------------------- 1 | package com.redislabs.redisgraph.impl.resultset; 2 | 3 | import redis.clients.jedis.exceptions.JedisDataException; 4 | 5 | enum ResultSetScalarTypes { 6 | VALUE_UNKNOWN, 7 | VALUE_NULL, 8 | VALUE_STRING, 9 | VALUE_INTEGER, // 64 bit long. 10 | VALUE_BOOLEAN, 11 | VALUE_DOUBLE, 12 | VALUE_ARRAY, 13 | VALUE_EDGE, 14 | VALUE_NODE, 15 | VALUE_PATH, 16 | VALUE_MAP, 17 | VALUE_POINT; 18 | 19 | private static final ResultSetScalarTypes[] values = values(); 20 | 21 | public static ResultSetScalarTypes getValue(int index) { 22 | try { 23 | return values[index]; 24 | } catch(IndexOutOfBoundsException e) { 25 | throw new JedisDataException("Unrecognized response type"); 26 | } 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/redislabs/redisgraph/impl/resultset/StatisticsImpl.java: -------------------------------------------------------------------------------- 1 | package com.redislabs.redisgraph.impl.resultset; 2 | 3 | import java.util.EnumMap; 4 | import java.util.List; 5 | import java.util.Map; 6 | import java.util.Objects; 7 | 8 | import com.redislabs.redisgraph.Statistics; 9 | 10 | import redis.clients.jedis.util.SafeEncoder; 11 | 12 | /** 13 | * Query result statistics interface implementation 14 | */ 15 | public class StatisticsImpl implements Statistics { 16 | //members 17 | private final List raw; 18 | private final Map statistics; 19 | 20 | /** 21 | * A raw representation of query execution statistics is a list of strings 22 | * (byte arrays which need to be de-serialized). 23 | * Each string is built in the form of "K:V" where K is statistics label and V is its value. 24 | * @param raw a raw representation of the query execution statistics 25 | */ 26 | public StatisticsImpl(List raw){ 27 | this.raw = raw; 28 | this.statistics = new EnumMap<>(Statistics.Label.class); // lazy loaded 29 | } 30 | 31 | 32 | /** 33 | * 34 | * @param label the requested statistic label as key 35 | * @return a string with the value, if key exists, null otherwise 36 | */ 37 | @Override 38 | public String getStringValue(Statistics.Label label) { 39 | return getStatistics().get(label); 40 | } 41 | 42 | private Map getStatistics(){ 43 | if(statistics.size() == 0) { 44 | for(byte[] tuple : this.raw) { 45 | String text = SafeEncoder.encode(tuple); 46 | String[] rowTuple = text.split(":"); 47 | if(rowTuple.length == 2) { 48 | Statistics.Label label = Statistics.Label.getEnum(rowTuple[0]); 49 | if(label != null) { 50 | this.statistics.put( label, rowTuple[1].trim()); 51 | } 52 | } 53 | } 54 | } 55 | return statistics; 56 | } 57 | 58 | /** 59 | * 60 | * @param label the requested statistic label as key 61 | * @return a string with the value, if key exists, 0 otherwise 62 | */ 63 | public int getIntValue(Statistics.Label label) { 64 | String value = getStringValue(label); 65 | return value==null ? 0 : Integer.parseInt(value); 66 | } 67 | 68 | /** 69 | * 70 | * @return number of nodes created after query execution 71 | */ 72 | @Override 73 | public int nodesCreated() { 74 | return getIntValue(Label.NODES_CREATED); 75 | } 76 | 77 | /** 78 | * 79 | * @return number of nodes deleted after query execution 80 | */ 81 | @Override 82 | public int nodesDeleted() { 83 | return getIntValue(Label.NODES_DELETED); 84 | } 85 | 86 | /** 87 | * 88 | * @return number of indices added after query execution 89 | */ 90 | @Override 91 | public int indicesAdded() { 92 | return getIntValue(Label.INDICES_ADDED); 93 | } 94 | 95 | 96 | @Override 97 | public int indicesDeleted() {return getIntValue(Label.INDICES_DELETED);} 98 | 99 | /** 100 | * 101 | * @return number of labels added after query execution 102 | */ 103 | @Override 104 | public int labelsAdded() { 105 | return getIntValue(Label.LABELS_ADDED); 106 | } 107 | 108 | /** 109 | * 110 | * @return number of relationship deleted after query execution 111 | */ 112 | @Override 113 | public int relationshipsDeleted() { 114 | return getIntValue(Label.RELATIONSHIPS_DELETED); 115 | } 116 | 117 | /** 118 | * 119 | * @return number of relationship created after query execution 120 | */ 121 | @Override 122 | public int relationshipsCreated() { 123 | return getIntValue(Label.RELATIONSHIPS_CREATED); 124 | } 125 | 126 | /** 127 | * 128 | * @return number of properties set after query execution 129 | */ 130 | @Override 131 | public int propertiesSet() { 132 | return getIntValue(Label.PROPERTIES_SET); 133 | } 134 | 135 | /** 136 | * 137 | * @return The execution plan was cached on RedisGraph. 138 | */ 139 | @Override 140 | public boolean cachedExecution() { 141 | return getIntValue(Label.CACHED_EXECUTION) == 1; 142 | } 143 | 144 | @Override 145 | public boolean equals(Object o) { 146 | if (this == o) return true; 147 | if (!(o instanceof StatisticsImpl)) return false; 148 | StatisticsImpl that = (StatisticsImpl) o; 149 | return Objects.equals(raw, that.raw) && 150 | Objects.equals(getStatistics(), that.getStatistics()); 151 | } 152 | 153 | @Override 154 | public int hashCode() { 155 | return Objects.hash(raw, getStatistics()); 156 | } 157 | 158 | @Override 159 | public String toString() { 160 | final StringBuilder sb = new StringBuilder("StatisticsImpl{"); 161 | sb.append("statistics=").append(getStatistics()); 162 | sb.append('}'); 163 | return sb.toString(); 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /src/test/java/com/redislabs/redisgraph/InstantiationTest.java: -------------------------------------------------------------------------------- 1 | package com.redislabs.redisgraph; 2 | 3 | import org.junit.After; 4 | import org.junit.Assert; 5 | 6 | import com.redislabs.redisgraph.impl.api.RedisGraph; 7 | 8 | import redis.clients.jedis.Jedis; 9 | import redis.clients.jedis.JedisPool; 10 | 11 | public class InstantiationTest { 12 | private RedisGraphContextGenerator client; 13 | 14 | public void createDefaultClient() { 15 | client = new RedisGraph(); 16 | ResultSet resultSet = client.query("g", "CREATE ({name:'bsb'})"); 17 | Assert.assertEquals(1, resultSet.getStatistics().nodesCreated()); 18 | } 19 | 20 | public void createClientWithHostAndPort() { 21 | client = new RedisGraph("localhost", 6379); 22 | ResultSet resultSet = client.query("g", "CREATE ({name:'bsb'})"); 23 | Assert.assertEquals(1, resultSet.getStatistics().nodesCreated()); 24 | } 25 | 26 | public void createClientWithJedisInstance() { 27 | client = new RedisGraph(new Jedis()); 28 | ResultSet resultSet = client.query("g", "CREATE ({name:'bsb'})"); 29 | Assert.assertEquals(1, resultSet.getStatistics().nodesCreated()); 30 | } 31 | 32 | public void createClientWithJedisPool() { 33 | client = new RedisGraph(new JedisPool()); 34 | ResultSet resultSet = client.query("g", "CREATE ({name:'bsb'})"); 35 | Assert.assertEquals(1, resultSet.getStatistics().nodesCreated()); 36 | } 37 | 38 | @After 39 | public void closeClient() { 40 | if (client != null) { 41 | client.deleteGraph("g"); 42 | client.close(); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/test/java/com/redislabs/redisgraph/IterableTest.java: -------------------------------------------------------------------------------- 1 | package com.redislabs.redisgraph; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | 5 | import org.junit.After; 6 | import org.junit.Before; 7 | import org.junit.Test; 8 | 9 | import com.redislabs.redisgraph.impl.api.RedisGraph; 10 | 11 | public class IterableTest { 12 | 13 | private RedisGraphContextGenerator api; 14 | 15 | @Before 16 | public void createApi() { 17 | api = new RedisGraph(); 18 | } 19 | 20 | @After 21 | public void deleteGraph() { 22 | 23 | api.deleteGraph("social"); 24 | api.close(); 25 | } 26 | 27 | @Test 28 | public void testRecordsIterator() { 29 | api.query("social", "UNWIND(range(0,50)) as i CREATE(:N{i:i})"); 30 | 31 | ResultSet rs = api.query("social", "MATCH(n) RETURN n"); 32 | int count = 0; 33 | while (rs.hasNext()) { 34 | rs.next(); 35 | count++; 36 | } 37 | assertEquals(rs.size(), count); 38 | } 39 | 40 | @Test 41 | public void testRecordsIterable() { 42 | api.query("social", "UNWIND(range(0,50)) as i CREATE(:N{i:i})"); 43 | 44 | ResultSet rs = api.query("social", "MATCH(n) RETURN n"); 45 | int count = 0; 46 | for (@SuppressWarnings("unused") 47 | Record row : rs) { 48 | count++; 49 | } 50 | assertEquals(rs.size(), count); 51 | } 52 | 53 | @Test 54 | public void testRecordsIteratorAndIterable() { 55 | api.query("social", "UNWIND(range(0,50)) as i CREATE(:N{i:i})"); 56 | 57 | ResultSet rs = api.query("social", "MATCH(n) RETURN n"); 58 | rs.next(); 59 | int count = 0; 60 | for (@SuppressWarnings("unused") 61 | Record row : rs) { 62 | count++; 63 | } 64 | assertEquals(rs.size(), count); 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/test/java/com/redislabs/redisgraph/PipelineTest.java: -------------------------------------------------------------------------------- 1 | package com.redislabs.redisgraph; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | 6 | import org.junit.After; 7 | import org.junit.Assert; 8 | import org.junit.Before; 9 | import org.junit.Test; 10 | 11 | import com.redislabs.redisgraph.graph_entities.Node; 12 | import com.redislabs.redisgraph.graph_entities.Property; 13 | import com.redislabs.redisgraph.impl.api.RedisGraph; 14 | import com.redislabs.redisgraph.impl.resultset.ResultSetImpl; 15 | 16 | public class PipelineTest { 17 | 18 | private RedisGraphContextGenerator api; 19 | 20 | public PipelineTest() { 21 | } 22 | 23 | @Before 24 | public void createApi() { 25 | api = new RedisGraph(); 26 | } 27 | 28 | @After 29 | public void deleteGraph() { 30 | api.deleteGraph("social"); 31 | api.close(); 32 | } 33 | 34 | @Test 35 | public void testSync() { 36 | try (RedisGraphContext c = api.getContext()) { 37 | RedisGraphPipeline pipeline = c.pipelined(); 38 | pipeline.set("x", "1"); 39 | pipeline.query("social", "CREATE (:Person {name:'a'})"); 40 | pipeline.query("g", "CREATE (:Person {name:'a'})"); 41 | pipeline.incr("x"); 42 | pipeline.get("x"); 43 | pipeline.query("social", "MATCH (n:Person) RETURN n"); 44 | pipeline.deleteGraph("g"); 45 | pipeline.callProcedure("social", "db.labels"); 46 | List results = pipeline.syncAndReturnAll(); 47 | 48 | // Redis set command 49 | Assert.assertEquals(String.class, results.get(0).getClass()); 50 | Assert.assertEquals("OK", results.get(0)); 51 | 52 | // Redis graph command 53 | Assert.assertEquals(ResultSetImpl.class, results.get(1).getClass()); 54 | ResultSet resultSet = (ResultSet) results.get(1); 55 | Assert.assertEquals(1, resultSet.getStatistics().nodesCreated()); 56 | Assert.assertEquals(1, resultSet.getStatistics().propertiesSet()); 57 | 58 | Assert.assertEquals(ResultSetImpl.class, results.get(2).getClass()); 59 | resultSet = (ResultSet) results.get(2); 60 | Assert.assertEquals(1, resultSet.getStatistics().nodesCreated()); 61 | Assert.assertEquals(1, resultSet.getStatistics().propertiesSet()); 62 | 63 | // Redis incr command 64 | Assert.assertEquals(Long.class, results.get(3).getClass()); 65 | Assert.assertEquals(2L, results.get(3)); 66 | 67 | // Redis get command 68 | Assert.assertEquals(String.class, results.get(4).getClass()); 69 | Assert.assertEquals("2", results.get(4)); 70 | 71 | // Graph query result 72 | Assert.assertEquals(ResultSetImpl.class, results.get(5).getClass()); 73 | resultSet = (ResultSet) results.get(5); 74 | 75 | Assert.assertNotNull(resultSet.getHeader()); 76 | Header header = resultSet.getHeader(); 77 | 78 | List schemaNames = header.getSchemaNames(); 79 | Assert.assertNotNull(schemaNames); 80 | Assert.assertEquals(1, schemaNames.size()); 81 | Assert.assertEquals("n", schemaNames.get(0)); 82 | 83 | Property nameProperty = new Property<>("name", "a"); 84 | 85 | Node expectedNode = new Node(); 86 | expectedNode.setId(0); 87 | expectedNode.addLabel("Person"); 88 | expectedNode.addProperty(nameProperty); 89 | // see that the result were pulled from the right graph 90 | Assert.assertEquals(1, resultSet.size()); 91 | Assert.assertTrue(resultSet.hasNext()); 92 | Record record = resultSet.next(); 93 | Assert.assertFalse(resultSet.hasNext()); 94 | Assert.assertEquals(Arrays.asList("n"), record.keys()); 95 | Assert.assertEquals(expectedNode, record.getValue("n")); 96 | 97 | Assert.assertEquals(ResultSetImpl.class, results.get(7).getClass()); 98 | resultSet = (ResultSet) results.get(7); 99 | 100 | Assert.assertNotNull(resultSet.getHeader()); 101 | header = resultSet.getHeader(); 102 | 103 | schemaNames = header.getSchemaNames(); 104 | Assert.assertNotNull(schemaNames); 105 | Assert.assertEquals(1, schemaNames.size()); 106 | Assert.assertEquals("label", schemaNames.get(0)); 107 | 108 | Assert.assertEquals(1, resultSet.size()); 109 | Assert.assertTrue(resultSet.hasNext()); 110 | record = resultSet.next(); 111 | Assert.assertFalse(resultSet.hasNext()); 112 | Assert.assertEquals(Arrays.asList("label"), record.keys()); 113 | Assert.assertEquals("Person", record.getValue("label")); 114 | } 115 | } 116 | 117 | @Test 118 | public void testReadOnlyQueries() { 119 | try (RedisGraphContext c = api.getContext()) { 120 | RedisGraphPipeline pipeline = c.pipelined(); 121 | 122 | pipeline.set("x", "1"); 123 | pipeline.query("social", "CREATE (:Person {name:'a'})"); 124 | pipeline.query("g", "CREATE (:Person {name:'a'})"); 125 | pipeline.readOnlyQuery("social", "MATCH (n:Person) RETURN n"); 126 | pipeline.deleteGraph("g"); 127 | pipeline.callProcedure("social", "db.labels"); 128 | List results = pipeline.syncAndReturnAll(); 129 | 130 | // Redis set command 131 | Assert.assertEquals(String.class, results.get(0).getClass()); 132 | Assert.assertEquals("OK", results.get(0)); 133 | 134 | // Redis graph command 135 | Assert.assertEquals(ResultSetImpl.class, results.get(1).getClass()); 136 | ResultSet resultSet = (ResultSet) results.get(1); 137 | Assert.assertEquals(1, resultSet.getStatistics().nodesCreated()); 138 | Assert.assertEquals(1, resultSet.getStatistics().propertiesSet()); 139 | 140 | Assert.assertEquals(ResultSetImpl.class, results.get(2).getClass()); 141 | resultSet = (ResultSet) results.get(2); 142 | Assert.assertEquals(1, resultSet.getStatistics().nodesCreated()); 143 | Assert.assertEquals(1, resultSet.getStatistics().propertiesSet()); 144 | 145 | // Graph read-only query result 146 | Assert.assertEquals(ResultSetImpl.class, results.get(5).getClass()); 147 | resultSet = (ResultSet) results.get(3); 148 | 149 | Assert.assertNotNull(resultSet.getHeader()); 150 | Header header = resultSet.getHeader(); 151 | 152 | List schemaNames = header.getSchemaNames(); 153 | Assert.assertNotNull(schemaNames); 154 | Assert.assertEquals(1, schemaNames.size()); 155 | Assert.assertEquals("n", schemaNames.get(0)); 156 | 157 | Property nameProperty = new Property<>("name", "a"); 158 | 159 | Node expectedNode = new Node(); 160 | expectedNode.setId(0); 161 | expectedNode.addLabel("Person"); 162 | expectedNode.addProperty(nameProperty); 163 | // see that the result were pulled from the right graph 164 | Assert.assertEquals(1, resultSet.size()); 165 | Assert.assertTrue(resultSet.hasNext()); 166 | Record record = resultSet.next(); 167 | Assert.assertFalse(resultSet.hasNext()); 168 | Assert.assertEquals(Arrays.asList("n"), record.keys()); 169 | Assert.assertEquals(expectedNode, record.getValue("n")); 170 | 171 | Assert.assertEquals(ResultSetImpl.class, results.get(5).getClass()); 172 | resultSet = (ResultSet) results.get(5); 173 | 174 | Assert.assertNotNull(resultSet.getHeader()); 175 | header = resultSet.getHeader(); 176 | 177 | schemaNames = header.getSchemaNames(); 178 | Assert.assertNotNull(schemaNames); 179 | Assert.assertEquals(1, schemaNames.size()); 180 | Assert.assertEquals("label", schemaNames.get(0)); 181 | 182 | Assert.assertEquals(1, resultSet.size()); 183 | Assert.assertTrue(resultSet.hasNext()); 184 | record = resultSet.next(); 185 | Assert.assertFalse(resultSet.hasNext()); 186 | Assert.assertEquals(Arrays.asList("label"), record.keys()); 187 | Assert.assertEquals("Person", record.getValue("label")); 188 | } 189 | } 190 | 191 | @Test 192 | public void testWaitReplicas() { 193 | try (RedisGraphContext c = api.getContext()) { 194 | RedisGraphPipeline pipeline = c.pipelined(); 195 | pipeline.set("x", "1"); 196 | pipeline.query("social", "CREATE (:Person {name:'a'})"); 197 | pipeline.query("g", "CREATE (:Person {name:'a'})"); 198 | pipeline.waitReplicas(0, 100L); 199 | List results = pipeline.syncAndReturnAll(); 200 | Assert.assertEquals(0L, results.get(3)); 201 | } 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /src/test/java/com/redislabs/redisgraph/RedisGraphAPITest.java: -------------------------------------------------------------------------------- 1 | package com.redislabs.redisgraph; 2 | 3 | import java.util.*; 4 | import java.util.stream.Collectors; 5 | import java.util.stream.IntStream; 6 | 7 | import org.junit.After; 8 | import org.junit.Assert; 9 | import org.junit.Before; 10 | import org.junit.Test; 11 | 12 | import com.redislabs.redisgraph.Statistics.Label; 13 | import com.redislabs.redisgraph.graph_entities.Edge; 14 | import com.redislabs.redisgraph.graph_entities.Node; 15 | import com.redislabs.redisgraph.graph_entities.Path; 16 | import com.redislabs.redisgraph.graph_entities.Point; 17 | import com.redislabs.redisgraph.graph_entities.Property; 18 | import com.redislabs.redisgraph.impl.api.RedisGraph; 19 | import com.redislabs.redisgraph.test.utils.PathBuilder; 20 | 21 | public class RedisGraphAPITest { 22 | 23 | private RedisGraphContextGenerator client; 24 | 25 | @Before 26 | public void createApi() { 27 | client = new RedisGraph(); 28 | } 29 | 30 | @After 31 | public void deleteGraph() { 32 | client.deleteGraph("social"); 33 | client.close(); 34 | } 35 | 36 | @Test 37 | public void testCreateNode() { 38 | // Create a node 39 | ResultSet resultSet = client.query("social", "CREATE ({name:'roi',age:32})"); 40 | 41 | Assert.assertEquals(1, resultSet.getStatistics().nodesCreated()); 42 | Assert.assertNull(resultSet.getStatistics().getStringValue(Label.NODES_DELETED)); 43 | Assert.assertNull(resultSet.getStatistics().getStringValue(Label.RELATIONSHIPS_CREATED)); 44 | Assert.assertNull(resultSet.getStatistics().getStringValue(Label.RELATIONSHIPS_DELETED)); 45 | Assert.assertEquals(2, resultSet.getStatistics().propertiesSet()); 46 | Assert.assertNotNull(resultSet.getStatistics().getStringValue(Label.QUERY_INTERNAL_EXECUTION_TIME)); 47 | 48 | Assert.assertFalse(resultSet.hasNext()); 49 | 50 | try { 51 | resultSet.next(); 52 | Assert.fail(); 53 | } catch (NoSuchElementException ignored) { 54 | } 55 | } 56 | 57 | @Test 58 | public void testCreateLabeledNode() { 59 | // Create a node with a label 60 | ResultSet resultSet = client.query("social", "CREATE (:human{name:'danny',age:12})"); 61 | Assert.assertFalse(resultSet.hasNext()); 62 | Assert.assertEquals("1", resultSet.getStatistics().getStringValue(Label.NODES_CREATED)); 63 | Assert.assertEquals("2", resultSet.getStatistics().getStringValue(Label.PROPERTIES_SET)); 64 | Assert.assertNotNull(resultSet.getStatistics().getStringValue(Label.QUERY_INTERNAL_EXECUTION_TIME)); 65 | } 66 | 67 | @Test 68 | public void testConnectNodes() { 69 | // Create both source and destination nodes 70 | Assert.assertNotNull(client.query("social", "CREATE (:person{name:'roi',age:32})")); 71 | Assert.assertNotNull(client.query("social", "CREATE (:person{name:'amit',age:30})")); 72 | 73 | // Connect source and destination nodes. 74 | ResultSet resultSet = client.query("social", 75 | "MATCH (a:person), (b:person) WHERE (a.name = 'roi' AND b.name='amit') CREATE (a)-[:knows]->(b)"); 76 | 77 | Assert.assertFalse(resultSet.hasNext()); 78 | Assert.assertNull(resultSet.getStatistics().getStringValue(Label.NODES_CREATED)); 79 | Assert.assertNull(resultSet.getStatistics().getStringValue(Label.PROPERTIES_SET)); 80 | Assert.assertEquals(1, resultSet.getStatistics().relationshipsCreated()); 81 | Assert.assertEquals(0, resultSet.getStatistics().relationshipsDeleted()); 82 | Assert.assertNotNull(resultSet.getStatistics().getStringValue(Label.QUERY_INTERNAL_EXECUTION_TIME)); 83 | } 84 | 85 | @Test 86 | public void testDeleteNodes() { 87 | Assert.assertNotNull(client.query("social", "CREATE (:person{name:'roi',age:32})")); 88 | Assert.assertNotNull(client.query("social", "CREATE (:person{name:'amit',age:30})")); 89 | ResultSet deleteResult = client.query("social", "MATCH (a:person) WHERE (a.name = 'roi') DELETE a"); 90 | 91 | Assert.assertFalse(deleteResult.hasNext()); 92 | Assert.assertNull(deleteResult.getStatistics().getStringValue(Label.NODES_CREATED)); 93 | Assert.assertNull(deleteResult.getStatistics().getStringValue(Label.PROPERTIES_SET)); 94 | Assert.assertNull(deleteResult.getStatistics().getStringValue(Label.NODES_CREATED)); 95 | Assert.assertNull(deleteResult.getStatistics().getStringValue(Label.RELATIONSHIPS_CREATED)); 96 | Assert.assertNull(deleteResult.getStatistics().getStringValue(Label.RELATIONSHIPS_DELETED)); 97 | Assert.assertEquals(1, deleteResult.getStatistics().nodesDeleted()); 98 | Assert.assertNotNull(deleteResult.getStatistics().getStringValue(Label.QUERY_INTERNAL_EXECUTION_TIME)); 99 | 100 | Assert.assertNotNull(client.query("social", "CREATE (:person{name:'roi',age:32})")); 101 | Assert.assertNotNull(client.query("social", 102 | "MATCH (a:person), (b:person) WHERE (a.name = 'roi' AND b.name='amit') CREATE (a)-[:knows]->(a)")); 103 | deleteResult = client.query("social", "MATCH (a:person) WHERE (a.name = 'roi') DELETE a"); 104 | 105 | Assert.assertFalse(deleteResult.hasNext()); 106 | Assert.assertNull(deleteResult.getStatistics().getStringValue(Label.NODES_CREATED)); 107 | Assert.assertNull(deleteResult.getStatistics().getStringValue(Label.PROPERTIES_SET)); 108 | Assert.assertNull(deleteResult.getStatistics().getStringValue(Label.NODES_CREATED)); 109 | Assert.assertNull(deleteResult.getStatistics().getStringValue(Label.RELATIONSHIPS_CREATED)); 110 | Assert.assertEquals(1, deleteResult.getStatistics().relationshipsDeleted()); 111 | Assert.assertEquals(1, deleteResult.getStatistics().nodesDeleted()); 112 | 113 | Assert.assertNotNull(deleteResult.getStatistics().getStringValue(Label.QUERY_INTERNAL_EXECUTION_TIME)); 114 | 115 | } 116 | 117 | @Test 118 | public void testDeleteRelationship() { 119 | 120 | Assert.assertNotNull(client.query("social", "CREATE (:person{name:'roi',age:32})")); 121 | Assert.assertNotNull(client.query("social", "CREATE (:person{name:'amit',age:30})")); 122 | Assert.assertNotNull(client.query("social", 123 | "MATCH (a:person), (b:person) WHERE (a.name = 'roi' AND b.name='amit') CREATE (a)-[:knows]->(a)")); 124 | ResultSet deleteResult = client.query("social", "MATCH (a:person)-[e]->() WHERE (a.name = 'roi') DELETE e"); 125 | 126 | Assert.assertFalse(deleteResult.hasNext()); 127 | Assert.assertNull(deleteResult.getStatistics().getStringValue(Label.NODES_CREATED)); 128 | Assert.assertNull(deleteResult.getStatistics().getStringValue(Label.PROPERTIES_SET)); 129 | Assert.assertNull(deleteResult.getStatistics().getStringValue(Label.NODES_CREATED)); 130 | Assert.assertNull(deleteResult.getStatistics().getStringValue(Label.RELATIONSHIPS_CREATED)); 131 | Assert.assertNull(deleteResult.getStatistics().getStringValue(Label.NODES_DELETED)); 132 | Assert.assertEquals(1, deleteResult.getStatistics().relationshipsDeleted()); 133 | 134 | Assert.assertNotNull(deleteResult.getStatistics().getStringValue(Label.QUERY_INTERNAL_EXECUTION_TIME)); 135 | 136 | } 137 | 138 | @Test 139 | public void testIndex() { 140 | // Create both source and destination nodes 141 | Assert.assertNotNull(client.query("social", "CREATE (:person{name:'roi',age:32})")); 142 | 143 | ResultSet createIndexResult = client.query("social", "CREATE INDEX ON :person(age)"); 144 | Assert.assertFalse(createIndexResult.hasNext()); 145 | Assert.assertEquals(1, createIndexResult.getStatistics().indicesAdded()); 146 | 147 | // since RediSearch as index, those action are allowed 148 | ResultSet createNonExistingIndexResult = client.query("social", "CREATE INDEX ON :person(age1)"); 149 | Assert.assertFalse(createNonExistingIndexResult.hasNext()); 150 | Assert.assertNotNull(createNonExistingIndexResult.getStatistics().getStringValue(Label.INDICES_ADDED)); 151 | Assert.assertEquals(1, createNonExistingIndexResult.getStatistics().indicesAdded()); 152 | 153 | ResultSet createExistingIndexResult = client.query("social", "CREATE INDEX ON :person(age)"); 154 | Assert.assertFalse(createExistingIndexResult.hasNext()); 155 | Assert.assertNotNull(createExistingIndexResult.getStatistics().getStringValue(Label.INDICES_ADDED)); 156 | Assert.assertEquals(0, createExistingIndexResult.getStatistics().indicesAdded()); 157 | 158 | ResultSet deleteExistingIndexResult = client.query("social", "DROP INDEX ON :person(age)"); 159 | Assert.assertFalse(deleteExistingIndexResult.hasNext()); 160 | Assert.assertNotNull(deleteExistingIndexResult.getStatistics().getStringValue(Label.INDICES_DELETED)); 161 | Assert.assertEquals(1, deleteExistingIndexResult.getStatistics().indicesDeleted()); 162 | 163 | } 164 | 165 | @Test 166 | public void testHeader() { 167 | 168 | Assert.assertNotNull(client.query("social", "CREATE (:person{name:'roi',age:32})")); 169 | Assert.assertNotNull(client.query("social", "CREATE (:person{name:'amit',age:30})")); 170 | Assert.assertNotNull(client.query("social", 171 | "MATCH (a:person), (b:person) WHERE (a.name = 'roi' AND b.name='amit') CREATE (a)-[:knows]->(a)")); 172 | 173 | ResultSet queryResult = client.query("social", "MATCH (a:person)-[r:knows]->(b:person) RETURN a,r, a.age"); 174 | 175 | Header header = queryResult.getHeader(); 176 | Assert.assertNotNull(header); 177 | Assert.assertEquals("HeaderImpl{" 178 | + "schemaTypes=[COLUMN_SCALAR, COLUMN_SCALAR, COLUMN_SCALAR], " 179 | + "schemaNames=[a, r, a.age]}", header.toString()); 180 | // Assert.assertEquals(-1901778507, header.hashCode()); 181 | 182 | List schemaNames = header.getSchemaNames(); 183 | 184 | Assert.assertNotNull(schemaNames); 185 | Assert.assertEquals(3, schemaNames.size()); 186 | Assert.assertEquals("a", schemaNames.get(0)); 187 | Assert.assertEquals("r", schemaNames.get(1)); 188 | Assert.assertEquals("a.age", schemaNames.get(2)); 189 | 190 | } 191 | 192 | @Test 193 | public void testRecord() { 194 | String name = "roi"; 195 | int age = 32; 196 | double doubleValue = 3.14; 197 | boolean boolValue = true; 198 | 199 | String place = "TLV"; 200 | int since = 2000; 201 | 202 | Property nameProperty = new Property<>("name", name); 203 | Property ageProperty = new Property<>("age", age); 204 | Property doubleProperty = new Property<>("doubleValue", doubleValue); 205 | Property trueBooleanProperty = new Property<>("boolValue", true); 206 | Property falseBooleanProperty = new Property<>("boolValue", false); 207 | 208 | Property placeProperty = new Property<>("place", place); 209 | Property sinceProperty = new Property<>("since", since); 210 | 211 | Node expectedNode = new Node(); 212 | expectedNode.setId(0); 213 | expectedNode.addLabel("person"); 214 | expectedNode.addProperty(nameProperty); 215 | expectedNode.addProperty(ageProperty); 216 | expectedNode.addProperty(doubleProperty); 217 | expectedNode.addProperty(trueBooleanProperty); 218 | Assert.assertEquals( 219 | "Node{labels=[person], id=0, " 220 | + "propertyMap={name=Property{name='name', value=roi}, " 221 | + "boolValue=Property{name='boolValue', value=true}, " 222 | + "doubleValue=Property{name='doubleValue', value=3.14}, " 223 | + "age=Property{name='age', value=32}}}", 224 | expectedNode.toString()); 225 | 226 | Edge expectedEdge = new Edge(); 227 | expectedEdge.setId(0); 228 | expectedEdge.setSource(0); 229 | expectedEdge.setDestination(1); 230 | expectedEdge.setRelationshipType("knows"); 231 | expectedEdge.addProperty(placeProperty); 232 | expectedEdge.addProperty(sinceProperty); 233 | expectedEdge.addProperty(doubleProperty); 234 | expectedEdge.addProperty(falseBooleanProperty); 235 | Assert.assertEquals("Edge{relationshipType='knows', source=0, destination=1, id=0, " 236 | + "propertyMap={boolValue=Property{name='boolValue', value=false}, " 237 | + "place=Property{name='place', value=TLV}, " 238 | + "doubleValue=Property{name='doubleValue', value=3.14}, " 239 | + "since=Property{name='since', value=2000}}}", expectedEdge.toString()); 240 | 241 | Map params = new HashMap<>(); 242 | params.put("name", name); 243 | params.put("age", age); 244 | params.put("boolValue", boolValue); 245 | params.put("doubleValue", doubleValue); 246 | 247 | Assert.assertNotNull(client.query("social", 248 | "CREATE (:person{name:$name,age:$age, doubleValue:$doubleValue, boolValue:$boolValue})", params)); 249 | Assert.assertNotNull(client.query("social", "CREATE (:person{name:'amit',age:30})")); 250 | Assert.assertNotNull( 251 | client.query("social", "MATCH (a:person), (b:person) WHERE (a.name = 'roi' AND b.name='amit') " + 252 | "CREATE (a)-[:knows{place:'TLV', since:2000,doubleValue:3.14, boolValue:false}]->(b)")); 253 | 254 | ResultSet resultSet = client.query("social", "MATCH (a:person)-[r:knows]->(b:person) RETURN a,r, " + 255 | "a.name, a.age, a.doubleValue, a.boolValue, " + 256 | "r.place, r.since, r.doubleValue, r.boolValue"); 257 | Assert.assertNotNull(resultSet); 258 | 259 | Assert.assertEquals(0, resultSet.getStatistics().nodesCreated()); 260 | Assert.assertEquals(0, resultSet.getStatistics().nodesDeleted()); 261 | Assert.assertEquals(0, resultSet.getStatistics().labelsAdded()); 262 | Assert.assertEquals(0, resultSet.getStatistics().propertiesSet()); 263 | Assert.assertEquals(0, resultSet.getStatistics().relationshipsCreated()); 264 | Assert.assertEquals(0, resultSet.getStatistics().relationshipsDeleted()); 265 | Assert.assertNotNull(resultSet.getStatistics().getStringValue(Label.QUERY_INTERNAL_EXECUTION_TIME)); 266 | 267 | Assert.assertEquals(1, resultSet.size()); 268 | Assert.assertTrue(resultSet.hasNext()); 269 | Record record = resultSet.next(); 270 | Assert.assertFalse(resultSet.hasNext()); 271 | 272 | Node node = record.getValue(0); 273 | Assert.assertNotNull(node); 274 | 275 | Assert.assertEquals(expectedNode, node); 276 | 277 | node = record.getValue("a"); 278 | Assert.assertEquals(expectedNode, node); 279 | 280 | Edge edge = record.getValue(1); 281 | Assert.assertNotNull(edge); 282 | Assert.assertEquals(expectedEdge, edge); 283 | 284 | edge = record.getValue("r"); 285 | Assert.assertEquals(expectedEdge, edge); 286 | 287 | Assert.assertEquals(Arrays.asList("a", "r", "a.name", "a.age", "a.doubleValue", "a.boolValue", 288 | "r.place", "r.since", "r.doubleValue", "r.boolValue"), record.keys()); 289 | 290 | Assert.assertEquals(Arrays.asList(expectedNode, expectedEdge, 291 | name, (long) age, doubleValue, true, 292 | place, (long) since, doubleValue, false), 293 | record.values()); 294 | 295 | Node a = record.getValue("a"); 296 | for (String propertyName : expectedNode.getEntityPropertyNames()) { 297 | Assert.assertEquals(expectedNode.getProperty(propertyName), a.getProperty(propertyName)); 298 | } 299 | 300 | Assert.assertEquals("roi", record.getString(2)); 301 | Assert.assertEquals("32", record.getString(3)); 302 | Assert.assertEquals(32L, ((Long) record.getValue(3)).longValue()); 303 | Assert.assertEquals(32L, ((Long) record.getValue("a.age")).longValue()); 304 | Assert.assertEquals("roi", record.getString("a.name")); 305 | Assert.assertEquals("32", record.getString("a.age")); 306 | 307 | } 308 | 309 | @Test 310 | public void testMultiThread() { 311 | 312 | Assert.assertNotNull(client.query("social", 313 | "CREATE (:person {name:'roi', age:32})-[:knows]->(:person {name:'amit',age:30}) ")); 314 | 315 | List resultSets = IntStream.range(0, 16).parallel() 316 | .mapToObj(i -> client.query("social", "MATCH (a:person)-[r:knows]->(b:person) RETURN a,r, a.age")) 317 | .collect(Collectors.toList()); 318 | 319 | Property nameProperty = new Property<>("name", "roi"); 320 | Property ageProperty = new Property<>("age", 32); 321 | Property lastNameProperty = new Property<>("lastName", "a"); 322 | 323 | Node expectedNode = new Node(); 324 | expectedNode.setId(0); 325 | expectedNode.addLabel("person"); 326 | expectedNode.addProperty(nameProperty); 327 | expectedNode.addProperty(ageProperty); 328 | 329 | Edge expectedEdge = new Edge(); 330 | expectedEdge.setId(0); 331 | expectedEdge.setSource(0); 332 | expectedEdge.setDestination(1); 333 | expectedEdge.setRelationshipType("knows"); 334 | 335 | for (ResultSet resultSet : resultSets) { 336 | Assert.assertNotNull(resultSet.getHeader()); 337 | Header header = resultSet.getHeader(); 338 | List schemaNames = header.getSchemaNames(); 339 | Assert.assertNotNull(schemaNames); 340 | Assert.assertEquals(3, schemaNames.size()); 341 | Assert.assertEquals("a", schemaNames.get(0)); 342 | Assert.assertEquals("r", schemaNames.get(1)); 343 | Assert.assertEquals("a.age", schemaNames.get(2)); 344 | Assert.assertEquals(1, resultSet.size()); 345 | Assert.assertTrue(resultSet.hasNext()); 346 | Record record = resultSet.next(); 347 | Assert.assertFalse(resultSet.hasNext()); 348 | Assert.assertEquals(Arrays.asList("a", "r", "a.age"), record.keys()); 349 | Assert.assertEquals(Arrays.asList(expectedNode, expectedEdge, 32L), record.values()); 350 | } 351 | 352 | // test for update in local cache 353 | expectedNode.removeProperty("name"); 354 | expectedNode.removeProperty("age"); 355 | expectedNode.addProperty(lastNameProperty); 356 | expectedNode.removeLabel("person"); 357 | expectedNode.addLabel("worker"); 358 | expectedNode.setId(2); 359 | 360 | expectedEdge.setRelationshipType("worksWith"); 361 | expectedEdge.setSource(2); 362 | expectedEdge.setDestination(3); 363 | expectedEdge.setId(1); 364 | 365 | Assert.assertNotNull(client.query("social", "CREATE (:worker{lastName:'a'})")); 366 | Assert.assertNotNull(client.query("social", "CREATE (:worker{lastName:'b'})")); 367 | Assert.assertNotNull(client.query("social", 368 | "MATCH (a:worker), (b:worker) WHERE (a.lastName = 'a' AND b.lastName='b') CREATE (a)-[:worksWith]->(b)")); 369 | 370 | resultSets = IntStream.range(0, 16).parallel() 371 | .mapToObj(i -> client.query("social", "MATCH (a:worker)-[r:worksWith]->(b:worker) RETURN a,r")) 372 | .collect(Collectors.toList()); 373 | 374 | for (ResultSet resultSet : resultSets) { 375 | Assert.assertNotNull(resultSet.getHeader()); 376 | Header header = resultSet.getHeader(); 377 | List schemaNames = header.getSchemaNames(); 378 | Assert.assertNotNull(schemaNames); 379 | Assert.assertEquals(2, schemaNames.size()); 380 | Assert.assertEquals("a", schemaNames.get(0)); 381 | Assert.assertEquals("r", schemaNames.get(1)); 382 | Assert.assertEquals(1, resultSet.size()); 383 | Assert.assertTrue(resultSet.hasNext()); 384 | Record record = resultSet.next(); 385 | Assert.assertFalse(resultSet.hasNext()); 386 | Assert.assertEquals(Arrays.asList("a", "r"), record.keys()); 387 | Assert.assertEquals(Arrays.asList(expectedNode, expectedEdge), record.values()); 388 | } 389 | } 390 | 391 | @Test 392 | public void testAdditionToProcedures() { 393 | 394 | Assert.assertNotNull(client.query("social", "CREATE (:person{name:'roi',age:32})")); 395 | Assert.assertNotNull(client.query("social", "CREATE (:person{name:'amit',age:30})")); 396 | Assert.assertNotNull(client.query("social", 397 | "MATCH (a:person), (b:person) WHERE (a.name = 'roi' AND b.name='amit') CREATE (a)-[:knows]->(b)")); 398 | 399 | // expected objects init 400 | Property nameProperty = new Property<>("name", "roi"); 401 | Property ageProperty = new Property<>("age", 32); 402 | Property lastNameProperty = new Property<>("lastName", "a"); 403 | 404 | Node expectedNode = new Node(); 405 | expectedNode.setId(0); 406 | expectedNode.addLabel("person"); 407 | expectedNode.addProperty(nameProperty); 408 | expectedNode.addProperty(ageProperty); 409 | 410 | Edge expectedEdge = new Edge(); 411 | expectedEdge.setId(0); 412 | expectedEdge.setSource(0); 413 | expectedEdge.setDestination(1); 414 | expectedEdge.setRelationshipType("knows"); 415 | 416 | ResultSet resultSet = client.query("social", "MATCH (a:person)-[r:knows]->(b:person) RETURN a,r"); 417 | Assert.assertNotNull(resultSet.getHeader()); 418 | Header header = resultSet.getHeader(); 419 | List schemaNames = header.getSchemaNames(); 420 | Assert.assertNotNull(schemaNames); 421 | Assert.assertEquals(2, schemaNames.size()); 422 | Assert.assertEquals("a", schemaNames.get(0)); 423 | Assert.assertEquals("r", schemaNames.get(1)); 424 | Assert.assertEquals(1, resultSet.size()); 425 | Assert.assertTrue(resultSet.hasNext()); 426 | Record record = resultSet.next(); 427 | Assert.assertFalse(resultSet.hasNext()); 428 | Assert.assertEquals(Arrays.asList("a", "r"), record.keys()); 429 | Assert.assertEquals(Arrays.asList(expectedNode, expectedEdge), record.values()); 430 | 431 | // test for local cache updates 432 | 433 | expectedNode.removeProperty("name"); 434 | expectedNode.removeProperty("age"); 435 | expectedNode.addProperty(lastNameProperty); 436 | expectedNode.removeLabel("person"); 437 | expectedNode.addLabel("worker"); 438 | expectedNode.setId(2); 439 | expectedEdge.setRelationshipType("worksWith"); 440 | expectedEdge.setSource(2); 441 | expectedEdge.setDestination(3); 442 | expectedEdge.setId(1); 443 | Assert.assertNotNull(client.query("social", "CREATE (:worker{lastName:'a'})")); 444 | Assert.assertNotNull(client.query("social", "CREATE (:worker{lastName:'b'})")); 445 | Assert.assertNotNull(client.query("social", 446 | "MATCH (a:worker), (b:worker) WHERE (a.lastName = 'a' AND b.lastName='b') CREATE (a)-[:worksWith]->(b)")); 447 | resultSet = client.query("social", "MATCH (a:worker)-[r:worksWith]->(b:worker) RETURN a,r"); 448 | Assert.assertNotNull(resultSet.getHeader()); 449 | header = resultSet.getHeader(); 450 | schemaNames = header.getSchemaNames(); 451 | Assert.assertNotNull(schemaNames); 452 | Assert.assertEquals(2, schemaNames.size()); 453 | Assert.assertEquals("a", schemaNames.get(0)); 454 | Assert.assertEquals("r", schemaNames.get(1)); 455 | Assert.assertEquals(1, resultSet.size()); 456 | Assert.assertTrue(resultSet.hasNext()); 457 | record = resultSet.next(); 458 | Assert.assertFalse(resultSet.hasNext()); 459 | Assert.assertEquals(Arrays.asList("a", "r"), record.keys()); 460 | Assert.assertEquals(Arrays.asList(expectedNode, expectedEdge), record.values()); 461 | 462 | } 463 | 464 | @Test 465 | public void testEscapedQuery() { 466 | Map params1 = new HashMap(); 467 | params1.put("s1", "S\"'"); 468 | params1.put("s2", "S'\""); 469 | Assert.assertNotNull(client.query("social", "CREATE (:escaped{s1:$s1,s2:$s2})", params1)); 470 | 471 | Map params2 = new HashMap(); 472 | params2.put("s1", "S\"'"); 473 | params2.put("s2", "S'\""); 474 | Assert.assertNotNull(client.query("social", "MATCH (n) where n.s1=$s1 and n.s2=$s2 RETURN n", params2)); 475 | 476 | Assert.assertNotNull(client.query("social", "MATCH (n) where n.s1='S\"' RETURN n")); 477 | 478 | } 479 | 480 | @Test 481 | public void testContextedAPI() { 482 | 483 | String name = "roi"; 484 | int age = 32; 485 | double doubleValue = 3.14; 486 | boolean boolValue = true; 487 | 488 | String place = "TLV"; 489 | int since = 2000; 490 | 491 | Property nameProperty = new Property<>("name", name); 492 | Property ageProperty = new Property<>("age", age); 493 | Property doubleProperty = new Property<>("doubleValue", doubleValue); 494 | Property trueBooleanProperty = new Property<>("boolValue", true); 495 | Property falseBooleanProperty = new Property<>("boolValue", false); 496 | 497 | Property placeProperty = new Property<>("place", place); 498 | Property sinceProperty = new Property<>("since", since); 499 | 500 | Node expectedNode = new Node(); 501 | expectedNode.setId(0); 502 | expectedNode.addLabel("person"); 503 | expectedNode.addProperty(nameProperty); 504 | expectedNode.addProperty(ageProperty); 505 | expectedNode.addProperty(doubleProperty); 506 | expectedNode.addProperty(trueBooleanProperty); 507 | 508 | Edge expectedEdge = new Edge(); 509 | expectedEdge.setId(0); 510 | expectedEdge.setSource(0); 511 | expectedEdge.setDestination(1); 512 | expectedEdge.setRelationshipType("knows"); 513 | expectedEdge.addProperty(placeProperty); 514 | expectedEdge.addProperty(sinceProperty); 515 | expectedEdge.addProperty(doubleProperty); 516 | expectedEdge.addProperty(falseBooleanProperty); 517 | 518 | Map params = new HashMap<>(); 519 | params.put("name", name); 520 | params.put("age", age); 521 | params.put("boolValue", boolValue); 522 | params.put("doubleValue", doubleValue); 523 | try (RedisGraphContext c = client.getContext()) { 524 | Assert.assertNotNull(c.query("social", 525 | "CREATE (:person{name:$name, age:$age, doubleValue:$doubleValue, boolValue:$boolValue})", params)); 526 | Assert.assertNotNull(c.query("social", "CREATE (:person{name:'amit',age:30})")); 527 | Assert.assertNotNull( 528 | c.query("social", "MATCH (a:person), (b:person) WHERE (a.name = 'roi' AND b.name='amit') " + 529 | "CREATE (a)-[:knows{place:'TLV', since:2000,doubleValue:3.14, boolValue:false}]->(b)")); 530 | 531 | ResultSet resultSet = c.query("social", "MATCH (a:person)-[r:knows]->(b:person) RETURN a,r, " + 532 | "a.name, a.age, a.doubleValue, a.boolValue, " + 533 | "r.place, r.since, r.doubleValue, r.boolValue"); 534 | Assert.assertNotNull(resultSet); 535 | 536 | Assert.assertEquals(0, resultSet.getStatistics().nodesCreated()); 537 | Assert.assertEquals(0, resultSet.getStatistics().nodesDeleted()); 538 | Assert.assertEquals(0, resultSet.getStatistics().labelsAdded()); 539 | Assert.assertEquals(0, resultSet.getStatistics().propertiesSet()); 540 | Assert.assertEquals(0, resultSet.getStatistics().relationshipsCreated()); 541 | Assert.assertEquals(0, resultSet.getStatistics().relationshipsDeleted()); 542 | Assert.assertNotNull(resultSet.getStatistics().getStringValue(Label.QUERY_INTERNAL_EXECUTION_TIME)); 543 | 544 | Assert.assertEquals(1, resultSet.size()); 545 | Assert.assertTrue(resultSet.hasNext()); 546 | Record record = resultSet.next(); 547 | Assert.assertFalse(resultSet.hasNext()); 548 | 549 | Node node = record.getValue(0); 550 | Assert.assertNotNull(node); 551 | 552 | Assert.assertEquals(expectedNode, node); 553 | 554 | node = record.getValue("a"); 555 | Assert.assertEquals(expectedNode, node); 556 | 557 | Edge edge = record.getValue(1); 558 | Assert.assertNotNull(edge); 559 | Assert.assertEquals(expectedEdge, edge); 560 | 561 | edge = record.getValue("r"); 562 | Assert.assertEquals(expectedEdge, edge); 563 | 564 | Assert.assertEquals(Arrays.asList("a", "r", "a.name", "a.age", "a.doubleValue", "a.boolValue", 565 | "r.place", "r.since", "r.doubleValue", "r.boolValue"), record.keys()); 566 | 567 | Assert.assertEquals(Arrays.asList(expectedNode, expectedEdge, 568 | name, (long) age, doubleValue, true, 569 | place, (long) since, doubleValue, false), 570 | record.values()); 571 | 572 | Node a = record.getValue("a"); 573 | for (String propertyName : expectedNode.getEntityPropertyNames()) { 574 | Assert.assertEquals(expectedNode.getProperty(propertyName), a.getProperty(propertyName)); 575 | } 576 | 577 | Assert.assertEquals("roi", record.getString(2)); 578 | Assert.assertEquals("32", record.getString(3)); 579 | Assert.assertEquals(32L, ((Long) (record.getValue(3))).longValue()); 580 | Assert.assertEquals(32L, ((Long) record.getValue("a.age")).longValue()); 581 | Assert.assertEquals("roi", record.getString("a.name")); 582 | Assert.assertEquals("32", record.getString("a.age")); 583 | } 584 | } 585 | 586 | @Test 587 | public void testArraySupport() { 588 | 589 | Node expectedANode = new Node(); 590 | expectedANode.setId(0); 591 | expectedANode.addLabel("person"); 592 | Property aNameProperty = new Property<>("name", "a"); 593 | Property aAgeProperty = new Property<>("age", 32); 594 | Property> aListProperty = new Property<>("array", Arrays.asList(0L, 1L, 2L)); 595 | expectedANode.addProperty(aNameProperty); 596 | expectedANode.addProperty(aAgeProperty); 597 | expectedANode.addProperty(aListProperty); 598 | 599 | Node expectedBNode = new Node(); 600 | expectedBNode.setId(1); 601 | expectedBNode.addLabel("person"); 602 | Property bNameProperty = new Property<>("name", "b"); 603 | Property bAgeProperty = new Property<>("age", 30); 604 | Property> bListProperty = new Property<>("array", Arrays.asList(3L, 4L, 5L)); 605 | expectedBNode.addProperty(bNameProperty); 606 | expectedBNode.addProperty(bAgeProperty); 607 | expectedBNode.addProperty(bListProperty); 608 | 609 | Assert.assertNotNull(client.query("social", "CREATE (:person{name:'a',age:32,array:[0,1,2]})")); 610 | Assert.assertNotNull(client.query("social", "CREATE (:person{name:'b',age:30,array:[3,4,5]})")); 611 | 612 | // test array 613 | 614 | ResultSet resultSet = client.query("social", "WITH [0,1,2] as x return x"); 615 | 616 | // check header 617 | Assert.assertNotNull(resultSet.getHeader()); 618 | Header header = resultSet.getHeader(); 619 | 620 | List schemaNames = header.getSchemaNames(); 621 | Assert.assertNotNull(schemaNames); 622 | Assert.assertEquals(1, schemaNames.size()); 623 | Assert.assertEquals("x", schemaNames.get(0)); 624 | 625 | // check record 626 | Assert.assertEquals(1, resultSet.size()); 627 | Assert.assertTrue(resultSet.hasNext()); 628 | Record record = resultSet.next(); 629 | Assert.assertFalse(resultSet.hasNext()); 630 | Assert.assertEquals(Arrays.asList("x"), record.keys()); 631 | 632 | List x = record.getValue("x"); 633 | Assert.assertEquals(Arrays.asList(0L, 1L, 2L), x); 634 | 635 | // test collect 636 | resultSet = client.query("social", "MATCH(n) return collect(n) as x"); 637 | 638 | Assert.assertNotNull(resultSet.getHeader()); 639 | header = resultSet.getHeader(); 640 | 641 | schemaNames = header.getSchemaNames(); 642 | Assert.assertNotNull(schemaNames); 643 | Assert.assertEquals(1, schemaNames.size()); 644 | Assert.assertEquals("x", schemaNames.get(0)); 645 | 646 | // check record 647 | Assert.assertEquals(1, resultSet.size()); 648 | Assert.assertTrue(resultSet.hasNext()); 649 | record = resultSet.next(); 650 | Assert.assertFalse(resultSet.hasNext()); 651 | Assert.assertEquals(Arrays.asList("x"), record.keys()); 652 | x = record.getValue("x"); 653 | Assert.assertEquals(Arrays.asList(expectedANode, expectedBNode), x); 654 | 655 | // test unwind 656 | resultSet = client.query("social", "unwind([0,1,2]) as x return x"); 657 | 658 | Assert.assertNotNull(resultSet.getHeader()); 659 | header = resultSet.getHeader(); 660 | 661 | schemaNames = header.getSchemaNames(); 662 | Assert.assertNotNull(schemaNames); 663 | Assert.assertEquals(1, schemaNames.size()); 664 | Assert.assertEquals("x", schemaNames.get(0)); 665 | 666 | // check record 667 | Assert.assertEquals(3, resultSet.size()); 668 | 669 | for (long i = 0; i < 3; i++) { 670 | Assert.assertTrue(resultSet.hasNext()); 671 | record = resultSet.next(); 672 | Assert.assertEquals(Arrays.asList("x"), record.keys()); 673 | Assert.assertEquals(i, (long) record.getValue("x")); 674 | 675 | } 676 | 677 | } 678 | 679 | @Test 680 | public void testPath() { 681 | List nodes = new ArrayList<>(3); 682 | for (int i = 0; i < 3; i++) { 683 | Node node = new Node(); 684 | node.setId(i); 685 | node.addLabel("L1"); 686 | nodes.add(node); 687 | } 688 | 689 | List edges = new ArrayList<>(2); 690 | for (int i = 0; i < 2; i++) { 691 | Edge edge = new Edge(); 692 | edge.setId(i); 693 | edge.setRelationshipType("R1"); 694 | edge.setSource(i); 695 | edge.setDestination(i + 1); 696 | edges.add(edge); 697 | } 698 | 699 | Set expectedPaths = new HashSet<>(); 700 | 701 | Path path01 = new PathBuilder(2).append(nodes.get(0)).append(edges.get(0)).append(nodes.get(1)).build(); 702 | Path path12 = new PathBuilder(2).append(nodes.get(1)).append(edges.get(1)).append(nodes.get(2)).build(); 703 | Path path02 = new PathBuilder(3).append(nodes.get(0)).append(edges.get(0)).append(nodes.get(1)) 704 | .append(edges.get(1)).append(nodes.get(2)).build(); 705 | 706 | expectedPaths.add(path01); 707 | expectedPaths.add(path12); 708 | expectedPaths.add(path02); 709 | 710 | client.query("social", "CREATE (:L1)-[:R1]->(:L1)-[:R1]->(:L1)"); 711 | 712 | ResultSet resultSet = client.query("social", "MATCH p = (:L1)-[:R1*]->(:L1) RETURN p"); 713 | 714 | Assert.assertEquals(expectedPaths.size(), resultSet.size()); 715 | for (int i = 0; i < resultSet.size(); i++) { 716 | Path p = resultSet.next().getValue("p"); 717 | Assert.assertTrue(expectedPaths.contains(p)); 718 | expectedPaths.remove(p); 719 | } 720 | 721 | } 722 | 723 | @Test 724 | public void testNullGraphEntities() { 725 | // Create two nodes connected by a single outgoing edge. 726 | Assert.assertNotNull(client.query("social", "CREATE (:L)-[:E]->(:L2)")); 727 | // Test a query that produces 1 record with 3 null values. 728 | ResultSet resultSet = client.query("social", "OPTIONAL MATCH (a:NONEXISTENT)-[e]->(b) RETURN a, e, b"); 729 | Assert.assertEquals(1, resultSet.size()); 730 | Assert.assertTrue(resultSet.hasNext()); 731 | Record record = resultSet.next(); 732 | Assert.assertFalse(resultSet.hasNext()); 733 | Assert.assertEquals(Arrays.asList(null, null, null), record.values()); 734 | 735 | // Test a query that produces 2 records, with 2 null values in the second. 736 | resultSet = client.query("social", "MATCH (a) OPTIONAL MATCH (a)-[e]->(b) RETURN a, e, b ORDER BY ID(a)"); 737 | Assert.assertEquals(2, resultSet.size()); 738 | record = resultSet.next(); 739 | Assert.assertEquals(3, record.size()); 740 | 741 | Assert.assertNotNull(record.getValue(0)); 742 | Assert.assertNotNull(record.getValue(1)); 743 | Assert.assertNotNull(record.getValue(2)); 744 | 745 | record = resultSet.next(); 746 | Assert.assertEquals(3, record.size()); 747 | 748 | Assert.assertNotNull(record.getValue(0)); 749 | Assert.assertNull(record.getValue(1)); 750 | Assert.assertNull(record.getValue(2)); 751 | 752 | // Test a query that produces 2 records, the first containing a path and the 753 | // second containing a null value. 754 | resultSet = client.query("social", "MATCH (a) OPTIONAL MATCH p = (a)-[e]->(b) RETURN p"); 755 | Assert.assertEquals(2, resultSet.size()); 756 | 757 | record = resultSet.next(); 758 | Assert.assertEquals(1, record.size()); 759 | Assert.assertNotNull(record.getValue(0)); 760 | 761 | record = resultSet.next(); 762 | Assert.assertEquals(1, record.size()); 763 | Assert.assertNull(record.getValue(0)); 764 | } 765 | 766 | @Test 767 | public void test64bitnumber() { 768 | long value = 1 << 40; 769 | Map params = new HashMap<>(); 770 | params.put("val", value); 771 | ResultSet resultSet = client.query("social", "CREATE (n {val:$val}) RETURN n.val", params); 772 | Assert.assertEquals(1, resultSet.size()); 773 | Record r = resultSet.next(); 774 | Assert.assertEquals(Long.valueOf(value), r.getValue(0)); 775 | } 776 | 777 | @Test 778 | public void testCachedExecution() { 779 | client.query("social", "CREATE (:N {val:1}), (:N {val:2})"); 780 | 781 | // First time should not be loaded from execution cache 782 | Map params = new HashMap<>(); 783 | params.put("val", 1L); 784 | ResultSet resultSet = client.query("social", "MATCH (n:N {val:$val}) RETURN n.val", params); 785 | Assert.assertEquals(1, resultSet.size()); 786 | Record r = resultSet.next(); 787 | Assert.assertEquals(params.get("val"), r.getValue(0)); 788 | Assert.assertFalse(resultSet.getStatistics().cachedExecution()); 789 | 790 | // Run in loop many times to make sure the query will be loaded 791 | // from cache at least once 792 | for (int i = 0; i < 64; i++) { 793 | resultSet = client.query("social", "MATCH (n:N {val:$val}) RETURN n.val", params); 794 | } 795 | Assert.assertEquals(1, resultSet.size()); 796 | r = resultSet.next(); 797 | Assert.assertEquals(params.get("val"), r.getValue(0)); 798 | Assert.assertTrue(resultSet.getStatistics().cachedExecution()); 799 | } 800 | 801 | @Test 802 | public void testMapDataType() { 803 | Map expected = new HashMap<>(); 804 | expected.put("a", (long) 1); 805 | expected.put("b", "str"); 806 | expected.put("c", null); 807 | List d = new ArrayList<>(); 808 | d.add((long) 1); 809 | d.add((long) 2); 810 | d.add((long) 3); 811 | expected.put("d", d); 812 | expected.put("e", true); 813 | Map f = new HashMap<>(); 814 | f.put("x", (long) 1); 815 | f.put("y", (long) 2); 816 | expected.put("f", f); 817 | ResultSet res = client.query("social", "RETURN {a:1, b:'str', c:NULL, d:[1,2,3], e:True, f:{x:1, y:2}}"); 818 | Assert.assertEquals(1, res.size()); 819 | Record r = res.next(); 820 | Map actual = r.getValue(0); 821 | Assert.assertEquals(expected, actual); 822 | } 823 | 824 | @Test 825 | public void testGeoPointLatLon() { 826 | ResultSet rs = client.query("social", "CREATE (:restaurant" 827 | + " {location: point({latitude:30.27822306, longitude:-97.75134723})})"); 828 | Assert.assertEquals(1, rs.getStatistics().nodesCreated()); 829 | Assert.assertEquals(1, rs.getStatistics().propertiesSet()); 830 | 831 | assertTestGeoPoint(); 832 | } 833 | 834 | @Test 835 | public void testGeoPointLonLat() { 836 | ResultSet rs = client.query("social", "CREATE (:restaurant" 837 | + " {location: point({longitude:-97.75134723, latitude:30.27822306})})"); 838 | Assert.assertEquals(1, rs.getStatistics().nodesCreated()); 839 | Assert.assertEquals(1, rs.getStatistics().propertiesSet()); 840 | 841 | assertTestGeoPoint(); 842 | } 843 | 844 | private void assertTestGeoPoint() { 845 | ResultSet results = client.query("social", "MATCH (restaurant) RETURN restaurant"); 846 | Assert.assertEquals(1, results.size()); 847 | Record record = results.next(); 848 | Assert.assertEquals(1, record.size()); 849 | Assert.assertEquals(Collections.singletonList("restaurant"), record.keys()); 850 | Node node = record.getValue(0); 851 | Property property = node.getProperty("location"); 852 | Assert.assertEquals(new Point(30.27822306, -97.75134723), property.getValue()); 853 | } 854 | 855 | @Test 856 | public void timeoutArgument() { 857 | ResultSet rs = client.query("social", "UNWIND range(0,100) AS x WITH x AS x WHERE x = 100 RETURN x", 1L); 858 | Assert.assertEquals(1, rs.size()); 859 | Record r = rs.next(); 860 | Assert.assertEquals(Long.valueOf(100), r.getValue(0)); 861 | } 862 | 863 | @Test 864 | public void testCachedExecutionReadOnly() { 865 | client.query("social", "CREATE (:N {val:1}), (:N {val:2})"); 866 | 867 | // First time should not be loaded from execution cache 868 | Map params = new HashMap<>(); 869 | params.put("val", 1L); 870 | ResultSet resultSet = client.readOnlyQuery("social", "MATCH (n:N {val:$val}) RETURN n.val", params); 871 | Assert.assertEquals(1, resultSet.size()); 872 | Record r = resultSet.next(); 873 | Assert.assertEquals(params.get("val"), r.getValue(0)); 874 | Assert.assertFalse(resultSet.getStatistics().cachedExecution()); 875 | 876 | // Run in loop many times to make sure the query will be loaded 877 | // from cache at least once 878 | for (int i = 0; i < 64; i++) { 879 | resultSet = client.readOnlyQuery("social", "MATCH (n:N {val:$val}) RETURN n.val", params); 880 | } 881 | Assert.assertEquals(1, resultSet.size()); 882 | r = resultSet.next(); 883 | Assert.assertEquals(params.get("val"), r.getValue(0)); 884 | Assert.assertTrue(resultSet.getStatistics().cachedExecution()); 885 | } 886 | 887 | @Test 888 | public void testSimpleReadOnly() { 889 | client.query("social", "CREATE (:person{name:'filipe',age:30})"); 890 | ResultSet rsRo = client.readOnlyQuery("social", "MATCH (a:person) WHERE (a.name = 'filipe') RETURN a.age"); 891 | Assert.assertEquals(1, rsRo.size()); 892 | Record r = rsRo.next(); 893 | Assert.assertEquals(Long.valueOf(30), r.getValue(0)); 894 | } 895 | } 896 | -------------------------------------------------------------------------------- /src/test/java/com/redislabs/redisgraph/TransactionTest.java: -------------------------------------------------------------------------------- 1 | package com.redislabs.redisgraph; 2 | 3 | import java.util.Arrays; 4 | import java.util.HashMap; 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | import org.junit.After; 9 | import org.junit.Assert; 10 | import org.junit.Before; 11 | import org.junit.Test; 12 | 13 | import com.redislabs.redisgraph.graph_entities.Node; 14 | import com.redislabs.redisgraph.graph_entities.Property; 15 | import com.redislabs.redisgraph.impl.api.RedisGraph; 16 | import com.redislabs.redisgraph.impl.resultset.ResultSetImpl; 17 | 18 | public class TransactionTest { 19 | 20 | private RedisGraphContextGenerator api; 21 | 22 | public TransactionTest() { 23 | } 24 | 25 | @Before 26 | public void createApi(){ 27 | api = new RedisGraph(); 28 | } 29 | 30 | @After 31 | public void deleteGraph() { 32 | api.deleteGraph("social"); 33 | api.close(); 34 | } 35 | 36 | @Test 37 | public void testMultiExec(){ 38 | try (RedisGraphContext c = api.getContext()) { 39 | RedisGraphTransaction transaction = c.multi(); 40 | 41 | transaction.set("x", "1"); 42 | transaction.query("social", "CREATE (:Person {name:'a'})"); 43 | transaction.query("g", "CREATE (:Person {name:'a'})"); 44 | transaction.incr("x"); 45 | transaction.get("x"); 46 | transaction.query("social", "MATCH (n:Person) RETURN n"); 47 | transaction.deleteGraph("g"); 48 | transaction.callProcedure("social", "db.labels"); 49 | List results = transaction.exec(); 50 | 51 | // Redis set command 52 | Assert.assertEquals(String.class, results.get(0).getClass()); 53 | Assert.assertEquals("OK", results.get(0)); 54 | 55 | // Redis graph command 56 | Assert.assertEquals(ResultSetImpl.class, results.get(1).getClass()); 57 | ResultSet resultSet = (ResultSet) results.get(1); 58 | Assert.assertEquals(1, resultSet.getStatistics().nodesCreated()); 59 | Assert.assertEquals(1, resultSet.getStatistics().propertiesSet()); 60 | 61 | 62 | Assert.assertEquals(ResultSetImpl.class, results.get(2).getClass()); 63 | resultSet = (ResultSet) results.get(2); 64 | Assert.assertEquals(1, resultSet.getStatistics().nodesCreated()); 65 | Assert.assertEquals(1, resultSet.getStatistics().propertiesSet()); 66 | 67 | // Redis incr command 68 | Assert.assertEquals(Long.class, results.get(3).getClass()); 69 | Assert.assertEquals(2L, results.get(3)); 70 | 71 | // Redis get command 72 | Assert.assertEquals(String.class, results.get(4).getClass()); 73 | Assert.assertEquals("2", results.get(4)); 74 | 75 | // Graph query result 76 | Assert.assertEquals(ResultSetImpl.class, results.get(5).getClass()); 77 | resultSet = (ResultSet) results.get(5); 78 | 79 | Assert.assertNotNull(resultSet.getHeader()); 80 | Header header = resultSet.getHeader(); 81 | 82 | 83 | List schemaNames = header.getSchemaNames(); 84 | Assert.assertNotNull(schemaNames); 85 | Assert.assertEquals(1, schemaNames.size()); 86 | Assert.assertEquals("n", schemaNames.get(0)); 87 | 88 | Property nameProperty = new Property<>("name", "a"); 89 | 90 | Node expectedNode = new Node(); 91 | expectedNode.setId(0); 92 | expectedNode.addLabel("Person"); 93 | expectedNode.addProperty(nameProperty); 94 | // see that the result were pulled from the right graph 95 | Assert.assertEquals(1, resultSet.size()); 96 | Assert.assertTrue(resultSet.hasNext()); 97 | Record record = resultSet.next(); 98 | Assert.assertFalse(resultSet.hasNext()); 99 | Assert.assertEquals(Arrays.asList("n"), record.keys()); 100 | Assert.assertEquals(expectedNode, record.getValue("n")); 101 | 102 | Assert.assertEquals(ResultSetImpl.class, results.get(7).getClass()); 103 | resultSet = (ResultSet) results.get(7); 104 | 105 | Assert.assertNotNull(resultSet.getHeader()); 106 | header = resultSet.getHeader(); 107 | 108 | 109 | schemaNames = header.getSchemaNames(); 110 | Assert.assertNotNull(schemaNames); 111 | Assert.assertEquals(1, schemaNames.size()); 112 | Assert.assertEquals("label", schemaNames.get(0)); 113 | 114 | Assert.assertEquals(1, resultSet.size()); 115 | Assert.assertTrue(resultSet.hasNext()); 116 | record = resultSet.next(); 117 | Assert.assertFalse(resultSet.hasNext()); 118 | Assert.assertEquals(Arrays.asList("label"), record.keys()); 119 | Assert.assertEquals("Person", record.getValue("label")); 120 | } 121 | } 122 | 123 | @Test 124 | public void testWriteTransactionWatch(){ 125 | 126 | RedisGraphContext c1 = api.getContext(); 127 | RedisGraphContext c2 = api.getContext(); 128 | 129 | c1.watch("social"); 130 | RedisGraphTransaction t1 = c1.multi(); 131 | 132 | 133 | t1.query("social", "CREATE (:Person {name:'a'})"); 134 | c2.query("social", "CREATE (:Person {name:'b'})"); 135 | List returnValue = t1.exec(); 136 | Assert.assertNull(returnValue); 137 | c1.close(); 138 | c2.close(); 139 | } 140 | 141 | @Test 142 | public void testReadTransactionWatch(){ 143 | 144 | RedisGraphContext c1 = api.getContext(); 145 | RedisGraphContext c2 = api.getContext(); 146 | Assert.assertNotEquals(c1.getConnectionContext(), c2.getConnectionContext()); 147 | c1.query("social", "CREATE (:Person {name:'a'})"); 148 | c1.watch("social"); 149 | RedisGraphTransaction t1 = c1.multi(); 150 | 151 | Map params = new HashMap<>(); 152 | params.put("name", 'b'); 153 | t1.query("social", "CREATE (:Person {name:$name})", params); 154 | c2.query("social", "MATCH (n) return n"); 155 | List returnValue = t1.exec(); 156 | 157 | Assert.assertNotNull(returnValue); 158 | c1.close(); 159 | c2.close(); 160 | } 161 | 162 | @Test 163 | public void testMultiExecWithReadOnlyQueries(){ 164 | try (RedisGraphContext c = api.getContext()) { 165 | RedisGraphTransaction transaction = c.multi(); 166 | 167 | transaction.set("x", "1"); 168 | transaction.query("social", "CREATE (:Person {name:'a'})"); 169 | transaction.query("g", "CREATE (:Person {name:'a'})"); 170 | transaction.readOnlyQuery("social", "MATCH (n:Person) RETURN n"); 171 | transaction.deleteGraph("g"); 172 | transaction.callProcedure("social", "db.labels"); 173 | List results = transaction.exec(); 174 | 175 | // Redis set command 176 | Assert.assertEquals(String.class, results.get(0).getClass()); 177 | Assert.assertEquals("OK", results.get(0)); 178 | 179 | // Redis graph command 180 | Assert.assertEquals(ResultSetImpl.class, results.get(1).getClass()); 181 | ResultSet resultSet = (ResultSet) results.get(1); 182 | Assert.assertEquals(1, resultSet.getStatistics().nodesCreated()); 183 | Assert.assertEquals(1, resultSet.getStatistics().propertiesSet()); 184 | 185 | 186 | Assert.assertEquals(ResultSetImpl.class, results.get(2).getClass()); 187 | resultSet = (ResultSet) results.get(2); 188 | Assert.assertEquals(1, resultSet.getStatistics().nodesCreated()); 189 | Assert.assertEquals(1, resultSet.getStatistics().propertiesSet()); 190 | 191 | // Graph read-only query result 192 | Assert.assertEquals(ResultSetImpl.class, results.get(5).getClass()); 193 | resultSet = (ResultSet) results.get(3); 194 | 195 | Assert.assertNotNull(resultSet.getHeader()); 196 | Header header = resultSet.getHeader(); 197 | 198 | List schemaNames = header.getSchemaNames(); 199 | Assert.assertNotNull(schemaNames); 200 | Assert.assertEquals(1, schemaNames.size()); 201 | Assert.assertEquals("n", schemaNames.get(0)); 202 | 203 | Property nameProperty = new Property<>("name", "a"); 204 | 205 | Node expectedNode = new Node(); 206 | expectedNode.setId(0); 207 | expectedNode.addLabel("Person"); 208 | expectedNode.addProperty(nameProperty); 209 | // see that the result were pulled from the right graph 210 | Assert.assertEquals(1, resultSet.size()); 211 | Assert.assertTrue(resultSet.hasNext()); 212 | Record record = resultSet.next(); 213 | Assert.assertFalse(resultSet.hasNext()); 214 | Assert.assertEquals(Arrays.asList("n"), record.keys()); 215 | Assert.assertEquals(expectedNode, record.getValue("n")); 216 | 217 | Assert.assertEquals(ResultSetImpl.class, results.get(5).getClass()); 218 | resultSet = (ResultSet) results.get(5); 219 | 220 | Assert.assertNotNull(resultSet.getHeader()); 221 | header = resultSet.getHeader(); 222 | 223 | schemaNames = header.getSchemaNames(); 224 | Assert.assertNotNull(schemaNames); 225 | Assert.assertEquals(1, schemaNames.size()); 226 | Assert.assertEquals("label", schemaNames.get(0)); 227 | 228 | Assert.assertEquals(1, resultSet.size()); 229 | Assert.assertTrue(resultSet.hasNext()); 230 | record = resultSet.next(); 231 | Assert.assertFalse(resultSet.hasNext()); 232 | Assert.assertEquals(Arrays.asList("label"), record.keys()); 233 | Assert.assertEquals("Person", record.getValue("label")); 234 | } 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /src/test/java/com/redislabs/redisgraph/exceptions/JRedisGraphErrorTest.java: -------------------------------------------------------------------------------- 1 | package com.redislabs.redisgraph.exceptions; 2 | 3 | import static org.junit.Assert.assertThrows; 4 | import static org.junit.Assert.assertTrue; 5 | 6 | import java.util.HashMap; 7 | 8 | import org.junit.After; 9 | import org.junit.Assert; 10 | import org.junit.Before; 11 | import org.junit.Test; 12 | 13 | import com.redislabs.redisgraph.RedisGraphContext; 14 | import com.redislabs.redisgraph.RedisGraphContextGenerator; 15 | import com.redislabs.redisgraph.impl.api.RedisGraph; 16 | 17 | public class JRedisGraphErrorTest { 18 | 19 | private RedisGraphContextGenerator api; 20 | 21 | @Before 22 | public void createApi() { 23 | api = new RedisGraph(); 24 | Assert.assertNotNull(api.query("social", "CREATE (:person{mixed_prop: 'strval'}), (:person{mixed_prop: 50})")); 25 | } 26 | 27 | @After 28 | public void deleteGraph() { 29 | 30 | api.deleteGraph("social"); 31 | api.close(); 32 | } 33 | 34 | @Test 35 | public void testSyntaxErrorReporting() { 36 | JRedisGraphException exception = assertThrows(JRedisGraphException.class, 37 | () -> api.query("social", "RETURN toUpper(5)")); 38 | assertTrue(exception.getMessage().contains("Type mismatch: expected String or Null but was Integer")); 39 | } 40 | 41 | @Test 42 | public void testRuntimeErrorReporting() { 43 | JRedisGraphException exception = assertThrows(JRedisGraphException.class, 44 | () -> api.query("social", "MATCH (p:person) RETURN toUpper(p.mixed_prop)")); 45 | assertTrue(exception.getMessage().contains("Type mismatch: expected String or Null but was Integer")); 46 | } 47 | 48 | @Test 49 | public void testExceptionFlow() { 50 | 51 | try { 52 | // Issue a query that causes a compile-time error 53 | api.query("social", "RETURN toUpper(5)"); 54 | } catch (Exception e) { 55 | Assert.assertEquals(JRedisGraphException.class, e.getClass()); 56 | Assert.assertTrue(e.getMessage().contains("Type mismatch: expected String or Null but was Integer")); 57 | } 58 | 59 | // On general api usage, user should get a new connection 60 | 61 | try { 62 | // Issue a query that causes a compile-time error 63 | api.query("social", "MATCH (p:person) RETURN toUpper(p.mixed_prop)"); 64 | } catch (Exception e) { 65 | Assert.assertEquals(JRedisGraphException.class, e.getClass()); 66 | Assert.assertTrue(e.getMessage().contains("Type mismatch: expected String or Null but was Integer")); 67 | } 68 | } 69 | 70 | @Test 71 | public void testContextSyntaxErrorReporting() { 72 | RedisGraphContext c = api.getContext(); 73 | 74 | JRedisGraphException exception = assertThrows(JRedisGraphException.class, 75 | () -> c.query("social", "RETURN toUpper(5)")); 76 | assertTrue(exception.getMessage().contains("Type mismatch: expected String or Null but was Integer")); 77 | } 78 | 79 | @Test 80 | public void testMissingParametersSyntaxErrorReporting() { 81 | JRedisGraphException exception = assertThrows(JRedisGraphException.class, 82 | () -> api.query("social", "RETURN $param")); 83 | assertTrue(exception.getMessage().contains("Missing parameters")); 84 | } 85 | 86 | @Test 87 | public void testMissingParametersSyntaxErrorReporting2() { 88 | JRedisGraphException exception = assertThrows(JRedisGraphException.class, 89 | () -> api.query("social", "RETURN $param", new HashMap<>())); 90 | assertTrue(exception.getMessage().contains("Missing parameters")); 91 | } 92 | 93 | @Test 94 | public void testContextRuntimeErrorReporting() { 95 | RedisGraphContext c = api.getContext(); 96 | 97 | JRedisGraphException exception = assertThrows(JRedisGraphException.class, 98 | () -> c.query("social", "MATCH (p:person) RETURN toUpper(p.mixed_prop)")); 99 | assertTrue(exception.getMessage().contains("Type mismatch: expected String or Null but was Integer")); 100 | } 101 | 102 | @Test 103 | public void testContextExceptionFlow() { 104 | 105 | RedisGraphContext c = api.getContext(); 106 | try { 107 | // Issue a query that causes a compile-time error 108 | c.query("social", "RETURN toUpper(5)"); 109 | } catch (Exception e) { 110 | Assert.assertEquals(JRedisGraphException.class, e.getClass()); 111 | Assert.assertTrue(e.getMessage().contains("Type mismatch: expected String or Null but was Integer")); 112 | } 113 | 114 | // On contexted api usage, connection should stay open 115 | try { 116 | // Issue a query that causes a compile-time error 117 | c.query("social", "MATCH (p:person) RETURN toUpper(p.mixed_prop)"); 118 | } catch (Exception e) { 119 | Assert.assertEquals(JRedisGraphException.class, e.getClass()); 120 | Assert.assertTrue(e.getMessage().contains("Type mismatch: expected String or Null but was Integer")); 121 | } 122 | } 123 | 124 | @Test 125 | public void timeoutException() { 126 | JRedisGraphException exception = assertThrows(JRedisGraphException.class, 127 | () -> api.query("social", "UNWIND range(0,100000) AS x WITH x AS x WHERE x = 10000 RETURN x", 1L)); 128 | assertTrue(exception.getMessage().contains("Query timed out")); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/test/java/com/redislabs/redisgraph/graph_entities/PathTest.java: -------------------------------------------------------------------------------- 1 | package com.redislabs.redisgraph.graph_entities; 2 | 3 | import nl.jqno.equalsverifier.EqualsVerifier; 4 | import org.junit.Test; 5 | 6 | import java.util.List; 7 | import java.util.concurrent.ThreadLocalRandom; 8 | import java.util.stream.Collectors; 9 | import java.util.stream.IntStream; 10 | 11 | import static org.junit.jupiter.api.Assertions.*; 12 | 13 | public class PathTest { 14 | 15 | private Node buildNode(int id){ 16 | Node n = new Node(); 17 | n.setId(0); 18 | return n; 19 | } 20 | 21 | private Edge buildEdge(int id, int src, int dst){ 22 | Edge e = new Edge(); 23 | e.setId(id); 24 | e.setSource(src); 25 | e.setDestination(dst); 26 | return e; 27 | } 28 | 29 | private List buildNodeArray(int size) { 30 | return IntStream.range(0, size).mapToObj(i -> buildNode(i)).collect(Collectors.toList()); 31 | } 32 | 33 | private List buildEdgeArray(int size){ 34 | return IntStream.range(0, size).mapToObj(i -> buildEdge(i, i, i+1)).collect(Collectors.toList()); 35 | } 36 | 37 | private Path buildPath(int nodeCount){ 38 | return new Path(buildNodeArray(nodeCount), buildEdgeArray(nodeCount-1)); 39 | } 40 | 41 | @Test 42 | public void testEmptyPath(){ 43 | Path path = buildPath(0); 44 | assertEquals(0, path.length()); 45 | assertEquals(0, path.nodeCount()); 46 | assertThrows(IndexOutOfBoundsException.class, ()->path.getNode(0)); 47 | assertThrows(IndexOutOfBoundsException.class, ()->path.getEdge(0)); 48 | } 49 | 50 | @Test 51 | public void testSingleNodePath(){ 52 | Path path = buildPath(1); 53 | assertEquals(0, path.length()); 54 | assertEquals(1, path.nodeCount()); 55 | Node n = new Node(); 56 | n.setId(0); 57 | assertEquals(n, path.firstNode()); 58 | assertEquals(n, path.lastNode()); 59 | assertEquals(n, path.getNode(0)); 60 | } 61 | 62 | @Test 63 | public void testRandomLengthPath(){ 64 | int nodeCount = ThreadLocalRandom.current().nextInt(2, 100 + 1); 65 | Path path = buildPath(nodeCount); 66 | assertEquals(buildNodeArray(nodeCount), path.getNodes()); 67 | assertEquals(buildEdgeArray(nodeCount-1), path.getEdges()); 68 | assertDoesNotThrow(()->path.getEdge(0)); 69 | } 70 | 71 | @Test 72 | public void hashCodeEqualTest(){ 73 | EqualsVerifier.forClass(Path.class).verify(); 74 | } 75 | } -------------------------------------------------------------------------------- /src/test/java/com/redislabs/redisgraph/impl/UtilsTest.java: -------------------------------------------------------------------------------- 1 | package com.redislabs.redisgraph.impl; 2 | 3 | import static org.junit.Assert.assertThrows; 4 | 5 | import java.util.Arrays; 6 | import java.util.HashMap; 7 | import java.util.IllegalFormatConversionException; 8 | import java.util.List; 9 | import java.util.Map; 10 | 11 | import org.junit.Assert; 12 | import org.junit.Test; 13 | 14 | public class UtilsTest { 15 | 16 | @Test 17 | public void testPrepareProcedure() { 18 | Assert.assertEquals("CALL prc()", 19 | Utils.prepareProcedure("prc", Arrays.asList(new String[]{}), new HashMap<>())); 20 | 21 | Assert.assertEquals("CALL prc(\"a\",\"b\")", 22 | Utils.prepareProcedure("prc", Arrays.asList(new String[]{"a", "b"}), new HashMap<>())); 23 | 24 | Map> kwargs = new HashMap<>(); 25 | kwargs.put("y", Arrays.asList(new String[]{"ka", "kb"})); 26 | Assert.assertEquals("CALL prc(\"a\",\"b\")ka,kb", 27 | Utils.prepareProcedure("prc", Arrays.asList(new String[]{"a", "b"}), kwargs)); 28 | 29 | Assert.assertEquals("CALL prc()ka,kb", Utils.prepareProcedure("prc", Arrays.asList(new String[]{}), kwargs)); 30 | } 31 | 32 | @SuppressWarnings("deprecation") 33 | @Test 34 | public void prepareQuery() { 35 | Assert.assertEquals("query %s %d end of query", Utils.prepareQuery("query %s %d end of query")); 36 | 37 | Assert.assertEquals("query 'a' 33 end of query", Utils.prepareQuery("query %s %d end of query", "a", 33)); 38 | 39 | assertThrows(IllegalFormatConversionException.class, 40 | () -> Utils.prepareQuery("query %s %d end of query", "a", "b")); 41 | } 42 | 43 | @Test 44 | public void testParamsPrep() { 45 | Map params = new HashMap<>(); 46 | params.put("param", ""); 47 | Assert.assertEquals("CYPHER param=\"\" RETURN $param", Utils.prepareQuery("RETURN $param", params)); 48 | params.put("param", "\""); 49 | Assert.assertEquals("CYPHER param=\"\\\"\" RETURN $param", Utils.prepareQuery("RETURN $param", params)); 50 | params.put("param", "\"st"); 51 | Assert.assertEquals("CYPHER param=\"\\\"st\" RETURN $param", Utils.prepareQuery("RETURN $param", params)); 52 | params.put("param", 1); 53 | Assert.assertEquals("CYPHER param=1 RETURN $param", Utils.prepareQuery("RETURN $param", params)); 54 | params.put("param", 2.3); 55 | Assert.assertEquals("CYPHER param=2.3 RETURN $param", Utils.prepareQuery("RETURN $param", params)); 56 | params.put("param", true); 57 | Assert.assertEquals("CYPHER param=true RETURN $param", Utils.prepareQuery("RETURN $param", params)); 58 | params.put("param", false); 59 | Assert.assertEquals("CYPHER param=false RETURN $param", Utils.prepareQuery("RETURN $param", params)); 60 | params.put("param", null); 61 | Assert.assertEquals("CYPHER param=null RETURN $param", Utils.prepareQuery("RETURN $param", params)); 62 | params.put("param", "str"); 63 | Assert.assertEquals("CYPHER param=\"str\" RETURN $param", Utils.prepareQuery("RETURN $param", params)); 64 | params.put("param", "s\"tr"); 65 | Assert.assertEquals("CYPHER param=\"s\\\"tr\" RETURN $param", Utils.prepareQuery("RETURN $param", params)); 66 | Integer arr[] = {1, 2, 3}; 67 | params.put("param", arr); 68 | Assert.assertEquals("CYPHER param=[1, 2, 3] RETURN $param", Utils.prepareQuery("RETURN $param", params)); 69 | List list = Arrays.asList(1, 2, 3); 70 | params.put("param", list); 71 | Assert.assertEquals("CYPHER param=[1, 2, 3] RETURN $param", Utils.prepareQuery("RETURN $param", params)); 72 | String strArr[] = {"1", "2", "3"}; 73 | params.put("param", strArr); 74 | Assert.assertEquals("CYPHER param=[\"1\", \"2\", \"3\"] RETURN $param", 75 | Utils.prepareQuery("RETURN $param", params)); 76 | List stringList = Arrays.asList("1", "2", "3"); 77 | params.put("param", stringList); 78 | Assert.assertEquals("CYPHER param=[\"1\", \"2\", \"3\"] RETURN $param", 79 | Utils.prepareQuery("RETURN $param", params)); 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /src/test/java/com/redislabs/redisgraph/test/utils/PathBuilder.java: -------------------------------------------------------------------------------- 1 | package com.redislabs.redisgraph.test.utils; 2 | 3 | import com.redislabs.redisgraph.graph_entities.Edge; 4 | import com.redislabs.redisgraph.graph_entities.Node; 5 | import com.redislabs.redisgraph.graph_entities.Path; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | public final class PathBuilder{ 11 | private final List nodes; 12 | private final List edges; 13 | private Class currentAppendClass; 14 | 15 | public PathBuilder() { 16 | this.nodes = new ArrayList<>(0); 17 | this.edges = new ArrayList<>(0); 18 | currentAppendClass = Node.class; 19 | } 20 | 21 | public PathBuilder(int nodesCount){ 22 | nodes = new ArrayList<>(nodesCount); 23 | edges = new ArrayList<>(nodesCount-1 >= 0 ? nodesCount -1 : 0); 24 | currentAppendClass = Node.class; 25 | } 26 | 27 | public PathBuilder append(Object object){ 28 | Class c = object.getClass(); 29 | if(!currentAppendClass.equals(c)) throw new IllegalArgumentException("Path Builder expected " + currentAppendClass.getSimpleName() + " but was " + c.getSimpleName()); 30 | if(c.equals(Node.class)) return appendNode((Node)object); 31 | else return appendEdge((Edge)object); 32 | } 33 | 34 | private PathBuilder appendEdge(Edge edge) { 35 | edges.add(edge); 36 | currentAppendClass = Node.class; 37 | return this; 38 | } 39 | 40 | private PathBuilder appendNode(Node node){ 41 | nodes.add(node); 42 | currentAppendClass = Edge.class; 43 | return this; 44 | } 45 | 46 | public Path build(){ 47 | if(nodes.size() != edges.size() + 1) throw new IllegalArgumentException("Path builder nodes count should be edge count + 1"); 48 | return new Path(nodes, edges); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/test/java/com/redislabs/redisgraph/test/utils/PathBuilderTest.java: -------------------------------------------------------------------------------- 1 | package com.redislabs.redisgraph.test.utils; 2 | 3 | import static org.junit.Assert.assertThrows; 4 | import static org.junit.Assert.assertTrue; 5 | 6 | import org.junit.Test; 7 | 8 | import com.redislabs.redisgraph.graph_entities.Edge; 9 | 10 | public class PathBuilderTest { 11 | 12 | @Test 13 | public void testPathBuilderSizeException() { 14 | IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> { 15 | PathBuilder builder = new PathBuilder(0); 16 | builder.build(); 17 | }); 18 | assertTrue(exception.getMessage().equalsIgnoreCase("Path builder nodes count should be edge count + 1")); 19 | } 20 | 21 | @Test 22 | public void testPathBuilderArgumentsException() { 23 | IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> { 24 | PathBuilder builder = new PathBuilder(0); 25 | builder.append(new Edge()); 26 | }); 27 | assertTrue(exception.getMessage().equalsIgnoreCase("Path Builder expected Node but was Edge")); 28 | } 29 | } 30 | --------------------------------------------------------------------------------