Как верно создать поиск в JSONB с использованием Criteria API

у меня вопрос как можно работать с jsonb колонкой, в которой массив json элементов, например колонка имеет такие данные [{"key1": "value1"}, {"key2": "value2"},...].

Использую postgresql, hibernate, jpa

как я могу создать toPredicate чтобы получить отфильтрованные данные, которые имеют в значении, например value1?

Я пробивал сделать вот так, но данный код дает исключение при использовании:

public class MyEntityWithValue implements Specification<MyEntity> {
    private String valueToFind;

    public MyEntityWithValue(String valueToFind) {
        this.valueToFind= valueToFind;
    }

    @Override
    public Predicate toPredicate(Root<MyEntity> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
        if (valueToFind== null || valueToFind.isEmpty()) {
            return builder.conjunction();
        }

        Subquery<MyEntity> subquery = query.subquery(MyEntity.class);
        Root<MyEntity> subRoot = subquery.from(MyEntity.class);

        Expression<Boolean> jsonContains = builder.function(
                "json_contains",
                Boolean.class,
                subRoot.get("columnWithJsonB"),
                builder.literal("{\"key1\":\"" + valueToFind + "\"}")
        );

        subquery.select(subRoot)
                .where(jsonContains);

        return builder.exists(subquery);
    }
}

unexpected AST node: function (json_contains)

использую я так:

public class MyCriterion {

    private String columnWithJsonB;
}

private Specification<MyEntity> createSpecification(MyCriterion criterion) {
        Specification<MyEntity> spec = Specification.where(new MyEntityWithValue (criterion.getColumnWithJsonB()));
        return spec;
    }
@Override
    public Page<MyEntityResponseDTO> findAllWithPaginationAndSorting(MyCriterion myCriterion, int page, int perPage, List<String> sortBy, String sortOrder) {
        Pageable pageable = PageRequest.of(page - 1, perPage, Sort.by(createSortOrder(sortBy, sortOrder)));
        Page<MyEntity> mye = myERepo.findAll(createSpecification(myCriterion), pageable);

        return mye.map(MyEntityResponseDtoMapper::toDto);
    }

Буду благодарен за любую подсказку


Ответы (1 шт):

Автор решения: Дмитрий Рихтер

Попробуйте такой вариант подогнать под себя:

@Override
public Predicate toPredicate(Root root, CriteriaQuery<?> query, CriteriaBuilder cb) {
    return cb.equal(
        cb.function("jsonb_extract_path_text", String.class, root.get("attributes"), cb.literal(key)),
        value
    );
}

Добавлю что поле JSONB можно представить не только как String

@Column(columnDefinition = "jsonb")
private String attributes;

но и как объект

@Column(columnDefinition = "jsonb")
private MyObject attributes;

где

public record MyObject(String key1, String key2) {}

Источник: https://www.baeldung.com/spring-data-jpa-querying-jsonb-columns

→ Ссылка