2016年10月2日 星期日

JForum advanced search照日期排序的bug

JForum的Search的功能似乎有些bug,在search時如果選擇以date來做排序的話,會不管選正排還是逆排,結果都是沒有照date來排序,事實上它總是以(id)主鍵來排序的,應該算是一個bug。

經過追蹤程式碼及上網搜尋相關資料後,找到了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;
 }


參考資料:

  1. JForum 搜索时按时间排序的问题解决
  2. Bug for searching with descending order

Google Chrome Web Push (客製化訊息 - 需加密)

Google Chrome Web Push是Google的一項服務,可以讓管理者向User主動推播訊息,
跟之前介紹的對Android GCM推播很像(如何向GCM Server傳送資料如何接收GCM Server發送的Registration ID訊息,以php、Java及JSP為例,以php、Java及JSP為例),事實上Google Chrome Web Push目前也是利用GCM服務來實現的。

Google Chrome Web Push可以寫死彈跳訊息的code在client端(註冊時會記錄),也可以動態訊息給client,不過因為安全的問題,必須使用Google規定的方式來加密訊息。

在這邊就來一步步展示如何做一個簡單的Google Chrome Web Push實現: