7

I need to add a predicate to my list of existing predicates for a JSONB column.

Entity:

@Entity
@Table(name = "a")
@TypeDefs({
 @TypeDef(name = "jsonb", typeClass = JsonBinaryType.class),
})
public class EntityA {
 @Id
 @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "a_id_seq")
 @SequenceGenerator(sequenceName = "a_id_seq", allocationSize = 1, name = "a_id_seq")
 @Column(name = "id")
 private long id;
 @Column(name = "name")
 private String name;
 @Column(name = "json")
 @Type(type = "jsonb")
 private Json json;
 private static class Json {
 private String name;
 private Integer number;
 private String random;
 }
}

Specification:

 public Specification<EntityA> buildSpecification(Filter filter){
 return (root, query, cb) -> {
 List<Predicate> predicates = new ArrayList<>();
 Expression<String> nameExpression = root.get("name");
 Predicate namePredicate = nameExpression.in(filter.getNames());
 predicates.add(namePredicate);
//TODO add a predicate for JSONB here
 return cb.and(predicates.toArray(new Predicate[0]));
 };

My input will be a List jsonNames or List jsonNumbers and I want to build CriteriaBuilder.In with this input and fetch any matches.

Filter:

@Data
public class Filter {
 private List<String> names;
 private List<String> jsonNames;
 private List<Integer> jsonNumbers;
}
asked Aug 5, 2019 at 3:10

1 Answer 1

10

For PostgreSQL

Predicate inJsonNumbers = cb
 .function("jsonb_extract_path_text", 
 String.class, 
 root.get("json"), 
 cb.literal("number"))
 .in(filter.getJsonNumbers())
Predicate inJsonNames = cb
 .function("jsonb_extract_path_text", 
 String.class, 
 root.get("json"), 
 cb.literal("name"))
 .in(filter.getJsonNames())
answered Aug 5, 2019 at 6:08
Sign up to request clarification or add additional context in comments.

13 Comments

thanks @nikolay-shevchenko! this works for jsonNames but not for the jsonNumbers and throws the below error- Caused by: org.hibernate.exception.SQLGrammarException: could not extract ResultSet.. Caused by: org.postgresql.util.PSQLException: ERROR: operator does not exist: text = integer Hint: No operator matches the given name and argument type(s). You might need to add explicit type casts.
also, is it possible to combine both conditions in one predicate? I added a third field "random" but I need only matches with "name" and "number" as one predicate.
@Niv you can combine predicates using cb.and() and cb.or()
As for your exception -- probably that's because you store numbers as strings inside JSON. "name": "A", "number": "45" instead of "name": "A", "number": 45. If so, and if you don't wanna change the data, you should convert filter.getJsonNumbers() to list of String-s before passing it to cb.function()
@NikolaiShevchenko How to get this working for array objects. I want to get a field from json array in nested json. Is there any way or function to do that?
|

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.