发送 Search 请求
更新 domain/gateway/book_manager.go:
@@ -12,4 +12,5 @@ import (
// BookManager manages all books
type BookManager interface {
IndexBook(ctx context.Context, b *model.Book) (string, error)
+ SearchBooks(ctx context.Context, query string) ([]*model.Book, error)
}
更新 infrastructure/search/es.go:
@@ -5,8 +5,11 @@ package search
import (
"context"
+ "encoding/json"
"github.com/elastic/go-elasticsearch/v8"
+ "github.com/elastic/go-elasticsearch/v8/typedapi/core/search"
+ "github.com/elastic/go-elasticsearch/v8/typedapi/types"
"literank.com/fulltext-books/domain/model"
)
@@ -42,3 +45,29 @@ func (s *ElasticSearchEngine) IndexBook(ctx context.Context, b *model.Book) (str
}
return resp.Id_, nil
}
+
+// SearchBooks search from ES and return a list of books
+func (s *ElasticSearchEngine) SearchBooks(ctx context.Context, query string) ([]*model.Book, error) {
+ resp, err := s.client.Search().Index(INDEX_BOOK).
+ Request(&search.Request{
+ Query: &types.Query{
+ MultiMatch: &types.MultiMatchQuery{
+ Query: query,
+ Fields: []string{"title", "author", "content"},
+ },
+ },
+ }).
+ Do(ctx)
+ if err != nil {
+ return nil, err
+ }
+ books := make([]*model.Book, 0)
+ for _, hit := range resp.Hits.Hits {
+ var b model.Book
+ if err := json.Unmarshal(hit.Source_, &b); err != nil {
+ return nil, err
+ }
+ books = append(books, &b)
+ }
+ return books, nil
+}
此处我们使用 MultiMatch
来进行多字段查询:“title”,“author”和“content”。
更新 application/executor/book_operator.go:
@@ -24,3 +24,7 @@ func NewBookOperator(b gateway.BookManager) *BookOperator {
func (o *BookOperator) CreateBook(ctx context.Context, b *model.Book) (string, error) {
return o.bookManager.IndexBook(ctx, b)
}
+
+func (o *BookOperator) SearchBooks(ctx context.Context, query string) ([]*model.Book, error) {
+ return o.bookManager.SearchBooks(ctx, query)
+}
更新 adapter/router.go:
@@ -14,6 +14,8 @@ import (
"literank.com/fulltext-books/domain/model"
)
+const fieldQuery = "q"
+
// RestHandler handles all restful requests
type RestHandler struct {
bookOperator *executor.BookOperator
@@ -31,6 +33,7 @@ func MakeRouter(wireHelper *application.WireHelper) (*gin.Engine, error) {
// Create a new Gin router
r := gin.Default()
+ r.GET("/books", rest.searchBooks)
r.POST("/books", rest.createBook)
return r, nil
}
@@ -51,3 +54,13 @@ func (r *RestHandler) createBook(c *gin.Context) {
}
c.JSON(http.StatusCreated, gin.H{"id": bookID})
}
+
+func (r *RestHandler) searchBooks(c *gin.Context) {
+ books, err := r.bookOperator.SearchBooks(c, c.Query(fieldQuery))
+ if err != nil {
+ fmt.Printf("Failed to search books: %v\n", err)
+ c.JSON(http.StatusNotFound, gin.H{"error": "failed to search books"})
+ return
+ }
+ c.JSON(http.StatusOK, books)
+}
重启后再次使用 curl
测试:
curl 'http://localhost:8080/books?q=katniss+hunger'
样例响应:
[
{
"title": "The Hunger Games",
"author": "Suzanne Collins",
"published_at": "2008-09-14",
"content": "In a dystopian future, teenagers are forced to participate in a televised death match called the Hunger Games. Katniss Everdeen volunteers to take her sister‘s place and becomes a symbol of rebellion."
}
]
http://localhost:8080/books?q=new%20york%20circus%20girl
在 URL 编码中,
%20
和+
都可以表示空格,但是它们的使用场景稍有区别。
赞!你刚刚完成了一个全文检索。
注意:
全文检索需要语言分词器。默认的标准分词器对中、日、韩文等语言处理效果不理想。 可以使用语言特定的分词器以获取更佳效果。
Loading...
> 此处输出代码运行结果