Reputation: 1290
I would like to build a hibernate query over multiple entities with entity-specific restrictions. Ideally, all results would be returned from the same query so that they could be sorted against each other by relevance.
The entities are Event, Group, and Place (each subclass of Postable) and Profile. The restriction on Events is that they start today or in the future. The restriction on Postables is that they all have a visibility of PostableVisibility.PUBLIC.
The Profile class does not have a visibility field and only the Event class has a schedule.startDate. Also, Profiles and Postables have different fields to search (with 'name' in common). I have been able to create visibility/start date restrictions individually but could not apply both restrictions and get results from all classes.
Here's some detail on the domain model:
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@Table(name = "postables")
public abstract class Postable{
/**
* determines who can see the postable in relation to the creatorId
*/
@Column(name = "visibility", nullable = false)
@Enumerated(EnumType.STRING)
@Field(analyze=Analyze.NO)
protected PostableVisibility visibility;
@Column(name = "name", nullable = false)
@Field
protected String name;
@Column(name = "type", nullable = false)
@Field
protected String type;
@Column(name = "description", nullable = false)
@Field
protected String description;
}
@Entity
@Table(name = "events")
@Indexed
public class Event extends Postable {
@IndexedEmbedded
private Schedule schedule;
}
@Embeddable
public class Schedule {
@Column(name = "end_date")
private LocalDate endDate;
@Column(name = "end_time")
private LocalTime endTime;
@Column(name = "start_date")
@Field
private LocalDate startDate;
@Column(name = "start_time")
@Field
private LocalTime startTime;
}
@Entity
@Table(name = "groups")
@Indexed
public class Group extends Postable {
}
@Entity
@Table(name = "places")
@Indexed
public class Place extends Postable {
}
public class Profile {
@Column(name = "name")
@Field
private String name;
@Column(name = "username")
@Field(analyze= Analyze.NO)
private String username;
}
And finally, the attempt to build a query. I've tried this with different combinations of query builders but no luck. With the query below, the user query, visibility restriction, and current event restriction are all applied but I only get results for Events.
public List<Object> searchAll(String userQueryString) {
int maxResults = 20;
Session session = this.currentSession();
FullTextSession fullTextSession = Search.getFullTextSession(session);
// Query builders:
QueryBuilder postableQB = fullTextSession.getSearchFactory().buildQueryBuilder().forEntity(Postable.class).get();
QueryBuilder eventQB = fullTextSession.getSearchFactory().buildQueryBuilder().forEntity(Event.class).get();
// Filter queries
Query publicPostableQuery = postableQB.keyword().onField("visibility").matching(PostableVisibility.PUBLIC).createQuery();
String defaultTimeZone = "America/New_York";
LocalDate today = LocalDate.now(ZoneId.of(defaultTimeZone));
Query currentEventQuery = eventQB.range().onField("schedule.startDate").above(today).createQuery();
// User text query
Query userQuery = postableQB.simpleQueryString().onField("name").boostedTo(5f) // working
.andField("type").boostedTo(3f)
.andField("description")
.andField("username").boostedTo(5f)
.matching(userQueryString)
.createQuery();
Query combinedQuery = postableQB.bool()
.must(userQuery)
.must(publicPostableQuery)
.must(currentEventQuery)
.createQuery();
FullTextQuery fullTextQuery = fullTextSession.createFullTextQuery(combinedQuery, Postable.class, Profile.class);
fullTextQuery.setFirstResult(0).setMaxResults(maxResults);
return fullTextQuery.list();
}
Upvotes: 2
Views: 2117
Reputation: 9977
Your current query requires that all results have a startDate
in the future, even if they are not Events. Since non-Events don't have a startDate
, they don't match.
A better way to express what you want would be to ask that none of the results has a startDate
in the past. That way, documents without a startDate
will match too. The same goes for visibility, which is not included in a Profile.
Query privatePostableQuery = postableQB.bool()
.should(postableQB.keyword().onField("visibility").matching(PostableVisibility.FOLLOWERS).createQuery())
.should(postableQB.keyword().onField("visibility").matching(PostableVisibility.INVITE).createQuery())
.createQuery();
String defaultTimeZone = "America/New_York";
LocalDate today = LocalDate.now(ZoneId.of(defaultTimeZone));
Query pastEventQuery = eventQB.range().onField("schedule.startDate").below(today).excludeLimit().createQuery();
// User text query
Query userQuery = postableQB.simpleQueryString().onField("name").boostedTo(5f) // working
.andField("type").boostedTo(3f)
.andField("description")
.andField("username").boostedTo(5f)
.matching(userQueryString)
.createQuery();
Query combinedQuery = postableQB.bool()
.must(userQuery)
.must(privatePostableQuery).not()
.must(pastEventQuery).not()
.createQuery();
Upvotes: 1