Skip to content

Field named id treated as document id. #1261

New issue

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

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

Already on GitHub? Sign in to your account

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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
import org.springframework.data.mapping.Association;
import org.springframework.data.mapping.AssociationHandler;
import org.springframework.data.mapping.MappingException;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentPropertyAccessor;
import org.springframework.data.mapping.PreferredConstructor.Parameter;
import org.springframework.data.mapping.PropertyHandler;
Expand Down Expand Up @@ -271,7 +272,8 @@ public void doWithPersistentProperty(final CouchbasePersistentProperty prop) {
|| prop.isAnnotationPresent(N1qlJoin.class)) {
return;
}
Object obj = prop.isIdProperty() && parent == null ? source.getId() : getValueInternal(prop, source, instance);
Object obj = prop == entity.getIdProperty() && parent == null ? source.getId()
: getValueInternal(prop, source, instance, entity);
accessor.setProperty(prop, obj);
}

Expand All @@ -286,7 +288,7 @@ private boolean isIdConstructionProperty(final CouchbasePersistentProperty prope

entity.doWithAssociations((AssociationHandler<CouchbasePersistentProperty>) association -> {
CouchbasePersistentProperty inverseProp = association.getInverse();
Object obj = getValueInternal(inverseProp, source, instance);
Object obj = getValueInternal(inverseProp, source, instance, entity);
accessor.setProperty(inverseProp, obj);
});

Expand All @@ -302,8 +304,8 @@ private boolean isIdConstructionProperty(final CouchbasePersistentProperty prope
* @return the actual property value.
*/
protected Object getValueInternal(final CouchbasePersistentProperty property, final CouchbaseDocument source,
final Object parent) {
return new CouchbasePropertyValueProvider(source, spELContext, parent).getPropertyValue(property);
final Object parent, PersistentEntity entity) {
return new CouchbasePropertyValueProvider(source, spELContext, parent, entity).getPropertyValue(property);
}

/**
Expand All @@ -318,7 +320,7 @@ protected Object getValueInternal(final CouchbasePersistentProperty property, fi
private ParameterValueProvider<CouchbasePersistentProperty> getParameterProvider(
final CouchbasePersistentEntity<?> entity, final CouchbaseDocument source,
final DefaultSpELExpressionEvaluator evaluator, final Object parent) {
CouchbasePropertyValueProvider provider = new CouchbasePropertyValueProvider(source, evaluator, parent);
CouchbasePropertyValueProvider provider = new CouchbasePropertyValueProvider(source, evaluator, parent, entity);
PersistentEntityParameterValueProvider<CouchbasePersistentProperty> parameterProvider = new PersistentEntityParameterValueProvider<>(
entity, provider, parent);

Expand Down Expand Up @@ -503,7 +505,7 @@ protected void writeInternal(final Object source, final CouchbaseDocument target
final TreeMap<Integer, String> suffixes = new TreeMap<>();
final TreeMap<Integer, String> idAttributes = new TreeMap<>();

target.setExpiration((int)(entity.getExpiryDuration().getSeconds()));
target.setExpiration((int) (entity.getExpiryDuration().getSeconds()));

entity.doWithProperties(new PropertyHandler<CouchbasePersistentProperty>() {
@Override
Expand Down Expand Up @@ -926,19 +928,25 @@ private class CouchbasePropertyValueProvider implements PropertyValueProvider<Co
*/
private final Object parent;

/**
* The entity of the property
*/
private final PersistentEntity entity;

public CouchbasePropertyValueProvider(final CouchbaseDocument source, final SpELContext factory,
final Object parent) {
this(source, new DefaultSpELExpressionEvaluator(source, factory), parent);
final Object parent, final PersistentEntity entity) {
this(source, new DefaultSpELExpressionEvaluator(source, factory), parent, entity);
}

public CouchbasePropertyValueProvider(final CouchbaseDocument source,
final DefaultSpELExpressionEvaluator evaluator, final Object parent) {
final DefaultSpELExpressionEvaluator evaluator, final Object parent, final PersistentEntity entity) {
Assert.notNull(source, "CouchbaseDocument must not be null!");
Assert.notNull(evaluator, "DefaultSpELExpressionEvaluator must not be null!");

this.source = source;
this.evaluator = evaluator;
this.parent = parent;
this.entity = entity;
}

@Override
Expand All @@ -947,7 +955,7 @@ public <R> R getPropertyValue(final CouchbasePersistentProperty property) {
String expression = property.getSpelExpression();
Object value = expression != null ? evaluator.evaluate(expression) : source.get(property.getFieldName());

if (property.isIdProperty() && parent == null) {
if (property == entity.getIdProperty() && parent == null) {
return (R) source.getId();
}
if (value == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.springframework.data.couchbase.core.query.QueryCriteria;
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.context.MappingContext;
import org.springframework.data.repository.query.ParameterAccessor;
Expand All @@ -57,6 +58,7 @@ public class N1qlQueryCreator extends AbstractQueryCreator<Query, QueryCriteria>
private final QueryMethod queryMethod;
private final CouchbaseConverter converter;
private final String bucketName;
private final PersistentEntity entity;

public N1qlQueryCreator(final PartTree tree, final ParameterAccessor accessor, final QueryMethod queryMethod,
final CouchbaseConverter converter, final String bucketName) {
Expand All @@ -67,13 +69,14 @@ 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());
}

@Override
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(bucketName, path, property)), iterator);
return from(part, property, where(addMetaIfRequired(bucketName, path, property, entity)), iterator);
}

@Override
Expand Down Expand Up @@ -107,7 +110,7 @@ protected QueryCriteria and(final Part part, final QueryCriteria base, final Ite
PersistentPropertyPath<CouchbasePersistentProperty> path = context.getPersistentPropertyPath(part.getProperty());
CouchbasePersistentProperty property = path.getLeafProperty();

return from(part, property, base.and(addMetaIfRequired(bucketName, path, property)), iterator);
return from(part, property, base.and(addMetaIfRequired(bucketName, path, property, entity)), iterator);
}

@Override
Expand Down Expand Up @@ -186,11 +189,11 @@ private QueryCriteria from(final Part part, final CouchbasePersistentProperty pr

public static N1QLExpression addMetaIfRequired(String bucketName,
final PersistentPropertyPath<CouchbasePersistentProperty> persistentPropertyPath,
final CouchbasePersistentProperty property) {
if (property.isIdProperty()) {
final CouchbasePersistentProperty property, final PersistentEntity entity) {
if (entity != null && property == entity.getIdProperty()) {
return path(meta(i(bucketName)), i(META_ID_PROPERTY));
}
if (property.isVersionProperty()) {
if (property == entity.getVersionProperty()) {
return path(meta(i(bucketName)), i(META_CAS_PROPERTY));
}
if (property.isExpirationProperty()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.couchbase.client.core.error.CouchbaseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
Expand All @@ -51,6 +50,7 @@
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.util.Assert;

import com.couchbase.client.core.error.CouchbaseException;
import com.couchbase.client.core.error.InvalidArgumentException;
import com.couchbase.client.java.json.JsonArray;
import com.couchbase.client.java.json.JsonObject;
Expand Down Expand Up @@ -213,18 +213,18 @@ private void getProjectedFieldsInternal(String bucketName, CouchbasePersistentPr
persistentEntity.doWithProperties(new PropertyHandler<CouchbasePersistentProperty>() {
@Override
public void doWithPersistentProperty(final CouchbasePersistentProperty prop) {
if (prop.isIdProperty() && parent == null) {
if (prop == persistentEntity.getIdProperty() && parent == null) {
return;
}
if (prop.isVersionProperty()) {
if (prop == persistentEntity.getVersionProperty() && parent == null) {
return;
}
String projectField = null;

if (fieldList == null || fieldList.contains(prop.getFieldName())) {
PersistentPropertyPath<CouchbasePersistentProperty> path = couchbaseConverter.getMappingContext()
.getPersistentPropertyPath(prop.getName(), resultClass.getType());
projectField = N1qlQueryCreator.addMetaIfRequired(bucketName, path, prop).toString();
projectField = N1qlQueryCreator.addMetaIfRequired(bucketName, path, prop, persistentEntity).toString();
if (sb.length() > 0) {
sb.append(", ");
}
Expand All @@ -250,7 +250,7 @@ public void doWithPersistentProperty(final CouchbasePersistentProperty prop) {
// needs further discussion as removing a field from an entity could cause this and not necessarily be an error
if (fieldList != null && !fieldList.isEmpty()) {
throw new CouchbaseException(
"projected fields (" + fieldList + ") not found in entity: " + persistentEntity.getName());
"projected fields (" + fieldList + ") not found in entity: " + persistentEntity.getName());
}
} else {
for (String field : fields) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import org.springframework.data.couchbase.core.query.QueryCriteria;
import org.springframework.data.couchbase.domain.Address;
import org.springframework.data.couchbase.domain.Airport;
import org.springframework.data.couchbase.domain.AssessmentDO;
import org.springframework.data.couchbase.domain.Course;
import org.springframework.data.couchbase.domain.NaiveAuditorAware;
import org.springframework.data.couchbase.domain.Submission;
Expand Down Expand Up @@ -132,6 +133,21 @@ void findByMatchingQuery() {
assertEquals(1, foundUsers.size());
}

@Test
void findAssessmentDO() {
AssessmentDO ado = new AssessmentDO();
ado.setEventTimestamp(44444444);// this is also an @IdAttribute
ado.setId("123");
ado = couchbaseTemplate.upsertById(AssessmentDO.class).one(ado);

Query specialUsers = new Query(QueryCriteria.where(i("id")).is(ado.getId()));
final List<AssessmentDO> foundUsers = couchbaseTemplate.findByQuery(AssessmentDO.class)
.withConsistency(QueryScanConsistency.REQUEST_PLUS).matching(specialUsers).all();
assertEquals("123", foundUsers.get(0).getId(), "id");
assertEquals("44444444", foundUsers.get(0).getDocumentId(), "documentId");
assertEquals(ado, foundUsers.get(0));
}

@Test
void findByMatchingQueryProjected() {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright 2012-2021 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.data.couchbase.domain;

import lombok.Data;
import lombok.NoArgsConstructor;

import org.springframework.data.annotation.Id;
import org.springframework.data.couchbase.core.mapping.Document;
import org.springframework.data.couchbase.core.mapping.Field;
import org.springframework.data.couchbase.core.mapping.id.GeneratedValue;
import org.springframework.data.couchbase.core.mapping.id.GenerationStrategy;
import org.springframework.data.couchbase.core.mapping.id.IdAttribute;

/**
* @author Michael Reiche
*/
@Document()
@Data
@NoArgsConstructor
public class AssessmentDO {
@Id @GeneratedValue(strategy = GenerationStrategy.USE_ATTRIBUTES) private String documentId;

@Field @IdAttribute private long eventTimestamp;

@Field("docType") private String documentType;

@Field private String id;
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,13 @@
import org.springframework.data.couchbase.domain.User;
import org.springframework.data.couchbase.domain.UserRepository;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
import org.springframework.data.repository.query.DefaultParameters;
import org.springframework.data.repository.query.ParameterAccessor;
import org.springframework.data.repository.query.Parameters;
import org.springframework.data.repository.query.ParametersParameterAccessor;
import org.springframework.data.repository.query.QueryMethod;
import org.springframework.data.repository.query.parser.PartTree;

import com.couchbase.client.java.json.JsonArray;
Expand Down Expand Up @@ -69,9 +72,10 @@ void createsQueryCorrectly() throws Exception {
String input = "findByFirstname";
PartTree tree = new PartTree(input, User.class);
Method method = UserRepository.class.getMethod(input, String.class);

N1qlQueryCreator creator = new N1qlQueryCreator(tree, getAccessor(getParameters(method), "Oliver"), null, converter,
bucketName);
QueryMethod queryMethod = new QueryMethod(method, new DefaultRepositoryMetadata(UserRepository.class),
new SpelAwareProxyProjectionFactory());
N1qlQueryCreator creator = new N1qlQueryCreator(tree, getAccessor(getParameters(method), "Oliver"), queryMethod,
converter, bucketName);
Query query = creator.createQuery();

assertEquals(query.export(), " WHERE " + where(i("firstname")).is("Oliver").export());
Expand All @@ -82,9 +86,10 @@ void createsQueryFieldAnnotationCorrectly() throws Exception {
String input = "findByMiddlename";
PartTree tree = new PartTree(input, Person.class);
Method method = PersonRepository.class.getMethod(input, String.class);

N1qlQueryCreator creator = new N1qlQueryCreator(tree, getAccessor(getParameters(method), "Oliver"), null, converter,
bucketName);
QueryMethod queryMethod = new QueryMethod(method, new DefaultRepositoryMetadata(UserRepository.class),
new SpelAwareProxyProjectionFactory());
N1qlQueryCreator creator = new N1qlQueryCreator(tree, getAccessor(getParameters(method), "Oliver"), queryMethod,
converter, bucketName);
Query query = creator.createQuery();

assertEquals(query.export(), " WHERE " + where(i("nickname")).is("Oliver").export());
Expand All @@ -95,10 +100,12 @@ void queryParametersArray() throws Exception {
String input = "findByFirstnameIn";
PartTree tree = new PartTree(input, User.class);
Method method = UserRepository.class.getMethod(input, String[].class);
QueryMethod queryMethod = new QueryMethod(method, new DefaultRepositoryMetadata(UserRepository.class),
new SpelAwareProxyProjectionFactory());
Query expected = (new Query()).addCriteria(where(i("firstname")).in("Oliver", "Charles"));
N1qlQueryCreator creator = new N1qlQueryCreator(tree,
getAccessor(getParameters(method), new Object[] { new Object[] { "Oliver", "Charles" } }), null, converter,
bucketName);
getAccessor(getParameters(method), new Object[] { new Object[] { "Oliver", "Charles" } }), queryMethod,
converter, bucketName);
Query query = creator.createQuery();

// Query expected = (new Query()).addCriteria(where("firstname").in("Oliver", "Charles"));
Expand All @@ -115,11 +122,12 @@ void queryParametersJsonArray() throws Exception {
String input = "findByFirstnameIn";
PartTree tree = new PartTree(input, User.class);
Method method = UserRepository.class.getMethod(input, JsonArray.class);

QueryMethod queryMethod = new QueryMethod(method, new DefaultRepositoryMetadata(UserRepository.class),
new SpelAwareProxyProjectionFactory());
JsonArray jsonArray = JsonArray.create();
jsonArray.add("Oliver");
jsonArray.add("Charles");
N1qlQueryCreator creator = new N1qlQueryCreator(tree, getAccessor(getParameters(method), jsonArray), null,
N1qlQueryCreator creator = new N1qlQueryCreator(tree, getAccessor(getParameters(method), jsonArray), queryMethod,
converter, bucketName);
Query query = creator.createQuery();

Expand All @@ -137,11 +145,13 @@ void queryParametersList() throws Exception {
String input = "findByFirstnameIn";
PartTree tree = new PartTree(input, User.class);
Method method = UserRepository.class.getMethod(input, String[].class);
QueryMethod queryMethod = new QueryMethod(method, new DefaultRepositoryMetadata(UserRepository.class),
new SpelAwareProxyProjectionFactory());
List<String> list = new LinkedList<>();
list.add("Oliver");
list.add("Charles");
N1qlQueryCreator creator = new N1qlQueryCreator(tree, getAccessor(getParameters(method), new Object[] { list }),
null, converter, bucketName);
queryMethod, converter, bucketName);
Query query = creator.createQuery();

Query expected = (new Query()).addCriteria(where(i("firstname")).in("Oliver", "Charles"));
Expand All @@ -159,8 +169,10 @@ void createsAndQueryCorrectly() throws Exception {
String input = "findByFirstnameAndLastname";
PartTree tree = new PartTree(input, User.class);
Method method = UserRepository.class.getMethod(input, String.class, String.class);
N1qlQueryCreator creator = new N1qlQueryCreator(tree, getAccessor(getParameters(method), "John", "Doe"), null,
converter, bucketName);
QueryMethod queryMethod = new QueryMethod(method, new DefaultRepositoryMetadata(UserRepository.class),
new SpelAwareProxyProjectionFactory());
N1qlQueryCreator creator = new N1qlQueryCreator(tree, getAccessor(getParameters(method), "John", "Doe"),
queryMethod, converter, bucketName);
Query query = creator.createQuery();

assertEquals(" WHERE " + where(i("firstname")).is("John").and(i("lastname")).is("Doe").export(), query.export());
Expand All @@ -171,9 +183,10 @@ void createsQueryFindByIdIsNotNullAndFirstname() throws Exception {
String input = "findByIdIsNotNullAndFirstnameEquals";
PartTree tree = new PartTree(input, User.class);
Method method = UserRepository.class.getMethod(input, String.class);

N1qlQueryCreator creator = new N1qlQueryCreator(tree, getAccessor(getParameters(method), "Oliver"), null, converter,
bucketName);
QueryMethod queryMethod = new QueryMethod(method, new DefaultRepositoryMetadata(UserRepository.class),
new SpelAwareProxyProjectionFactory());
N1qlQueryCreator creator = new N1qlQueryCreator(tree, getAccessor(getParameters(method), "Oliver"), queryMethod,
converter, bucketName);
Query query = creator.createQuery();

assertEquals(query.export(),
Expand All @@ -185,9 +198,10 @@ void createsQueryFindByVersionEqualsAndAndFirstname() throws Exception {
String input = "findByVersionEqualsAndFirstnameEquals";
PartTree tree = new PartTree(input, User.class);
Method method = UserRepository.class.getMethod(input, Long.class, String.class);

QueryMethod queryMethod = new QueryMethod(method, new DefaultRepositoryMetadata(UserRepository.class),
new SpelAwareProxyProjectionFactory());
N1qlQueryCreator creator = new N1qlQueryCreator(tree,
getAccessor(getParameters(method), 1611287177404088320L, "Oliver"), null, converter, bucketName);
getAccessor(getParameters(method), 1611287177404088320L, "Oliver"), queryMethod, converter, bucketName);
Query query = creator.createQuery();

assertEquals(query.export(), " WHERE " + where(x("META(`" + bucketName + "`).`cas`")).is(1611287177404088320L)
Expand Down