Как задать условие фильтрации по дистанции на карте с использованием функции `earth_box` в JPA?

Как правильно использовать спецификации (фреймворк RSQL) для добавления условия запроса фильтрации в Java? Как вызвать метод "earth_box" для фильтрации точек из таблицы и какие типы Java возвращают функции "ll_to_earth" и "earth_box"? Как написать сравнение с оператором "@>"? 

Пример кода, который мне дан, использует спецификации для создания фильтрационного запроса.
  • 13 декабря 2023 г. 8:52
Ответы на вопрос 2
Для задания условия фильтрации по дистанции на карте с использованием функции `earth_box` в JPA можно использовать следующий код:

```java
import com.querydsl.core.types.dsl.BooleanExpression;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

public class EarthBoxSpecification implements Specification<YourEntity> {

    private final double latitude;
    private final double longitude;
    private final double distance;

    public EarthBoxSpecification(double latitude, double longitude, double distance) {
        this.latitude = latitude;
        this.longitude = longitude;
        this.distance = distance;
    }

    @Override
    public Predicate toPredicate(Root<YourEntity> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
        double earthRadius = 6371; // Earth radius in kilometers
        double distanceInDegrees = distance / earthRadius;

        // Convert latitude and longitude of the center point to earth coordinates
        CriteriaBuilder.Coalesce<Double> latitudeEarth = criteriaBuilder.coalesce();
        latitudeEarth.value(criteriaBuilder.function("ll_to_earth", Double.class, criteriaBuilder.literal(latitude), criteriaBuilder.literal(0)));
        CriteriaBuilder.Coalesce<Double> longitudeEarth = criteriaBuilder.coalesce();
        longitudeEarth.value(criteriaBuilder.function("ll_to_earth", Double.class, criteriaBuilder.literal(0), criteriaBuilder.literal(longitude)));

        // Create a box around the center point with the specified distance
        CriteriaBuilder.Coalesce<?> box = criteriaBuilder.coalesce();
        box.value(criteriaBuilder.function("earth_box", Boolean.class, latitudeEarth, longitudeEarth, criteriaBuilder.literal(distanceInDegrees)));

        // Use the ">=" operator to check if a point is inside the box
        return criteriaBuilder.ge(criteriaBuilder.literal(true), box);
    }
}
```

Чтобы использовать спецификации (фреймворк RSQL) для добавления условия запроса фильтрации в Java, вам потребуется подключить зависимость для RSQL, например:

```xml
<dependency>
    <groupId>cz.jirutka.rsql</groupId>
    <artifactId>rsql-parser</artifactId>
    <version>0.9.6</version>
</dependency>
```

Затем вы можете использовать спецификации, чтобы создать условия фильтрации на основе строки запроса RSQL. Например:

```java
import cz.jirutka.rsql.parser.RSQLParser;
import cz.jirutka.rsql.parser.ast.Node;
import cz.jirutka.rsql.parser.ast.RSQLVisitor;
import org.springframework.data.jpa.domain.Specification;

public class RsqlSpecification<T> implements Specification<T> {

    private final Node node;

    public RsqlSpecification(String rsql) {
        RSQLParser parser = new RSQLParser();
        this.node = parser.parse(rsql);
    }

    @Override
    public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
        RSQLVisitor<Predicate, Root<T>> visitor = new YourRsqlVisitor<T>();
        return node.accept(visitor, root);
    }
}
```

Реализация `YourRsqlVisitor` предоставляет конкретные условия фильтрации на основе узлов RSQL. Например, вы можете добавить поддержку оператора "@>" следующим образом:

```java
import cz.jirutka.rsql.parser.ast.ComparisonOperator;
import cz.jirutka.rsql.parser.ast.ComparisonOperatorProxy;
import cz.jirutka.rsql.parser.ast.RSQLOperators;
import cz.jirutka.rsql.parser.ast.RSQLVisitor;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

public class YourRsqlVisitor<T> implements RSQLVisitor<Predicate, Root<T>> {

    private static final ComparisonOperatorProxy<Double> YOUR_OPERATOR = new ComparisonOperatorProxy<Double>("@>", true) {
        @Override
        public ComparisonOperator getOperator() {
            return RSQLOperators.EQUAL;
        }

        @Override
        protected Double parseSingleValue(String value) {
            // Parse your comparison value here, e.g. Double.parseDouble(value)
            return Double.parseDouble(value);
        }
    };

    @Override
    public Predicate visit(ComparisonNode node, Root<T> root) {
        ComparisonOperator operator = node.getOperator();

        if (operator.equals(YOUR_OPERATOR.getOperator())) {
            String property = node.getSelector();
            Double value = YOUR_OPERATOR.parseSingleValue(node.getArguments().get(
Решение. 
Регистрируем новую функцию в диалекте, у меня hibernate 6.3.1:
Меняем в конфигурациях диалект на свой:

Дальше дописываем условие:
Похожие вопросы