Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Native query support for reactive repositories #42

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*-
* Copyright (c) 2020, 2023 Oracle and/or its affiliates. All rights reserved.
*
* Licensed under the Universal Permissive License v 1.0 as shown at
* https://oss.oracle.com/licenses/upl/
*/

package com.oracle.nosql.spring.data.repository.query;

import com.oracle.nosql.spring.data.core.ReactiveNosqlOperations;
import com.oracle.nosql.spring.data.core.query.NosqlQuery;
import com.oracle.nosql.spring.data.core.query.StringQuery;
import com.oracle.nosql.spring.data.repository.Query;
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;

import static com.oracle.nosql.spring.data.repository.query.StringBasedNosqlQuery.hasAmbiguousProjectionFlags;

public class ReactiveStringBasedNosqlQuery extends AbstractReactiveNosqlQuery {
private final String query;

private final boolean isCountQuery;
private final boolean isExistsQuery;
private final boolean isDeleteQuery;

public ReactiveStringBasedNosqlQuery(NosqlQueryMethod method,
ReactiveNosqlOperations operations,
QueryMethodEvaluationContextProvider evaluationContextProvider) {
this(method.getAnnotatedQuery(), method, operations,
evaluationContextProvider);
}

public ReactiveStringBasedNosqlQuery(String query,
NosqlQueryMethod method,
ReactiveNosqlOperations operations,
QueryMethodEvaluationContextProvider evaluationContextProvider) {
super(method, operations);
this.query = query;
if (method.hasAnnotatedQuery()) {
Query queryAnnotation = method.getQueryAnnotation();
isCountQuery = queryAnnotation.count();
isExistsQuery = queryAnnotation.exists();
isDeleteQuery = queryAnnotation.delete();
if (hasAmbiguousProjectionFlags(isCountQuery, isExistsQuery,
isDeleteQuery)) {
throw new IllegalArgumentException(
String.format("Manually defined query for %s cannot be a " +
"count and exists or delete query at the same time!",
method));
}
} else {
isCountQuery = false;
isExistsQuery = false;
isDeleteQuery = false;
}
}

@Override
protected NosqlQuery createQuery(NosqlParameterAccessor accessor) {
return new StringQuery(getQueryMethod(), query, accessor);
}

@Override
protected boolean isDeleteQuery() {
return isDeleteQuery;
}

@Override
protected boolean isExistsQuery() {
return isExistsQuery;
}

@Override
protected boolean isCountQuery() {
return isCountQuery;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ protected boolean isCountQuery() {
return isCountQuery;
}

private static boolean hasAmbiguousProjectionFlags(boolean isCountQuery, boolean isExistsQuery,
static boolean hasAmbiguousProjectionFlags(boolean isCountQuery, boolean isExistsQuery,
boolean isDeleteQuery) {
return countBooleanTrueValues(isCountQuery, isExistsQuery, isDeleteQuery) > 1;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import com.oracle.nosql.spring.data.repository.query.NosqlQueryMethod;
import com.oracle.nosql.spring.data.repository.query.PartTreeReactiveNosqlQuery;

import com.oracle.nosql.spring.data.repository.query.ReactiveStringBasedNosqlQuery;
import org.springframework.context.ApplicationContext;
import org.springframework.data.projection.ProjectionFactory;
import org.springframework.data.repository.core.EntityInformation;
Expand Down Expand Up @@ -69,13 +70,16 @@ protected Optional<QueryLookupStrategy> getQueryLookupStrategy(
private static class ReactiveNosqlQueryLookupStrategy implements QueryLookupStrategy {
private final ApplicationContext applicationContext;
private final ReactiveNosqlOperations nosqlOperations;
private final QueryMethodEvaluationContextProvider
evaluationContextProvider;

public ReactiveNosqlQueryLookupStrategy(
ApplicationContext applicationContext,
ReactiveNosqlOperations operations,
QueryMethodEvaluationContextProvider provider) {
this.applicationContext = applicationContext;
this.nosqlOperations = operations;
this.evaluationContextProvider = provider;
}

@Override
Expand All @@ -87,7 +91,18 @@ public RepositoryQuery resolveQuery(Method method, RepositoryMetadata metadata,

Assert.notNull(queryMethod, "queryMethod must not be null!");
Assert.notNull(nosqlOperations, "dbOperations must not be null!");
return new PartTreeReactiveNosqlQuery(queryMethod, nosqlOperations);

String namedQueryName = queryMethod.getNamedQueryName();
if (namedQueries.hasQuery(namedQueryName)) {
String namedQuery = namedQueries.getQuery(namedQueryName);
return new ReactiveStringBasedNosqlQuery(namedQuery, queryMethod,
nosqlOperations, evaluationContextProvider);
} else if (queryMethod.hasAnnotatedQuery()) {
return new ReactiveStringBasedNosqlQuery(queryMethod, nosqlOperations,
evaluationContextProvider);
} else {
return new PartTreeReactiveNosqlQuery(queryMethod, nosqlOperations);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -197,4 +197,20 @@ public void testIgnoreCase() {
machines.forEach(m -> TestCase.assertEquals(machineCache.get(m.getMachineId())
, m));
}

@Test
public void testNative() {
List<Machine> machines = repo.
findAllByLocationNative().collectList().block();
TestCase.assertEquals(8, machines.size());
machines.forEach(m -> {
TestCase.assertNotNull(m.getMachineId());
TestCase.assertNotNull(m.getMachineId().getName());
TestCase.assertNotNull(m.getMachineId().getVersion());
});

machines = repo.findByMachineIdNameNative("name3").collectList().block();
TestCase.assertEquals(4, machines.size());
machines.forEach(m -> TestCase.assertEquals(machineCache.get(m.getMachineId()), m));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

import com.oracle.nosql.spring.data.repository.Query;
import com.oracle.nosql.spring.data.repository.ReactiveNosqlRepository;
import org.springframework.data.repository.query.Param;
import reactor.core.publisher.Flux;

public interface ReactiveMachineRepository extends ReactiveNosqlRepository<Machine, MachineId> {
Expand All @@ -33,4 +34,13 @@ Flux<Machine> findByMachineIdNameOrMachineIdVersion(String name,

//Ignore case
Flux<Machine> findByMachineIdNameIgnoreCase(String name);

//native
@Query("SELECT * FROM Machine m WHERE m" +
".kv_json_.location='newyork'")
Flux<Machine> findAllByLocationNative();

@Query(value = "DECLARE $name STRING; SELECT * FROM Machine AS m " +
"WHERE m.name = $name")
Flux<Machine> findByMachineIdNameNative(@Param("$name") String name);
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
*/
package com.oracle.nosql.spring.data.test.reactive;

import com.oracle.nosql.spring.data.repository.Query;
import com.oracle.nosql.spring.data.repository.ReactiveNosqlRepository;
import com.oracle.nosql.spring.data.test.app.Customer;

import org.springframework.data.repository.query.Param;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

Expand All @@ -24,4 +26,20 @@ public interface CustomerReactiveRepository
Mono<Long> countByLastName(String last);

Flux<Customer> deleteByLastName(String last);

@Query("SELECT * FROM Customer AS c WHERE c.kv_json_.firstName = 'John'")
Flux<Customer> findCustomersByFirstNameJohn();

@Query(value = "DECLARE $firstName STRING; SELECT * FROM Customer AS c " +
"WHERE c.kv_json_.firstName = $firstName")
Flux<Customer> findCustomersByFirstName(@Param("$firstName") String firstName);

@Query("DECLARE $firstName STRING; $last STRING; " +
"SELECT * FROM Customer AS c " +
"WHERE c.kv_json_.firstName = $firstName AND " +
"c.kv_json_.lastName = $last")
Flux<Customer> findCustomersWithLastAndFirstNames(
@Param("$last") String paramLast,
@Param("$firstName") String firstName
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,19 @@ public void testRepo() {
Mono<Long> count = repo.count();
StepVerifier.create(count).expectNext(7L).verifyComplete();

//native queries
List<Customer> johns =
repo.findCustomersByFirstNameJohn().collectList().block();
Assert.assertTrue(johns.contains(c3) && johns.contains(c4));

johns = repo.findCustomersByFirstName("John").collectList().block();
Assert.assertTrue(johns.size() == 2 &&
johns.contains(c3) && johns.contains(c4));

johns = repo.findCustomersWithLastAndFirstNames("Doe", "John").
collectList().block();
Assert.assertTrue(johns.size() == 1 && johns.contains(c4));

// deleteById
repo.deleteById(c7.customerId).subscribe();
exists = repo.existsById(c7.customerId);
Expand Down