Skip to content

Commit

Permalink
Merge pull request #5 from TeamWanari/1.0.4-improvements
Browse files Browse the repository at this point in the history
1.0.4 improvements
  • Loading branch information
aBnormaLz authored Jun 1, 2017
2 parents b8147c4 + 5b5811e commit 3349da0
Show file tree
Hide file tree
Showing 10 changed files with 244 additions and 72 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,11 @@ There are constant postfixes defined in CouchbaseQueryExecutor, helping you make
|TO_FILTER|Gives back documents that has lower value in the field|`filters.put("estimatedTime" + CouchbaseQueryExecutor.TO_FILTER, 20)`|
|NOT_FILTER|Compares the field and the given value. Gives back the document if the field doesn't equal to the value|`filters.put("title" + CouchbaseQueryExecutor.NOT_FILTER, "i don't need this")`|
|IN_FILTER|Compares the field and the given value. Gives back the document any of the values match the field|`filters.put("age" + CouchbaseQueryExecutor.IN_FILTER, JsonArray.from(17, 18))`|
|NULL|Gives back document if it has the field, but the value is null|`filters.put("fieldName" + CouchbaseQueryExecutor.NULL_FILTER, null);`|
|NOT_NULL|Gives back document if it has the field, but the value is not null|`filters.put("fieldName" + CouchbaseQueryExecutor.NOT_NULL_FILTER, null);`|
|MISSING|Gives back document if it doesn't have the field|`filters.put("fieldName" + CouchbaseQueryExecutor.MISSING_FILTER, null);`|
|NULL_OR_MISSING|Gives back document if it doesn't have the field, or if it has but value is null|`filters.put("fieldName" + CouchbaseQueryExecutor.NULL_OR_MISSING_FILTER, null);`|


Also if you add the `CouchbaseQueryExecutor.IGNORE_CASE_ORDER` postfix to a sort param in the Pageable object, it will sort the documents ignoring case. Actually in SpringData, you have to provide this postfix in the query string, and the executor will check the ending of the parameter. Therefore:

Expand Down
39 changes: 0 additions & 39 deletions src/main/java/com/wanari/utils/couchbase/CouchbasePage.java

This file was deleted.

139 changes: 108 additions & 31 deletions src/main/java/com/wanari/utils/couchbase/CouchbaseQueryExecutor.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,16 @@
import com.couchbase.client.java.query.dsl.Sort;
import com.couchbase.client.java.query.dsl.path.FromPath;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wanari.utils.couchbase.converters.*;
import com.wanari.utils.couchbase.exceptions.NonUniqueResultException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.couchbase.core.CouchbaseTemplate;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Component;

import javax.inject.Inject;
import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
Expand All @@ -24,20 +28,71 @@
import static com.couchbase.client.java.query.dsl.Expression.x;

@Component
public class CouchbaseQueryExecutor {
public class CouchbaseQueryExecutor<T> {

@Value("${couchbase-query-executor.with-sync-gateway:false}")
private Boolean withSyncGateway;

@Value("${couchbase-query-executor.use-default-id-fields:true}")
private Boolean useDefaultIdFields;

public static final String CONTAINS_FILTER = "_contains";
public static final String FROM_FILTER = "_from";
public static final String TO_FILTER = "_to";
public static final String NOT_FILTER = "_not";
public static final String IN_FILTER = "_in";
public static final String NULL_FILTER = "_null";
public static final String NOT_NULL_FILTER = "_notnull";
public static final String MISSING_FILTER = "_missing";
public static final String NULL_OR_MISSING_FILTER = "_nullormissing";
private static final String IGNORE_CASE_ORDER = "_ignorecase";
private DataConverter<T> converter;
private final ObjectMapper objectMapper;

@Inject
private CouchbaseQueryExecutorConfiguration couchbaseConfiguration;
private final CouchbaseQueryExecutorConfiguration couchbaseConfiguration;

public CouchbaseQueryExecutor(CouchbaseQueryExecutorConfiguration couchbaseConfiguration, ObjectMapper objectMapper) {
this.couchbaseConfiguration = couchbaseConfiguration;
this.objectMapper = objectMapper;
}

@Inject
private ObjectMapper objectMapper;
@PostConstruct
private void init() {
if(withSyncGateway) {
if(useDefaultIdFields) {
converter = new SyncGatewayDataConverter<>(objectMapper);
} else {
converter = new SyncGatewayDataConverterWithReflection<>(objectMapper);
}
} else {
if(useDefaultIdFields) {
converter = new CouchbaseDataConverter<>(objectMapper);
} else {
converter = new CouchbaseDataConverterWithReflection<>(objectMapper);
}
}
}

public DataConverter<T> getConverter() {
return converter;
}

public void setConverter(DataConverter<T> converter) {
this.converter = converter;
}

private String getPropertyKey(String key) {
return key.replaceAll("[\\\\.]", "_");
}

private JsonObject paramaterizeParams(JsonObject params) {
return JsonObject.from(
params.toMap().entrySet().stream().collect(Collectors.toMap(
entry -> getPropertyKey(entry.getKey()),
entry -> params.get(entry.getKey())
))
);
}

private CouchbaseTemplate createTemplate() {
try {
Expand All @@ -47,50 +102,43 @@ private CouchbaseTemplate createTemplate() {
}
}

public <T> Optional<T> findOne(JsonObject params, Class<T> clazz) {
public Optional<T> findOne(JsonObject params, Class<T> clazz) {
List<T> documents = find(params, clazz);
return asOptional(documents, params);
}

public <T> CouchbasePage<T> find(JsonObject params, Pageable pageable, Class<T> clazz) {
CouchbasePage<T> page = new CouchbasePage<>(pageable);
public Page<T> find(JsonObject params, Pageable pageable, Class<T> clazz) {
CouchbaseTemplate template = createTemplate();

Statement query = createQueryStatement(params, pageable);
N1qlQuery queryWithParameter = N1qlQuery.parameterized(query, params);
N1qlQuery queryWithParameter = N1qlQuery.parameterized(query, paramaterizeParams(params));

page.data = convertToDataList(template.findByN1QLProjection(queryWithParameter, LinkedHashMap.class), clazz);
page.totalElements = count(params);
page.calculateTotalPages();
List<T> data = convertToDataList(template.findByN1QLProjection(queryWithParameter, LinkedHashMap.class), clazz);
Integer count = count(params);

return page;
return new PageImpl<>(data, pageable, count);
}

public <T> List<T> find(JsonObject params, Class<T> clazz) {
public List<T> find(JsonObject params, Class<T> clazz) {
CouchbaseTemplate template = createTemplate();

Statement query = createQueryStatement(params);
N1qlQuery queryWithParameter = N1qlQuery.parameterized(query, params);
N1qlQuery queryWithParameter = N1qlQuery.parameterized(query, paramaterizeParams(params));

return convertToDataList(template.findByN1QLProjection(queryWithParameter, LinkedHashMap.class), clazz);
}

private <T> List<T> convertToDataList(List<LinkedHashMap> queriedList, Class<T> clazz) {
private List<T> convertToDataList(List<LinkedHashMap> queriedList, Class<T> clazz) {
return queriedList.stream()
.map(hashMap -> {
LinkedHashMap data = (LinkedHashMap) hashMap.get("data");
data.put("_id", hashMap.get("id"));
data.put("_rev", ((LinkedHashMap) data.get("_sync")).get("rev"));
return objectMapper.convertValue(data, clazz);
})
.map(hashMap -> converter.apply(hashMap, clazz))
.collect(Collectors.toList());
}

public Integer count(JsonObject params) {
CouchbaseTemplate template = createTemplate();

Statement query = createCountStatement(params);
N1qlQuery queryWithParams = N1qlQuery.parameterized(query, params);
N1qlQuery queryWithParams = N1qlQuery.parameterized(query, paramaterizeParams(params));
LinkedHashMap countMap = ((LinkedHashMap) template.findByN1QLProjection(queryWithParams, Object.class).get(0));

return ((Integer) countMap.get("count"));
Expand All @@ -101,7 +149,7 @@ public Integer sum(JsonObject params, String field) {
CouchbaseTemplate template = createTemplate();

Statement query = createSumStatement(params, field);
N1qlQuery queryWithParams = N1qlQuery.parameterized(query, params);
N1qlQuery queryWithParams = N1qlQuery.parameterized(query, paramaterizeParams(params));
LinkedHashMap sumMap = ((LinkedHashMap) template.findByN1QLProjection(queryWithParams, Object.class).get(0));

return ((Integer) sumMap.get("sum"));
Expand Down Expand Up @@ -172,22 +220,35 @@ private Expression composeWhere(Expression bucketName, JsonObject params) {

private Expression createExpression(String key) {
String propertyKey = key;
key = getPropertyKey(key);

if(key.endsWith(CONTAINS_FILTER)) {
propertyKey = key.substring(0, key.length() - CONTAINS_FILTER.length());
propertyKey = propertyKey.substring(0, propertyKey.length() - CONTAINS_FILTER.length());
return createContainsExpression(propertyKey, key);
} else if(key.endsWith(FROM_FILTER)) {
propertyKey = key.substring(0, key.length() - FROM_FILTER.length());
propertyKey = propertyKey.substring(0, propertyKey.length() - FROM_FILTER.length());
return createGreaterThanOrEqualsExpression(propertyKey, key);
} else if(key.endsWith(TO_FILTER)) {
propertyKey = key.substring(0, key.length() - TO_FILTER.length());
propertyKey = propertyKey.substring(0, propertyKey.length() - TO_FILTER.length());
return createLessThanOrEqualsExpression(propertyKey, key);
} else if(key.endsWith(NOT_FILTER)) {
propertyKey = key.substring(0, key.length() - NOT_FILTER.length());
propertyKey = propertyKey.substring(0, propertyKey.length() - NOT_FILTER.length());
return createNotEqualsExpression(propertyKey, key);
} else if(key.endsWith(IN_FILTER)) {
propertyKey = key.substring(0, key.length() - IN_FILTER.length());
propertyKey = propertyKey.substring(0, propertyKey.length() - IN_FILTER.length());
return createInExpression(propertyKey, key);
} else if(key.endsWith(NULL_FILTER)) {
propertyKey = propertyKey.substring(0, propertyKey.length() - NULL_FILTER.length());
return createNullExpression(propertyKey, key);
} else if(key.endsWith(NOT_NULL_FILTER)) {
propertyKey = propertyKey.substring(0, propertyKey.length() - NOT_NULL_FILTER.length());
return createNotNullExpression(propertyKey, key);
} else if(key.endsWith(MISSING_FILTER)) {
propertyKey = propertyKey.substring(0, propertyKey.length() - MISSING_FILTER.length());
return createMissingExpression(propertyKey, key);
} else if(key.endsWith(NULL_OR_MISSING_FILTER)) {
propertyKey = propertyKey.substring(0, propertyKey.length() - NULL_OR_MISSING_FILTER.length());
return createNullOrMissingExpression(propertyKey, key);
} else {
return createEqualsExpression(propertyKey, key);
}
Expand Down Expand Up @@ -217,6 +278,22 @@ private Expression createContainsExpression(String propertyKey, String key) {
return x("CONTAINS(LOWER(" + propertyKey + "), LOWER($" + key + "))");
}

private Expression createNullExpression(String propertyKey, String key) {
return x(propertyKey + " IS NULL");
}

private Expression createNotNullExpression(String propertyKey, String key) {
return x(propertyKey + " IS NOT NULL");
}

private Expression createMissingExpression(String propertyKey, String key) {
return x(propertyKey + " IS MISSING");
}

private Expression createNullOrMissingExpression(String propertyKey, String key) {
return x(propertyKey + " IS NULL OR " + propertyKey + " IS MISSING");
}

private String lowerCase(String input) {
return "LOWER(" + input + ")";
}
Expand Down Expand Up @@ -246,7 +323,7 @@ private Sort[] fromPageable(Pageable pageable) {
return orderBy.toArray(new Sort[orderBy.size()]);
}

private <T> Optional<T> asOptional(List<T> documents, JsonObject params) {
private Optional<T> asOptional(List<T> documents, JsonObject params) {
if(documents.isEmpty()) {
return Optional.empty();
}
Expand Down
5 changes: 3 additions & 2 deletions src/main/java/com/wanari/utils/couchbase/SpringContext.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package com.wanari.utils.couchbase;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class SpringContext {

@Bean
public CouchbaseQueryExecutor couchbaseQueryExecutor() {
return new CouchbaseQueryExecutor();
public CouchbaseQueryExecutor couchbaseQueryExecutor(CouchbaseQueryExecutorConfiguration couchbaseConfiguration, ObjectMapper objectMapper) {
return new CouchbaseQueryExecutor(couchbaseConfiguration, objectMapper);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.wanari.utils.couchbase.converters;

import com.fasterxml.jackson.databind.ObjectMapper;

import java.util.HashMap;
import java.util.LinkedHashMap;

public class CouchbaseDataConverter<T> implements DataConverter<T> {
private final ObjectMapper objectMapper;

public CouchbaseDataConverter(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}

@Override
public T apply(HashMap hashMap, Class<T> clazz) {
LinkedHashMap data = (LinkedHashMap) hashMap.get("data");
data.put("id", hashMap.get("id"));
return objectMapper.convertValue(data, clazz);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.wanari.utils.couchbase.converters;

import com.fasterxml.jackson.databind.ObjectMapper;

import java.util.HashMap;
import java.util.LinkedHashMap;

public class CouchbaseDataConverterWithReflection<T> implements DataConverterWithReflection<T> {

private final ObjectMapper objectMapper;

public CouchbaseDataConverterWithReflection(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}

@Override
public T apply(HashMap hashMap, Class<T> clazz) {
LinkedHashMap data = (LinkedHashMap) hashMap.get("data");

T obj = objectMapper.convertValue(data, clazz);
setId(obj, hashMap, clazz);

return obj;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.wanari.utils.couchbase.converters;

import java.util.HashMap;
import java.util.function.BiFunction;

@FunctionalInterface
public interface DataConverter<R> extends BiFunction<HashMap, Class<R>, R> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.wanari.utils.couchbase.converters;

import com.couchbase.client.java.repository.annotation.Id;

import java.util.Arrays;
import java.util.HashMap;

public interface DataConverterWithReflection<T> extends DataConverter<T> {
default void setId(T obj, HashMap hashMap, Class<T> clazz) {
Arrays.stream(clazz.getDeclaredFields()).forEach(field -> {
if(field.getAnnotation(Id.class) != null) {
try {
field.setAccessible(true);
field.set(obj, hashMap.get("id"));
} catch(IllegalAccessException e) {
throw new RuntimeException(e);
}
}
});
}
}
Loading

0 comments on commit 3349da0

Please sign in to comment.