Spring Data Solr 教程: 排序

JeOam發表於2014-02-19

當實現一個字元查詢功能時,一般都會以搜尋結果的質量高低來降序顯示 relevancy of each search result。 這也正是Solr的做法。
然而,有時也需要採用手工干預的方式來調整展示順序。其中一個場景就是在之前文中 previous part of my Spring Data Solr tutorial。 提到的”常規”檢索的實現
該文說明了如何使用 Spring Data Solr 來分類查詢結果。具體就是說明如何通過修改示例應用的查詢功能以使用id欄位的值來降序展示查詢結果。
文章可以分為三大塊:

  • 第一部分講解了如何在查詢語句中設定排序條件
  • 第二部分說的是使用查詢方法時如何排序查詢結果
  • 第三部分告訴我們如何進行動態查詢結果的排序

我們們繼續往下看

定義排序條件

查詢語句中的排序是通過 Sort 類來實現的。常見的排序方式如下:

  • 按單個欄位結果排序
  • 當按多個不同欄位的排序結果相同時按多個欄位組合排序
  • 當按多個不同欄位的排序結果不同時按多個欄位組合排序

下來看看如何建立實現上述條件的Sort物件
首先,建立一個按單欄位排序的Sort物件。假設我們想按照Id欄位的值來升序排列,實現程式碼如下:

new Sort(Sort.Direction.ASC, "id")

其次,建立滿足場景二中的Sort物件。這裡假設我們使用id 和description 為查詢欄位並將結果以降序排列。實現程式碼如下:

new Sort(Sort.Direction.DESC, "id", "description")

最後,建立滿足場景三的Sort物件。這裡假設降序排列description升序排列id 欄位的查詢結果。實現程式碼如下:

new Sort(Sort.Direction.DESC, "description").and(new Sort(Sort.Direction.ASC, "id"))

現在知道了如何建立相應的Sort物件,再來看看如何在具體情況中的使用。

查詢方法結果排序

當使用查詢方法時,可以按以下步驟來排序查詢結果:

  1. 在方法體中加入Sort引數。用來傳遞排序設定
  2. 在服務層建立一個Sort物件,當呼叫查詢方法時將其作為變數傳遞過去

下面具體看一下是如何實現的.

修改Repository介面

我們可以通過在查詢方法體中新增一個變數來控制將來查詢結果的排序情況。下面來看看查詢方法的定義:

從方法名派生查詢語句

如果使用了從方法名生成查詢語句的策略,那需要在TodoDocumentRepositoryinterface的
findByTitleContainsOrDescriptionContains() 方法中新增Sort引數。原始碼如下:

import org.springframework.data.domain.Sort;
import org.springframework.data.solr.repository.Query;
import org.springframework.data.solr.repository.SolrCrudRepository;

import java.util.List;

public interface TodoDocumentRepository extends PartialUpdateRepository, SolrCrudRepository<TodoDocument, String> {

    public List<TodoDocument> findByTitleContainsOrDescriptionContains(String title, String description, Sort sort);
}

命名查詢語句

如果使用命名查詢語句的策略,那需要在odoDocumentRepository 的thefindByNamedQuery() 方法中新增Sort引數。原始碼如下:

import org.springframework.data.domain.Sort;
import org.springframework.data.solr.repository.Query;
import org.springframework.data.solr.repository.SolrCrudRepository;

import java.util.List;

public interface TodoDocumentRepository extends PartialUpdateRepository, SolrCrudRepository<TodoDocument, String> {

    @Query(name = "TodoDocument.findByNamedQuery")
    public List<TodoDocument> findByNamedQuery(String searchTerm, Sort sort);
}

注意: 如果使用的是Spring Data Solr RC1該功能不能正常工作源於 known bug.
如此,要麼使用snapshot依賴或是使用RC2版本。

@Query 註解

如果使用@Query註解模式,我們要在theTodoDocumentRepository 介面中的 findByQueryAnnotation() 方法中新增Sort 引數。原始碼如下:

import org.springframework.data.domain.Sort;
import org.springframework.data.solr.repository.Query;
import org.springframework.data.solr.repository.SolrCrudRepository;

import java.util.List;

public interface TodoDocumentRepository extends PartialUpdateRepository, SolrCrudRepository<TodoDocument, String> {

    @Query("title:*?0* OR description:*?0*")
    public List<TodoDocument> findByQueryAnnotation(String searchTerm, Sort sort);
}

注意: 如果使用的是Spring Data Solr RC1該功能不能正常工作源於 known bug。如此,要麼使用snapshot依賴或是使用RC2版本。

查詢方法

通過修改RepositoryIndexService 類中的thesearch() 方法實現修改查詢方法體

  1. 建立sortByIdDesc()方法實現以文件的id為關鍵字降序排列結果
  2. 通過呼叫 TodoDocumentRepository介面中定義的查詢方法獲取查詢結果
  3. 返回查詢結果集

再來看看search()方法的不同實現方式。

從方法名稱生成查詢

當我們按照方法名稱策略通過查詢生成來構造我們的查詢時,可以使用TodoDocumentRepository介面的findByTitleContainsOrDescriptionContains() 方法獲得查詢結果。
RepositoryTodoIndexService 類的相關部分原始碼大致如下:

import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;

@Service
public class RepositoryTodoIndexService implements TodoIndexService {

    @Resource
    private TodoDocumentRepository repository;

    @Override
    public List<TodoDocument> search(String searchTerm) {
        return repository.findByTitleContainsOrDescriptionContains(searchTerm, searchTerm, sortByIdDesc());
    }

    private Sort sortByIdDesc() {
        return new Sort(Sort.Direction.DESC, "id");
    }

    //Other methods are omitted
}

命名查詢
當我們用命名查詢構建我們的查詢時,我們可以利用TodoDocumentRepository介面的findByNamedQuery()方法得到查詢結果。
RepositoryTodoIndexService 相關部分的程式碼如下:

import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;

@Service
public class RepositoryTodoIndexService implements TodoIndexService {

    @Resource
    private TodoDocumentRepository repository;

    @Override
    public List<TodoDocument> search(String searchTerm) {
        return repository.findByNamedQuery(searchTerm, sortByIdDesc());
    }

    private Sort sortByIdDesc() {
        return new Sort(Sort.Direction.DESC, "id");
    }

    //Other methods are omitted
}

@Query 註解
當我們使用@Query註解來構建我們的查詢時,我們可以使用TodoDocumentRepository介面的findByQueryAnnotation()方法來獲取查詢結果。
RepositoryTodoIndexService 類的相關部分如下:

import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;

@Service
public class RepositoryTodoIndexService implements TodoIndexService {

    @Resource
    private TodoDocumentRepository repository;

    @Override
    public List<TodoDocument> search(String searchTerm) {
        return repository.findByQueryAnnotation(searchTerm, sortByIdDesc());
    }

    private Sort sortByIdDesc() {
        return new Sort(Sort.Direction.DESC, "id");
    }

    //Other methods are omitted
}

排序動態查詢的查詢結果

因為 動態查詢 是通過 在倉儲介面中新增自定義方法實現的, 所以對動態查詢的結果進行排序的步驟不會影響到我們的例項應用中的Service層。
我們可以通過對我們自定義倉儲介面的實現進行如下修改來排序動態查詢的結果集。

  1. 在TodoDocumentRepositoryImpl類中新增一個 private sortByIdDesc() 方法。這個方法返回一個Sort物件,這個物件指定查詢結果集以id降序排列。
  2. 修改TodoDocumentRepositoryImpl 的search()方法.使用Query 介面的addSort()方法來設定執行查詢的排序選項,並且將建立的Sort物件作為引數傳遞給addSort()。

TodoDocumentRepositoryImpl 類的相關方法如下:

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Sort;
import org.springframework.data.solr.core.SolrTemplate;
import org.springframework.data.solr.core.query.Criteria;
import org.springframework.data.solr.core.query.SimpleQuery;
import org.springframework.stereotype.Repository;

import javax.annotation.Resource;
import java.util.List;

@Repository
public class TodoDocumentRepositoryImpl implements CustomTodoDocumentRepository {

    @Resource
    private SolrTemplate solrTemplate;

    @Override
    public List<TodoDocument> search(String searchTerm) {
        String[] words = searchTerm.split(" ");

        Criteria conditions = createSearchConditions(words);
        SimpleQuery search = new SimpleQuery(conditions);

        //SET SORT OPTIONS
        search.addSort(sortByIdDesc());

        Page results = solrTemplate.queryForPage(search, TodoDocument.class);
        return results.getContent();
    }

    private Criteria createSearchConditions(String[] words) {
        Criteria conditions = null;

        for (String word: words) {
            if (conditions == null) {
                conditions = new Criteria("id").contains(word)
                        .or(new Criteria("description").contains(word));
            }
            else {
                conditions = conditions.or(new Criteria("id").contains(word))
                        .or(new Criteria("description").contains(word));
            }
        }

        return conditions;
    }

    private Sort sortByIdDesc() {
        return new Sort(Sort.Direction.DESC, "id");
    }

    //Other methods are omitted
}

總結

現在我們已經瞭解了使用Spring Data Solr時,如何對查詢結果進行排序。本教程教會我們三件事:

  • 我們知道可以使用Sort 類來指定排序選項。
  • 我們學習到可以對query方法增加一個新的引數的查詢,來對結果進行排序。
  • 我們學習到可以給一個動態查詢設定排序選項,方法是使用Query介面的addSort()方法。

下一次,我的Spring Data Solr教程會描述如何對我們的查詢結果進行分頁
P.S. 本部落格文章中的樣例程式已經發布到Github (query方法動態查詢)。


原文: Spring Data Solr Tutorial: Sorting
轉載自:開源中國 – petert, lwei, 趙亮-碧海情天, GoodLoser, HTCKF

相關文章