From: Hibernate Search Order by child-count
I've got:
@Indexed
@Entity
public class TParent implements java.io.Serializable {
.....
private Set<TChild> TChildSet = new HashSet<TChild>(0);
@ContainedIn
@FieldBridge(impl = CollectionCountBridge.class)
@Field(analyze = Analyze.NO)
@OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY, mappedBy="TParent")
public Set<TChild> getTChildSet() {
return this.TChildSet;
}
and
@Indexed
@Entity
public class TChild implements java.io.Serializable {
.....
private TParent TParent;
@IndexedEmbedded
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name="parent_id", nullable=false)
public TParent getTParent() {
return this.TParent;
}
and thanks to Don Roby, I've applied this CustomBridge
public class CollectionCountBridge extends IntegerBridge {
@Override
public String objectToString(Object object) {
if (object == null || (!(object instanceof Collection))) {
return null;
}
Collection<?> coll = (Collection<?>) object;
int size = coll.size();
System.out.println("col.size(): " + size);
return super.objectToString(size);
}
}
I'm listing TParents. I'm trying to order them by the TChildSet count.
It works perfectly when I build the index first. I can list TParent's and order them by the by the number of TChild's.
FullTextQuery fullTextQuery = fullTextSession.createFullTextQuery(luceneQuery, TParent.class);
fullTextQuery.setSort(new Sort(new SortField("TChildSet", SortField.INT, true)));
Now...
When I add a new TChild, the CustomBridge code is executed. I'd expect to see the ordering change as I add TChild's.
I can see that the TChild count is increased (as per the System.out.println() in the CustomBridge code).
However, the query does not order them correctly. The original sort order from when the index was first built remains. Adding new TChild's has no effect on the sort order.
I guess the index is not being updated but I'm not sure.
Any help or pointers would be great.
Thanks Very Much
John
Edit
The immediate problem is that the value is not being updated in the index. This can be seen using Luke.
I think your problem is that you index the number (integer) as string, but you want to have numeric ordering.
You have two options. If you stay with the string approach you will need to create padded strings (eg if you know you never have more than 100 elements you would pad all numbers like 003, etc). This way the lexical ordering will behave as the numerical one.
Probably the better alternative is to index the count as a numeric field. This way the sorting will be automatically numeric. Something like (untested):
public class CollectionCountFieldBridge {
void set(String name, Object value, Document document, LuceneOptions luceneOptions) {
if (object == null || (!(object instanceof Collection))) {
return null;
}
Collection<?> coll = (Collection<?>) object;
int size = coll.size();
luceneOptions.addNumericFieldToDocument( name, value, document );;
}
}
The Problem
In an Entity, performing an update to an index field that is not a DB table field then saving the Entity will not result in an index update. (Saving the Entity with an update to an index field that is a DB table field will cause an index update automatically).
Solution
(Simply) Manually update the index
public static void addTChild() {
Session session = null;
try {
session = HibernateUtil.getSession();
session.beginTransaction();
TParent tParent = (TParent)session.get(TParent.class, 6L);
TChild tChild = new TChild(tParent, "Auto added " + new Date().toString(), 'y');
Long id = (Long)session.save(tChild);
tChild = (TChild)session.get(TChild.class, id);
tParent.getTChildSet().add(tChild);
session.save(tParent);
/*
* Manually update the index.
* Necessary due to fact that the index field updated
* is not a DB table field and hence will not cause an index update
*/
FullTextSession fullTextSession = Search.getFullTextSession(session);
fullTextSession.index(tParent);
session.getTransaction().commit();
} catch (Exception ex) {
session.getTransaction().rollback();
ex.printStackTrace();
} finally{
if(session != null) {
session.close();
}
}
}
The Entities:
/*
* TParent
*/
@ContainedIn
@FieldBridge(impl = CollectionCountBridge.class)
@Field(analyze = Analyze.NO, store = Store.YES, name = "cCount")
@OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY, mappedBy="TParent")
public List<TChild> getTChildSet() {
return this.TChildSet;
}
and:
/*
* TChild
*/
@IndexedEmbedded
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name="parent_id", nullable=false)
public TParent getTParent() {
return this.TParent;
}
And that's it as far as I can determine.