Skip to content

Commit

Permalink
Merge pull request #28623 from cescoffier/redis-graph
Browse files Browse the repository at this point in the history
Implement the Redis Graph commands
  • Loading branch information
gsmet committed Oct 17, 2022
2 parents d288f27 + dbfcf98 commit faa924c
Show file tree
Hide file tree
Showing 22 changed files with 1,377 additions and 4 deletions.
2 changes: 2 additions & 0 deletions docs/src/main/asciidoc/redis-reference.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,8 @@ As mentioned above, the API is divided into groups:
- cuckoo - `.cuckoo()` (requires the https://redis.com/modules/redis-bloom/[redis-bloom] module on the server side, which also provides the cuckoo filter commands)
- count-min - `.countmin()` (requires the https://redis.com/modules/redis-bloom/[redis-bloom] module on the server side, which also provides the count-min filter commands)
- top-k - `.topk()` (requires the https://redis.com/modules/redis-bloom/[redis-bloom] module on the server side, which also provides the top-k filter commands)
- graph - `.graph()` (requires the https://redis.com/modules/redis-graph/[redis-graph] module on the server side, which also provides the graph commands)
These commands are marked as experimental, as we would need feedback before making them stable.

Each of these methods returns an object that lets you execute the commands related to the group.
The following snippet demonstrates how to use the _hash_ group:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import io.quarkus.redis.datasource.countmin.ReactiveCountMinCommands;
import io.quarkus.redis.datasource.cuckoo.ReactiveCuckooCommands;
import io.quarkus.redis.datasource.geo.ReactiveGeoCommands;
import io.quarkus.redis.datasource.graph.ReactiveGraphCommands;
import io.quarkus.redis.datasource.hash.ReactiveHashCommands;
import io.quarkus.redis.datasource.hyperloglog.ReactiveHyperLogLogCommands;
import io.quarkus.redis.datasource.json.ReactiveJsonCommands;
Expand All @@ -23,6 +24,7 @@
import io.quarkus.redis.datasource.transactions.TransactionResult;
import io.quarkus.redis.datasource.transactions.TransactionalRedisDataSource;
import io.quarkus.redis.datasource.value.ReactiveValueCommands;
import io.smallrye.common.annotation.Experimental;
import io.smallrye.mutiny.Uni;
import io.vertx.mutiny.redis.client.Command;
import io.vertx.mutiny.redis.client.Redis;
Expand Down Expand Up @@ -494,6 +496,27 @@ default <V> ReactiveTopKCommands<String, V> topk(Class<V> valueType) {
*/
<K, V> ReactiveTopKCommands<K, V> topk(Class<K> redisKeyType, Class<V> valueType);

/**
* Gets the object to manipulate graphs.
* This group requires the <a href="https://redis.io/docs/stack/graph/">RedisGraph module</a>.
*
* @return the object to manipulate graphs lists.
*/
@Experimental("The Redis graph support is experimental")
default ReactiveGraphCommands<String> graph() {
return graph(String.class);
}

/**
* Gets the object to manipulate graphs.
* This group requires the <a href="https://redis.io/docs/stack/graph/">RedisGraph module</a>.
*
* @param <K> the type of keys
* @return the object to manipulate graphs lists.
*/
@Experimental("The Redis graph support is experimental")
<K> ReactiveGraphCommands<K> graph(Class<K> redisKeyType);

/**
* Executes a command.
* This method is used to execute commands not offered by the API.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import io.quarkus.redis.datasource.countmin.CountMinCommands;
import io.quarkus.redis.datasource.cuckoo.CuckooCommands;
import io.quarkus.redis.datasource.geo.GeoCommands;
import io.quarkus.redis.datasource.graph.GraphCommands;
import io.quarkus.redis.datasource.hash.HashCommands;
import io.quarkus.redis.datasource.hyperloglog.HyperLogLogCommands;
import io.quarkus.redis.datasource.json.JsonCommands;
Expand All @@ -23,6 +24,7 @@
import io.quarkus.redis.datasource.transactions.TransactionResult;
import io.quarkus.redis.datasource.transactions.TransactionalRedisDataSource;
import io.quarkus.redis.datasource.value.ValueCommands;
import io.smallrye.common.annotation.Experimental;
import io.vertx.mutiny.redis.client.Command;
import io.vertx.mutiny.redis.client.Response;

Expand Down Expand Up @@ -483,6 +485,27 @@ default <V> TopKCommands<String, V> topk(Class<V> valueType) {
*/
<K, V> TopKCommands<K, V> topk(Class<K> redisKeyType, Class<V> valueType);

/**
* Gets the object to manipulate graphs.
* This group requires the <a href="https://redis.io/docs/stack/graph/">RedisGraph module</a>.
*
* @return the object to manipulate graphs lists.
*/
@Experimental("The Redis graph support is experimental")
default GraphCommands<String> graph() {
return graph(String.class);
}

/**
* Gets the object to manipulate graphs.
* This group requires the <a href="https://redis.io/docs/stack/graph/">RedisGraph module</a>.
*
* @param <K> the type of keys
* @return the object to manipulate graphs lists.
*/
@Experimental("The Redis graph support is experimental")
<K> GraphCommands<K> graph(Class<K> redisKeyType);

/**
* Gets the objects to publish and receive messages.
*
Expand Down Expand Up @@ -526,4 +549,5 @@ default <V> TopKCommands<String, V> topk(Class<V> valueType) {
* @return the reactive data source.
*/
ReactiveRedisDataSource getReactive();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package io.quarkus.redis.datasource.graph;

import java.time.Duration;
import java.util.List;
import java.util.Map;

import io.quarkus.redis.datasource.RedisCommands;
import io.smallrye.common.annotation.Experimental;

/**
* Allows executing commands from the {@code graph} group.
* These commands require the <a href="https://redis.io/docs/stack/graph/">Redis Graph</a> module to be installed in the
* Redis server.
* <p>
* See <a href="https://redis.io/commands/?group=graph">the graph command list</a> for further information about
* these commands.
* <p>
*
* @param <K> the type of the key
*/
@Experimental("The Redis graph support is experimental")
public interface GraphCommands<K> extends RedisCommands {

/**
* Execute the command <a href="https://redis.io/commands/graph.delete">GRAPH.DELETE</a>.
* Summary: Completely removes the graph and all of its entities.
* Group: graph
*
* @param key the key, must not be {@code null}
*/
void graphDelete(K key);

/**
* Execute the command <a href="https://redis.io/commands/graph.delete">GRAPH.EXPLAIN</a>.
* Summary: Constructs a query execution plan but does not run it. Inspect this execution plan to better understand
* how your query will get executed.
* Group: graph
* <p>
*
* @param key the key, must not be {@code null}
* @param query the query, must not be {@code null}
* @return the string representation of the query execution plan
*/
String graphExplain(K key, String query);

/**
* Execute the command <a href="https://redis.io/commands/graph.list">GRAPH.LIST</a>.
* Summary: Lists all graph keys in the keyspace.
* Group: graph
* <p>
*
* @return the list of list of keys storing graphs
*/
List<K> graphList();

/**
* Execute the command <a href="https://redis.io/commands/graph.delete">GRAPH.QUERY</a>.
* Summary: Executes the given query against a specified graph.
* Group: graph
* <p>
*
* @param key the key, must not be {@code null}
* @param query the query, must not be {@code null}
* @return a map, potentially empty, containing the requested items to return. The map is empty if
* the query does not have a {@code return} clause. For each request item, a {@link GraphQueryResponseItem} is
* returned. It can represent a scalar item
* ({@link io.quarkus.redis.datasource.graph.GraphQueryResponseItem.ScalarItem}),
* a node ({@link io.quarkus.redis.datasource.graph.GraphQueryResponseItem.NodeItem}, or a relation
* ({@link io.quarkus.redis.datasource.graph.GraphQueryResponseItem.RelationItem}).
*/
List<Map<String, GraphQueryResponseItem>> graphQuery(K key, String query);

/**
* Execute the command <a href="https://redis.io/commands/graph.delete">GRAPH.QUERY</a>.
* Summary: Executes the given query against a specified graph.
* Group: graph
* <p>
*
* @param key the key, must not be {@code null}
* @param query the query, must not be {@code null}
* @param timeout a timeout, must not be {@code null}
* @return a map, potentially empty, containing the requested items to return. The map is empty if
* the query does not have a {@code return} clause. For each request item, a {@link GraphQueryResponseItem} is
* returned. It can represent a scalar item
* ({@link io.quarkus.redis.datasource.graph.GraphQueryResponseItem.ScalarItem}),
* a node ({@link io.quarkus.redis.datasource.graph.GraphQueryResponseItem.NodeItem}, or a relation
* ({@link io.quarkus.redis.datasource.graph.GraphQueryResponseItem.RelationItem}).
*/
List<Map<String, GraphQueryResponseItem>> graphQuery(K key, String query, Duration timeout);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package io.quarkus.redis.datasource.graph;

import java.util.List;

public interface GraphQueryResponseItem {

enum Kind {
SCALAR,
NODE,
RELATION
}

Kind kind();

String name();

default ScalarItem asScalarItem() {
if (this instanceof ScalarItem) {
return (ScalarItem) this;
}
throw new ClassCastException("Cannot cast " + this + " of kind " + kind() + " to a " + ScalarItem.class.getName());
}

default NodeItem asNodeItem() {
if (this instanceof NodeItem) {
return (NodeItem) this;
}
throw new ClassCastException("Cannot cast " + this + " of kind " + kind() + " to a " + NodeItem.class.getName());
}

default RelationItem asRelationItem() {
if (this instanceof RelationItem) {
return (RelationItem) this;
}
throw new ClassCastException("Cannot cast " + this + " of kind " + kind() + " to a " + RelationItem.class.getName());
}

interface ScalarItem extends GraphQueryResponseItem {

boolean asBoolean();

int asInteger();

double asDouble();

boolean isNull();

String asString();

@Override
default Kind kind() {
return Kind.SCALAR;
}
}

interface NodeItem extends GraphQueryResponseItem {
long id();

List<String> labels();

List<ScalarItem> properties();

ScalarItem get(String property);

@Override
default Kind kind() {
return Kind.NODE;
}
}

interface RelationItem extends GraphQueryResponseItem {
long id();

String type();

long source();

long destination();

List<ScalarItem> properties();

ScalarItem get(String property);

@Override
default Kind kind() {
return Kind.RELATION;
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package io.quarkus.redis.datasource.graph;

import java.time.Duration;
import java.util.List;
import java.util.Map;

import io.quarkus.redis.datasource.ReactiveRedisCommands;
import io.smallrye.mutiny.Uni;

/**
* Allows executing commands from the {@code graph} group.
* These commands require the <a href="https://redis.io/docs/stack/graph/">Redis Graph</a> module to be installed in the
* Redis server.
* <p>
* See <a href="https://redis.io/commands/?group=graph">the graph command list</a> for further information about
* these commands.
* <p>
*
* @param <K> the type of the key
*/
public interface ReactiveGraphCommands<K> extends ReactiveRedisCommands {

/**
* Execute the command <a href="https://redis.io/commands/graph.delete">GRAPH.DELETE</a>.
* Summary: Completely removes the graph and all of its entities.
* Group: graph
*
* @param key the key, must not be {@code null}
* @return a uni producing {@code null} when the operation completes
**/
Uni<Void> graphDelete(K key);

/**
* Execute the command <a href="https://redis.io/commands/graph.delete">GRAPH.EXPLAIN</a>.
* Summary: Constructs a query execution plan but does not run it. Inspect this execution plan to better understand
* how your query will get executed.
* Group: graph
* <p>
*
* @param key the key, must not be {@code null}
* @param query the query, must not be {@code null}
* @return a uni producing the string representation of the query execution plan
*/
Uni<String> graphExplain(K key, String query);

/**
* Execute the command <a href="https://redis.io/commands/graph.list">GRAPH.LIST</a>.
* Summary: Lists all graph keys in the keyspace.
* Group: graph
* <p>
*
* @return a uni producing the list of list of keys storing graphs
*/
Uni<List<K>> graphList();

/**
* Execute the command <a href="https://redis.io/commands/graph.delete">GRAPH.QUERY</a>.
* Summary: Executes the given query against a specified graph.
* Group: graph
* <p>
*
* @param key the key, must not be {@code null}
* @param query the query, must not be {@code null}
* @return a map, potentially empty, containing the requested items to return. The map is empty if
* the query does not have a {@code return} clause. For each request item, a {@link GraphQueryResponseItem} is
* returned. It can represent a scalar item
* ({@link io.quarkus.redis.datasource.graph.GraphQueryResponseItem.ScalarItem}),
* a node ({@link io.quarkus.redis.datasource.graph.GraphQueryResponseItem.NodeItem}, or a relation
* ({@link io.quarkus.redis.datasource.graph.GraphQueryResponseItem.RelationItem}).
*/
Uni<List<Map<String, GraphQueryResponseItem>>> graphQuery(K key, String query);

/**
* Execute the command <a href="https://redis.io/commands/graph.delete">GRAPH.QUERY</a>.
* Summary: Executes the given query against a specified graph.
* Group: graph
* <p>
*
* @param key the key, must not be {@code null}
* @param query the query, must not be {@code null}
* @param timeout a timeout, must not be {@code null}
* @return a map, potentially empty, containing the requested items to return. The map is empty if
* the query does not have a {@code return} clause. For each request item, a {@link GraphQueryResponseItem} is
* returned. It can represent a scalar item
* ({@link io.quarkus.redis.datasource.graph.GraphQueryResponseItem.ScalarItem}),
* a node ({@link io.quarkus.redis.datasource.graph.GraphQueryResponseItem.NodeItem}, or a relation
* ({@link io.quarkus.redis.datasource.graph.GraphQueryResponseItem.RelationItem}).
*/
Uni<List<Map<String, GraphQueryResponseItem>>> graphQuery(K key, String query, Duration timeout);

}

0 comments on commit faa924c

Please sign in to comment.