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

Update Query supporting param match #472

Merged
merged 47 commits into from
Jan 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
b673ab0
chore: update validation at return
otaviojava Jan 4, 2024
7360c95
feat: create implementation to column paramter based query
otaviojava Jan 4, 2024
932f8fb
docs: create documentation at column parameter based query
otaviojava Jan 4, 2024
6362438
feat: convert to singleton the column parater based
otaviojava Jan 4, 2024
6cd5373
feat: convert the column parater to enum
otaviojava Jan 4, 2024
56afe49
test: create column paramter based query
otaviojava Jan 4, 2024
210c564
feat: create null condition
otaviojava Jan 4, 2024
74dd87d
feat: create implementation to column paramter based
otaviojava Jan 4, 2024
f8c5b02
test: create scenarios to column parameter based query
otaviojava Jan 4, 2024
0d27eff
feat: create document parameter based query
otaviojava Jan 4, 2024
97c64bc
style: organize imports
otaviojava Jan 4, 2024
3d48166
test: create initial structure of document
otaviojava Jan 4, 2024
4782a52
test: create document paramter query based
otaviojava Jan 4, 2024
d61ea6d
feat: implement repository type using paramter based
otaviojava Jan 4, 2024
fa54bec
chore: update abstract repository proxy
otaviojava Jan 4, 2024
2a61fa6
docs: create documentation to Abstract repository proxy
otaviojava Jan 4, 2024
d43fe4b
test: create scenarios to test when this is long
otaviojava Jan 4, 2024
e4d5446
test: create long scenario to all operations
otaviojava Jan 4, 2024
9c44a94
feat: add long condition at the return
otaviojava Jan 4, 2024
c1f2451
test: create query test to long scenario
otaviojava Jan 4, 2024
7a3e216
feat: include the implementation to keyvalue repository
otaviojava Jan 4, 2024
f3d042d
test: create test to keyvaleu repository
otaviojava Jan 4, 2024
0926131
feat: create test at keyvlue repository
otaviojava Jan 4, 2024
5b7773c
feat: create implemetnation to query paramter
otaviojava Jan 4, 2024
dce8d5d
chore: update query empty params
otaviojava Jan 4, 2024
822d1da
style: remove not used imports
otaviojava Jan 4, 2024
244f8a9
style: remove not used imports
otaviojava Jan 4, 2024
50f9879
feat: create abstract document repository proxy
otaviojava Jan 4, 2024
ad8266b
feat: create implementation to column repository
otaviojava Jan 5, 2024
5a03c65
test: create column repository
otaviojava Jan 5, 2024
8cb598b
style: remove extra lines at Abstractgraph repository
otaviojava Jan 5, 2024
eaeae5e
refactor: update graph query method to
otaviojava Jan 5, 2024
d45309b
test: create test to Abstractrepository
otaviojava Jan 5, 2024
7e7423c
feat: create implementation to RepositoryReflectionUtils
otaviojava Jan 5, 2024
890be05
test: create scenario to By annotation
otaviojava Jan 5, 2024
74375b2
feat: create column repository
otaviojava Jan 5, 2024
83e6002
feat: refactoring the abstractdocuent repository using reflection util
otaviojava Jan 5, 2024
ddce2c4
feat: add method name by convention
otaviojava Jan 5, 2024
12e40e7
feat: update select query converter to validate graph by name
otaviojava Jan 5, 2024
0bfdd59
feat: add query condition at graph repository
otaviojava Jan 5, 2024
136a36f
feat: update at abstracatgraph repository var
otaviojava Jan 5, 2024
d098157
test: create graph repository proxy
otaviojava Jan 5, 2024
cf68075
docs: create match parameter documetantation at changelog
otaviojava Jan 5, 2024
a1e97f9
test: create scenario to abstractrepository
otaviojava Jan 5, 2024
7517868
style: optmizes imports at DocumentParameterBasedQuery
otaviojava Jan 5, 2024
5dd5c64
style: organize code style
otaviojava Jan 5, 2024
7e87776
style: fix style at based query classes
otaviojava Jan 5, 2024
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
1 change: 1 addition & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ and this project adheres to https://semver.org/spec/v2.0.0.html[Semantic Version
=== Added

- Add support to operations annotations (Insert, Update, Delete and Save) from Jakarta Data
- Add support to match parameters

== [1.0.4] - 2023-12-19

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@
import org.eclipse.jnosql.communication.column.ColumnDeleteQuery;
import org.eclipse.jnosql.communication.column.ColumnQuery;
import org.eclipse.jnosql.mapping.core.repository.DynamicQueryMethodReturn;
import org.eclipse.jnosql.mapping.core.repository.RepositoryReflectionUtils;

import java.lang.reflect.Method;
import java.util.Map;

/**
* Template method to Repository proxy on column
Expand Down Expand Up @@ -50,8 +52,8 @@ protected Object executeDeleteByAll(Object instance, Method method, Object[] par
@Override
protected Object executeFindAll(Object instance, Method method, Object[] params) {
Class<?> type = entityMetadata().type();
ColumnQuery queryFindAll = ColumnQuery.select().from(entityMetadata().name()).build();
return executeFindByQuery(method, params, type, updateQueryDynamically(params, queryFindAll));
var query = ColumnQuery.select().from(entityMetadata().name()).build();
return executeFindByQuery(method, params, type, updateQueryDynamically(params, query));
}

@Override
Expand All @@ -70,4 +72,12 @@ protected Object executeFindByQuery(Object instance, Method method, Object[] par
return executeFindByQuery(method, params, type, query(method, params));
}

@Override
protected Object executeParameterBased(Object instance, Method method, Object[] params) {
Class<?> type = entityMetadata().type();
Map<String, Object> parameters = RepositoryReflectionUtils.INSTANCE.getBy(method, params);
var query = ColumnParameterBasedQuery.INSTANCE.toQuery(parameters, entityMetadata());
return executeFindByQuery(method, params, type, updateQueryDynamically(params, query));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* Copyright (c) 2023 Contributors to the Eclipse Foundation
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
* and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php.
*
* You may elect to redistribute this code under either of these licenses.
*
* Contributors:
*
* Otavio Santana
*/
package org.eclipse.jnosql.mapping.column.query;

import jakarta.enterprise.inject.spi.CDI;
import org.eclipse.jnosql.communication.column.ColumnCondition;
import org.eclipse.jnosql.communication.column.ColumnQuery;
import org.eclipse.jnosql.mapping.column.MappingColumnQuery;
import org.eclipse.jnosql.mapping.core.Converters;
import org.eclipse.jnosql.mapping.metadata.EntityMetadata;
import org.eclipse.jnosql.mapping.metadata.FieldMetadata;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.IntFunction;

import static org.eclipse.jnosql.mapping.core.util.ConverterUtil.getValue;

/**
* The ColumnParameterBasedQuery class is responsible for generating Column queries based on a set of parameters.
* It leverages the provided parameters, pageable information, and entity metadata to construct a ColumnQuery object
* tailored for querying a specific entity's columns.
*/
public enum ColumnParameterBasedQuery {


INSTANCE;
private static final IntFunction<ColumnCondition[]> TO_ARRAY = ColumnCondition[]::new;

/**
* Constructs a ColumnQuery based on the provided parameters, pageable information, and entity metadata.
*
* @param params The map of parameters used for filtering columns.
* @param entityMetadata Metadata describing the structure of the entity.
* @return A ColumnQuery instance tailored for the specified entity.
*/
public ColumnQuery toQuery(Map<String, Object> params, EntityMetadata entityMetadata) {
var convert = CDI.current().select(Converters.class).get();
List<ColumnCondition> conditions = new ArrayList<>();
for (Map.Entry<String, Object> entry : params.entrySet()) {
conditions.add(getCondition(convert, entityMetadata, entry));
}

var columnCondition = columnCondition(conditions);
var columnFamily = entityMetadata.name();
return new MappingColumnQuery(Collections.emptyList(), 0L, 0L, columnCondition, columnFamily);
}

private ColumnCondition columnCondition(List<ColumnCondition> conditions) {
if (conditions.isEmpty()) {
return null;
} else if (conditions.size() == 1) {
return conditions.get(0);
}
return ColumnCondition.and(conditions.toArray(TO_ARRAY));
}

private ColumnCondition getCondition(Converters convert, EntityMetadata entityMetadata, Map.Entry<String, Object> entry) {
var name = entityMetadata.fieldMapping(entry.getKey())
.map(FieldMetadata::name)
.orElse(entry.getKey());
var value = getValue(entry.getValue(), entityMetadata, entry.getKey(), convert);
return ColumnCondition.eq(name, value);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*
* Copyright (c) 2023 Contributors to the Eclipse Foundation
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
* and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php.
*
* You may elect to redistribute this code under either of these licenses.
*
* Contributors:
*
* Otavio Santana
*/
package org.eclipse.jnosql.mapping.column.query;

import jakarta.data.Sort;
import jakarta.data.page.Pageable;
import jakarta.inject.Inject;
import org.assertj.core.api.SoftAssertions;
import org.eclipse.jnosql.communication.Condition;
import org.eclipse.jnosql.communication.TypeReference;
import org.eclipse.jnosql.communication.column.Column;
import org.eclipse.jnosql.communication.column.ColumnCondition;
import org.eclipse.jnosql.communication.column.ColumnQuery;
import org.eclipse.jnosql.mapping.column.ColumnEntityConverter;
import org.eclipse.jnosql.mapping.column.MockProducer;
import org.eclipse.jnosql.mapping.column.entities.Person;
import org.eclipse.jnosql.mapping.column.spi.ColumnExtension;
import org.eclipse.jnosql.mapping.core.Converters;
import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension;
import org.eclipse.jnosql.mapping.metadata.EntitiesMetadata;
import org.eclipse.jnosql.mapping.metadata.EntityMetadata;
import org.eclipse.jnosql.mapping.reflection.Reflections;
import org.jboss.weld.junit5.auto.AddExtensions;
import org.jboss.weld.junit5.auto.AddPackages;
import org.jboss.weld.junit5.auto.EnableAutoWeld;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.util.Collections;
import java.util.List;
import java.util.Map;


@EnableAutoWeld
@AddPackages(value = {Converters.class, ColumnEntityConverter.class})
@AddPackages(MockProducer.class)
@AddPackages(Reflections.class)
@AddExtensions({EntityMetadataExtension.class, ColumnExtension.class})
class ColumnParameterBasedQueryTest {

@Inject
private EntitiesMetadata entitiesMetadata;

private EntityMetadata metadata;
@BeforeEach
void setUp(){
this.metadata = entitiesMetadata.get(Person.class);
}

@Test
void shouldCreateQuerySingleParameter(){
Map<String, Object> params = Map.of("name", "Ada");
ColumnQuery query = ColumnParameterBasedQuery.INSTANCE.toQuery(params, metadata);

SoftAssertions.assertSoftly(soft ->{
soft.assertThat(query.limit()).isEqualTo(0L);
soft.assertThat(query.skip()).isEqualTo(0L);
soft.assertThat(query.name()).isEqualTo("Person");
soft.assertThat(query.sorts()).isEmpty();
soft.assertThat(query.condition()).isNotEmpty();
soft.assertThat(query.condition()).get().isEqualTo(ColumnCondition.eq(Column.of("name", "Ada")));
});
}

@Test
void shouldCreateQueryMultipleParams(){
Map<String, Object> params = Map.of("name", "Ada", "age", 10);
ColumnQuery query = ColumnParameterBasedQuery.INSTANCE.toQuery(params, metadata);

SoftAssertions.assertSoftly(soft ->{
soft.assertThat(query.limit()).isEqualTo(0L);
soft.assertThat(query.skip()).isEqualTo(0L);
soft.assertThat(query.name()).isEqualTo("Person");
soft.assertThat(query.sorts()).isEmpty();
soft.assertThat(query.condition()).isNotEmpty();
var condition = query.condition().orElseThrow();
soft.assertThat(condition.condition()).isEqualTo(Condition.AND);
soft.assertThat(condition.column().get(new TypeReference<List<ColumnCondition>>() {
})).contains(ColumnCondition.eq(Column.of("name", "Ada")),
ColumnCondition.eq(Column.of("age", 10)));
});

}

@Test
void shouldCreateQueryEmptyParams(){
Map<String, Object> params = Collections.emptyMap();
ColumnQuery query = ColumnParameterBasedQuery.INSTANCE.toQuery(params, metadata);

SoftAssertions.assertSoftly(soft ->{
soft.assertThat(query.limit()).isEqualTo(0L);
soft.assertThat(query.skip()).isEqualTo(0L);
soft.assertThat(query.name()).isEqualTo("Person");
soft.assertThat(query.sorts()).isEmpty();
soft.assertThat(query.condition()).isEmpty();
});
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package org.eclipse.jnosql.mapping.column.query;

import jakarta.data.exceptions.MappingException;
import jakarta.data.repository.By;
import jakarta.data.repository.Delete;
import jakarta.data.repository.Insert;
import jakarta.data.repository.OrderBy;
Expand Down Expand Up @@ -848,6 +849,20 @@ void shouldSaveUsingAnnotation(){
Mockito.verify(template).insert(person);
}

@Test
void shouldExecuteMatchParameter(){
personRepository.find("Ada");
ArgumentCaptor<ColumnQuery> captor = ArgumentCaptor.forClass(ColumnQuery.class);
verify(template).select(captor.capture());
ColumnQuery query = captor.getValue();
SoftAssertions.assertSoftly(softly -> {
softly.assertThat(query.name()).isEqualTo("Person");
var condition = query.condition().orElseThrow();
softly.assertThat(condition.condition()).isEqualTo(Condition.EQUALS);
softly.assertThat(condition.column()).isEqualTo(Column.of("name", "Ada"));
});
}

public interface BaseQuery<T> {

List<T> findByNameLessThan(String name);
Expand Down Expand Up @@ -941,6 +956,8 @@ default Map<Boolean, List<Person>> partcionate(String name) {
@OrderBy("name")
@OrderBy("age")
List<Person> findByException();

List<Person> find(@By("name") String name);
}

public interface VendorRepository extends PageableRepository<Vendor, String> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,17 @@ public abstract class AbstractRepositoryProxy<T, K> implements InvocationHandler
*/
protected abstract Object executeFindByQuery(Object instance, Method method, Object[] params);

/**
* Executes a custom operation based on the method and parameters, allowing for parameter-based queries.
* This method is meant to handle repository methods that involve custom parameter-based logic.
*
* @param instance The instance on which the method was invoked.
* @param method The method being invoked, representing the custom parameter-based operation.
* @param params The parameters of the method, providing input for the parameter-based operation.
* @return The result of the custom parameter-based operation.
*/
protected abstract Object executeParameterBased(Object instance, Method method, Object[] params);

@Override
public Object invoke(Object instance, Method method, Object[] params) throws Throwable {

Expand All @@ -128,30 +139,33 @@ public Object invoke(Object instance, Method method, Object[] params) throws Thr
return unwrapInvocationTargetException(() -> method.invoke(repository(), params));
}
case FIND_BY -> {
return executeFindByQuery(instance, method, params);
return unwrapInvocationTargetException(() -> executeFindByQuery(instance, method, params));
}
case COUNT_BY -> {
return executeCountByQuery(instance, method, params);
return unwrapInvocationTargetException(() -> executeCountByQuery(instance, method, params));
}
case EXISTS_BY -> {
return executeExistByQuery(instance, method, params);
return unwrapInvocationTargetException(() -> executeExistByQuery(instance, method, params));
}
case FIND_ALL -> {
return executeFindAll(instance, method, params);
return unwrapInvocationTargetException(() -> executeFindAll(instance, method, params));
}
case DELETE_BY -> {
return executeDeleteByAll(instance, method, params);
return unwrapInvocationTargetException(() -> executeDeleteByAll(instance, method, params));
}
case OBJECT_METHOD -> {
return unwrapInvocationTargetException(() -> method.invoke(this, params));
return unwrapInvocationTargetException(() -> unwrapInvocationTargetException(() -> method.invoke(this, params)));
}
case DEFAULT_METHOD -> {
return unwrapInvocationTargetException(() -> InvocationHandler.invokeDefault(instance, method, params));
}
case ORDER_BY ->
throw new MappingException("Eclipse JNoSQL has not support for method that has OrderBy annotation");
case QUERY -> {
return executeQuery(instance, method, params);
return unwrapInvocationTargetException(() -> executeQuery(instance, method, params));
}
case PARAMETER_BASED -> {
return unwrapInvocationTargetException(() -> executeParameterBased(instance, method, params));
}
case CUSTOM_REPOSITORY -> {
Object customRepository = CDI.current().select(method.getDeclaringClass()).get();
Expand Down
Loading