I have an entity Message with a one-to-many relation to an entity Header. How can I create a tuple based search query like
(message.headerKey="foo" and message.headerValue="123") and
(message.headerKey="bar" and message.headerValue="456")
My current logic would also match when I swap the header values in my search criteria
(message.headerKey="foo" and message.headerValue="456") and
(message.headerKey="bar" and message.headerValue="123")
How can I do a tuple based query using the Hibernate Search API?
This is my Message Entity:
@Entity
@Table(name="MESSAGE")
@Indexed
public class MessageEntity implements Serializable {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
@Column(name="id")
private Long id;
@Column(name="message_timestamp")
private Date timestamp;
@Column(name="payload")
@Field(index=Index.YES, analyze=Analyze.YES, store=Store.NO)
private String payload;
@OneToMany(cascade = { CascadeType.PERSIST, CascadeType.MERGE }, mappedBy = "message")
@IndexedEmbedded
private List<HeaderEntity> headers;
// Getters and Setters
}
This is my Header Entity:
@Entity
@Table(name="HEADER")
public class HeaderEntity implements Serializable {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
@Column(name="header_key")
@Field(index=Index.YES, analyze=Analyze.YES, store=Store.NO)
private String headerKey;
@Column(name="header_value")
Field(index=Index.YES, analyze=Analyze.YES, store=Store.NO)
private String headerValue;
@ManyToOne(cascade=CascadeType.ALL)
@JoinColumn(name="message_id")
private MessageEntity message;
// Getters and Setters
}
This is my search logic:
public List<MessageEntity> search(Header[] headers) {
FullTextEntityManager fullTextEntityManager = org.hibernate.search.jpa.Search.getFullTextEntityManager(mgr);
QueryBuilder qb = fullTextEntityManager.getSearchFactory().buildQueryBuilder().forEntity(MessageEntity.class).get();
TermMatchingContext onFieldKey = qb.keyword().onField("headers.headerKey");
TermMatchingContext onFieldValue = qb.keyword().onField("headers.headerValue");
BooleanJunction<BooleanJunction> bool = qb.bool();
org.apache.lucene.search.Query query = null;
for (Header header : headers) {
bool.must(onFieldKey.matching(header.getKey()).createQuery());
bool.must(onFieldValue.matching(header.getValue()).createQuery());
}
query = bool.createQuery();
FullTextQuery persistenceQuery = fullTextEntityManager.createFullTextQuery(query, MessageEntity.class);
persistenceQuery.setMaxResults(10);
return persistenceQuery.getResultList();
}
Your approach will indeed not work. The problem is that Lucene is a flat data structure, in particular associations (embedded entities) are just "added" to the Lucene
Documentof the owning entity. In your case theMessageEntitydocument will contain two fields perheaderKeyrespectivelyheaderValue. Once with "foo" and "bar" as value and56" as values. once with "123" and "456" as values. There is no notion that two of these values are acutally a pair.One potential solution is to create a unique field/value pair. Using a custom class bridge you could create a "keyValueField" containing header key and value as concatenated value. In your query you would then target this field using concatenated query parameters.