diff --git a/pom.xml b/pom.xml
index 821942c19..9f25d2a2e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -18,8 +18,8 @@
- 3.2.3
- 3.2.3
+ 3.2.4
+ 3.2.4
2.7.0-SNAPSHOT
spring.data.couchbase
diff --git a/src/main/java/org/springframework/data/couchbase/cache/CouchbaseCacheConfiguration.java b/src/main/java/org/springframework/data/couchbase/cache/CouchbaseCacheConfiguration.java
index 841815b41..031e8b445 100644
--- a/src/main/java/org/springframework/data/couchbase/cache/CouchbaseCacheConfiguration.java
+++ b/src/main/java/org/springframework/data/couchbase/cache/CouchbaseCacheConfiguration.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2020 the original author or authors
+ * Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -64,7 +64,7 @@ public static CouchbaseCacheConfiguration defaultCacheConfig() {
*
* {@link String} to {@link byte byte[]} using UTF-8 encoding.
* {@link SimpleKey} to {@link String}
- *
+ *
* @param registry must not be {@literal null}.
*/
public static void registerDefaultConverters(final ConverterRegistry registry) {
diff --git a/src/main/java/org/springframework/data/couchbase/cache/CouchbaseCacheManager.java b/src/main/java/org/springframework/data/couchbase/cache/CouchbaseCacheManager.java
index a96c8e161..3ec1f1ff7 100644
--- a/src/main/java/org/springframework/data/couchbase/cache/CouchbaseCacheManager.java
+++ b/src/main/java/org/springframework/data/couchbase/cache/CouchbaseCacheManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2020 the original author or authors
+ * Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -228,7 +228,7 @@ public CouchbaseCacheManagerBuilder withCacheConfiguration(String cacheName,
/**
* Disable in-flight {@link org.springframework.cache.Cache} creation for unconfigured caches.
- *
+ *
* {@link CouchbaseCacheManager#getMissingCache(String)} returns {@literal null} for any unconfigured
* {@link org.springframework.cache.Cache} instead of a new {@link CouchbaseCache} instance. This allows eg.
* {@link org.springframework.cache.support.CompositeCacheManager} to chime in.
diff --git a/src/main/java/org/springframework/data/couchbase/config/AbstractCouchbaseConfiguration.java b/src/main/java/org/springframework/data/couchbase/config/AbstractCouchbaseConfiguration.java
index 2c9ff9718..6982d64f4 100644
--- a/src/main/java/org/springframework/data/couchbase/config/AbstractCouchbaseConfiguration.java
+++ b/src/main/java/org/springframework/data/couchbase/config/AbstractCouchbaseConfiguration.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2021 the original author or authors
+ * Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -254,8 +254,6 @@ public String typeKey() {
/**
* Creates a {@link MappingCouchbaseConverter} using the configured {@link #couchbaseMappingContext}.
- *
- * @throws Exception on Bean construction failure.
*/
@Bean
public MappingCouchbaseConverter mappingCouchbaseConverter(CouchbaseMappingContext couchbaseMappingContext,
@@ -283,7 +281,6 @@ public TranslationService couchbaseTranslationService() {
/**
* Creates a {@link CouchbaseMappingContext} equipped with entity classes scanned from the mapping base package.
*
- * @throws Exception on Bean construction failure.
*/
@Bean
public CouchbaseMappingContext couchbaseMappingContext(CustomConversions customConversions) throws Exception {
@@ -299,7 +296,6 @@ public CouchbaseMappingContext couchbaseMappingContext(CustomConversions customC
/**
* Creates a {@link ObjectMapper} for the jsonSerializer of the ClusterEnvironment
*
- * @throws Exception on Bean construction failure.
* @return ObjectMapper
*/
@@ -337,11 +333,9 @@ public CustomConversions customConversions() {
/**
* Return the base package to scan for mapped {@link Document}s. Will return the package name of the configuration
* class (the concrete class, not this one here) by default.
- *
*
* So if you have a {@code com.acme.AppConfig} extending {@link AbstractCouchbaseConfiguration} the base package will
* be considered {@code com.acme} unless the method is overridden to implement alternate behavior.
- *
*
* @return the base package to scan for mapped {@link Document} classes or {@literal null} to not enable scanning for
* entities.
diff --git a/src/main/java/org/springframework/data/couchbase/core/CouchbaseExceptionTranslator.java b/src/main/java/org/springframework/data/couchbase/core/CouchbaseExceptionTranslator.java
index 3ae3cb21a..264c8334e 100644
--- a/src/main/java/org/springframework/data/couchbase/core/CouchbaseExceptionTranslator.java
+++ b/src/main/java/org/springframework/data/couchbase/core/CouchbaseExceptionTranslator.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2020 the original author or authors
+ * Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -33,7 +33,7 @@
/**
* Simple {@link PersistenceExceptionTranslator} for Couchbase.
- *
+ *
* Convert the given runtime exception to an appropriate exception from the {@code org.springframework.dao} hierarchy.
* Return {@literal null} if no translation is appropriate: any other exception may have resulted from user code, and
* should not be translated.
diff --git a/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplateSupport.java b/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplateSupport.java
index ffb70fc65..6754f0297 100644
--- a/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplateSupport.java
+++ b/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplateSupport.java
@@ -1,6 +1,5 @@
/*
-/*
- * Copyright 2012-2021 the original author or authors
+ * Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,6 +16,9 @@
package org.springframework.data.couchbase.core;
+import java.util.Map;
+import java.util.Set;
+
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
@@ -40,6 +42,7 @@
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.model.ConvertingPropertyAccessor;
import org.springframework.util.Assert;
+import org.springframework.util.ClassUtils;
/**
* Internal encode/decode support for CouchbaseTemplate.
@@ -84,7 +87,18 @@ public CouchbaseDocument encodeEntity(final Object entityToEncode) {
public T decodeEntity(String id, String source, long cas, Class entityClass) {
final CouchbaseDocument converted = new CouchbaseDocument(id);
converted.setId(id);
- CouchbasePersistentEntity> persistentEntity = mappingContext.getRequiredPersistentEntity(entityClass);
+ CouchbasePersistentEntity persistentEntity = couldBePersistentEntity(entityClass);
+
+ if (persistentEntity == null) { // method could return a Long, Boolean, String etc.
+ // QueryExecutionConverters.unwrapWrapperTypes will recursively unwrap until there is nothing left
+ // to unwrap. This results in List being unwrapped past String[] to String, so this may also be a
+ // Collection (or Array) of entityClass. We have no way of knowing - so just assume it is what we are told.
+ // if this is a Collection or array, only the first element will be returned.
+ Set> set = ((CouchbaseDocument) translationService.decode(source, converted))
+ .getContent().entrySet();
+ return (T) set.iterator().next().getValue();
+ }
+
if (cas != 0 && persistentEntity.getVersionProperty() != null) {
converted.put(persistentEntity.getVersionProperty().getName(), cas);
}
@@ -98,6 +112,12 @@ public T decodeEntity(String id, String source, long cas, Class entityCla
N1qlJoinResolver.handleProperties(persistentEntity, accessor, template.reactive(), id);
return accessor.getBean();
}
+ CouchbasePersistentEntity couldBePersistentEntity(Class> entityClass) {
+ if (ClassUtils.isPrimitiveOrWrapper(entityClass) || entityClass == String.class) {
+ return null;
+ }
+ return mappingContext.getPersistentEntity(entityClass);
+ }
@Override
public Object applyUpdatedCas(final Object entity, CouchbaseDocument converted, final long cas) {
diff --git a/src/main/java/org/springframework/data/couchbase/core/ReactiveCouchbaseTemplateSupport.java b/src/main/java/org/springframework/data/couchbase/core/ReactiveCouchbaseTemplateSupport.java
index d2f9d69a0..7805b2b94 100644
--- a/src/main/java/org/springframework/data/couchbase/core/ReactiveCouchbaseTemplateSupport.java
+++ b/src/main/java/org/springframework/data/couchbase/core/ReactiveCouchbaseTemplateSupport.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2021 the original author or authors
+ * Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,6 +18,9 @@
import reactor.core.publisher.Mono;
+import java.util.Map;
+import java.util.Set;
+
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
@@ -42,6 +45,7 @@
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.model.ConvertingPropertyAccessor;
import org.springframework.util.Assert;
+import org.springframework.util.ClassUtils;
/**
* Internal encode/decode support for {@link ReactiveCouchbaseTemplate}.
@@ -84,7 +88,19 @@ public Mono decodeEntity(String id, String source, long cas, Class ent
return Mono.fromSupplier(() -> {
final CouchbaseDocument converted = new CouchbaseDocument(id);
converted.setId(id);
- CouchbasePersistentEntity> persistentEntity = mappingContext.getRequiredPersistentEntity(entityClass);
+
+ CouchbasePersistentEntity persistentEntity = couldBePersistentEntity(entityClass);
+
+ if (persistentEntity == null) { // method could return a Long, Boolean, String etc.
+ // QueryExecutionConverters.unwrapWrapperTypes will recursively unwrap until there is nothing left
+ // to unwrap. This results in List being unwrapped past String[] to String, so this may also be a
+ // Collection (or Array) of entityClass. We have no way of knowing - so just assume it is what we are told.
+ // if this is a Collection or array, only the first element will be returned.
+ Set> set = ((CouchbaseDocument) translationService.decode(source, converted))
+ .getContent().entrySet();
+ return (T) set.iterator().next().getValue();
+ }
+
if (cas != 0 && persistentEntity.getVersionProperty() != null) {
converted.put(persistentEntity.getVersionProperty().getName(), cas);
}
@@ -100,6 +116,13 @@ public Mono decodeEntity(String id, String source, long cas, Class ent
});
}
+ CouchbasePersistentEntity couldBePersistentEntity(Class> entityClass) {
+ if (ClassUtils.isPrimitiveOrWrapper(entityClass) || entityClass == String.class) {
+ return null;
+ }
+ return mappingContext.getPersistentEntity(entityClass);
+ }
+
@Override
public Mono applyUpdatedCas(final Object entity, CouchbaseDocument converted, final long cas) {
return Mono.fromSupplier(() -> {
diff --git a/src/main/java/org/springframework/data/couchbase/core/convert/AbstractCouchbaseConverter.java b/src/main/java/org/springframework/data/couchbase/core/convert/AbstractCouchbaseConverter.java
index 0e70fb47c..e216779ff 100644
--- a/src/main/java/org/springframework/data/couchbase/core/convert/AbstractCouchbaseConverter.java
+++ b/src/main/java/org/springframework/data/couchbase/core/convert/AbstractCouchbaseConverter.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2021 the original author or authors
+ * Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,7 +22,7 @@
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.data.convert.CustomConversions;
-import org.springframework.data.convert.EntityInstantiators;
+import org.springframework.data.mapping.model.EntityInstantiators;
/**
* An abstract {@link CouchbaseConverter} that provides the basics for the {@link MappingCouchbaseConverter}.
diff --git a/src/main/java/org/springframework/data/couchbase/core/convert/CouchbaseCustomConversions.java b/src/main/java/org/springframework/data/couchbase/core/convert/CouchbaseCustomConversions.java
index bfffbe514..ba265d4e4 100644
--- a/src/main/java/org/springframework/data/couchbase/core/convert/CouchbaseCustomConversions.java
+++ b/src/main/java/org/springframework/data/couchbase/core/convert/CouchbaseCustomConversions.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2021 the original author or authors.
+ * Copyright 2017-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,11 +24,9 @@
/**
* Value object to capture custom conversion.
- *
*
* Types that can be mapped directly onto JSON are considered simple ones, because they neither need deeper inspection
* nor nested conversion.
- *
*
* @author Michael Nitschinger
* @author Oliver Gierke
diff --git a/src/main/java/org/springframework/data/couchbase/core/convert/CouchbaseJsr310Converters.java b/src/main/java/org/springframework/data/couchbase/core/convert/CouchbaseJsr310Converters.java
index e001f33fa..ead6f1ae4 100644
--- a/src/main/java/org/springframework/data/couchbase/core/convert/CouchbaseJsr310Converters.java
+++ b/src/main/java/org/springframework/data/couchbase/core/convert/CouchbaseJsr310Converters.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2020 the original author or authors.
+ * Copyright 2017-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,6 +27,7 @@
import java.time.LocalTime;
import java.time.Period;
import java.time.ZoneId;
+import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
@@ -71,6 +72,9 @@ private CouchbaseJsr310Converters() {
converters.add(StringToDurationConverter.INSTANCE);
converters.add(PeriodToStringConverter.INSTANCE);
converters.add(StringToPeriodConverter.INSTANCE);
+ converters.add(ZonedDateTimeToLongConverter.INSTANCE);
+ converters.add(NumberToZonedDateTimeConverter.INSTANCE);
+
return converters;
}
@@ -99,6 +103,31 @@ public Long convert(LocalDateTime source) {
}
}
+ @ReadingConverter
+ public enum NumberToZonedDateTimeConverter implements Converter {
+
+ INSTANCE;
+
+ @Override
+ public ZonedDateTime convert(Number source) {
+ return source == null ? null
+ : ZonedDateTime.ofInstant(DateConverters.SerializedObjectToDateConverter.INSTANCE.convert(source).toInstant(),
+ systemDefault());
+ }
+ }
+
+ @WritingConverter
+ public enum ZonedDateTimeToLongConverter implements Converter {
+
+ INSTANCE;
+
+ @Override
+ public Long convert(ZonedDateTime source) {
+ return source == null ? null
+ : DateConverters.DateToLongConverter.INSTANCE.convert(Date.from(source.toInstant()));
+ }
+ }
+
@ReadingConverter
public enum NumberToLocalDateConverter implements Converter {
diff --git a/src/main/java/org/springframework/data/couchbase/core/convert/CustomConversions.java b/src/main/java/org/springframework/data/couchbase/core/convert/CustomConversions.java
index b68dc4f9a..8b19a21ad 100644
--- a/src/main/java/org/springframework/data/couchbase/core/convert/CustomConversions.java
+++ b/src/main/java/org/springframework/data/couchbase/core/convert/CustomConversions.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2020 the original author or authors
+ * Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,11 +20,9 @@
/**
* Value object to capture custom conversion.
- *
*
* Types that can be mapped directly onto JSON are considered simple ones, because they neither need deeper inspection
* nor nested conversion.
- *
*
* @author Michael Nitschinger
* @author Oliver Gierke
diff --git a/src/main/java/org/springframework/data/couchbase/core/convert/MappingCouchbaseConverter.java b/src/main/java/org/springframework/data/couchbase/core/convert/MappingCouchbaseConverter.java
index 421560203..6436cba0b 100644
--- a/src/main/java/org/springframework/data/couchbase/core/convert/MappingCouchbaseConverter.java
+++ b/src/main/java/org/springframework/data/couchbase/core/convert/MappingCouchbaseConverter.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2021 the original author or authors
+ * Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -36,7 +36,6 @@
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.data.annotation.Transient;
import org.springframework.data.convert.CustomConversions;
-import org.springframework.data.convert.EntityInstantiator;
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
import org.springframework.data.couchbase.core.mapping.CouchbaseList;
import org.springframework.data.couchbase.core.mapping.CouchbaseMappingContext;
@@ -60,6 +59,7 @@
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.model.ConvertingPropertyAccessor;
import org.springframework.data.mapping.model.DefaultSpELExpressionEvaluator;
+import org.springframework.data.mapping.model.EntityInstantiator;
import org.springframework.data.mapping.model.ParameterValueProvider;
import org.springframework.data.mapping.model.PersistentEntityParameterValueProvider;
import org.springframework.data.mapping.model.PropertyValueProvider;
@@ -816,7 +816,6 @@ public void setApplicationContext(ApplicationContext applicationContext) {
/**
* COPIED Set the {@link EntityCallbacks} instance to use when invoking
* {@link org.springframework.data.mapping.callback.EntityCallback callbacks} like the {@link AfterConvertCallback}.
- *
* Overrides potentially existing {@link EntityCallbacks}.
*
* @param entityCallbacks must not be {@literal null}.
diff --git a/src/main/java/org/springframework/data/couchbase/core/mapping/BasicCouchbasePersistentProperty.java b/src/main/java/org/springframework/data/couchbase/core/mapping/BasicCouchbasePersistentProperty.java
index d5e61466b..9d1d0b686 100644
--- a/src/main/java/org/springframework/data/couchbase/core/mapping/BasicCouchbasePersistentProperty.java
+++ b/src/main/java/org/springframework/data/couchbase/core/mapping/BasicCouchbasePersistentProperty.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2021 the original author or authors
+ * Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -32,11 +32,9 @@
/**
* Implements annotated property representations of a given {@link Field} instance.
- *
*
* This object is used to gather information out of properties on objects that need to be persisted. For example, it
* supports overriding of the actual property name by providing custom annotations.
- *
*
* @author Michael Nitschinger
* @author Mark Paluch
@@ -71,7 +69,7 @@ protected Association createAssociation() {
/**
* Returns the field name of the property.
- *
+ *
* The field name can be different from the actual property name by using a custom annotation.
*/
@Override
diff --git a/src/main/java/org/springframework/data/couchbase/core/mapping/CouchbaseDocument.java b/src/main/java/org/springframework/data/couchbase/core/mapping/CouchbaseDocument.java
index 0e2164536..3df69933b 100644
--- a/src/main/java/org/springframework/data/couchbase/core/mapping/CouchbaseDocument.java
+++ b/src/main/java/org/springframework/data/couchbase/core/mapping/CouchbaseDocument.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2020 the original author or authors
+ * Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,18 +23,14 @@
/**
* A {@link CouchbaseDocument} is an abstract representation of a document stored inside Couchbase Server.
- *
*
* It acts like a {@link TreeMap}, but only allows those types to be written that are supported by the underlying
* storage format, which is currently JSON. Note that JSON conversion is not happening here, but performed at a
* different stage based on the payload stored in the {@link CouchbaseDocument}.
- *
- *
*
* In addition to the actual content, meta data is also stored. This especially refers to the document ID and its
* expiration time. Note that this information is not mandatory, since documents can be nested and therefore only the
* topmost document most likely has an ID.
- *
*
* @author Michael Nitschinger
*/
@@ -114,7 +110,7 @@ public final Object get(final String key) {
/**
* Returns the current payload, including all recursive elements.
- *
+ *
* It either returns the raw results or makes sure that the recusrive elements are also exported properly.
*
* @return
@@ -187,10 +183,8 @@ public final int size(final boolean recursive) {
/**
* Returns the underlying payload.
- *
*
* Note that unlike {@link #export()}, the nested objects are not converted, so the "raw" map is returned.
- *
*
* @return the underlying payload.
*/
@@ -268,10 +262,8 @@ public CouchbaseDocument setId(String id) {
/**
* Verifies that only values of a certain and supported type can be stored.
- *
*
* If this is not the case, a {@link IllegalArgumentException} is thrown.
- *
*
* @param value the object to verify its type.
*/
diff --git a/src/main/java/org/springframework/data/couchbase/core/mapping/CouchbaseList.java b/src/main/java/org/springframework/data/couchbase/core/mapping/CouchbaseList.java
index 4a1b90e98..a6ba8990f 100644
--- a/src/main/java/org/springframework/data/couchbase/core/mapping/CouchbaseList.java
+++ b/src/main/java/org/springframework/data/couchbase/core/mapping/CouchbaseList.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2020 the original author or authors
+ * Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,11 +25,9 @@
/**
* A {@link CouchbaseList} is an abstract list that represents an array stored in a (most of the times JSON) document.
- *
*
* This {@link CouchbaseList} is part of the potentially nested structure inside one or more {@link CouchbaseDocument}s.
* It can also contain them recursively, depending on how the document is modeled.
- *
*/
public class CouchbaseList implements CouchbaseStorable {
@@ -145,7 +143,7 @@ public final int size(final boolean recursive) {
/**
* Returns the current payload, including all recursive elements.
- *
+ *
* It either returns the raw results or makes sure that the recusrive elements are also exported properly.
*
* @return
diff --git a/src/main/java/org/springframework/data/couchbase/core/mapping/CouchbasePersistentEntity.java b/src/main/java/org/springframework/data/couchbase/core/mapping/CouchbasePersistentEntity.java
index 8d236d320..f768aab9c 100644
--- a/src/main/java/org/springframework/data/couchbase/core/mapping/CouchbasePersistentEntity.java
+++ b/src/main/java/org/springframework/data/couchbase/core/mapping/CouchbasePersistentEntity.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2021 the original author or authors
+ * Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -34,7 +34,7 @@ public interface CouchbasePersistentEntity extends PersistentEntity
+ *
* The Couchbase format for expiration time is: - for TTL < 31 days (<= 30 * 24 * 60 * 60): expressed as a TTL in
* seconds - for TTL > 30 days: expressed as Unix UTC time of expiry (number of SECONDS since the Epoch)
*
@@ -45,7 +45,7 @@ public interface CouchbasePersistentEntity extends PersistentEntity
+ *
* The Couchbase format for expiration time is: - for TTL < 31 days (<= 30 * 24 * 60 * 60): expressed as a TTL in
* seconds - for TTL > 30 days: expressed as Unix UTC time of expiry (number of SECONDS since the Epoch)
*
diff --git a/src/main/java/org/springframework/data/couchbase/core/mapping/CouchbasePersistentProperty.java b/src/main/java/org/springframework/data/couchbase/core/mapping/CouchbasePersistentProperty.java
index 7525f6c43..dba19b16d 100644
--- a/src/main/java/org/springframework/data/couchbase/core/mapping/CouchbasePersistentProperty.java
+++ b/src/main/java/org/springframework/data/couchbase/core/mapping/CouchbasePersistentProperty.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2021 the original author or authors
+ * Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,7 +27,7 @@ public interface CouchbasePersistentProperty extends PersistentProperty
+ *
* The field name can be different from the actual property name by using a custom annotation.
*/
String getFieldName();
diff --git a/src/main/java/org/springframework/data/couchbase/core/mapping/CouchbaseStorable.java b/src/main/java/org/springframework/data/couchbase/core/mapping/CouchbaseStorable.java
index 965acbbb9..b4e5ae15a 100644
--- a/src/main/java/org/springframework/data/couchbase/core/mapping/CouchbaseStorable.java
+++ b/src/main/java/org/springframework/data/couchbase/core/mapping/CouchbaseStorable.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2020 the original author or authors
+ * Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,7 +18,7 @@
/**
* Marker Interface to identify either a {@link CouchbaseDocument} or a {@link CouchbaseList}.
- *
+ *
* This interface will be extended in the future to refactor the needed infrastructure into the common interface.
*
* @author Michael Nitschinger
diff --git a/src/main/java/org/springframework/data/couchbase/core/mapping/event/ValidatingCouchbaseEventListener.java b/src/main/java/org/springframework/data/couchbase/core/mapping/event/ValidatingCouchbaseEventListener.java
index bdfffa572..33f9e12e2 100644
--- a/src/main/java/org/springframework/data/couchbase/core/mapping/event/ValidatingCouchbaseEventListener.java
+++ b/src/main/java/org/springframework/data/couchbase/core/mapping/event/ValidatingCouchbaseEventListener.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2020 the original author or authors
+ * Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/src/main/java/org/springframework/data/couchbase/core/query/N1qlSecondaryIndexed.java b/src/main/java/org/springframework/data/couchbase/core/query/N1qlSecondaryIndexed.java
index a4d1e4039..50b9597f0 100644
--- a/src/main/java/org/springframework/data/couchbase/core/query/N1qlSecondaryIndexed.java
+++ b/src/main/java/org/springframework/data/couchbase/core/query/N1qlSecondaryIndexed.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2020 the original author or authors
+ * Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,10 +26,10 @@
/**
* This annotation is targeted at {@link CouchbaseRepository Repository} interfaces, indicating that the framework
* should ensure a N1QL Secondary Index is present when the repository is instantiated.
- *
+ *
* Said index will relate to the "type" field (the one bearing type information) and restrict on documents that match
* the repository's entity class.
- *
+ *
* Be sure to also use {@link N1qlPrimaryIndexed} to make sure the PRIMARY INDEX is there as well.
*
* @author Simon Baslé
diff --git a/src/main/java/org/springframework/data/couchbase/core/query/Query.java b/src/main/java/org/springframework/data/couchbase/core/query/Query.java
index cfcbf7d87..e0b38e66a 100644
--- a/src/main/java/org/springframework/data/couchbase/core/query/Query.java
+++ b/src/main/java/org/springframework/data/couchbase/core/query/Query.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2021 the original author or authors
+ * Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -383,7 +383,6 @@ public QueryOptions buildQueryOptions(QueryOptions options, QueryScanConsistency
* This will find annotations included in composed annotations as well. Ideally
*
* @param method representing the query.
- * @return the query with the annotations applied
*/
public void setMeta(CouchbaseQueryMethod method, Class> typeToRead) {
meta = OptionsBuilder.buildMeta(method, typeToRead);
diff --git a/src/main/java/org/springframework/data/couchbase/core/query/QueryCriteria.java b/src/main/java/org/springframework/data/couchbase/core/query/QueryCriteria.java
index 9726e0737..df9fd644a 100644
--- a/src/main/java/org/springframework/data/couchbase/core/query/QueryCriteria.java
+++ b/src/main/java/org/springframework/data/couchbase/core/query/QueryCriteria.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2021 the original author or authors
+ * Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,15 +25,13 @@
import java.util.List;
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
-import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
-import org.springframework.data.couchbase.core.mapping.CouchbaseList;
import org.springframework.lang.Nullable;
+import org.springframework.util.CollectionUtils;
import com.couchbase.client.core.error.InvalidArgumentException;
import com.couchbase.client.java.json.JsonArray;
import com.couchbase.client.java.json.JsonObject;
import com.couchbase.client.java.json.JsonValue;
-import org.springframework.util.CollectionUtils;
/**
* @author Michael Nitschinger
@@ -283,18 +281,30 @@ public QueryCriteria between(@Nullable Object o1, @Nullable Object o2) {
public QueryCriteria in(@Nullable Object... o) {
operator = "IN";
- format = "%1$s in ( %3$s )";
- // IN takes a single argument that is a list
+ format = "%1$s in ( ";
if (o.length > 0) {
if (o[0] instanceof JsonArray || o[0] instanceof List || o[0] instanceof Object[]) {
if (o.length != 1) {
throw new RuntimeException("IN cannot take multiple lists");
}
- value = o;
+ if (o[0] instanceof Object[]) {
+ value = (Object[]) o[0];
+ } else if (o[0] instanceof JsonArray) {
+ JsonArray ja = ((JsonArray) o[0]);
+ value = ja.toList().toArray();
+ } else if (o[0] instanceof List) {
+ List l = ((List) o[0]);
+ value = l.toArray();
+ }
} else {
- value = new Object[1];
- value[0] = o; // JsonArray.from(o);
+ value = o;
+ }
+ for (int i = 0; i < value.length; i++) {
+ if (i > 0)
+ format = format + ", ";
+ format = format + "%" + (i + 3) + "$s";
}
+ format = format + " )";
}
return this;
}
@@ -414,15 +424,13 @@ private String maybeWrapValue(N1QLExpression key, Object value, int[] paramIndex
if (paramIndexPtr[0] >= 0) {
JsonArray params = (JsonArray) parameters;
// from StringBasedN1qlQueryParser.getPositionalPlaceholderValues()
- try {
+
+ if (value instanceof Object[] || value instanceof Collection) {
+ addAsCollection(params, asCollection(value), converter);
+ } else {
params.add(convert(converter, value));
- } catch (InvalidArgumentException iae) {
- if (value instanceof Object[] || value instanceof Collection) {
- addAsCollection(params, asCollection(value), converter);
- } else {
- throw iae;
- }
}
+
return "$" + (++paramIndexPtr[0]); // these are generated in order
} else {
JsonObject params = (JsonObject) parameters;
@@ -488,7 +496,6 @@ private static Collection> asCollection(final Object source) {
return source.getClass().isArray() ? CollectionUtils.arrayToList(source) : Collections.singleton(source);
}
-
private String maybeBackTic(String value) {
if (value == null || (value.startsWith("`") && value.endsWith("`"))) {
return value;
diff --git a/src/main/java/org/springframework/data/couchbase/core/query/StringQuery.java b/src/main/java/org/springframework/data/couchbase/core/query/StringQuery.java
index 47248e624..52ed05d13 100644
--- a/src/main/java/org/springframework/data/couchbase/core/query/StringQuery.java
+++ b/src/main/java/org/springframework/data/couchbase/core/query/StringQuery.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2020 the original author or authors.
+ * Copyright 2017-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,7 +28,7 @@
*
*
* @Query("#{#n1ql.selectEntity} where #{#n1ql.filter} and firstname = $1 and lastname = $2")
- * List getByFirstnameAndLastname(String firstname, String lastname);
+ * List<User> getByFirstnameAndLastname(String firstname, String lastname);
*
*
* It must include the SELECT ... FROM ... preferably via the #n1ql expression, in addition to any predicates required,
diff --git a/src/main/java/org/springframework/data/couchbase/core/query/ViewIndexed.java b/src/main/java/org/springframework/data/couchbase/core/query/ViewIndexed.java
index 43b212151..001ecd405 100644
--- a/src/main/java/org/springframework/data/couchbase/core/query/ViewIndexed.java
+++ b/src/main/java/org/springframework/data/couchbase/core/query/ViewIndexed.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2020 the original author or authors
+ * Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,10 +26,10 @@
/**
* This annotation is targeted at {@link CouchbaseRepository Repository} interfaces, indicating that the framework
* should ensure a View is present when the repository is instantiated.
- *
+ *
* The view must at least be described as a design document name and view name. Default map function will filter
* documents on the type associated to the repository, and default reduce function is "_count".
- *
+ *
* One can specify a custom reduce function as well as a non-default map function.
*
* @author Simon Baslé
diff --git a/src/main/java/org/springframework/data/couchbase/core/query/WithConsistency.java b/src/main/java/org/springframework/data/couchbase/core/query/WithConsistency.java
index e44bc7876..99470174f 100644
--- a/src/main/java/org/springframework/data/couchbase/core/query/WithConsistency.java
+++ b/src/main/java/org/springframework/data/couchbase/core/query/WithConsistency.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013-2020 the original author or authors.
+ * Copyright 2013-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,7 +30,7 @@
/**
* Annotation to set the scan consistency of N1QL queries with Couchbase. This controls whether couchbase waits for all
* changes to be processed by an index or whether stale results are acceptable.
- *
+ *
* If not set, the default consistency set in {@link AbstractCouchbaseConfiguration#getDefaultConsistency()} is used.
*
* @author Johannes Jasper.
diff --git a/src/main/java/org/springframework/data/couchbase/core/support/AnyId.java b/src/main/java/org/springframework/data/couchbase/core/support/AnyId.java
index ea3a7facc..9b77d031a 100644
--- a/src/main/java/org/springframework/data/couchbase/core/support/AnyId.java
+++ b/src/main/java/org/springframework/data/couchbase/core/support/AnyId.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 the original author or authors
+ * Copyright 2020-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,7 +18,7 @@
import java.util.Collection;
/**
- * A common interface for those that support one(T), all(Collection)
+ * A common interface for those that support one(T), all(Collection<T>)
*
* @author Michael Reiche
* @param - the entity class
diff --git a/src/main/java/org/springframework/data/couchbase/core/support/AnyIdReactive.java b/src/main/java/org/springframework/data/couchbase/core/support/AnyIdReactive.java
index 9bd97ae15..7a387fcb9 100644
--- a/src/main/java/org/springframework/data/couchbase/core/support/AnyIdReactive.java
+++ b/src/main/java/org/springframework/data/couchbase/core/support/AnyIdReactive.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 the original author or authors
+ * Copyright 2020-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,7 +21,7 @@
import java.util.Collection;
/**
- * A common interface for those that support one(T), all(Collection)
+ * A common interface for those that support one(T), all(Collection<T%gt;)
*
* @author Michael Reiche
* @param - the entity class
diff --git a/src/main/java/org/springframework/data/couchbase/core/support/OneAndAll.java b/src/main/java/org/springframework/data/couchbase/core/support/OneAndAll.java
index cacad7ecd..1cc17c634 100644
--- a/src/main/java/org/springframework/data/couchbase/core/support/OneAndAll.java
+++ b/src/main/java/org/springframework/data/couchbase/core/support/OneAndAll.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 the original author or authors
+ * Copyright 2020-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,7 +20,7 @@
import java.util.stream.Stream;
/**
- * A common interface for those that support one(T), all(Collection)
+ * A common interface for those that support one(T), all(Collection<T>)
*
* @author Michael Reiche
*
diff --git a/src/main/java/org/springframework/data/couchbase/core/support/OneAndAllEntity.java b/src/main/java/org/springframework/data/couchbase/core/support/OneAndAllEntity.java
index be028fa84..da4f0dcb2 100644
--- a/src/main/java/org/springframework/data/couchbase/core/support/OneAndAllEntity.java
+++ b/src/main/java/org/springframework/data/couchbase/core/support/OneAndAllEntity.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 the original author or authors
+ * Copyright 2020-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,7 +18,7 @@
import java.util.Collection;
/**
- * A common interface for those that support one(T), all(Collection)
+ * A common interface for those that support one(T), all(Collection<T>)
*
* @author Michael Reiche
*
diff --git a/src/main/java/org/springframework/data/couchbase/core/support/OneAndAllEntityReactive.java b/src/main/java/org/springframework/data/couchbase/core/support/OneAndAllEntityReactive.java
index 67fbba7b1..a4f77fea4 100644
--- a/src/main/java/org/springframework/data/couchbase/core/support/OneAndAllEntityReactive.java
+++ b/src/main/java/org/springframework/data/couchbase/core/support/OneAndAllEntityReactive.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 the original author or authors
+ * Copyright 2020-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,7 +21,7 @@
import java.util.Collection;
/**
- * A common interface for those that support one(T), all(Collection)
+ * A common interface for those that support one(T), all(Collection<T>)
*
* @author Michael Reiche
* @param - the entity class
diff --git a/src/main/java/org/springframework/data/couchbase/core/support/OneAndAllExists.java b/src/main/java/org/springframework/data/couchbase/core/support/OneAndAllExists.java
index 1b4041ae6..42736507f 100644
--- a/src/main/java/org/springframework/data/couchbase/core/support/OneAndAllExists.java
+++ b/src/main/java/org/springframework/data/couchbase/core/support/OneAndAllExists.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 the original author or authors
+ * Copyright 2020-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,11 +21,9 @@
import java.util.stream.Stream;
/**
- * A common interface for those that support one(T), all(Collection)
+ * A common interface for those that support one(T), all(Collection<T>)
*
* @author Michael Reiche
- *
- * @param - the entity class
*/
public interface OneAndAllExists {
boolean one(String id);
diff --git a/src/main/java/org/springframework/data/couchbase/core/support/OneAndAllExistsReactive.java b/src/main/java/org/springframework/data/couchbase/core/support/OneAndAllExistsReactive.java
index 67487c3b5..03d244d45 100644
--- a/src/main/java/org/springframework/data/couchbase/core/support/OneAndAllExistsReactive.java
+++ b/src/main/java/org/springframework/data/couchbase/core/support/OneAndAllExistsReactive.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 the original author or authors
+ * Copyright 2020-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,11 +21,10 @@
import java.util.Map;
/**
- * A common interface for those that support one(T), all(Collection)
+ * A common interface for those that support one(T), all(Collection<T>)
*
* @author Michael Reiche
*
- * @param - the entity class
*/
public interface OneAndAllExistsReactive {
Mono one(String id);
diff --git a/src/main/java/org/springframework/data/couchbase/core/support/OneAndAllId.java b/src/main/java/org/springframework/data/couchbase/core/support/OneAndAllId.java
index 370388c02..104849003 100644
--- a/src/main/java/org/springframework/data/couchbase/core/support/OneAndAllId.java
+++ b/src/main/java/org/springframework/data/couchbase/core/support/OneAndAllId.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 the original author or authors
+ * Copyright 2020-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,7 +18,7 @@
import java.util.Collection;
/**
- * A common interface for those that support one(String), all(Collection)
+ * A common interface for those that support one(String), all(Collection<String>)
*
* @author Michael Reiche
*
diff --git a/src/main/java/org/springframework/data/couchbase/core/support/OneAndAllIdReactive.java b/src/main/java/org/springframework/data/couchbase/core/support/OneAndAllIdReactive.java
index 5c8b74f29..b7e000d65 100644
--- a/src/main/java/org/springframework/data/couchbase/core/support/OneAndAllIdReactive.java
+++ b/src/main/java/org/springframework/data/couchbase/core/support/OneAndAllIdReactive.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 the original author or authors
+ * Copyright 2020-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,7 +21,7 @@
import java.util.Collection;
/**
- * A common interface for those that support one(String), all(Collection)
+ * A common interface for those that support one(String), all(Collection<String>)
*
* @author Michael Reiche
* @param - the entity class
diff --git a/src/main/java/org/springframework/data/couchbase/core/support/OneAndAllReactive.java b/src/main/java/org/springframework/data/couchbase/core/support/OneAndAllReactive.java
index 7e5414dab..ef477759c 100644
--- a/src/main/java/org/springframework/data/couchbase/core/support/OneAndAllReactive.java
+++ b/src/main/java/org/springframework/data/couchbase/core/support/OneAndAllReactive.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 the original author or authors
+ * Copyright 2020-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,7 +23,7 @@
import java.util.stream.Stream;
/**
- * A common interface for those that support one(T), all(Collection)
+ * A common interface for those that support one(T), all(Collection<T>)
*
* @author Michael Reiche
* @param - the entity class
diff --git a/src/main/java/org/springframework/data/couchbase/repository/DynamicProxyable.java b/src/main/java/org/springframework/data/couchbase/repository/DynamicProxyable.java
index c97f9b9ed..01e56fb32 100644
--- a/src/main/java/org/springframework/data/couchbase/repository/DynamicProxyable.java
+++ b/src/main/java/org/springframework/data/couchbase/repository/DynamicProxyable.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2021 the original author or authors.
+ * Copyright 2017-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,7 +26,7 @@
/**
* The generic parameter needs to be REPO which is either a CouchbaseRepository parameterized on T,ID or a
* ReactiveCouchbaseRepository parameterized on T,ID. i.e.: interface AirportRepository extends
- * CouchbaseRepository, DynamicProxyable
+ * CouchbaseRepository<Airport, String>, DynamicProxyable<AirportRepository>
*
* @param
* @author Michael Reiche
diff --git a/src/main/java/org/springframework/data/couchbase/repository/Query.java b/src/main/java/org/springframework/data/couchbase/repository/Query.java
index 21562a787..39eab1425 100644
--- a/src/main/java/org/springframework/data/couchbase/repository/Query.java
+++ b/src/main/java/org/springframework/data/couchbase/repository/Query.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2020 the original author or authors
+ * Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,23 +23,25 @@
import org.springframework.data.annotation.QueryAnnotation;
import org.springframework.data.couchbase.core.CouchbaseTemplate;
+import org.springframework.data.couchbase.repository.query.StringBasedN1qlQueryParser;
+import org.springframework.data.couchbase.repository.query.StringN1qlQueryCreator;
/**
- * Annotation to support the use of N1QL queries with Couchbase.
- *
+ * Annotation to support the use of N1QL queries with Couchbase. Queries are crated by {@link StringN1qlQueryCreator}
+ *
* Using it without parameter will resolve the query from the method name. Providing a value (an inline N1QL statement)
* will execute that statement instead.
- *
+ *
* In this case, one can use a placeholder notation of {@code ?0}, {@code ?1} and so on.
- *
+ *
* Also, SpEL in the form #{spelExpression}
is supported, including the following N1QL variables that will
* be replaced by the underlying {@link CouchbaseTemplate} associated information:
*
- * {@value StringN1qlBasedQuery#SPEL_SELECT_FROM_CLAUSE} (see {@link StringN1qlBasedQuery#SPEL_SELECT_FROM_CLAUSE})
+ * {@value StringBasedN1qlQueryParser#SPEL_SELECT_FROM_CLAUSE} (see {@link StringBasedN1qlQueryParser#SPEL_SELECT_FROM_CLAUSE})
*
- * {@value StringN1qlBasedQuery#SPEL_BUCKET} (see {@link StringN1qlBasedQuery#SPEL_BUCKET})
- * {@value StringN1qlBasedQuery#SPEL_ENTITY} (see {@link StringN1qlBasedQuery#SPEL_ENTITY})
- * {@value StringN1qlBasedQuery#SPEL_FILTER} (see {@link StringN1qlBasedQuery#SPEL_FILTER})
+ * {@value StringBasedN1qlQueryParser#SPEL_BUCKET} (see {@link StringBasedN1qlQueryParser#SPEL_BUCKET})
+ * {@value StringBasedN1qlQueryParser#SPEL_ENTITY} (see {@link StringBasedN1qlQueryParser#SPEL_ENTITY})
+ * {@value StringBasedN1qlQueryParser#SPEL_FILTER} (see {@link StringBasedN1qlQueryParser#SPEL_FILTER})
*
*
* @author Simon Baslé.
diff --git a/src/main/java/org/springframework/data/couchbase/repository/query/CountFragment.java b/src/main/java/org/springframework/data/couchbase/repository/query/CountFragment.java
index 918192c95..0503e1b4d 100644
--- a/src/main/java/org/springframework/data/couchbase/repository/query/CountFragment.java
+++ b/src/main/java/org/springframework/data/couchbase/repository/query/CountFragment.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2020 the original author or authors
+ * Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,9 +18,9 @@
/**
* An utility entity that allows to extract total row count out of a COUNT(*) N1QL query.
- *
+ *
* The query should use the COUNT_ALIAS, eg.: SELECT COUNT(*) AS count FROM default;
- *
+ *
* This ensures that the framework will be able to map the JSON result to this {@link CountFragment} class so that it
* can be used.
*/
diff --git a/src/main/java/org/springframework/data/couchbase/repository/query/N1qlQueryCreator.java b/src/main/java/org/springframework/data/couchbase/repository/query/N1qlQueryCreator.java
index e4b3279e1..8af78c70c 100644
--- a/src/main/java/org/springframework/data/couchbase/repository/query/N1qlQueryCreator.java
+++ b/src/main/java/org/springframework/data/couchbase/repository/query/N1qlQueryCreator.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2021 the original author or authors
+ * Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -69,7 +69,7 @@ public N1qlQueryCreator(final PartTree tree, final ParameterAccessor accessor, f
this.queryMethod = queryMethod;
this.converter = converter;
this.bucketName = bucketName;
- this.entity = converter.getMappingContext().getPersistentEntity(queryMethod.getReturnedObjectType());
+ this.entity = converter.getMappingContext().getPersistentEntity(queryMethod.getEntityInformation().getJavaType());
}
@Override
diff --git a/src/main/java/org/springframework/data/couchbase/repository/query/OldN1qlQueryCreator.java b/src/main/java/org/springframework/data/couchbase/repository/query/OldN1qlQueryCreator.java
index 0bda1358d..a8bbe2215 100644
--- a/src/main/java/org/springframework/data/couchbase/repository/query/OldN1qlQueryCreator.java
+++ b/src/main/java/org/springframework/data/couchbase/repository/query/OldN1qlQueryCreator.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2019 the original author or authors
+ * Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -75,7 +75,6 @@
*
* NEAR, WITHIN: geospatial is not supported in N1QL as of now
*
- *
*
* @author Simon Baslé
* @author Subhashni Balakrishnan
diff --git a/src/main/java/org/springframework/data/couchbase/repository/query/StringBasedN1qlQueryParser.java b/src/main/java/org/springframework/data/couchbase/repository/query/StringBasedN1qlQueryParser.java
index 3aa200729..6a5e4c584 100644
--- a/src/main/java/org/springframework/data/couchbase/repository/query/StringBasedN1qlQueryParser.java
+++ b/src/main/java/org/springframework/data/couchbase/repository/query/StringBasedN1qlQueryParser.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2021 the original author or authors.
+ * Copyright 2017-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -33,6 +33,7 @@
import org.slf4j.LoggerFactory;
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProperty;
+import org.springframework.data.couchbase.core.mapping.Expiration;
import org.springframework.data.couchbase.core.query.N1QLExpression;
import org.springframework.data.couchbase.repository.Query;
import org.springframework.data.couchbase.repository.query.support.N1qlUtils;
@@ -44,7 +45,6 @@
import org.springframework.data.repository.query.ParameterAccessor;
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
import org.springframework.data.repository.query.ReturnedType;
-import org.springframework.data.util.TypeInformation;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.common.TemplateParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
@@ -130,10 +130,11 @@ public StringBasedN1qlQueryParser(String statement, CouchbaseQueryMethod queryMe
this.queryMethod = queryMethod;
this.couchbaseConverter = couchbaseConverter;
String collection = queryMethod.getCollection();
- this.statementContext = createN1qlSpelValues(bucketName, collection, null, null, typeField, typeValue, false, null,
- null);
- this.countContext = createN1qlSpelValues(bucketName, collection, null, null, typeField, typeValue, true, null,
- null);
+ this.statementContext = createN1qlSpelValues(bucketName, collection,
+ queryMethod.getEntityInformation().getJavaType(), queryMethod.getReturnedObjectType(), typeField, typeValue,
+ false, null, null);
+ this.countContext = createN1qlSpelValues(bucketName, collection, queryMethod.getEntityInformation().getJavaType(),
+ queryMethod.getReturnedObjectType(), typeField, typeValue, true, null, null);
this.parsedExpression = getExpression(accessor, getParameters(accessor), null, parser, evaluationContextProvider);
checkPlaceholders(this.parsedExpression.toString());
}
@@ -161,12 +162,11 @@ public N1qlSpelValues createN1qlSpelValues(String bucketName, String collection,
String b = collection != null ? collection : bucketName;
Assert.isTrue(!(distinctFields != null && fields != null),
"only one of project(fields) and distinct(distinctFields) can be specified");
- String projectedFields = getProjectedFields(b, resultClass, fields);
String entity = "META(" + i(b) + ").id AS " + SELECT_ID + ", META(" + i(b) + ").cas AS " + SELECT_CAS;
String count = "COUNT(*) AS " + CountFragment.COUNT_ALIAS;
String selectEntity;
if (distinctFields != null) {
- String distinctFieldsStr = distinctFields.length == 0 ? projectedFields : getDistinctFields(distinctFields);
+ String distinctFieldsStr = getProjectedOrDistinctFields(b, domainClass, fields, distinctFields);
if (isCount) {
selectEntity = "SELECT COUNT( DISTINCT {" + distinctFieldsStr + "} ) " + CountFragment.COUNT_ALIAS + " FROM "
+ i(b);
@@ -176,6 +176,7 @@ public N1qlSpelValues createN1qlSpelValues(String bucketName, String collection,
} else if (isCount) {
selectEntity = "SELECT " + count + " FROM " + i(b);
} else {
+ String projectedFields = getProjectedOrDistinctFields(b, domainClass, fields, distinctFields);
selectEntity = "SELECT " + entity + (!projectedFields.isEmpty() ? ", " : " ") + projectedFields + " FROM " + i(b);
}
String typeSelection = "`" + typeField + "` = \"" + typeValue + "\"";
@@ -183,68 +184,66 @@ public N1qlSpelValues createN1qlSpelValues(String bucketName, String collection,
String delete = N1QLExpression.delete().from(b).toString();
String returning = " returning " + N1qlUtils.createReturningExpressionForDelete(b).toString();
- return new N1qlSpelValues(selectEntity, entity, b, typeSelection, delete, returning);
+ return new N1qlSpelValues(selectEntity, entity, i(b).toString(), typeSelection, delete, returning);
}
- private String getDistinctFields(String... distinctFields) {
- return i(distinctFields).toString();
- }
-
- private String getProjectedFields(String b, Class resultClass, String[] fields) {
-
+ private String getProjectedOrDistinctFields(String b, Class resultClass, String[] fields, String[] distinctFields) {
+ if (distinctFields != null && distinctFields.length != 0) {
+ return i(distinctFields).toString();
+ }
String projectedFields = i(b) + ".*";
if (resultClass != null) {
PersistentEntity persistentEntity = couchbaseConverter.getMappingContext().getPersistentEntity(resultClass);
StringBuilder sb = new StringBuilder();
- getProjectedFieldsInternal(b, null, sb, persistentEntity.getTypeInformation(), fields/*, ""*/);
+ getProjectedFieldsInternal(b, null, sb, persistentEntity, fields, distinctFields != null);
projectedFields = sb.toString();
}
return projectedFields;
}
private void getProjectedFieldsInternal(String bucketName, CouchbasePersistentProperty parent, StringBuilder sb,
- TypeInformation resultClass, String[] fields/*, String path*/) {
+ PersistentEntity persistentEntity, String[] fields, boolean forDistinct) {
- if (resultClass != null) {
+ if (persistentEntity != null) {
Set fieldList = fields != null ? new HashSet<>(Arrays.asList(fields)) : null;
- PersistentEntity persistentEntity = couchbaseConverter.getMappingContext().getPersistentEntity(resultClass);
- // CouchbasePersistentProperty property = path.getLeafProperty();
- persistentEntity.doWithProperties(new PropertyHandler() {
- @Override
- public void doWithPersistentProperty(final CouchbasePersistentProperty prop) {
- if (prop == persistentEntity.getIdProperty() && parent == null) {
- return;
- }
- if (prop == persistentEntity.getVersionProperty() && parent == null) {
- return;
- }
- String projectField = null;
-
- if (fieldList == null || fieldList.contains(prop.getFieldName())) {
- PersistentPropertyPath path = couchbaseConverter.getMappingContext()
- .getPersistentPropertyPath(prop.getName(), resultClass.getType());
- projectField = N1qlQueryCreator.addMetaIfRequired(bucketName, path, prop, persistentEntity).toString();
- if (sb.length() > 0) {
- sb.append(", ");
- }
- sb.append(projectField); // from N1qlQueryCreator
- }
+ // do not include the id and cas metadata fields.
- if (fieldList != null) {
- fieldList.remove(prop.getFieldName());
+ persistentEntity.doWithProperties((PropertyHandler) prop -> {
+ if (prop == persistentEntity.getIdProperty() && parent == null) {
+ return;
+ }
+ if (prop == persistentEntity.getVersionProperty() && parent == null) {
+ return;
+ }
+ // for distinct when no distinctFields were provided, do not include the expiration field.
+ if (forDistinct && prop.findAnnotation(Expiration.class) != null && parent == null) {
+ return;
+ }
+ String projectField = null;
+
+ if (fieldList == null || fieldList.contains(prop.getFieldName())) {
+ PersistentPropertyPath path = couchbaseConverter.getMappingContext()
+ .getPersistentPropertyPath(prop.getName(), persistentEntity.getTypeInformation().getType());
+ projectField = N1qlQueryCreator.addMetaIfRequired(bucketName, path, prop, persistentEntity).toString();
+ if (sb.length() > 0) {
+ sb.append(", ");
}
- // 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
- // And the n1ql to project lower-level properties is complex
-
- // if (!conversions.isSimpleType(prop.getType())) {
- // getProjectedFieldsInternal(prop, sb, prop.getTypeInformation(), path+prop.getName()+".");
- // } else {
+ sb.append(projectField); // from N1qlQueryCreator
+ }
- // }
+ if (fieldList != null) {
+ fieldList.remove(prop.getFieldName());
}
+ // 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
+ // And the n1ql to project lower-level properties is complex
+
+ // if (!conversions.isSimpleType(prop.getType())) {
+ // getProjectedFieldsInternal(prop, sb, prop.getTypeInformation(), path+prop.getName()+".");
+ // } else {
+ // }
});
// throw an exception if there is an request for a field not in the entity.
// needs further discussion as removing a field from an entity could cause this and not necessarily be an error
diff --git a/src/test/java/org/springframework/data/couchbase/core/CouchbaseTemplateQueryCollectionIntegrationTests.java b/src/test/java/org/springframework/data/couchbase/core/CouchbaseTemplateQueryCollectionIntegrationTests.java
index c84e09eae..7cb960916 100644
--- a/src/test/java/org/springframework/data/couchbase/core/CouchbaseTemplateQueryCollectionIntegrationTests.java
+++ b/src/test/java/org/springframework/data/couchbase/core/CouchbaseTemplateQueryCollectionIntegrationTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 the original author or authors
+ * Copyright 2021-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -349,20 +349,15 @@ void distinctReactive() {
assertEquals(7, airports2.size());
// count( distinct icao )
- // not currently possible to have multiple fields in COUNT(DISTINCT field1, field2, ... ) due to MB43475
Long count1 = reactiveCouchbaseTemplate.findByQuery(Airport.class).distinct(new String[] { "icao" })
.as(Airport.class).withConsistency(QueryScanConsistency.REQUEST_PLUS).inCollection(collectionName).count()
.block();
assertEquals(2, count1);
- // count( distinct (all fields in icaoClass) // which only has one field
- // not currently possible to have multiple fields in COUNT(DISTINCT field1, field2, ... ) due to MB43475
- Class icaoClass = (new Object() {
- String icao;
- }).getClass();
- long count2 = (long) reactiveCouchbaseTemplate.findByQuery(Airport.class).distinct(new String[] {}).as(icaoClass)
+ // count (distinct { iata, icao } )
+ Long count2 = reactiveCouchbaseTemplate.findByQuery(Airport.class).distinct(new String[] {"iata", "icao"})
.withConsistency(QueryScanConsistency.REQUEST_PLUS).inCollection(collectionName).count().block();
- assertEquals(2, count2);
+ assertEquals(7, count2);
} finally {
reactiveCouchbaseTemplate.removeById().inCollection(collectionName)
diff --git a/src/test/java/org/springframework/data/couchbase/core/CouchbaseTemplateQueryIntegrationTests.java b/src/test/java/org/springframework/data/couchbase/core/CouchbaseTemplateQueryIntegrationTests.java
index f7987c6a0..3239e22c3 100644
--- a/src/test/java/org/springframework/data/couchbase/core/CouchbaseTemplateQueryIntegrationTests.java
+++ b/src/test/java/org/springframework/data/couchbase/core/CouchbaseTemplateQueryIntegrationTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2021 the original author or authors
+ * Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -265,15 +265,6 @@ void distinct() {
.as(Airport.class).withConsistency(QueryScanConsistency.REQUEST_PLUS).count();
assertEquals(7, count1);
- // count( distinct (all fields in icaoClass)
- Class icaoClass = (new Object() {
- String iata;
- String icao;
- }).getClass();
- long count2 = couchbaseTemplate.findByQuery(Airport.class).distinct(new String[] {}).as(icaoClass)
- .withConsistency(QueryScanConsistency.REQUEST_PLUS).count();
- assertEquals(7, count2);
-
} finally {
couchbaseTemplate.removeById()
.all(Arrays.stream(iatas).map((iata) -> "airports::" + iata).collect(Collectors.toSet()));
@@ -305,19 +296,14 @@ void distinctReactive() {
assertEquals(7, airports2.size());
// count( distinct icao )
- // not currently possible to have multiple fields in COUNT(DISTINCT field1, field2, ... ) due to MB43475
- long count1 = reactiveCouchbaseTemplate.findByQuery(Airport.class).distinct(new String[] { "icao" })
+ Long count1 = reactiveCouchbaseTemplate.findByQuery(Airport.class).distinct(new String[] { "icao" })
.as(Airport.class).withConsistency(QueryScanConsistency.REQUEST_PLUS).count().block();
assertEquals(2, count1);
- // count( distinct (all fields in icaoClass) // which only has one field
- // not currently possible to have multiple fields in COUNT(DISTINCT field1, field2, ... ) due to MB43475
- Class icaoClass = (new Object() {
- String icao;
- }).getClass();
- long count2 = (long) reactiveCouchbaseTemplate.findByQuery(Airport.class).distinct(new String[] {}).as(icaoClass)
+ // count( distinct { icao, iata } )
+ Long count2 = reactiveCouchbaseTemplate.findByQuery(Airport.class).distinct(new String[] { "icao", "iata" })
.withConsistency(QueryScanConsistency.REQUEST_PLUS).count().block();
- assertEquals(2, count2);
+ assertEquals(7, count2);
} finally {
reactiveCouchbaseTemplate.removeById()
diff --git a/src/test/java/org/springframework/data/couchbase/core/query/QueryCriteriaTests.java b/src/test/java/org/springframework/data/couchbase/core/query/QueryCriteriaTests.java
index fe7edbeb6..8c8ce4d57 100644
--- a/src/test/java/org/springframework/data/couchbase/core/query/QueryCriteriaTests.java
+++ b/src/test/java/org/springframework/data/couchbase/core/query/QueryCriteriaTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2021 the original author or authors.
+ * Copyright 2017-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,12 +24,12 @@
import static org.springframework.data.couchbase.core.query.QueryCriteria.where;
import static org.springframework.data.couchbase.repository.query.support.N1qlUtils.escapedBucket;
+import java.util.Arrays;
+
import org.junit.jupiter.api.Test;
import com.couchbase.client.java.json.JsonArray;
-import java.util.Arrays;
-
/**
* @author Mauro Monti
*/
@@ -85,8 +85,9 @@ void testNestedOrCriteria() {
void testNestedNotIn() {
QueryCriteria c = where(i("name")).is("Bubba").or(where(i("age")).gt(12).or(i("country")).is("Austria"))
.and(where(i("state")).notIn(new String[] { "Alabama", "Florida" }));
- assertEquals("`name` = \"Bubba\" or (`age` > 12 or `country` = \"Austria\") and "
- + "(not( (`state` in ( [\"Alabama\",\"Florida\"] )) ))", c.export());
+ JsonArray parameters = JsonArray.create();
+ assertEquals("`name` = $1 or (`age` > $2 or `country` = $3) and (not( (`state` in ( $4, $5 )) ))",
+ c.export(new int[1], parameters, null));
}
@Test
@@ -224,21 +225,22 @@ void testBetween() {
@Test
void testIn() {
String[] args = new String[] { "gump", "davis" };
- QueryCriteria c = where(i("name")).in((Object)args);
- assertEquals("`name` in ( [\"gump\",\"davis\"] )", c.export());
+ QueryCriteria c = where(i("name")).in((Object) args);
+ assertEquals("`name` in ( \"gump\", \"davis\" )", c.export());
JsonArray parameters = JsonArray.create();
- assertEquals("`name` in ( $1 )", c.export(new int[1], parameters, null));
- assertEquals(arrayToString(args), parameters.get(0).toString());
+ assertEquals("`name` in ( $1, $2 )", c.export(new int[1], parameters, null));
+ assertEquals(arrayToString(args), parameters.toString());
}
@Test
void testNotIn() {
String[] args = new String[] { "gump", "davis" };
- QueryCriteria c = where(i("name")).notIn((Object)args);
- assertEquals("not( (`name` in ( [\"gump\",\"davis\"] )) )", c.export());
+ QueryCriteria c = where(i("name")).notIn((Object) args);
+ assertEquals("not( (`name` in ( \"gump\", \"davis\" )) )", c.export());
+ // this tests creating parameters from the args.
JsonArray parameters = JsonArray.create();
- assertEquals("not( (`name` in ( $1 )) )", c.export(new int[1], parameters, null));
- assertEquals(arrayToString(args), parameters.get(0).toString());
+ assertEquals("not( (`name` in ( $1, $2 )) )", c.export(new int[1], parameters, null));
+ assertEquals(arrayToString(args), parameters.toString());
}
@Test
@@ -261,7 +263,6 @@ void testKeys() {
assertEquals(" USE KEYS []", expression.keys(Arrays.asList()).toString());
}
-
@Test // https://github.com/spring-projects/spring-data-couchbase/issues/1066
void testCriteriaCorrectlyEscapedWhenUsingMetaOnLHS() {
final String bucketName = "sample-bucket";
diff --git a/src/test/java/org/springframework/data/couchbase/core/query/ReactiveCouchbaseTemplateQueryCollectionIntegrationTests.java b/src/test/java/org/springframework/data/couchbase/core/query/ReactiveCouchbaseTemplateQueryCollectionIntegrationTests.java
index afc7f5c15..5c5fd29cf 100644
--- a/src/test/java/org/springframework/data/couchbase/core/query/ReactiveCouchbaseTemplateQueryCollectionIntegrationTests.java
+++ b/src/test/java/org/springframework/data/couchbase/core/query/ReactiveCouchbaseTemplateQueryCollectionIntegrationTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 the original author or authors
+ * Copyright 2021-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -344,20 +344,15 @@ void distinctReactive() {
assertEquals(7, airports2.size());
// count( distinct icao )
- // not currently possible to have multiple fields in COUNT(DISTINCT field1, field2, ... ) due to MB43475
Long count1 = reactiveCouchbaseTemplate.findByQuery(Airport.class).distinct(new String[] { "icao" })
.as(Airport.class).withConsistency(QueryScanConsistency.REQUEST_PLUS).inCollection(collectionName).count()
.block();
assertEquals(2, count1);
- // count( distinct (all fields in icaoClass) // which only has one field
- // not currently possible to have multiple fields in COUNT(DISTINCT field1, field2, ... ) due to MB43475
- Class icaoClass = (new Object() {
- String icao;
- }).getClass();
- long count2 = (long) reactiveCouchbaseTemplate.findByQuery(Airport.class).distinct(new String[] {}).as(icaoClass)
+ // count( distinct { iata, icao } )
+ Long count2 = reactiveCouchbaseTemplate.findByQuery(Airport.class).distinct(new String[] {"iata","icao"})
.withConsistency(QueryScanConsistency.REQUEST_PLUS).inCollection(collectionName).count().block();
- assertEquals(2, count2);
+ assertEquals(7, count2);
} finally {
reactiveCouchbaseTemplate.removeById().inCollection(collectionName)
diff --git a/src/test/java/org/springframework/data/couchbase/domain/AirportRepository.java b/src/test/java/org/springframework/data/couchbase/domain/AirportRepository.java
index 8c39699c0..d17c3d39e 100644
--- a/src/test/java/org/springframework/data/couchbase/domain/AirportRepository.java
+++ b/src/test/java/org/springframework/data/couchbase/domain/AirportRepository.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2021 the original author or authors.
+ * Copyright 2017-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -95,11 +95,11 @@ public interface AirportRepository extends CouchbaseRepository,
@ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS)
List deleteByIata(String iata);
- @Query("SELECT __cas, * from `#{#n1ql.bucket}` where iata = $1")
+ @Query("SELECT __cas, * from #{#n1ql.bucket} where iata = $1")
@ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS)
List getAllByIataNoID(String iata);
- @Query("SELECT __id, * from `#{#n1ql.bucket}` where iata = $1")
+ @Query("SELECT __id, * from #{#n1ql.bucket} where iata = $1")
@ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS)
List getAllByIataNoCAS(String iata);
@@ -122,10 +122,10 @@ public interface AirportRepository extends CouchbaseRepository,
Long countFancyExpression(@Param("projectIds") List projectIds, @Param("planIds") List planIds,
@Param("active") Boolean active);
- @Query("SELECT 1 FROM `#{#n1ql.bucket}` WHERE anything = 'count(*)'") // looks like count query, but is not
+ @Query("SELECT 1 FROM #{#n1ql.bucket} WHERE anything = 'count(*)'") // looks like count query, but is not
Long countBad();
- @Query("SELECT count(*) FROM `#{#n1ql.bucket}`")
+ @Query("SELECT count(*) FROM #{#n1ql.bucket}")
Long countGood();
@ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS)
@@ -135,6 +135,18 @@ Long countFancyExpression(@Param("projectIds") List projectIds, @Param("
@Query("#{#n1ql.selectEntity} WHERE #{#n1ql.filter} AND iata != $1")
Page getAllByIataNot(String iata, Pageable pageable);
+ @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS)
+ @Query("SELECT iata, \"\" as __id, 0 as __cas from #{#n1ql.bucket} WHERE #{#n1ql.filter} order by meta().id")
+ List getStrings();
+
+ @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS)
+ @Query("SELECT length(iata), \"\" as __id, 0 as __cas from #{#n1ql.bucket} WHERE #{#n1ql.filter} order by meta().id")
+ List getLongs();
+
+ @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS)
+ @Query("SELECT iata, icao, \"\" as __id, 0 as __cas from #{#n1ql.bucket} WHERE #{#n1ql.filter} order by meta().id")
+ List getStringArrays();
+
@ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS)
Optional findByIdAndIata(String id, String iata);
@@ -150,7 +162,7 @@ Long countFancyExpression(@Param("projectIds") List projectIds, @Param("
@ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS)
Long countDistinctIcaoBy();
- @Query("SELECT 1 FROM `#{#n1ql.bucket}` WHERE #{#n1ql.filter} " + " #{#projectIds != null ? 'AND blah IN $1' : ''} "
+ @Query("SELECT 1 FROM #{#n1ql.bucket} WHERE #{#n1ql.filter} " + " #{#projectIds != null ? 'AND blah IN $1' : ''} "
+ " #{#planIds != null ? 'AND blahblah IN $2' : ''} " + " #{#active != null ? 'AND false = $3' : ''} ")
Long countOne();
diff --git a/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryQueryIntegrationTests.java b/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryQueryIntegrationTests.java
index b9f093991..36d003f31 100644
--- a/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryQueryIntegrationTests.java
+++ b/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryQueryIntegrationTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2021 the original author or authors.
+ * Copyright 2017-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -102,7 +102,6 @@
import com.couchbase.client.java.env.ClusterEnvironment;
import com.couchbase.client.java.json.JsonArray;
import com.couchbase.client.java.kv.GetResult;
-import com.couchbase.client.java.kv.MutationState;
import com.couchbase.client.java.kv.UpsertOptions;
import com.couchbase.client.java.query.QueryOptions;
import com.couchbase.client.java.query.QueryScanConsistency;
@@ -276,6 +275,7 @@ void findBySimplePropertyReturnType() {
@Test
public void saveNotBoundedRequestPlus() {
+ airportRepository.withOptions(QueryOptions.queryOptions().scanConsistency(REQUEST_PLUS)).deleteAll();
ApplicationContext ac = new AnnotationConfigApplicationContext(ConfigRequestPlus.class);
// the Config class has been modified, these need to be loaded again
CouchbaseTemplate couchbaseTemplateRP = (CouchbaseTemplate) ac.getBean(COUCHBASE_TEMPLATE);
@@ -314,20 +314,21 @@ public void saveNotBoundedRequestPlus() {
@Test
public void saveNotBoundedWithDefaultRepository() {
-
+ airportRepository.withOptions(QueryOptions.queryOptions().scanConsistency(REQUEST_PLUS)).deleteAll();
ApplicationContext ac = new AnnotationConfigApplicationContext(Config.class);
// the Config class has been modified, these need to be loaded again
CouchbaseTemplate couchbaseTemplateRP = (CouchbaseTemplate) ac.getBean(COUCHBASE_TEMPLATE);
- AirportRepositoryScanConsistencyTest airportRepositoryRP = (AirportRepositoryScanConsistencyTest) ac.getBean("airportRepositoryScanConsistencyTest");
+ AirportRepositoryScanConsistencyTest airportRepositoryRP = (AirportRepositoryScanConsistencyTest) ac
+ .getBean("airportRepositoryScanConsistencyTest");
List sizeBeforeTest = airportRepositoryRP.findAll();
assertEquals(0, sizeBeforeTest.size());
- Airport vie = new Airport("airports::vie", "vie" , "low9");
+ Airport vie = new Airport("airports::vie", "vie", "low9");
Airport saved = airportRepositoryRP.save(vie);
List allSaved = airportRepositoryRP.findAll();
couchbaseTemplate.removeById(Airport.class).one(saved.getId());
- assertNotEquals( 1, allSaved.size(),"should not have found 1 airport");
+ assertNotEquals(1, allSaved.size(), "should not have found 1 airport");
}
@Test
@@ -335,19 +336,19 @@ public void saveRequestPlusWithDefaultRepository() {
ApplicationContext ac = new AnnotationConfigApplicationContext(ConfigRequestPlus.class);
// the Config class has been modified, these need to be loaded again
- AirportRepositoryScanConsistencyTest airportRepositoryRP = (AirportRepositoryScanConsistencyTest) ac.getBean("airportRepositoryScanConsistencyTest");
+ AirportRepositoryScanConsistencyTest airportRepositoryRP = (AirportRepositoryScanConsistencyTest) ac
+ .getBean("airportRepositoryScanConsistencyTest");
List sizeBeforeTest = airportRepositoryRP.findAll();
assertEquals(0, sizeBeforeTest.size());
- Airport vie = new Airport("airports::vie", "vie" , "low9");
+ Airport vie = new Airport("airports::vie", "vie", "low9");
Airport saved = airportRepositoryRP.save(vie);
List allSaved = airportRepositoryRP.findAll();
couchbaseTemplate.removeById(Airport.class).one(saved.getId());
- assertEquals( 1, allSaved.size(),"should have found 1 airport");
+ assertEquals(1, allSaved.size(), "should have found 1 airport");
}
-
@Test
void findByTypeAlias() {
Airport vie = null;
@@ -374,15 +375,14 @@ void findByEnum() {
Airport airport2 = airportRepository.findByIata(Iata.vie);
assertNotNull(airport2, "should have found " + vie);
assertEquals(airport2.getId(), vie.getId());
-
- Airport airport3 = airportRepository.findByIataIn(new Iata[]{Iata.vie, Iata.xxx});
+ Airport airport3 = airportRepository.findByIataIn(new Iata[] { Iata.vie, Iata.xxx });
assertNotNull(airport3, "should have found " + vie);
assertEquals(airport3.getId(), vie.getId());
java.util.Collection iatas = new ArrayList<>();
iatas.add(Iata.vie);
iatas.add(Iata.xxx);
- Airport airport4 = airportRepository.findByIataIn( iatas );
+ Airport airport4 = airportRepository.findByIataIn(iatas);
assertNotNull(airport4, "should have found " + vie);
assertEquals(airport4.getId(), vie.getId());
@@ -557,8 +557,29 @@ public void testExpiryAnnotation() {
assertThrows(DataRetrievalFailureException.class, () -> userRepository.delete(user));
}
+ @Test
+ void stringQueryReturnsSimpleType() {
+ Airport airport1 = new Airport("1", "myIata1", "MyIcao");
+ airportRepository.save(airport1);
+ Airport airport2 = new Airport("2", "myIata2__", "MyIcao");
+ airportRepository.save(airport2);
+ List iatas = airportRepository.getStrings();
+ assertEquals(Arrays.asList(airport1.getIata(), airport2.getIata()), iatas);
+ List iataLengths = airportRepository.getLongs();
+ assertEquals(Arrays.asList(airport1.getIata().length(), airport2.getIata().length()).toString(),
+ iataLengths.toString());
+ // this is somewhat broken, because decode is told that each "row" is just a String instead of a String[]
+ // As such, only the first element is returned. (QueryExecutionConverts.unwrapWrapperTypes)
+ List iataAndIcaos = airportRepository.getStringArrays();
+ assertEquals(airport1.getIata(), iataAndIcaos.get(0)[0]);
+ assertEquals(airport2.getIata(), iataAndIcaos.get(1)[0]);
+ airportRepository.deleteById(airport1.getId());
+ airportRepository.deleteById(airport2.getId());
+ }
+
@Test
void count() {
+ airportRepository.withOptions(QueryOptions.queryOptions().scanConsistency(REQUEST_PLUS)).deleteAll();
String[] iatas = { "JFK", "IAD", "SFO", "SJC", "SEA", "LAX", "PHX" };
airportRepository.countOne();
@@ -657,6 +678,7 @@ void threadSafeParametersTest() throws Exception {
void distinct() {
String[] iatas = { "JFK", "IAD", "SFO", "SJC", "SEA", "LAX", "PHX" };
String[] icaos = { "ic0", "ic1", "ic0", "ic1", "ic0", "ic1", "ic0" };
+ airportRepository.withOptions(QueryOptions.queryOptions().scanConsistency(REQUEST_PLUS)).deleteAll();
try {
for (int i = 0; i < iatas.length; i++) {
diff --git a/src/test/java/org/springframework/data/couchbase/repository/query/N1qlQueryCreatorTests.java b/src/test/java/org/springframework/data/couchbase/repository/query/N1qlQueryCreatorTests.java
index 52fe975bf..e60f3865e 100644
--- a/src/test/java/org/springframework/data/couchbase/repository/query/N1qlQueryCreatorTests.java
+++ b/src/test/java/org/springframework/data/couchbase/repository/query/N1qlQueryCreatorTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2021 the original author or authors.
+ * Copyright 2017-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -109,7 +109,7 @@ void queryParametersArray() throws Exception {
Query query = creator.createQuery();
// Query expected = (new Query()).addCriteria(where("firstname").in("Oliver", "Charles"));
- assertEquals(expected.export(new int[1]), query.export(new int[1]));
+ assertEquals(" WHERE `firstname` in ( $1, $2 )", query.export(new int[1]));
JsonObject expectedOptions = JsonObject.create();
expected.buildQueryOptions(null, null).build().injectParams(expectedOptions);
JsonObject actualOptions = JsonObject.create();
@@ -132,7 +132,7 @@ void queryParametersJsonArray() throws Exception {
Query query = creator.createQuery();
Query expected = (new Query()).addCriteria(where(i("firstname")).in("Oliver", "Charles"));
- assertEquals(expected.export(new int[1]), query.export(new int[1]));
+ assertEquals(" WHERE `firstname` in ( $1, $2 )", query.export(new int[1]));
JsonObject expectedOptions = JsonObject.create();
expected.buildQueryOptions(null, null).build().injectParams(expectedOptions);
JsonObject actualOptions = JsonObject.create();
@@ -156,7 +156,7 @@ void queryParametersList() throws Exception {
Query expected = (new Query()).addCriteria(where(i("firstname")).in("Oliver", "Charles"));
- assertEquals(expected.export(new int[1]), query.export(new int[1]));
+ assertEquals(" WHERE `firstname` in ( $1, $2 )", query.export(new int[1]));
JsonObject expectedOptions = JsonObject.create();
expected.buildQueryOptions(null, null).build().injectParams(expectedOptions);
JsonObject actualOptions = JsonObject.create();
diff --git a/src/test/java/org/springframework/data/couchbase/repository/query/StringN1qlQueryCreatorMockedTests.java b/src/test/java/org/springframework/data/couchbase/repository/query/StringN1qlQueryCreatorMockedTests.java
index a0de49fe0..66f124e3a 100644
--- a/src/test/java/org/springframework/data/couchbase/repository/query/StringN1qlQueryCreatorMockedTests.java
+++ b/src/test/java/org/springframework/data/couchbase/repository/query/StringN1qlQueryCreatorMockedTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2019 the original author or authors.
+ * Copyright 2017-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -85,7 +85,7 @@ void createsQueryCorrectly() throws Exception {
Query query = creator.createQuery();
assertEquals(
- "SELECT META(`travel-sample`).id AS __id, META(`travel-sample`).cas AS __cas, `travel-sample`.* FROM `travel-sample` where `_class` = \"org.springframework.data.couchbase.domain.User\" and firstname = $1 and lastname = $2",
+ "SELECT META(`travel-sample`).id AS __id, META(`travel-sample`).cas AS __cas, `firstname`, `lastname`, `createdBy`, `createdDate`, `lastModifiedBy`, `lastModifiedDate` FROM `travel-sample` where `_class` = \"org.springframework.data.couchbase.domain.User\" and firstname = $1 and lastname = $2",
query.toN1qlSelectString(couchbaseTemplate.reactive(), User.class, false));
}
@@ -104,7 +104,7 @@ void createsQueryCorrectly2() throws Exception {
Query query = creator.createQuery();
assertEquals(
- "SELECT META(`travel-sample`).id AS __id, META(`travel-sample`).cas AS __cas, `travel-sample`.* FROM `travel-sample` where `_class` = \"org.springframework.data.couchbase.domain.User\" and (firstname = $first or lastname = $last)",
+ "SELECT META(`travel-sample`).id AS __id, META(`travel-sample`).cas AS __cas, `firstname`, `lastname`, `createdBy`, `createdDate`, `lastModifiedBy`, `lastModifiedDate` FROM `travel-sample` where `_class` = \"org.springframework.data.couchbase.domain.User\" and (firstname = $first or lastname = $last)",
query.toN1qlSelectString(couchbaseTemplate.reactive(), User.class, false));
}
diff --git a/src/test/java/org/springframework/data/couchbase/util/JavaIntegrationTests.java b/src/test/java/org/springframework/data/couchbase/util/JavaIntegrationTests.java
index 5ba592487..90b9ab0c8 100644
--- a/src/test/java/org/springframework/data/couchbase/util/JavaIntegrationTests.java
+++ b/src/test/java/org/springframework/data/couchbase/util/JavaIntegrationTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020-2021 the original author or authors
+ * Copyright 2020-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,6 +27,12 @@
import static org.springframework.data.couchbase.config.BeanNames.REACTIVE_COUCHBASE_TEMPLATE;
import static org.springframework.data.couchbase.util.Util.waitUntilCondition;
+import okhttp3.Credentials;
+import okhttp3.FormBody;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+
import java.io.IOException;
import java.time.Duration;
import java.util.Collections;
@@ -39,7 +45,6 @@
import java.util.function.Function;
import java.util.function.Predicate;
-import com.couchbase.client.core.io.CollectionIdentifier;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Timeout;
import org.springframework.context.ApplicationContext;
@@ -60,6 +65,7 @@
import com.couchbase.client.core.error.QueryException;
import com.couchbase.client.core.error.ScopeNotFoundException;
import com.couchbase.client.core.error.UnambiguousTimeoutException;
+import com.couchbase.client.core.io.CollectionIdentifier;
import com.couchbase.client.core.json.Mapper;
import com.couchbase.client.core.service.ServiceType;
import com.couchbase.client.java.Bucket;
@@ -200,6 +206,7 @@ protected static void waitForQueryIndexerToHaveBucket(final Cluster cluster, fin
}
if (!ready) {
+ createAndDeleteBucket();// need to do this because of https://issues.couchbase.com/browse/MB-50132
try {
Thread.sleep(50);
} catch (InterruptedException e) {}
@@ -211,6 +218,32 @@ protected static void waitForQueryIndexerToHaveBucket(final Cluster cluster, fin
}
}
+ private static void createAndDeleteBucket() {
+ final OkHttpClient httpClient = new OkHttpClient.Builder().connectTimeout(30, TimeUnit.SECONDS)
+ .readTimeout(30, TimeUnit.SECONDS).writeTimeout(30, TimeUnit.SECONDS).build();
+ String hostPort = connectionString().replace("11210", "8091");
+ String bucketname = UUID.randomUUID().toString();
+ try {
+
+ Response postResponse = httpClient.newCall(new Request.Builder()
+ .header("Authorization", Credentials.basic(config().adminUsername(), config().adminPassword()))
+ .url("http://" + hostPort + "/pools/default/buckets/")
+ .post(new FormBody.Builder().add("name", bucketname).add("bucketType", "membase").add("ramQuotaMB", "100")
+ .add("replicaNumber", Integer.toString(0)).add("flushEnabled", "1").build())
+ .build()).execute();
+
+ if (postResponse.code() != 202) {
+ throw new IOException("Could not create bucket: " + postResponse + ", Reason: " + postResponse.body().string());
+ }
+ Response deleteResponse = httpClient.newCall(new Request.Builder()
+ .header("Authorization", Credentials.basic(config().adminUsername(), config().adminPassword()))
+ .url("http://" + hostPort + "/pools/default/buckets/" + bucketname).delete().build()).execute();
+ System.out.println("deleteResponse: " + deleteResponse);
+ } catch (IOException ioe) {
+ ioe.printStackTrace();
+ }
+ }
+
/**
* Improve test stability by waiting for a given service to report itself ready.
*/