1. 程式人生 > >壹立方商城----實現商品搜尋功能

壹立方商城----實現商品搜尋功能

1.效果展示

啟動專案,進入淘淘商城首頁,我們搜尋小米

然後商品搜尋頁面就會展示,所有搜尋到的商品(圖片無法顯示,是因為圖片地址失效了,可以顯示的是我自己新增的)

搜尋時,會有關鍵字分詞+高亮

還有分頁效果

2.功能分析

我們在e3-portal-web首頁展示中分析,輸入關鍵字後,點選搜尋後到底發生了什麼。

由於搜尋框是通用的,在搜尋結果頁面,上面也會有搜尋框,所以搜尋相關程式碼在header.jsp

在header.jsp中,使用者點選搜尋,或者按回車(event.keyCode==13),會將id=key提交到search函式。search函式在header.jsp唯一引入的base-v1.js中

我們搜尋search函式,發現在第33行。encodeURIComponent(document.getElementById(a).value)獲取到id=a(也就是key)的值,進行url編碼就是"%xx%xxx"這種,然後拼接到"http://localhost:8085/search.html?q="後面。window.location.href表示跳轉至url。

所以我們知道,當用戶點選搜尋時,跳轉"http://localhost:8085/search.html?q=xxx",並攜帶查詢關鍵字q,所以我們只需要,接收關鍵字,從索引庫查詢即可。這裡有個隱含的引數 "page頁碼",在搜尋結果頁面用於分頁,在搜尋頁面僅僅是搜尋,並不設定及

,所以預設page=1,搜尋時預設搜尋第一頁。

3.dao層

3.1功能分析

訪問索引庫的類。定義一些通用的資料訪問方法。

業務邏輯就是查詢索引庫。

引數:SolrQuery物件

業務邏輯:

  1. 根據Query物件進行查詢。
  2. 返回查詢結果。包括List<SearchItem>、查詢結果的總記錄數。

需要把返回結果封裝到pojo中,至少包含兩個屬性:List<SearchItem>、Long recordCount

再包含一個總頁數。

建立如下SearchResult物件,放入e3-common中

SearchResult.java

package cn.e3mall.common.pojo;

import java.io.Serializable;
import java.util.List;

public class SearchResult implements Serializable {

	private long recordCount;//總記錄數
	private int totalPages;//總頁數
	private List<SearchItem> itemList;//搜尋結果列表
	public long getRecordCount() {
		return recordCount;
	}
	public void setRecordCount(long recordCount) {
		this.recordCount = recordCount;
	}
	public int getTotalPages() {
		return totalPages;
	}
	public void setTotalPages(int totalPages) {
		this.totalPages = totalPages;
	}
	public List<SearchItem> getItemList() {
		return itemList;
	}
	public void setItemList(List<SearchItem> itemList) {
		this.itemList = itemList;
	}
	
}

3.2建立SearchDao

由於搜尋功能只在搜尋工程中用到,可以不寫介面,只寫類。返回值:SearchResult

在e3-search-service中建立com.taotao.search.dao包,在包中SearchDao建立用於訪問索引庫

從service層接收封裝好的SolrQuery查詢索引庫,獲取QueryResponse,從QueryResponse獲取SolrDocumentList,在從SolrDocumentList中獲取solr業務域中的欄位,並取出service層封裝的高亮,封裝成List<SearchItem>,放入SearchResult中SearchDao.java

package cn.e3mall.search.dao;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrServer;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.web.bind.annotation.ResponseBody;

import cn.e3mall.common.pojo.SearchItem;
import cn.e3mall.common.pojo.SearchResult;

/**
 * 商品搜尋dao
 * <p>
 * Title: SearchDao
 * </p>
 * 
 * @version 1.0
 */
@Repository
public class SearchDao {

	@Autowired
	private SolrServer solrServer;

	/**
	 * 根據SolrQuery查詢索引庫,封裝SearchResult的itemList、recordCount
	 * <p>
	 * Title: search
	 * </p>
	 * <p>
	 * Description:
	 * </p>
	 * 
	 * @param query
	 * @return
	 */
	public SearchResult search(SolrQuery query) throws Exception {
		// 1、根據query查詢索引庫
		QueryResponse queryResponse = solrServer.query(query);
		// 2、獲取商品列表
		SolrDocumentList solrDocumentList = queryResponse.getResults();
		// 3、取查詢結果總記錄數
		long numFound = solrDocumentList.getNumFound();
		SearchResult result = new SearchResult();
		result.setRecordCount(numFound);
		// 取商品列表,需要取高亮顯示
		Map<String, Map<String, List<String>>> highlighting = queryResponse.getHighlighting();
		// 封裝List<SearchItem>
		List<SearchItem> itemList = new ArrayList<>();
		for (SolrDocument solrDocument : solrDocumentList) {
			SearchItem item = new SearchItem();
			item.setId((String) solrDocument.get("id"));
			item.setCategory_name((String) solrDocument.get("item_category_name"));
			item.setImage((String) solrDocument.get("item_image"));
			item.setPrice((long) solrDocument.get("item_price"));
			item.setSell_point((String) solrDocument.get("item_sell_point"));
			// 取高亮顯示
			List<String> list = highlighting.get(solrDocument.get("id")).get("item_title");
			String title = "";
			if (list != null && list.size() > 0) {
				title = list.get(0);
			} else {
				title = (String) solrDocument.get("item_title");
			}
			item.setTitle(title);
			// 新增到商品列表
			itemList.add(item);
		}
		result.setItemList(itemList);
		// 返回結果
		return result;
	}

}

4.service層

4.1功能分析

引數:queryString:查詢條件

      Page:頁碼

      Rows:每頁顯示的記錄數。

業務邏輯:

  1. 建立一個SolrQuery物件。
  2. 設定查詢條件
  3. 設定分頁條件
  4. 需要指定預設搜尋域。
  5. 設定高亮
  6. 執行查詢,呼叫SearchDao。得到SearchResult
  7. 需要計算總頁數。
  8. 返回SearchResult

4.2建立service介面

在e3-search-interface的SearchService建立介面

SearchService.java

package cn.e3mall.search.service;

import cn.e3mall.common.pojo.SearchResult;

public interface SearchService {
	/**
	 * 根據查詢條件查詢
	 * @param queryString
	 * @param page
	 * @param rows
	 * @return
	 * @throws Exception
	 */
	SearchResult search(String keyword, int page, int rows)  throws Exception;
}

4.3建立service實現類

在e3-search-service的cn.e3mall.search.service.impl包的SearchServiceImpl實現介面

要注意注入searchDao依賴

設定查詢索引庫的主查詢條件和分頁條件,還需要設定高亮,最後獲取SearchResult封裝總頁數

package cn.e3mall.search.service.impl;

import org.apache.solr.client.solrj.SolrQuery;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import cn.e3mall.common.pojo.SearchResult;
import cn.e3mall.search.dao.SearchDao;
import cn.e3mall.search.service.SearchService;

/**
 * 商品搜尋Service
 * <p>
 * Title: SearchServiceImpl
 * </p>
 * 
 * @version 1.0
 */
@Service
public class SearchServiceImpl implements SearchService {

	@Autowired
	private SearchDao searchDao;

	@Override
	public SearchResult search(String keyword, int page, int rows) throws Exception {
		// 建立一個SolrQuery物件
		SolrQuery query = new SolrQuery();
		// 設定查詢條件
		query.setQuery(keyword);
		// 設定分頁條件
		if (page <= 0)
			page = 1;
		query.setStart((page - 1) * rows);
		query.setRows(rows);
		// 設定預設搜尋域
		query.set("df", "item_title");
		// 開啟高亮顯示
		query.setHighlight(true);
		query.addHighlightField("item_title");
		query.setHighlightSimplePre("<em style=\"color:red\">");
		query.setHighlightSimplePost("</em>");
		// 呼叫dao執行查詢
		SearchResult searchResult = searchDao.search(query);
		// 計算總頁數
		long recordCount = searchResult.getRecordCount();
		int totalPage = (int) (recordCount / rows);
		if (recordCount % rows > 0)
			totalPage++;
		// 新增到返回結果
		searchResult.setTotalPages(totalPage);
		// 返回結果
		return searchResult;
	}

}

4.4applicationContext-service.xml

dao層包掃描與釋出service服務

配置包掃面的時候,可以擴大包掃描的範圍

<!-- 配置包掃描器 -->
<context:component-scan base-package="cn.e3mall.search" />
<!-- 宣告需要暴露的服務介面 釋出服務 服務層暴露出來以後,表現層(web層)才能引用 -->
<dubbo:service interface="cn.e3mall.search.service.SearchItemService" ref="searchItemServiceImpl" timeout="600000" />

5.表現層

5.1匯入搜尋結果靜態頁面

5.2引入服務

5.3建立controller

請求的url:/search

引數:

1、q 查詢條件。

2、page 頁碼。預設為1

返回值:

邏輯檢視,返回值。String。

業務邏輯:

接收引數 呼叫服務查詢商品列表 把查詢結果傳遞給頁面。需要引數回顯。 在e3-search-web建立cn.e3mall.search.controller包,在cn.e3mall.search.controller建立SearchController

cn.e3mall.search.controller.SearchController.java

package cn.e3mall.search.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import cn.e3mall.common.pojo.SearchResult;
import cn.e3mall.search.service.SearchService;

/**
 * 商品搜尋Controller
 * <p>
 * Title: SearchController
 * </p>
 * 
 * @version 1.0
 */
@Controller
public class SearchController {

	@Autowired
	private SearchService searchService;

	@Value("${SEARCH_RESULT_ROWS}")
	private Integer SEARCH_RESULT_ROWS;

	@RequestMapping("/search")
	public String searchItemList(String keyword, @RequestParam(defaultValue = "1") Integer page, Model model)
			throws Exception {
		//get請求中文亂碼解決
		keyword = new String(keyword.getBytes("iso-8859-1"), "utf-8");
		// 查詢商品列表
		SearchResult searchResult = searchService.search(keyword, page, SEARCH_RESULT_ROWS);
		// 把結果傳遞給頁面
		model.addAttribute("query", keyword);
		model.addAttribute("totalPages", searchResult.getTotalPages());
		model.addAttribute("page", page);
		model.addAttribute("recourdCount", searchResult.getRecordCount());
		model.addAttribute("itemList", searchResult.getItemList());

		// 返回邏輯檢視
		return "search";
	}
}

5.4配置分頁大小

分頁大小放入配置檔案中

/e3-search-web/src/main/resources/conf/resource.properties

#搜尋結果每頁顯示的記錄數
SEARCH_RESULT_ROWS=60

還需要在springmvc.xml載入配置檔案

<!-- 載入配置檔案 -->
<context:property-placeholder location="classpath:conf/resource.properties" />

5.5測試

發現無法翻頁。

翻頁處理:在e3-search-web工程中:修改如下:

修改url的埠,在這裡可以看到前面說的page引數

5.6圖片無法顯示 這裡說的是有圖片的情況無法顯示。

資料庫中儲存的圖片是以逗號分隔的url列表,只需要展示第一張圖片即可。

方法:

  1. 向索引庫中新增文件時,只取第一張寫入索引庫
  2. 從文件列表轉換為商品列表時可以取一張。
  3. 在jsp中對列表拆分,只取一張展示。

可以在SearchItem中新增一個getImages方法:

public String[] getImages() {
		if(this.image != null&&!this.image.equals("")) {
			String[] strings = this.image.split(",");
			return strings;
		}
		return null;
	}

使用el表示式執行方法,獲取第一張圖片

參考文章