Polishing.

This commit is contained in:
Peter-Josef Meisch 2023-12-12 18:58:34 +01:00
parent 1d6a1b0f2f
commit fb9ccf7b44
No known key found for this signature in database
GPG Key ID: DE108246970C7708

View File

@ -15,8 +15,6 @@
*/
package org.springframework.data.elasticsearch.repository.query.parser;
import java.util.Collection;
import java.util.Iterator;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.geo.GeoBox;
@ -35,6 +33,9 @@ import org.springframework.data.repository.query.parser.Part;
import org.springframework.data.repository.query.parser.PartTree;
import org.springframework.lang.Nullable;
import java.util.Collection;
import java.util.Iterator;
/**
* ElasticsearchQueryCreator
*
@ -47,159 +48,159 @@ import org.springframework.lang.Nullable;
*/
public class ElasticsearchQueryCreator extends AbstractQueryCreator<CriteriaQuery, CriteriaQuery> {
private final MappingContext<?, ElasticsearchPersistentProperty> context;
private final MappingContext<?, ElasticsearchPersistentProperty> context;
public ElasticsearchQueryCreator(PartTree tree, ParameterAccessor parameters,
MappingContext<?, ElasticsearchPersistentProperty> context) {
super(tree, parameters);
this.context = context;
}
public ElasticsearchQueryCreator(PartTree tree, ParameterAccessor parameters,
MappingContext<?, ElasticsearchPersistentProperty> context) {
super(tree, parameters);
this.context = context;
}
public ElasticsearchQueryCreator(PartTree tree, MappingContext<?, ElasticsearchPersistentProperty> context) {
super(tree);
this.context = context;
}
public ElasticsearchQueryCreator(PartTree tree, MappingContext<?, ElasticsearchPersistentProperty> context) {
super(tree);
this.context = context;
}
@Override
protected CriteriaQuery create(Part part, Iterator<Object> iterator) {
PersistentPropertyPath<ElasticsearchPersistentProperty> path = context.getPersistentPropertyPath(
part.getProperty());
return new CriteriaQuery(from(part,
new Criteria(path.toDotPath(ElasticsearchPersistentProperty.QueryPropertyToFieldNameConverter.INSTANCE)),
iterator));
}
@Override
protected CriteriaQuery create(Part part, Iterator<Object> iterator) {
PersistentPropertyPath<ElasticsearchPersistentProperty> path = context.getPersistentPropertyPath(
part.getProperty());
return new CriteriaQuery(from(part,
new Criteria(path.toDotPath(ElasticsearchPersistentProperty.QueryPropertyToFieldNameConverter.INSTANCE)),
iterator));
}
@Override
protected CriteriaQuery and(Part part, CriteriaQuery base, Iterator<Object> iterator) {
if (base == null) {
return create(part, iterator);
}
PersistentPropertyPath<ElasticsearchPersistentProperty> path = context.getPersistentPropertyPath(
part.getProperty());
return base.addCriteria(from(part,
new Criteria(path.toDotPath(ElasticsearchPersistentProperty.QueryPropertyToFieldNameConverter.INSTANCE)),
iterator));
}
@Override
protected CriteriaQuery and(Part part, CriteriaQuery base, Iterator<Object> iterator) {
if (base == null) {
return create(part, iterator);
}
PersistentPropertyPath<ElasticsearchPersistentProperty> path = context.getPersistentPropertyPath(
part.getProperty());
return base.addCriteria(from(part,
new Criteria(path.toDotPath(ElasticsearchPersistentProperty.QueryPropertyToFieldNameConverter.INSTANCE)),
iterator));
}
@Override
protected CriteriaQuery or(CriteriaQuery base, CriteriaQuery query) {
return new CriteriaQuery(base.getCriteria().or(query.getCriteria()));
}
@Override
protected CriteriaQuery or(CriteriaQuery base, CriteriaQuery query) {
return new CriteriaQuery(base.getCriteria().or(query.getCriteria()));
}
@Override
protected CriteriaQuery complete(@Nullable CriteriaQuery query, Sort sort) {
@Override
protected CriteriaQuery complete(@Nullable CriteriaQuery query, Sort sort) {
if (query == null) {
// this is the case in a findAllByOrderByField method, add empty criteria
query = new CriteriaQuery(new Criteria());
}
return query.addSort(sort);
}
if (query == null) {
// this is the case in a findAllByOrderByField method, add empty criteria
query = new CriteriaQuery(new Criteria());
}
return query.addSort(sort);
}
private Criteria from(Part part, Criteria criteria, Iterator<?> parameters) {
private Criteria from(Part part, Criteria criteria, Iterator<?> parameters) {
Part.Type type = part.getType();
Part.Type type = part.getType();
return switch (type) {
case TRUE -> criteria.is(true);
case FALSE -> criteria.is(false);
case NEGATING_SIMPLE_PROPERTY -> criteria.is(parameters.next()).not();
case REGEX -> criteria.expression(parameters.next().toString());
case LIKE, STARTING_WITH -> criteria.startsWith(parameters.next().toString());
case ENDING_WITH -> criteria.endsWith(parameters.next().toString());
case CONTAINING -> criteria.contains(parameters.next().toString());
case GREATER_THAN -> criteria.greaterThan(parameters.next());
case AFTER, GREATER_THAN_EQUAL -> criteria.greaterThanEqual(parameters.next());
case LESS_THAN -> criteria.lessThan(parameters.next());
case BEFORE, LESS_THAN_EQUAL -> criteria.lessThanEqual(parameters.next());
case BETWEEN -> criteria.between(parameters.next(), parameters.next());
case IN -> criteria.in(asArray(parameters.next()));
case NOT_IN -> criteria.notIn(asArray(parameters.next()));
case SIMPLE_PROPERTY, WITHIN -> this.within(part, criteria, parameters);
case NEAR -> this.near(criteria, parameters);
case EXISTS, IS_NOT_NULL -> criteria.exists();
case IS_NULL -> criteria.not().exists();
case IS_EMPTY -> criteria.empty();
case IS_NOT_EMPTY -> criteria.notEmpty();
default -> throw new InvalidDataAccessApiUsageException("Illegal criteria found '" + type + "'.");
};
}
return switch (type) {
case TRUE -> criteria.is(true);
case FALSE -> criteria.is(false);
case NEGATING_SIMPLE_PROPERTY -> criteria.is(parameters.next()).not();
case REGEX -> criteria.expression(parameters.next().toString());
case LIKE, STARTING_WITH -> criteria.startsWith(parameters.next().toString());
case ENDING_WITH -> criteria.endsWith(parameters.next().toString());
case CONTAINING -> criteria.contains(parameters.next().toString());
case GREATER_THAN -> criteria.greaterThan(parameters.next());
case AFTER, GREATER_THAN_EQUAL -> criteria.greaterThanEqual(parameters.next());
case LESS_THAN -> criteria.lessThan(parameters.next());
case BEFORE, LESS_THAN_EQUAL -> criteria.lessThanEqual(parameters.next());
case BETWEEN -> criteria.between(parameters.next(), parameters.next());
case IN -> criteria.in(asArray(parameters.next()));
case NOT_IN -> criteria.notIn(asArray(parameters.next()));
case SIMPLE_PROPERTY, WITHIN -> within(part, criteria, parameters);
case NEAR -> near(criteria, parameters);
case EXISTS, IS_NOT_NULL -> criteria.exists();
case IS_NULL -> criteria.not().exists();
case IS_EMPTY -> criteria.empty();
case IS_NOT_EMPTY -> criteria.notEmpty();
default -> throw new InvalidDataAccessApiUsageException("Illegal criteria found '" + type + "'.");
};
}
private Criteria within(Part part, Criteria criteria, Iterator<?> parameters) {
private Criteria within(Part part, Criteria criteria, Iterator<?> parameters) {
Object firstParameter = parameters.next();
Object secondParameter;
Object firstParameter = parameters.next();
Object secondParameter;
if (part.getType() == Part.Type.SIMPLE_PROPERTY) {
if (part.getProperty().getType() != GeoPoint.class) {
if (firstParameter != null) {
return criteria.is(firstParameter);
} else {
// searching for null is a must_not (exists)
return criteria.exists().not();
}
} else {
// it means it's a simple find with exact geopoint matching (e.g. findByLocation)
// and because Elasticsearch does not have any kind of query with just a geopoint
// as argument we use a "geo distance" query with a distance of one meter.
secondParameter = ".001km";
}
} else {
secondParameter = parameters.next();
}
if (part.getType() == Part.Type.SIMPLE_PROPERTY) {
if (part.getProperty().getType() != GeoPoint.class) {
if (firstParameter != null) {
return criteria.is(firstParameter);
} else {
// searching for null is a must_not (exists)
return criteria.exists().not();
}
} else {
// it means it's a simple find with exact geopoint matching (e.g. findByLocation)
// and because Elasticsearch does not have any kind of query with just a geopoint
// as argument we use a "geo distance" query with a distance of one meter.
secondParameter = ".001km";
}
} else {
secondParameter = parameters.next();
}
return doWithinIfPossible(criteria, firstParameter, secondParameter);
}
return doWithinIfPossible(criteria, firstParameter, secondParameter);
}
private Criteria near(Criteria criteria, Iterator<?> parameters) {
private Criteria near(Criteria criteria, Iterator<?> parameters) {
Object firstParameter = parameters.next();
Object firstParameter = parameters.next();
if (firstParameter instanceof GeoBox geoBox) {
return criteria.boundedBy(geoBox);
}
if (firstParameter instanceof GeoBox geoBox) {
return criteria.boundedBy(geoBox);
}
if (firstParameter instanceof Box box) {
return criteria.boundedBy(GeoBox.fromBox(box));
}
if (firstParameter instanceof Box box) {
return criteria.boundedBy(GeoBox.fromBox(box));
}
Object secondParameter = parameters.next();
Object secondParameter = parameters.next();
return doWithinIfPossible(criteria, firstParameter, secondParameter);
}
return doWithinIfPossible(criteria, firstParameter, secondParameter);
}
/**
* Do a within query if possible, otherwise return the criteria unchanged.
*
* @param criteria must not be {@literal null}
* @param firstParameter must not be {@literal null}
* @param secondParameter must not be {@literal null}
* @return the criteria with the within query applied if possible.
* @author Junghoon Ban
*/
private Criteria doWithinIfPossible(Criteria criteria, Object firstParameter, Object secondParameter) {
/**
* Do a within query if possible, otherwise return the criteria unchanged.
*
* @param criteria must not be {@literal null}
* @param firstParameter must not be {@literal null}
* @param secondParameter must not be {@literal null}
* @return the criteria with the within query applied if possible.
* @author Junghoon Ban
*/
private Criteria doWithinIfPossible(Criteria criteria, Object firstParameter, Object secondParameter) {
if (firstParameter instanceof GeoPoint geoPoint && secondParameter instanceof String string) {
return criteria.within(geoPoint, string);
}
if (firstParameter instanceof GeoPoint geoPoint && secondParameter instanceof String string) {
return criteria.within(geoPoint, string);
}
if (firstParameter instanceof Point point && secondParameter instanceof Distance distance) {
return criteria.within(point, distance);
}
if (firstParameter instanceof Point point && secondParameter instanceof Distance distance) {
return criteria.within(point, distance);
}
if (firstParameter instanceof String firstString && secondParameter instanceof String secondString) {
return criteria.within(firstString, secondString);
}
if (firstParameter instanceof String firstString && secondParameter instanceof String secondString) {
return criteria.within(firstString, secondString);
}
return criteria;
}
return criteria;
}
private Object[] asArray(Object o) {
if (o instanceof Collection) {
return ((Collection<?>) o).toArray();
} else if (o.getClass().isArray()) {
return (Object[]) o;
}
return new Object[] { o };
}
private Object[] asArray(Object o) {
if (o instanceof Collection) {
return ((Collection<?>) o).toArray();
} else if (o.getClass().isArray()) {
return (Object[]) o;
}
return new Object[]{o};
}
}