org.springframework.data.cassandra.core.convert.QueryMapper Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of spring-data-cassandra Show documentation
Show all versions of spring-data-cassandra Show documentation
Cassandra support for Spring Data
/*
* Copyright 2017-2018 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
*
* http://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.cassandra.core.convert;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.data.cassandra.core.cql.CqlIdentifier;
import org.springframework.data.cassandra.core.mapping.CassandraMappingContext;
import org.springframework.data.cassandra.core.mapping.CassandraPersistentEntity;
import org.springframework.data.cassandra.core.mapping.CassandraPersistentProperty;
import org.springframework.data.cassandra.core.query.ColumnName;
import org.springframework.data.cassandra.core.query.Columns;
import org.springframework.data.cassandra.core.query.Columns.ColumnSelector;
import org.springframework.data.cassandra.core.query.Columns.FunctionCall;
import org.springframework.data.cassandra.core.query.Columns.Selector;
import org.springframework.data.cassandra.core.query.Criteria;
import org.springframework.data.cassandra.core.query.CriteriaDefinition;
import org.springframework.data.cassandra.core.query.CriteriaDefinition.Predicate;
import org.springframework.data.cassandra.core.query.Filter;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Order;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.PersistentPropertyPath;
import org.springframework.data.mapping.PropertyHandler;
import org.springframework.data.mapping.PropertyPath;
import org.springframework.data.mapping.PropertyReferenceException;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* Map {@link org.springframework.data.cassandra.core.query.Query} to CQL-specific data types.
*
* @author Mark Paluch
* @see ColumnName
* @see Columns
* @see Criteria
* @see Filter
* @see Sort
* @since 2.0
*/
public class QueryMapper {
private final CassandraConverter converter;
private final CassandraMappingContext mappingContext;
/**
* Creates a new {@link QueryMapper} with the given {@link CassandraConverter}.
*
* @param converter must not be {@literal null}.
*/
public QueryMapper(CassandraConverter converter) {
Assert.notNull(converter, "CassandraConverter must not be null");
this.converter = converter;
this.mappingContext = converter.getMappingContext();
}
/**
* Returns the configured {@link CassandraConverter} used to convert object values into Cassandra column typed values.
*
* @return the configured {@link CassandraConverter}.
* @see org.springframework.data.cassandra.core.convert.CassandraConverter
*/
protected CassandraConverter getConverter() {
return this.converter;
}
/**
* Returns the configured {@link MappingContext} containing mapping meta-data (persistent entities and properties)
* used to store (map) objects to Cassandra tables (rows/columns).
*
* @return the configured {@link MappingContext}.
*/
protected CassandraMappingContext getMappingContext() {
return this.mappingContext;
}
/**
* Map a {@link Filter} with a {@link CassandraPersistentEntity type hint}. Filter mapping translates property names
* to column names and maps {@link Predicate} values to simple Cassandra values.
*
* @param filter must not be {@literal null}.
* @param entity must not be {@literal null}.
* @return the mapped {@link Filter}.
*/
public Filter getMappedObject(Filter filter, CassandraPersistentEntity entity) {
Assert.notNull(filter, "Filter must not be null");
Assert.notNull(entity, "Entity must not be null");
List result = new ArrayList<>();
for (CriteriaDefinition criteriaDefinition : filter) {
Field field = createPropertyField(entity, criteriaDefinition.getColumnName());
field.getProperty().filter(CassandraPersistentProperty::isCompositePrimaryKey).ifPresent(it -> {
throw new IllegalArgumentException(
"Cannot use composite primary key directly. Reference a property of the composite primary key");
});
field.getProperty().filter(it -> it.getOrdinal() != null).ifPresent(it -> {
throw new IllegalArgumentException(
String.format("Cannot reference tuple value elements, property [%s]", field.getMappedKey()));
});
Predicate predicate = criteriaDefinition.getPredicate();
Object value = predicate.getValue();
Object mappedValue = value != null
? getConverter().convertToColumnType(value, getTypeInformation(field, value))
: null;
Predicate mappedPredicate = new Predicate(predicate.getOperator(), mappedValue);
result.add(Criteria.of(field.getMappedKey(), mappedPredicate));
}
return Filter.from(result);
}
/**
* Map {@link Columns} with a {@link CassandraPersistentEntity type hint} to {@link ColumnSelector}s.
*
* @param columns must not be {@literal null}.
* @param entity must not be {@literal null}.
* @return the mapped {@link Selector}s.
*/
public List getMappedSelectors(Columns columns, CassandraPersistentEntity entity) {
Assert.notNull(columns, "Columns must not be null");
Assert.notNull(entity, "CassandraPersistentEntity must not be null");
if (columns.isEmpty()) {
return Collections.emptyList();
}
List selectors = new ArrayList<>();
for (ColumnName column : columns) {
Field field = createPropertyField(entity, column);
columns.getSelector(column).ifPresent(selector ->
getCqlIdentifier(column, field).ifPresent(cqlIdentifier ->
selectors.add(getMappedSelector(selector, cqlIdentifier))));
}
if (columns.isEmpty()) {
addColumns(entity, selectors);
}
return selectors;
}
private void addColumns(CassandraPersistentEntity entity, List selectors) {
entity.doWithProperties((PropertyHandler) property -> {
if (property.isCompositePrimaryKey()) {
CassandraPersistentEntity primaryKeyEntity = mappingContext.getRequiredPersistentEntity(property);
addColumns(primaryKeyEntity, selectors);
} else {
selectors.add(ColumnSelector.from(property.getRequiredColumnName().toCql()));
}
});
}
private Selector getMappedSelector(Selector selector, CqlIdentifier cqlIdentifier) {
if (selector instanceof ColumnSelector) {
ColumnSelector columnSelector = (ColumnSelector) selector;
ColumnSelector mappedColumnSelector = ColumnSelector.from(cqlIdentifier);
return columnSelector.getAlias().map(mappedColumnSelector::as).orElse(mappedColumnSelector);
}
if (selector instanceof FunctionCall) {
FunctionCall functionCall = (FunctionCall) selector;
List