Expiration annotation on field will retrieve expiration in N1ql queries. (#1188)
Closes #1060.
This commit is contained in:
@@ -45,8 +45,9 @@ import org.springframework.data.repository.query.parser.PartTree;
|
||||
*/
|
||||
public class N1qlQueryCreator extends AbstractQueryCreator<Query, QueryCriteria> {
|
||||
|
||||
private static final String META_ID_PROPERTY = "id";
|
||||
private static final String META_CAS_PROPERTY = "cas";
|
||||
public static final String META_ID_PROPERTY = "id";
|
||||
public static final String META_CAS_PROPERTY = "cas";
|
||||
public static final String META_EXPIRATION_PROPERTY = "expiration";
|
||||
|
||||
private final ParameterAccessor accessor;
|
||||
private final MappingContext<?, CouchbasePersistentProperty> context;
|
||||
@@ -68,7 +69,7 @@ public class N1qlQueryCreator extends AbstractQueryCreator<Query, QueryCriteria>
|
||||
protected QueryCriteria create(final Part part, final Iterator<Object> iterator) {
|
||||
PersistentPropertyPath<CouchbasePersistentProperty> path = context.getPersistentPropertyPath(part.getProperty());
|
||||
CouchbasePersistentProperty property = path.getLeafProperty();
|
||||
return from(part, property, where(addMetaIfRequired(path, property)), iterator);
|
||||
return from(part, property, where(addMetaIfRequired(bucketName, path, property)), iterator);
|
||||
}
|
||||
|
||||
static Converter<? super CouchbasePersistentProperty, String> cvtr = new MyConverter();
|
||||
@@ -76,16 +77,7 @@ public class N1qlQueryCreator extends AbstractQueryCreator<Query, QueryCriteria>
|
||||
static class MyConverter implements Converter<CouchbasePersistentProperty, String> {
|
||||
@Override
|
||||
public String convert(CouchbasePersistentProperty source) {
|
||||
if (source.isIdProperty()) {
|
||||
return "META().id";
|
||||
} else if (source.isVersionProperty()) {
|
||||
return "META().cas";
|
||||
} else if (source.isExpirationProperty()) {
|
||||
return "META().expiration";
|
||||
} else {
|
||||
return new StringBuilder(source.getFieldName().length() + 2).append('`').append(source.getFieldName())
|
||||
.append('`').toString();
|
||||
}
|
||||
return new StringBuilder(source.getFieldName().length()+2).append("`").append(source.getFieldName()).append("`").toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,7 +90,7 @@ public class N1qlQueryCreator extends AbstractQueryCreator<Query, QueryCriteria>
|
||||
PersistentPropertyPath<CouchbasePersistentProperty> path = context.getPersistentPropertyPath(part.getProperty());
|
||||
CouchbasePersistentProperty property = path.getLeafProperty();
|
||||
|
||||
return from(part, property, base.and(addMetaIfRequired(path, property)), iterator);
|
||||
return from(part, property, base.and(addMetaIfRequired(bucketName,path, property)), iterator);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -174,7 +166,8 @@ public class N1qlQueryCreator extends AbstractQueryCreator<Query, QueryCriteria>
|
||||
}
|
||||
}
|
||||
|
||||
private N1QLExpression addMetaIfRequired(
|
||||
public static N1QLExpression addMetaIfRequired(
|
||||
String bucketName,
|
||||
final PersistentPropertyPath<CouchbasePersistentProperty> persistentPropertyPath,
|
||||
final CouchbasePersistentProperty property) {
|
||||
if (property.isIdProperty()) {
|
||||
@@ -183,6 +176,9 @@ public class N1qlQueryCreator extends AbstractQueryCreator<Query, QueryCriteria>
|
||||
if (property.isVersionProperty()) {
|
||||
return path(meta(i(bucketName)), i(META_CAS_PROPERTY));
|
||||
}
|
||||
if (property.isExpirationProperty()) {
|
||||
return path(meta(i(bucketName)), i(META_EXPIRATION_PROPERTY));
|
||||
}
|
||||
return x(persistentPropertyPath.toDotPath(cvtr));
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
package org.springframework.data.couchbase.repository.query;
|
||||
|
||||
import static org.springframework.data.couchbase.core.query.N1QLExpression.i;
|
||||
import static org.springframework.data.couchbase.core.query.N1QLExpression.meta;
|
||||
import static org.springframework.data.couchbase.core.query.N1QLExpression.path;
|
||||
import static org.springframework.data.couchbase.core.query.N1QLExpression.x;
|
||||
import static org.springframework.data.couchbase.core.support.TemplateUtils.SELECT_CAS;
|
||||
import static org.springframework.data.couchbase.core.support.TemplateUtils.SELECT_ID;
|
||||
@@ -29,6 +31,7 @@ import java.util.regex.Pattern;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
|
||||
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProperty;
|
||||
import org.springframework.data.couchbase.core.query.N1QLExpression;
|
||||
@@ -37,6 +40,7 @@ import org.springframework.data.couchbase.repository.query.support.N1qlUtils;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.mapping.PersistentPropertyPath;
|
||||
import org.springframework.data.mapping.PropertyHandler;
|
||||
import org.springframework.data.repository.query.Parameter;
|
||||
import org.springframework.data.repository.query.ParameterAccessor;
|
||||
@@ -183,22 +187,28 @@ public class StringBasedN1qlQueryParser {
|
||||
if (resultClass != null) {
|
||||
PersistentEntity persistentEntity = couchbaseConverter.getMappingContext().getPersistentEntity(resultClass);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
getProjectedFieldsInternal(null, sb, persistentEntity.getTypeInformation(), "");
|
||||
getProjectedFieldsInternal(b, null, sb, persistentEntity.getTypeInformation()/*, ""*/);
|
||||
projectedFields = sb.toString();
|
||||
}
|
||||
return projectedFields;
|
||||
}
|
||||
|
||||
private void getProjectedFieldsInternal(CouchbasePersistentProperty parent, StringBuilder sb,
|
||||
TypeInformation resultClass, String path) {
|
||||
private void getProjectedFieldsInternal(String bucketName, CouchbasePersistentProperty parent, StringBuilder sb,
|
||||
TypeInformation resultClass/*, String path*/) {
|
||||
|
||||
PersistentEntity persistentEntity = couchbaseConverter.getMappingContext().getPersistentEntity(resultClass);
|
||||
//CouchbasePersistentProperty property = path.getLeafProperty();
|
||||
persistentEntity.doWithProperties(new PropertyHandler<CouchbasePersistentProperty>() {
|
||||
@Override
|
||||
public void doWithPersistentProperty(final CouchbasePersistentProperty prop) {
|
||||
if (prop.isIdProperty() && parent == null) {
|
||||
return;
|
||||
}
|
||||
if (prop.isVersionProperty()) {
|
||||
return;
|
||||
}
|
||||
PersistentPropertyPath<CouchbasePersistentProperty> path = couchbaseConverter.getMappingContext().getPersistentPropertyPath(prop.getName(), resultClass.getType());
|
||||
|
||||
// The current limitation is that only top-level properties can be projected
|
||||
// This traversing of nested data structures would need to replicate the processing done by
|
||||
// MappingCouchbaseConverter. Either the read or write
|
||||
@@ -210,12 +220,7 @@ public class StringBasedN1qlQueryParser {
|
||||
if (sb.length() > 0) {
|
||||
sb.append(", ");
|
||||
}
|
||||
sb.append('`');
|
||||
if (path != null && path.length() != 0) {
|
||||
sb.append(path);
|
||||
}
|
||||
sb.append(prop.getName());
|
||||
sb.append('`');
|
||||
sb.append(N1qlQueryCreator.addMetaIfRequired(bucketName, path, prop)); // from N1qlQueryCreator
|
||||
// }
|
||||
}
|
||||
});
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.springframework.data.annotation.PersistenceConstructor;
|
||||
import org.springframework.data.annotation.TypeAlias;
|
||||
import org.springframework.data.annotation.Version;
|
||||
import org.springframework.data.couchbase.core.mapping.Document;
|
||||
import org.springframework.data.couchbase.core.mapping.Expiration;
|
||||
|
||||
/**
|
||||
* Airport entity
|
||||
@@ -41,6 +42,7 @@ public class Airport extends ComparableEntity {
|
||||
@Version Number version;
|
||||
|
||||
@CreatedBy private String createdBy;
|
||||
@Expiration private long expiration;
|
||||
|
||||
@PersistenceConstructor
|
||||
public Airport(String id, String iata, String icao) {
|
||||
@@ -61,6 +63,10 @@ public class Airport extends ComparableEntity {
|
||||
return icao;
|
||||
}
|
||||
|
||||
public long getExpiration() {
|
||||
return expiration;
|
||||
}
|
||||
|
||||
public Airport withId(String id) {
|
||||
return new Airport(id, this.iata, this.icao);
|
||||
}
|
||||
|
||||
@@ -42,8 +42,7 @@ public class User extends ComparableEntity {
|
||||
@Id private String id;
|
||||
private String firstname;
|
||||
private String lastname;
|
||||
@Transient
|
||||
private String transientInfo;
|
||||
@Transient private String transientInfo;
|
||||
@CreatedBy private String createdBy;
|
||||
@CreatedDate private long createdDate;
|
||||
@LastModifiedBy private String lastModifiedBy;
|
||||
@@ -97,9 +96,10 @@ public class User extends ComparableEntity {
|
||||
return Objects.hash(id, firstname, lastname);
|
||||
}
|
||||
|
||||
public String getTransientInfo(){
|
||||
public String getTransientInfo() {
|
||||
return transientInfo;
|
||||
}
|
||||
|
||||
public void setTransientInfo(String something) {
|
||||
transientInfo = something;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import static java.util.Arrays.asList;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
@@ -38,6 +39,7 @@ import java.util.concurrent.Future;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.couchbase.client.java.kv.UpsertOptions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -376,6 +378,15 @@ public class CouchbaseRepositoryQueryIntegrationTests extends ClusterAwareIntegr
|
||||
userRepository.delete(user);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExpiration() {
|
||||
Airport airport = new Airport("1", "iata21", "icao21");
|
||||
airportRepository.withOptions(UpsertOptions.upsertOptions().expiry(Duration.ofSeconds(10))).save(airport);
|
||||
Airport foundAirport = airportRepository.findByIata(airport.getIata());
|
||||
assertNotEquals(0, foundAirport.getExpiration());
|
||||
airportRepository.delete(airport);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStreamQuery() {
|
||||
User user1 = new User("1", "Dave", "Wilson");
|
||||
|
||||
Reference in New Issue
Block a user