Skip to content

Commit

Permalink
For Geode backend prefer getAll() function over OQL when query is a s…
Browse files Browse the repository at this point in the history
…imple ID lookup

fast-path for "region.getAll" use-case. ie get values for list of keys
assumes no projections / aggregations / sort / count etc.
  • Loading branch information
asereda-gs committed Dec 6, 2019
1 parent 7cc1fb8 commit 5426af6
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 11 deletions.
12 changes: 7 additions & 5 deletions criteria/geode/src/org/immutables/criteria/geode/Geodes.java
Expand Up @@ -106,11 +106,12 @@ static Object convert(Object value, Type destinationType) {


/**
* Geode (currently) doesn't support delete by query syntax ({@code DELETE ... WHERE ...}) and elements have to be
* removed explicitly by key (using {@link Map#remove(Object)} or {@link Region#removeAll} API)
* <p>Tries to detect if current criteria is based only on keys (IDs) only and extracts
* them from filter expression.
*
* <p>Tries to detect if current criteria is based only on keys (entity ID) and extracts them from expression (if it is only
* expression based on keys).
* <p><strong>Usage example</strong> Geode (currently) doesn't support delete by query syntax ({@code DELETE ... WHERE ...}) and elements have to be
* removed explicitly by key (using {@link Map#remove(Object)} or {@link Region#removeAll} API). With this method
* one can extract keys from expression and use delete by key API.
*
* <p>Example:
* <pre>
Expand All @@ -124,8 +125,9 @@ static Object convert(Object value, Type destinationType) {
*
* @param expr filter applied on entries for deletion
* @see Region#removeAll(Collection)
* @return List of keys present in the expression, empty optional otherwise
*/
static Optional<List<?>> canDeleteByKey(Expression expr, IdResolver idResolver) {
static Optional<List<?>> maybeKeyOnlyLookup(Expression expr, IdResolver idResolver) {
if (!(expr instanceof Call)) {
return Optional.empty();
}
Expand Down
Expand Up @@ -48,7 +48,7 @@ public WriteResult call() throws Exception {

Expression filter = operation.query().filter().orElseThrow(() -> new IllegalStateException("For " + operation));

Optional<List<?>> ids = Geodes.canDeleteByKey(filter, session.idResolver);
Optional<List<?>> ids = Geodes.maybeKeyOnlyLookup(filter, session.idResolver);
// special case when expression contains only ID / key attribute
if (ids.isPresent()) {
return deleteByKeys(ids.get());
Expand Down
31 changes: 26 additions & 5 deletions criteria/geode/src/org/immutables/criteria/geode/SyncSelect.java
Expand Up @@ -27,6 +27,8 @@

import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.Callable;
import java.util.function.Function;
import java.util.logging.Level;
Expand Down Expand Up @@ -65,26 +67,45 @@ private static ProjectedTuple toTuple(Query query, Object value) {
@Override
public Iterable<Object> call() throws Exception {

Oql oql = session.oqlGenerator().generate(operation.query());
Query query = operation.query();
Oql oql = session.oqlGenerator().generate(query);

if (GeodeBackend.logger.isLoggable(Level.FINE)) {
GeodeBackend.logger.log(Level.FINE, "Querying Geode with {0}", oql);
}

// fast-path for "region.getAll" use-case. ie get values for list of keys
// assumes no projections / aggregations / sort / count etc.
// plain get by key lookup
boolean maybeGetByKey = query.filter().isPresent()
&& !query.hasAggregations()
&& !query.hasProjections()
&& !query.count()
&& query.collations().isEmpty();

if (maybeGetByKey) {
Optional<List<?>> ids = Geodes.maybeKeyOnlyLookup(query.filter().get(), session.idResolver);
if (ids.isPresent()) {
return session.region.getAll(ids.get())
.values().stream()
.filter(Objects::nonNull) // skip missing keys (null values)
.collect(Collectors.toList());
}
}

// for projections use tuple function
Function<Object, Object> tupleFn;
if (operation.query().count()) {
if (query.count()) {
// geode will return integer for count(*)
tupleFn = x -> Geodes.convert(x, Long.class);
} else if (operation.query().hasProjections()) {
tupleFn = x -> Geodes.castNumbers(toTuple(operation.query(), x));
} else if (query.hasProjections()) {
tupleFn = x -> Geodes.castNumbers(toTuple(query, x));
} else {
tupleFn = Function.identity();
}

// convert existing collections to JDK-only implementations (eg. ImmutableList -> ArrayList)
Object[] variables = oql.variables().stream().map(BIND_VARIABLE_CONVERTER).toArray(Object[]::new);
@SuppressWarnings("unchecked")
Iterable<Object> result = (Iterable<Object>) session.queryService.newQuery(oql.oql()).execute(variables);
// lazy transform
return Iterables.transform(result, tupleFn::apply);
Expand Down

0 comments on commit 5426af6

Please sign in to comment.