We have the following situation
Given the following two entities
#Indexed
#Spatial(spatialMode = SpatialMode.HASH)
#Entity
#Table(name = "address")
Address{
#Field
#Basic
#Column(name = "state")
private String state;
#Field
#Basic
#Column(name = "town_city")
private String townCity;
#Field
#Longitude
#Basic
#Column(name = "x_coord")
private Double xCoord;
#Field
#Latitude
#Basic
#Column(name = "y_coord")
private Double yCoord;
}
And
#Indexed
#Entity
#Table(name = "person")
Person{
#Field
#Column(name = "weight")
private Double weight;
#Column(name = "age")
private Integer age;
#org.hibernate.annotations.Cache(usage =
org.hibernate.annotations.CacheConcurrencyStrategy.READ_WRITE)
#ManyToMany
#Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE})
#JoinTable(name = "person_address",
joinColumns = {#JoinColumn(name = "person_id")},
inverseJoinColumns = {#JoinColumn(name = "address_id")})
private Set<Address> addressSet = new HashSet<>();
}
Getters and Setters rest of the fields omitted
We want to return in our search results as an example people within a 5KM radius of a given position who are also within an age range.
So
FullTextSession fullTextSession = Search.getFullTextSession(entityManagerFactory.unwrap(SessionFactory.class).openSession());
this.queryBuilder = fullTextSession.getSearchFactory()
.buildQueryBuilder().forEntity(Person.class)
.overridesForField("identifiers.identifier_edge", "identifier_query_analyzer")
.get();
this.bool = queryBuilder.bool();
LocalDateTime lowerLocalDateTime = localDateTime.withYear(localDateTime.getYear() - upperAge);
lowerDate = Date.from(lowerLocalDateTime.atZone(ZoneId.systemDefault()).toInstant());
LocalDateTime upperLocalDateTime = localDateTime.withYear(localDateTime.getYear() - lowerAge);
upperDate = Date.from(upperLocalDateTime.atZone(ZoneId.systemDefault()).toInstant());
bool.must(getQueryBuilder().range().onField("datesOfBirth.dateOfBirth").from(lowerDate).to(upperDate).createQuery());
which will give us people within the relevant age range
We have a separte query to get the address id's within a radius around a given point
public Set<Integer> getSpatialAddressResults(SpatialSearchCommand spatialSearchCommand) {
FullTextSession fullTextSession = Search.getFullTextSession(entityManagerFactory.unwrap(SessionFactory.class).openSession());
this.userSearchPreference = userSearchPreference;
this.queryBuilder = fullTextSession.getSearchFactory()
.buildQueryBuilder().forEntity(Address.class)
.get();
this.bool = queryBuilder.bool();
Set<Integer> addressIdSet = new HashSet<>();
bool.must(getQueryBuilder().spatial()
.within(spatialSearchCommand.getRadius(), Unit.KM).ofLatitude
(spatialSearchCommand.getLat()).andLongitude(spatialSearchCommand.getLng()).createQuery());
FullTextQuery fullTextQuery =
fullTextSession.createFullTextQuery(bool.createQuery(), Address.class)
.setProjection("addressId")
.initializeObjectsWith(ObjectLookupMethod.SECOND_LEVEL_CACHE,
DatabaseRetrievalMethod.QUERY);
List results = fullTextQuery.list();
for (Object result : results) {
Object[] arrayResult = (Object[]) result;
addressIdSet.add(((Integer) arrayResult[0]));
}
if (addressIdSet.size() == 0) {
addressIdSet.add(-1);
}
return addressIdSet;
}
Which we use like below (in reality these are done in separate classes but for simplicity I have just shown the relevant code
Set<Integer> localAddressIds = getSpatialAddressResults(new SpatialSearchCommand(userSearchPreference.getRadius(), userSearchPreference.getLat(), userSearchPreference.getLng()));
if(localAddressIds.size() > 0){
BooleanJunction<BooleanJunction> localSquQueryBool = getQueryBuilder().bool();
for (Integer localAddressId : localAddressIds) {
localSquQueryBool.should(getQueryBuilder().keyword().onField("currentLocation.address.indexId").matching(localAddressId).createQuery());
if(!personSearchCommand.getCurrentOnly()){
localSquQueryBool.should(getQueryBuilder().keyword().onField("locations.address.indexId").matching(localAddressId).createQuery());
}
}
bool.must(localSquQueryBool.createQuery());
}
The problem is there can be a huge amount of addresses returned which results in a BooleanQueryTooManyClauses: maxClauseCount is set to 1024
The question really is what is the best way to combined queries on two different indexed entities to avoid problems like above.
Essentially, you are trying to implement a join operation. As you can see, there are technical challenges to joins which are not easily solved on the client side.
Generally, the recommended approach in Elasticsearch and Lucene is to avoid joins when you can. Instead, you will de-normalize your schema: within the document representing each person, embed a copy of every address. Then you will be able to express all your constraints in a single query targeting the person index.
This is done by annotating the addresses property in Person with #IndexedEmbedded.
Now, as you can imagine, this de-normalization comes at a cost: whenever an address is changed, Hibernate Search has to update the relevant person.
To that end, you will need to add a List<Person> property to your Address class and annotate it with #ContainedIn in order for Hibernate Search to be able to fetch the persons to reindex whenever an address is modified.
In short, change your model to this:
//#Indexed // No longer needed
#Spatial(spatialMode = SpatialMode.HASH, name = "location") // Give a name to the spatial field
#Entity
#Table(name = "address")
Address {
// Add this
#ManyToMany(mappedBy = "addressSet")
#ContainedIn
private Set<Person> personSet = new HashSet<>();
#Field
#Basic
#Column(name = "state")
private String state;
#Field
#Basic
#Column(name = "town_city")
private String townCity;
//#Field// This is not necessary
#Longitude
#Basic
#Column(name = "x_coord")
private Double xCoord;
//#Field// This is not necessary
#Latitude
#Basic
#Column(name = "y_coord")
private Double yCoord;
}
#Indexed
#Entity
#Table(name = "person")
Person{
#Field
#Column(name = "weight")
private Double weight;
#Column(name = "age")
private Integer age;
#org.hibernate.annotations.Cache(usage =
org.hibernate.annotations.CacheConcurrencyStrategy.READ_WRITE)
#ManyToMany
#Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE})
#JoinTable(name = "person_address",
joinColumns = {#JoinColumn(name = "person_id")},
inverseJoinColumns = {#JoinColumn(name = "address_id")})
#IndexedEmbedded // Add this
private Set<Address> addressSet = new HashSet<>();
#Transient
#IndexedEmbedded // Also add this
public Address getCurrentAddress() {
// This was missing in your schema, I suppose it's a getter that picks the current address from addressSet?
}
}
Then reindex. Your Person documents will now have two new fields: addressSet.location and currentAddress.location.
Then write your query like this:
FullTextSession fullTextSession = Search.getFullTextSession(entityManagerFactory.unwrap(SessionFactory.class).openSession());
this.queryBuilder = fullTextSession.getSearchFactory()
.buildQueryBuilder().forEntity(Person.class)
.overridesForField("identifiers.identifier_edge", "identifier_query_analyzer")
.get();
this.bool = queryBuilder.bool();
LocalDateTime lowerLocalDateTime = localDateTime.withYear(localDateTime.getYear() - upperAge);
lowerDate = Date.from(lowerLocalDateTime.atZone(ZoneId.systemDefault()).toInstant());
LocalDateTime upperLocalDateTime = localDateTime.withYear(localDateTime.getYear() - lowerAge);
upperDate = Date.from(upperLocalDateTime.atZone(ZoneId.systemDefault()).toInstant());
bool.must(getQueryBuilder().range().onField("datesOfBirth.dateOfBirth").from(lowerDate).to(upperDate).createQuery());
SpatialSearchCommand spatialSearchCommand = new SpatialSearchCommand(userSearchPreference.getRadius(), userSearchPreference.getLat(), userSearchPreference.getLng());
// The magic happens below
BooleanJunction<BooleanJunction> localSquQueryBool = getQueryBuilder().bool();
localSquQueryBool.should(getQueryBuilder().spatial()
.onField("currentAddress.location")
.within(spatialSearchCommand.getRadius(), Unit.KM)
.ofLatitude(spatialSearchCommand.getLat())
.andLongitude(spatialSearchCommand.getLng())
.createQuery());
if(!personSearchCommand.getCurrentOnly()) {
localSquQueryBool.should(getQueryBuilder().spatial()
.onField("addressSet.location")
.within(spatialSearchCommand.getRadius(), Unit.KM)
.ofLatitude(spatialSearchCommand.getLat())
.andLongitude(spatialSearchCommand.getLng())
.createQuery());
}
bool.must(localSquQueryBool.createQuery());
Related
Im new in Java Spring and I'm stuck at this point here for a while:
I have an entity model like this:
Entity Relation now
A channellist contains many channels. A channel can be assigned to many channellists. I have setup my code that this works fine. Now I would like to add a property "sort" to my channels, so I would be able to order the channels for every specific channellist in a way I would like to. From the database relation model I know I have to store this information in the joining table "Channellist_Channel".
MY PROBLEM IS: I dont understand how I'm able asign this property to my Entity "Channel" so that every Channel has a "sort"-value depending on the context of the channellist. I read for this case, I have to add a new Entity which will represent the join table "Channellist_Channel" and add the property "sort" there. But the puzzle in my head is just not complete to do it :/
Here are my two entitys Channellist and Channel
CHANNELLIST:
#Entity
#Table(name = "channellist", schema = "stream")
public class Channellist {
#Id
#Column(name = "ID")
private int id;
#Column(name = "DISPLAYNAME")
private String displayName;
#ManyToMany
#JoinTable(
schema = "stream",
name = "Channellist_Channel",
joinColumns = #JoinColumn(name = "channellist_id"),
inverseJoinColumns = #JoinColumn(name = "channel_id")
)
private Set<Channel> channels;
//Constructors...
//Getter Setter...
CHANNEL:
#Entity
#Table(name = "channel", schema = "stream")
public class Channel {
#Id
#Column(name = "id")
private String id;
#Column(name = "display_name")
private String displayName;
#Column(name = "type")
private int type;
#Column(name = "logo_url")
private String logoUrl;
#Column(name = "stream_url")
private String streamUrl;
#OneToMany(mappedBy = "channelId", fetch = FetchType.LAZY)
#OrderBy(value = "timeStart ASC")
private Set<ChannelEpg> programs;
#JsonIgnore
#ManyToMany
#JoinTable(
schema = "stream",
name = "Channellist_Channel",
joinColumns = #JoinColumn(name = "channel_id"),
inverseJoinColumns = #JoinColumn(name = "channellist_id")
)
private Set<Channellist> channellists;
//Constructors...
//Getters Setters...
My JSON Response from "GetAllChannellists: Please Ignore the TAG "programs" under "channels". Its not relevant for my problem.
JSON RESPONSE:
[
{"id":1,
"displayName":"Standard",
"channels":[
{"id":"344143862749137509158c22d1606ad5",
"displayName":"KiKa",
"type":0,
"logoUrl":"https://example.de/test/kika.png",
"streamUrl":"https://example.de/test/kika.m3u8",
"programs":[
{"channelId":"344143862749137509158c22d1606ad5",
"timeStart":"2022-08-09T11:30:00.000+00:00",
"timeEnd":"2022-08-09T11:40:00.000+00:00",
"title":"logo!",
"subTitle":null,
"description":"a verry long description, no one cares"},
{"channelId":"344143862749137509158c22d1606ad5",
"timeStart":"2022-08-09T11:40:00.000+00:00",
"timeEnd":"2022-08-09T12:10:00.000+00:00",
"title":"Tiere bis unters Dach",
"subTitle":"Jojo, der Held",
"description":"another long description, no one cares"},
[...]
{"id":2,
"displayName":"Deluxe",
"channels":[
[...]
My goal is it to make it look like this:
[
{"id":1,
"displayName":"Standard",
"channels":[
{"id":"344143862749137509158c22d1606ad5",
"displayName":"KiKa",
"sort":21,
"type":0,
"logoUrl":"https://example.de/test/kika.png",
"streamUrl":"https://example.de/test/kika.m3u8",
I am working on a Spring Boot project using Spring Data JPA and Hibernate mapping. I have the following doubt about how can I implement the following query.
I have an User entity class like this:
#Entity
#Table(name = "portal_user")
#Getter
#Setter
public class User implements Serializable {
private static final long serialVersionUID = 5062673109048808267L;
#Id
#Column(name = "id")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
#Column(name = "first_name")
#NotNull(message = "{NotNull.User.firstName.Validation}")
private String firstName;
#Column(name = "middle_name")
private String middleName;
#Column(name = "surname")
#NotNull(message = "{NotNull.User.surname.Validation}")
private String surname;
#Column(name = "sex")
#NotNull(message = "{NotNull.User.sex.Validation}")
private char sex;
#Column(name = "birthdate")
#NotNull(message = "{NotNull.User.birthdate.Validation}")
private Date birthdate;
#Column(name = "tax_code")
#NotNull(message = "{NotNull.User.taxCode.Validation}")
private String taxCode;
#Column(name = "e_mail")
#NotNull(message = "{NotNull.User.email.Validation}")
private String email;
#Column(name = "pswd")
#NotNull(message = "{NotNull.User.pswd.Validation}")
private String pswd;
#Column(name = "contact_number")
#NotNull(message = "{NotNull.User.contactNumber.Validation}")
private String contactNumber;
#Temporal(TemporalType.DATE)
#Column(name = "created_at")
private Date createdAt;
#Column(name = "is_active")
private boolean is_active;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "user", orphanRemoval = true)
#JsonManagedReference
private Set<Address> addressesList = new HashSet<>();
#ManyToMany(cascade = { CascadeType.MERGE })
#JoinTable(
name = "portal_user_user_type",
joinColumns = { #JoinColumn(name = "portal_user_id_fk") },
inverseJoinColumns = { #JoinColumn(name = "user_type_id_fk") }
)
Set<UserType> userTypes;
#ManyToOne(fetch = FetchType.EAGER)
#JsonProperty("subagent")
private User parent;
public User() {
super();
}
public User(String firstName, String middleName, String surname, char sex, Date birthdate, String taxCode,
String email, String pswd, String contactNumber, Date createdAt, boolean is_active) {
super();
this.firstName = firstName;
this.middleName = middleName;
this.surname = surname;
this.sex = sex;
this.birthdate = birthdate;
this.taxCode = taxCode;
this.email = email;
this.pswd = pswd;
this.contactNumber = contactNumber;
this.createdAt = createdAt;
this.is_active = is_active;
}
}
The instances of this class represents users of my system. An user can have a single specific parent (the concept is similar to that of a referral: an user can bring another user in the system). This is handled by this ManyToOne recursive relationship:
#ManyToOne(fetch = FetchType.EAGER)
#JsonProperty("subagent")
private User parent;
Basically an user contains is parent (who bring him\her into the platform). It works fine. So retrieving an user I can easily retrieve the information of who is its parent (it is contained into the retrieved User object).
Now I need to implement the inverse behavior: I have to define a "query" that starting from a parent retrieve all its children.
The previous User entity class maps the following DB table:
The highlighter parent_id contains the FK that define this recursive relationship. So it contains the PK of another user that is the parent.
I have this UserRepository repository interface (it extents the JpaRepository interface)
public interface UsersRepository extends JpaRepository<User, Integer> {
User findByemail(String email);
List<User> findByUserTypes_TypeName(String typeName);
}
As you can see I am using a "query by method" style. Is it possiblem implement a behavior like this using "query by method" style? (in case also JPQL could be fine)
You can do this
List<User> findByParent_Id(Integer id);
Or you can do this
#Query("SELECT u FROM User u WHERE u.id = ?1")
List<User> getReferredUsers(Integer id);
The relationship between the user and the parent is unidirectional in the given code. By making it bidirectional, it is easy to query the data in either ways.
Refer to below code to make it bidirectional. Also ensure the relevant FetchType to avoid the performance risk. Here FetchType.LAZY is used for one to many association so it queries the data using the proxy reference when needed.
#ManyToOne(fetch = FetchType.EAGER)
#JsonProperty("subagent")
#JsonBackReference
private User parent;
#JsonManagedReference
#OneToMany(fetch = FetchType.LAZY, mappedBy = "parent")
private Set<User> userSet = new HashSet<>();
Child entities are fetched only when parent.getUserSet is used because of the FetchType.Lazy
public Set<User> getUsers(int id) {
User parent = userRepository.getById(id);
return parent.getUserSet();
}
I am using Hibernate Search with Spring Boot to create a searchable rest api. Trying to POST an instance of "Training", I receive the following stack traces. None of the two are very insightful to me which is why I am reaching out for help.
Stack trace:
https://pastebin.com/pmurg1N3
It appears to me that it is trying to index a null entity!? How can that happen? Any ideas?
The entity:
#Entity #Getter #Setter #NoArgsConstructor
#ToString(onlyExplicitlyIncluded = true)
#Audited #Indexed(index = "Training")
#AnalyzerDef(name = "ngram",
tokenizer = #TokenizerDef(factory = StandardTokenizerFactory.class ),
filters = {
#TokenFilterDef(factory = StandardFilterFactory.class),
#TokenFilterDef(factory = LowerCaseFilterFactory.class),
#TokenFilterDef(factory = StopFilterFactory.class),
#TokenFilterDef(factory = NGramFilterFactory.class,
params = {
#Parameter(name = "minGramSize", value = "2"),
}
)
}
)
#Analyzer(definition = "ngram")
public class Training implements BaseEntity<Long>, OwnedEntity {
#Id
#GeneratedValue
#ToString.Include
private Long id;
#NotNull
#RestResourceMapper(context = RestResourceContext.IDENTITY, path = "/companies/{id}")
#JsonProperty(access = Access.WRITE_ONLY)
#JsonDeserialize(using = RestResourceURLSerializer.class)
private Long owner;
#NotNull
#Field(index = Index.YES, analyze = Analyze.YES, store = Store.YES)
private String name;
#Column(length = 10000)
private String goals;
#Column(length = 10000)
private String description;
#Enumerated(EnumType.STRING)
#Field(index = Index.YES, store = Store.YES, analyze = Analyze.NO, bridge=#FieldBridge(impl=EnumBridge.class))
private Audience audience;
#Enumerated(EnumType.STRING)
#Field(index = Index.YES, store = Store.YES, analyze = Analyze.NO, bridge=#FieldBridge(impl=EnumBridge.class))
private Level level;
#ManyToMany
#Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
#NotNull #Size(min = 1)
#IndexedEmbedded
private Set<ProductVersion> versions;
#NotNull
private Boolean enabled = false;
#NotNull
#Min(1)
#IndexedEmbedded
#Field(index = Index.YES, store = Store.YES, analyze = Analyze.NO)
#NumericField
private Integer maxStudents;
#NotNull
#ManyToOne(fetch = FetchType.LAZY)
private Agenda agenda;
#NotNull
#Min(1)
#Field(index = Index.YES, store = Store.YES, analyze = Analyze.NO)
#NumericField
private Integer durationDays;
#IndexedEmbedded
#Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
#ManyToMany(cascade = CascadeType.PERSIST)
private Set<Tag> tags = new HashSet<>();
I'd say either your versions collection or your tags collection contains null objects, which is generally not something we expect in a Hibernate ORM association, and apparently not something Hibernate Search expects either.
Can you check that in debug mode?
I have two entities. (Find code below)
I am trying to write a query that would count customDetails=:myCriteria of EntitiesA that are associated to EntityB of specific id.
I have written the necessary query using session.CreateSQLQuery that reads the associated_entitites table, however, I am unable to use it as the customDetails column is encrypted by hibernate's #ColumnTransformer and returns a BLOB. And I cannot replicate it in HQL as associated_entities is not mapped.
a
#Entity
public class entityA{
#Id
private int id;
#Column
#ColumnTransformer
private CustomDetails customDetails;
#ManyToMany(fetch = FetchType.EAGER,
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
},
mappedBy = "entitiesA")
private List<entityB> entitiesB;
//getters and setters
}
b
#Entity
public class entityB{
#Id
private int id;
#JoinTable(name = "associated_entities",
joinColumns = { #JoinColumn(name = "entityA_id") },
inverseJoinColumns = { #JoinColumn(name = "entityB_id") })
private List<EntityA> entitiesA;
//getters and setters
}
The solution I have found, but is not ideal as the logic is not done by hibernate. Had to write the logic in the DAOImpl.
Example code:
public Long getQuery(String criteria, String, fromdate, String todate){
Query theQuery = currentSession.createQuery(
"from EntityA a "+
"where a.CustomDetails >= :from "+
"and a.CustomDetails <= :to");
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
LocalDate From = LocalDate.parse(fromDate, formatter);
LocalDate To = LocalDate.parse(toDate, formatter);
theQuery.setParameter("from", From);
theQuery.setParameter("to", To);
Long count = (long)0;
List<EntityA> entities= theQuery.list();
for(EntityA EA:entities) {
for(EntityB EB: EA.getEntityB()) {
if(EB.someValue().equals(criteria)) count++;
}
}
return count;
Another solution I have found and is much preferred as the logic is performed by hibernate, which I have found to be a lot more faster, is to use two separate queries and utilise where :foo in elements()
Code example below (not matching question example, but idea and use of elements() should be clear)
Query<Object1> q1 = currentSession.createQuery("from Object1 o where o.objectNumber= :objectNumber");
q1.setParameter("objectNumber", objectNumber);
Object1 obj1 = q1.getSingleResult();
Query<Long> q2 = currentSession.createQuery("select count(id) from Object2 o where :object1param in elements(o.associatedObjects));
q2.setParameter("object1param ", obj1);
I try to sort a list of Items for a customer by ordered Date. The Date is only avalable through Item.orderPositions.order.orderDate . But #IndexedEmbedded doesn't work. There's no Exeption or Error but the result is only sorted by HS-logic.
#Entity
#Indexed
public class Item{
#Id
private long id;
#Field(index = Index.YES, store = Store.YES, analyse = Analyse.YES, analyser = #Analyzer(definition = Constant.ANALYSER))
private String description;
#OneToMany(mappedBy = "item")
#IndexedEmbedded
private List<OrderPosition> orderPositions;
#ManyToOne
#IndexedEmbedded
private Company company;
//getter&setter
}
#Entity
public class OrderPosition{
#Id
private long id;
#ManyToOne
private Item item;
#ManyToOne
#IndexedEmbedded
private Order order;
//getter&setter
}
#Entity
public class Order{
#Id
private long id;
#ManyToOne
private Customer customer;
#Field(index = Index.NO, store = Store.NO, analyze = Analyze.NO)
#SortableField
private String orderDate;
//getter&setter
}
#Entity
public class Company{
#Id
private long id;
#Field(index = Index.NO, store = Store.NO, analyze = Analyze.NO)
#SortableField
private String name;
//getter&setter
}
If I sort the List by Item.company.name it works fine.
queryService.buildFullTextQuery("searchText", Item.class, "description", "company.name").getResultList();
If I sort the List by Item.orderPosition.order.orderDate it's sorted by default(HS-logic)
queryService.buildFullTextQuery("searchText", Item.class, "description", "orderPositions.order.orderDate").getResultList();
I build the FullTextQuery this way:
public FullTextQuery buildFullTextQuery(#NonNull String searchText, #NonNull Class<?> clazz, #NonNull String... fields) throws Exception {
FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(getEntityManager());
QueryBuilder qb = fullTextEntityManager.getSearchFactory().buildQueryBuilder().forEntity(clazz).get();
Query query = qb.keyword().onField(fields[0]).matching(searchText).createQuery();
SortField sortField = new SortField(fields[1], SortField.Type.STRING, false);
Sort sort = new Sort(sortField);
return fullTextEntityManager.createFullTextQuery(query, clazz).setSort(sort);
}
I think HS can't find the association for #OneToMany. Is there a way to solve this prob?
Thank you in advance
I can't tell you what's going on exactly without the results of your queries, but you're definitely doing something wrong here: you are trying to sort on a multi-valued field. One item is linked to multiple orders, each having its own date. So there is multiple dates per item.
When you ask to compare two items that each have three dates, what should Hibernate Search do? Compare only the latest dates? Compare only the earliest dates? You didn't say, so your query is bound to return inconsistently ordered results.
Thing is, there is no way to tell Hibernate Search which value to pick in multi-valued fields, so your easiest way out is to explicitly create a single-valued field to sort on.
For instance, you could add a getter on Item to return the latest order, and add the #IndexedEmbedded there:
#Entity
#Indexed
public class Item{
#Id
private long id;
#Field(index = Index.YES, store = Store.YES, analyse = Analyse.YES, analyser = #Analyzer(definition = Constant.ANALYSER))
private String description;
#OneToMany(mappedBy = "item")
#IndexedEmbedded
private List<OrderPosition> orderPositions;
#ManyToOne
#IndexedEmbedded
private Company company;
#javax.persistence.Transient
public Order getLatestOrder() {
Order latestOrder;
// ... compute the latest order ...
return latestOrder;
}
//getter&setter
}
Then sort on latestOrder.orderDate and you should be good.