經過追蹤程式碼及上網搜尋相關資料後,找到了bug的產生原因,特別在這邊做個紀錄並提供一個個人的解決方法。
bug發生原因:
JForum使用了Lucene來實做文章(Post)檢索搜尋功能,Lucene的實做是沒有問題的,在search時選擇以date來做ASC及DESC排序時,Lucene的確是返回正確排序的post_id列表,但是JForum在用它自己寫的Sql語法去找對應這些post_id的Post detail詳細資料時,使用了IN [post_id]的寫法,造成了找出Post detail後,排序又變成以主鍵排序的順序了。
我們先來看一下出問題的地方,首先是
GenericLuceneDAO.java的getPostData(int[] postIds)方法,輸入參數postIds是Lucene返回的以正確排序的post_id,但在要取得對應的Post detail時,使用了SearchModel.getPostsDataForLucene這個Sql語法,這個語法寫在generic_queries.sql,內容為
SearchModel.getPostsDataForLucene = SELECT p.post_id, p.forum_id, p.topic_id, p.user_id, u.username, p.enable_bbcode, p.enable_smilies, p.post_time, pt.post_subject, pt.post_text, t.topic_title \ FROM jforum_posts p, jforum_posts_text pt, jforum_users u, jforum_topics t \ WHERE p.post_id IN (:posts:) \ AND p.post_id = pt.post_id \ AND p.topic_id = t.topic_id \ AND p.user_id = u.user_Id
可以看到在Sql語法中用了 p.post_id IN (:posts:) 這樣的語句( :posts: 字串會被替換掉),所以不管post_ids怎麼排序,Sql語法回傳的結果還是照著主鍵排。
我的解法是修改GenericLuceneDAO.java的getPostsData()方法,在Sql語法查完post detail後,再將它照著postIds的順序重新丟到一個新的ArrayList回傳,這樣這個方法回傳的結果就可以被修正成正確的了。
原本有bug的程式
public List getPostsData(int[] postIds)
{
if (postIds.length == 0) {
return new ArrayList();
}
List l = new ArrayList();
PreparedStatement p = null;
ResultSet rs = null;
try {
String sql = SystemGlobals.getSql("SearchModel.getPostsDataForLucene");
sql = sql.replaceAll(":posts:", this.buildInClause(postIds));
p = JForumExecutionContext.getConnection().prepareStatement(sql);
rs = p.executeQuery();
while (rs.next()) {
Post post = this.makePost(rs);
post.setPostUsername(rs.getString("username"));
l.add(post);
}
}
catch (SQLException e) {
throw new DatabaseException(e);
}
finally {
DbUtils.close(rs, p);
}
return l;
}
修正後的程式
public List getPostsData(int[] postIds)
{
if (postIds.length == 0) {
return new ArrayList();
}
List l = new ArrayList();
Post[] posts_orderByLucene = new Post[postIds.length];
PreparedStatement p = null;
ResultSet rs = null;
try {
String sql = SystemGlobals.getSql("SearchModel.getPostsDataForLucene");
sql = sql.replaceAll(":posts:", this.buildInClause(postIds));
p = JForumExecutionContext.getConnection().prepareStatement(sql);
rs = p.executeQuery();
while (rs.next()) {
Post post = this.makePost(rs);
post.setPostUsername(rs.getString("username"));
for (int i = 0; i< postIds.length ; i++){
if (postIds[i] == post.getId()){
posts_orderByLucene[i] = post;
break;
}
}
}
l = Arrays.asList(posts_orderByLucene);
}
catch (SQLException e) {
throw new DatabaseException(e);
}
finally {
DbUtils.close(rs, p);
}
return l;
}
參考資料:
沒有留言 :
張貼留言