精华内容
下载资源
问答
  • WEB项目中搜索功能是必不可少的,然而如果我们每次搜索都去数据库查询,将极大浪费性能,因此,我们需要一个全文搜索服务器——Solr。本博文将详细介绍Solr服务的搭建搜索功能实现。 一、Solr 1.Solr简介 Solr 是...

    概述

    WEB项目中搜索功能是必不可少的,然而如果我们每次搜索都去数据库查询,将极大浪费性能,因此,我们需要一个全文搜索服务器——Solr。本博文将详细介绍Solr服务的搭建搜索功能的实现。

    一、Solr

    1.Solr简介

    Solr 是Apache下的一个顶级开源项目,采用Java开发,它是基于Lucene的全文搜索服务器。Solr提供了比Lucene更为丰富的查询语言,同时实现了可配置、可扩展,并对索引、搜索性能进行了优化。

    Solr可以独立运行,运行在Jetty、Tomcat等这些Servlet容器中,Solr 索引的实现方法很简单,用 POST 方法向 Solr 服务器发送一个描述 Field 及其内容的 XML 文档,Solr根据xml文档添加、删除、更新索引 。Solr 搜索只需要发送 HTTP GET 请求,然后对 Solr 返回Xml、json等格式的查询结果进行解析,组织页面布局。Solr不提供构建UI的功能,Solr提供了一个管理界面,通过管理界面可以查询Solr的配置和运行情况。

    简单来说,就是Solr是一个全文检索服务器,只需要进行配置就可以实现全文检索服务。

    2.Solr的安装及配置

    详细安装过程及配置参照另一篇博客《Linux下Solr的安装及配置(CentOS为例)》

    3.Solr界面功能

    img

    img

    img

    img

    img

    4.维护索引库

    添加:添加一个json格式的文件就可以。

    修改:在solr中没有update,只需要添加一个新的文档,要求文档id和被修改文档的id一致。原理是先删除后添加。

    删除:使用xml格式。

    img

    删除两种方法:

    1、根据id删除:

    <delete>
    
    <id>test001</id>
    
    </delete>
    
    <commit/>
    

    2、根据查询删除:

    <delete>
    
    <query>*:*</query>
    
    </delete>
    
    <commit/>
    

    5.SolrJ客户端

    SolrJ是一个使Java应用程序可以轻松与Solr对话的API。SolrJ隐藏了许多连接到Solr的细节,并允许您的应用程序通过简单的高级方法与Solr进行交互。

    maven工程需要依赖solrj的jar包

    <!-- solr客户端 -->
    <dependency>
        <groupId>org.apache.solr</groupId>
        <artifactId>solr-solrj</artifactId>
    </dependency>
    

    solrj的使用

    public class SolrJTest {
    	@Test
    	public void addDocument() throws Exception{
    		//创建一个连接
    		SolrServer solrServer = new HttpSolrServer("http://192.168.10.130:8080/solr");
    		//创建一个文档对象
    		SolrInputDocument document = new SolrInputDocument();
    		document.addField("id", "test001");
    		document.addField("item_title", "测试视频");
    		document.addField("item_price", 53156);
    		//把文档对象写入索引库
    		solrServer.add(document);
    		//提交
    		solrServer.commit();
    	}
    	
    	@Test
    	public void deleteDocument() throws Exception{
    		SolrServer solrServer = new HttpSolrServer("http://192.168.10.130:8080/solr");
    		//solrServer.deleteById("test001");
    		solrServer.deleteByQuery("*:*");//删除所有
    		solrServer.commit();
    	}
    	
    	@Test
    	public void queryDocument() throws Exception{
    		SolrServer solrServer = new HttpSolrServer("http://192.168.10.130:8080/solr");
    		//创建一个查询对象
    		SolrQuery query = new SolrQuery();
    		//设置查询条件
    		query.setQuery("*:*");
    		//设置分页参数
    		query.setStart(20);
    		query.setRows(20);
    		//执行查询
    		QueryResponse response = solrServer.query(query);
    		//去查询结果
    		SolrDocumentList solrDocumentList = response.getResults();
    		System.out.println("共查到记录:"+solrDocumentList.getNumFound());
    		for (SolrDocument solrDocument : solrDocumentList) {
    			System.out.println(solrDocument.get("id"));
    			System.out.println(solrDocument.get("item_title"));
    			System.out.println(solrDocument.get("item_price"));
    			System.out.println(solrDocument.get("item_image"));
    		}
    	}
    }
    

    二、把商品信息导入到索引库

    使用java程序读取mysql数据库中的商品信息,然后创建solr文档对象,把商品信息写入索引库。

    需要发布一个服务。 为了灵活的进行分布式部署需要创建一搜素的服务工程发布 搜素服务。Taotao-search。

    1.系统架构

    img

    2.创建taotao-search工程

    img

    配置:

    pom.xml

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      <modelVersion>4.0.0</modelVersion>
      <parent>
        <groupId>com.taotao</groupId>
        <artifactId>taotao-parent</artifactId>
        <version>0.0.1-SNAPSHOT</version>
      </parent>
      <groupId>com.taotao</groupId>
      <artifactId>taotao-serach</artifactId>
      <version>0.0.1-SNAPSHOT</version>
      <packaging>war</packaging>
      <dependencies>
    		<dependency>
    			<groupId>com.taotao</groupId>
    			<artifactId>taotao-manager-mapper</artifactId>
    			<version>0.0.1-SNAPSHOT</version>
    		</dependency>
    		<!-- Spring -->
    		<dependency>
    			<groupId>org.springframework</groupId>
    			<artifactId>spring-context</artifactId>
    		</dependency>
    		<dependency>
    			<groupId>org.springframework</groupId>
    			<artifactId>spring-beans</artifactId>
    		</dependency>
    		<dependency>
    			<groupId>org.springframework</groupId>
    			<artifactId>spring-webmvc</artifactId>
    		</dependency>
    		<dependency>
    			<groupId>org.springframework</groupId>
    			<artifactId>spring-jdbc</artifactId>
    		</dependency>
    		<dependency>
    			<groupId>org.springframework</groupId>
    			<artifactId>spring-aspects</artifactId>
    		</dependency>
    		<dependency>
    			<groupId>javax.servlet</groupId>
    			<artifactId>servlet-api</artifactId>
    			<scope>provided</scope>
    		</dependency>
    		<dependency>
    			<groupId>javax.servlet</groupId>
    			<artifactId>jsp-api</artifactId>
    			<scope>provided</scope>
    		</dependency>
    		<!-- solr客户端 -->
    		<dependency>
    			<groupId>org.apache.solr</groupId>
    			<artifactId>solr-solrj</artifactId>
    		</dependency>
    	</dependencies>
    	<build>
    		<!-- 配置插件 -->
    		<plugins>
    			<plugin>
    				<groupId>org.apache.tomcat.maven</groupId>
    				<artifactId>tomcat7-maven-plugin</artifactId>
    				<configuration>
    					<port>8084</port>
    					<path>/</path>
    				</configuration>
    			</plugin>
    		</plugins>
    		<resources>
    			<resource>
    				<directory>src/main/java</directory>
    				<includes>
    					<include>**/*.properties</include>
    					<include>**/*.xml</include>
    				</includes>
    				<filtering>false</filtering>
    			</resource>
    			<resource>
    				<directory>src/main/resources</directory>
    				<includes>
    					<include>**/*.properties</include>
    					<include>**/*.xml</include>
    				</includes>
    				<filtering>false</filtering>
    			</resource>
    		</resources>
    		
    	</build>
      
    </project>
    

    其他配置参考《企业WEB项目前台工程搭建,ajax跨域的解决》 taotao-rest工程的配置,基本差不多

    3.sql语句

    SELECT
    	a.id,
    	a.title,
    	a.sell_point,
    	a.price,
    	a.image,
    	b.`name` category_name,
    	c.item_desc
    FROM
    	tb_item a
    LEFT JOIN tb_item_cat b ON a.cid = b.id
    LEFT JOIN tb_item_desc c ON a.id = c.item_id
    

    创建一个sql语句对应的pojo。

    public class Item {
    
    	private String id;
    	private String title;
    	private String sell_point;
    	private long price;
    	private String image;
    	private String category_name;
    	private String item_des;
    }
    

    4.Dao层

    需要创建一个mapper接口+mapper映射文件。名称相同且在同一目录下。

    接口定义:

    img

    Mapper文件:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
    <mapper namespace="com.taotao.search.mapper.ItemMapper" >
    	
    	<select id="getItemList" resultType="com.taotao.search.pojo.Item">
    		SELECT
    			a.id,
    			a.title,
    			a.sell_point,
    			a.price,
    			a.image,
    			b. NAME category_name
    		FROM
    			tb_item a
    		LEFT JOIN tb_item_cat b ON a.cid = b.id
    	</select>
    
    </mapper>
    

    5.Service层

    功能:导入所有的商品数据。没有参数。返回结果TaotaoResult。从数据库中查询出所有的商品数据。创建一个SolrInputDocument对象,把对象写入索引库。

    applicationContext-solr.xml

    <!-- 配置SolrServer对象 -->
    	<!-- 单机版 -->
    	<bean id="httpSolrServer" class="org.apache.solr.client.solrj.impl.HttpSolrServer">
    		<constructor-arg name="baseURL" value="${SOLR.SERVER.URL}"></constructor-arg>
    	</bean>
    
    

    SOLR.SERVER.URL在properties中定义

    @Service
    public class ItemServiceImpl implements ItemService {
    
    	@Autowired
    	private ItemMapper itemMapper;
    	
    	@Autowired
    	private SolrServer solrServer;
    	
    	@Override
    	public TaotaoResult importAllItems() {
    		try {
    			
    			//查询商品列表
    			List<Item> list = itemMapper.getItemList();
    			//把商品信息写入索引库
    			for (Item item : list) {
    				//创建一个SolrInputDocument对象
    				SolrInputDocument document = new SolrInputDocument();
    				document.setField("id", item.getId());
    				document.setField("item_title", item.getTitle());
    				document.setField("item_sell_point", item.getSell_point());
    				document.setField("item_price", item.getPrice());
    				document.setField("item_image", item.getImage());
    				document.setField("item_category_name", item.getCategory_name());
    				document.setField("item_desc", item.getItem_des());
    				//写入索引库
    				solrServer.add(document);
    			}
    			//提交修改
    			solrServer.commit();
    		} catch (Exception e) {
    			e.printStackTrace();
    			return TaotaoResult.build(500, ExceptionUtil.getStackTrace(e));
    		}
    		return TaotaoResult.ok();
    	}
    
    }
    

    6.Controller层

    功能:发布一个rest形式的服务。调用Service的服务方法,把数据导入到索引库中,返回TaotaoResult。

    Url:/search/manager/importall

    @Controller
    @RequestMapping("/manager")
    public class ItemController {
    	
    	@Autowired
    	private ItemService itemService;
    
    	/**
    	 * 导入商品数据到索引库
    	 */
    	@RequestMapping("/importall")
    	@ResponseBody
    	public TaotaoResult importAllItems() {
    		TaotaoResult result = itemService.importAllItems();
    		return result;
    	}
    }
    

    三、实现商品搜索功能

    实现商品搜索功能需要两步实现:

    第一步:在taotoa-search工程中发布服务

    第二步:在taotao-portal中调用服务并展示结果。

    1.发布搜索服务

    1.1需求分析

    http形式的服务。对外提供搜索服务是一个get形式的服务。调用此服务时需要查询条件,分页条件可以使用page(要显示第几页)、rows(每页显示的记录数)。返回一个json格式的数据。可以使用TaotaoResult包装一个商品列表转换成json。

    请求的url:/search/query/{查询条件}/{page}/{rows}

    ​ /search/query?q={查询条件}&page={page}&rows={rows}

    返回的结果:TaotaoResult包装商品列表。

    返回结果pojo

    public class SearchResult {
    
    	//商品列表
    	private List<Item> itemList;
    	//总记录数
    	private long recordCount;
    	//总页数
    	private long pageCount;
    	//当前页
    	private long curPage;
    }
    

    1.2Dao层

    分析:尽可能的做的通用一些。参数应该是SolrQuery。返回商品列表、查询结果总记录数

    @Repository
    public class SearchDaoImpl implements SearchDao {
    	@Autowired
    	private SolrServer solrServer;
    
    	@Override
    	public SearchResult search(SolrQuery query) throws Exception{
    		//返回值对象
    		SearchResult result = new SearchResult();
    		//根据查询条件查询索引库
    		QueryResponse queryResponse = solrServer.query(query);
    		//取查询结果
    		SolrDocumentList solrDocumentList = queryResponse.getResults();
    		//取查询结果总数量
    		result.setRecordCount(solrDocumentList.getNumFound());
    		//商品列表
    		List<Item> itemList = new ArrayList<Item>();
    		//取高亮显示
    		Map<String, Map<String, List<String>>> highlighting = queryResponse.getHighlighting();
    		//取商品列表
    		for (SolrDocument solrDocument : solrDocumentList) {
    			//创建一商品对象
    			Item item = new Item();
    			item.setId((String) solrDocument.get("id"));
    			//取高亮显示的结果
    			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);
    			item.setImage((String) solrDocument.get("item_image"));
    			item.setPrice((long) solrDocument.get("item_price"));
    			item.setSell_point((String) solrDocument.get("item_sell_point"));
    			item.setCategory_name((String) solrDocument.get("item_category_name"));
    			//添加的商品列表
    			itemList.add(item);
    		}
    		result.setItemList(itemList);
    		return result;
    	}
    
    }
    

    1.3Service层

    功能:接收查询条件。查询条件及分页条件(page、rows),创建一个SolrQuery对象。指定查询条件、分页条件、默认搜索域、高亮显示。调用dao层执行查询。得到查询结果计算总页数。返回SearchResult对象。

    @Service
    public class SearchServiceImpl implements SearchService {
    
    	@Autowired
    	private SearchDao searchDao;
    	@Override
    	public SearchResult search(String queryString, int page, int rows) throws Exception {
    		//创建查询对象
    		SolrQuery query = new SolrQuery();
    		//设置查询条件
    		query.setQuery(queryString);
    		//设置分页
    		query.setStart((page - 1) * rows);
    		query.setRows(rows);
    		//设置默认搜素域
    		query.set("df", "item_keywords");
    		//设置高亮显示
    		query.setHighlight(true);
    		query.addHighlightField("item_title");
    		query.setHighlightSimplePre("<em style=\"color:red\">");
    		query.setHighlightSimplePost("</em>");
    		//执行查询
    		SearchResult searchResult = searchDao.search(query);
    		//计算查询结果总页数
    		long recordCount = searchResult.getRecordCount();
    		long pageCount = recordCount / rows;
    		if (recordCount % rows > 0) {
    			pageCount++;
    		}
    		searchResult.setPageCount(pageCount);
    		searchResult.setCurPage(page);
    		
    		return searchResult;
    	}
    
    }
    

    1.4Controller层

    接收查询参数:查询条件、page、rows

    调用Service执行查询返回一个查询结果对象。

    把查询结果包装到TaotaoResult中返回,结果是json格式的数据。

    如果查询条件为空,返回状态码:400,消息:查询条件不能为空。

    Page为空:默认为1 Rows 为空:默认为50

    @Controller
    public class SearchController {
    	@Autowired 
    	private SearchService searchService;
    	
    	@RequestMapping(value="/query",method=RequestMethod.GET)
    	@ResponseBody
    	public TaotaoResult search(@RequestParam("q")String queryString,
    			@RequestParam(defaultValue="1")Integer page,@RequestParam(defaultValue="50")Integer rows) {
    		//查询条件不能为空
    		if(StringUtils.isBlank(queryString)) {
    			return TaotaoResult.build(400, "查询条件不能为空");
    		}
    		SearchResult searchResult = null;
    		try {
    			queryString = new String(queryString.getBytes("iso8859-1"),"utf-8");
    			searchResult = searchService.search(queryString, page, rows);
    		} catch (Exception e) {
    			e.printStackTrace();
    			return TaotaoResult.build(500, ExceptionUtil.getStackTrace(e));
    		}
    		return TaotaoResult.ok(searchResult);
    	}
    }
    

    1.5测试

    image-20200530140518978

    2.调用服务实现搜索功能

    2.1需求分析

    用户在首页中输入查询条件,点击查询向taotao-portal发送请求,参数就是查询的条件,页码。Taoto-portal调用taotao-search发布的服务进行搜索,参数应该是查询条件和页码及每页显示的记录数(参数可选)。Taotao-search返回一个json格式的数据(TaotaoResult包装一个SearchResult对象)。Taotao-portal接收json数据需要把json数据转换成java对象。把java对象传递给jsp页面,jsp渲染java对象得到商品查询结果页面。

    img

    请求url:http://localhost:8082/search.html?q=查询条件

    img

    返回结果:query:回显的查询条件 totalPages:总页数 itemList:商品列表 page:当前页码

    2.2Dao层

    没有

    2.3Service层

    接收两个参数1、查询条件2、页码。调用taotao-search的搜索服务。接收返回的json数据,把json转换成java对象返回SearchResult对象。

    @Service
    public class SearchServiceImpl implements SearchService {
    	@Value("${SEARCH_BASE_URL}")
    	private String SEARCH_BASE_URL;
    
    	@Override
    	public SearchResult search(String queryString, int page) {
    		//调用taotao-search服务
    		//查询参数
    		Map<String, String> param = new HashMap<String, String>();
    		param.put("q", queryString);
    		param.put("page", page+"");
    		try {
    			//调用服务
    			String json = HttpClientUtil.doGet(SEARCH_BASE_URL, param);
    			//把字符串转换成java对象
    			TaotaoResult taotaoResult = TaotaoResult.formatToPojo(json, SearchResult.class);
    			if(taotaoResult.getStatus()==200) {
    				SearchResult result = (SearchResult) taotaoResult.getData();
    				return result;
    			}
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    		return null;
    	}
    }
    

    2.4Controller层

    功能:接收请求的参数查询条件和页码。调用Service查询商品列表得到SearchResult对象。

    需要把query:回显的查询条件 totalPages:总页数 itemList:商品列表 page:当前页码

    传递到页面。返回一个逻辑视图search字符串。

    @Controller
    public class SearchController {
    	@Autowired
    	private SearchService searchService;
    	
    	@RequestMapping("/search")
    	public String search(@RequestParam("q")String queryString,@RequestParam(defaultValue="1")Integer page,Model model) {
    		if(queryString!=null) {
    			try {
    				queryString = new String(queryString.getBytes("iso8859-1"), "utf-8");
    			} catch (Exception e) {
    				e.printStackTrace();
    			}
    		}
    		SearchResult searchResult = searchService.search(queryString, page);
    		//向页面传递参数
    		model.addAttribute("query", queryString);
    		model.addAttribute("totalPages", searchResult.getPageCount());
    		model.addAttribute("itemList", searchResult.getItemList());
    		model.addAttribute("page", page);
    		return "search";
    	}
    }
    

    实现效果:

    image-20200530195838608

    2.5图片bug修改

    搜索结果中图片展示不出来,因为image字段中存储的图片是多张,使用逗号分隔。

    解决方法:

    img img

    结语

    LPL夏季赛开幕式整挺好,就是有点费韩国队🤣

    另外,Canna来个死全家套餐没意见吧🤢(不懂的科普一下哦,这逼故意打lpl时亮老鼠,老鼠在韩国翻译直接是瘟疫,什么意思不用我多说了吧)

    展开全文
  • changgou_web_search:存放静态资源,实现页面跳转。 changgou_service_search:负责从ES查询数据。 changgou_service_search_api:放实体对象及feign接口。 (1)创建changgou_web_search工程 pom的引入(前端使用的是...

    涉及微服务:
    changgou_web_search:存放静态资源,实现页面跳转。
    changgou_service_search:负责从ES查询数据。
    changgou_service_search_api:放实体对象及feign接口。
    (1)创建changgou_web_search工程
    pom的引入(前端使用的是thymeleaf模板引擎)

    <dependencies>
            <dependency>
                <groupId>com.changgou</groupId>
                <artifactId>changgou_common</artifactId>
                <version>1.0-SNAPSHOT</version>
            </dependency>
            <dependency>
                <groupId>com.changgou</groupId>
                <artifactId>changgou_service_search_api</artifactId>
                <version>1.0-SNAPSHOT</version>
            </dependency>
            <!--thymeleaf模板引擎-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-thymeleaf</artifactId>
            </dependency>
        </dependencies>

    存放静态资源创建类:

    Snipaste_2019-08-21_11-20-04.png

    这里注意static与templates文件夹名必须这样起,不然需要配置,麻烦!

    application.yml的配置:

    server:
      port: 9011
    spring:
      application:
        name: webSearch
      main:
        allow-bean-definition-overriding: true   #当遇到同样名字的时候,是否允许覆盖注册
      thymeleaf:
        cache: false #thymeleaf数据缓存关闭
    eureka:
      client:
        service-url:
          defaultZone: http://127.0.0.1:6868/eureka
      instance:
        prefer-ip-address: true
    feign:
      hystrix:
        enabled: true
      client:
        config:
          default:   #配置全局的feign的调用超时时间  如果 有指定的服务配置 默认的配置不会生效
            connectTimeout: 60000 # 指定的是 消费者 连接服务提供者的连接超时时间 是否能连接  单位是毫秒
            readTimeout: 80000  # 指定的是调用服务提供者的 服务 的超时时间()  单位是毫秒
    #hystrix 配置
    hystrix:
      command:
        default:
          execution:
            timeout:
              #如果enabled设置为false,则请求超时交给ribbon控制
              enabled: true
            isolation:
              strategy: SEMAPHORE
              thread:
                # 熔断器超时时间,默认:1000/毫秒
                timeoutInMilliseconds: 80000

    websearchController:

    package com.changgou.web.search.controller;
    
    import com.changgou.entity.Page;
    import com.changgou.web.search.service.WebSearchService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    
    import java.util.Map;
    import java.util.Set;
    
    /**
     * @author :gzy
     * @date :Created in 2019/8/20
     * @description :
     * @version: 1.0
     */
    @Controller
    @RequestMapping("/wsearch")
    public class WebSearchController {
        @Autowired
        private WebSearchService webSearchService;
        @GetMapping
        public String searchGoods(Model model,@RequestParam Map<String,String> searchmap){
            Map map = webSearchService.searchSkuInfoByEs(searchmap);
            model.addAttribute("result",map);
            model.addAttribute("searchMap",searchmap);
            model.addAttribute("page",new Page((int)map.get("total"),Integer.valueOf(map.get("pageNum").toString()),Page.pageSize));
            //http://search.changgou.com:9011/wsearch?keywords=手机
            StringBuilder url = new StringBuilder();
            url.append("http://search.changgou.com:9011/wsearch");
            if(null!=searchmap&&searchmap.size()>0){
                url.append("?");
                Set<Map.Entry<String, String>> entries = searchmap.entrySet();
                for (Map.Entry<String, String> entry : entries) {
                    if(!"pageNum".equals(entry.getKey())){
                        url.append("&").append(entry.getKey()).append("=").append(entry.getValue());
                    }
                }
            }
            model.addAttribute("url",url.toString());
            return "search";
        }
    }

    WebSearchService 实现类及接口省略,实现类主要通过Feign调用changgou_service_search微服务的查询功能。

    (2)changgou_service_search工程
    搜索功能实现类:

    /**
     * @author :gzy
     * @date :Created in 2019/8/19
     * @description :
     * @version: 1.0
     */
    @Service
    public class SearchServiceImpl implements SearchService {
        @Autowired
        private EsMapper esMapper;
    
        @Autowired
        private GoodsFeign goodsFeign;
    
        @Autowired
        private ElasticsearchTemplate elasticsearchTemplate;
    //从 数据库导入数据到Es
        @Override
        public void importData() {
            //创建索引
            elasticsearchTemplate.createIndex(SkuInfo.class);
            //设置映射
            elasticsearchTemplate.putMapping(SkuInfo.class);
            List<Sku> byStatus = goodsFeign.findByStatus("1");
            String string = JSON.toJSONString(byStatus);
            List<SkuInfo> skuInfos = JSON.parseArray(string, SkuInfo.class);
            for (SkuInfo skuInfo : skuInfos) {
                skuInfo.setSpecMap(JSON.parseObject(skuInfo.getSpec(),Map.class));
            }
            esMapper.saveAll(skuInfos);
        }
    //搜索功能
        @Override
        public Map searchSkuInfoByEs(Map<String, String> searchmap) {
            Map map=new HashMap<>();
            //条件对象  目标:为了合并 搜索条件及排序及分页及高亮
            NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
            //组合条件对象
            BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
    
            //本次查询关键词
            if(null!=searchmap.get("keywords") && !"".equals(searchmap.get("keywords").trim())){
                boolQueryBuilder.must(QueryBuilders.matchQuery("name",searchmap.get("keywords")));
            }
            else {
    //默认搜索条件
            }
    //分页
            String pageNum=searchmap.get("pageNum");
            if(StringUtils.isEmpty(pageNum)){
                 pageNum="1";
            }
            nativeSearchQueryBuilder.withPageable(PageRequest.of(Integer.valueOf(pageNum)-1,Page.pageSize));
            nativeSearchQueryBuilder.withQuery(boolQueryBuilder);
            AggregatedPage<SkuInfo> page = elasticsearchTemplate.queryForPage(nativeSearchQueryBuilder.build(), SkuInfo.class);
            List<SkuInfo> content = page.getContent();
          //查询到的数据
            map.put("rows",content);
           //当前页
            map.put("pageNum",pageNum);
          //数据总条数
            map.put("total",page.getTotalElements());
            return map;
        }
    }

    searcController:

    package com.changgou.search.controller;
    
    import com.changgou.entity.Result;
    import com.changgou.entity.StatusCode;
    import com.changgou.search.service.SearchService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.*;
    
    import java.security.cert.TrustAnchor;
    import java.util.Map;
    
    /**
     * @author :gzy
     * @date :Created in 2019/8/19
     * @description :
     * @version: 1.0
     */
    @RestController
    @CrossOrigin
    @RequestMapping("/search")
    public class SearchController {
        @Autowired
        private SearchService searchService;
        /*
        搜索,被远程调用
         */
        @GetMapping("/list")
        public Map search(@RequestParam Map<String,String> searchmap){
            return searchService.searchSkuInfoByEs(searchmap);
        }
        //导入数据
    
        @GetMapping("/importData")
        public Result importData(){
            searchService.saveall();
            return new Result(true,StatusCode.OK,"导入成功");
        }
    }

    (3)changgou_service_search_api
    暴露的feign接口:

    package com.changgou.search.feign;
    
    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    
    import java.util.Map;
    
    /**
     * @author :gzy
     * @date :Created in 2019/8/20
     * @description :
     * @version: 1.0
     */
    @FeignClient(name = "search")
    @RequestMapping("/search")
    public interface SearchFeign {
        @GetMapping("/list")
        Map search(@RequestParam(name = "searchmap") Map<String,String> searchmap);
    }
    展开全文
  •  完成关键字搜索功能  能够实现高亮显示效果  完成查询分类列表的功能  完成条件过滤构建功能  完成分页动态页码展示 一、完成关键字搜索 1.首先构建工程 pinyougou_search_interface pinyougou_search_...

    今日目标:

        完成关键字搜索功能
        能够实现高亮显示效果
        完成查询分类列表的功能
        完成条件过滤构建功能
        完成分页动态页码展示

    一、完成关键字搜索

    1.首先构建工程    pinyougou_search_interface    pinyougou_search_service    pinyougou_search_web

    参考content的工程,因为也不需要安全框架

    2.关键字的需求分析

    我们分析输入关键字,可以通过商品或者商家或者title等四个参数进行所有

    参数的分析:首先我们的返回的参数可能是itemList   规格  品牌   分类   价格区间    当前页   每页显示的条数,两种方法,一种是自己封装一个类,把这些参数封装起来,还有一种是通过Map实现,因为考虑到还要维护这个类,所以我们采用map的方式进行封装

    后端代码实现:

    service层的代码实现过程
    /**
     * 搜索功能的实现类
     */
    @Service
    @Transactional
    public class SearchServiceImpl implements SearchService {
    
        @Autowired
        private SolrTemplate solrTemplate;//注意添加solr的配置文件
        @Override
        public Map<String, Object> search(Map searchMap) {
            //创建高亮显示对象
            HighlightQuery query = new SimpleHighlightQuery();//接口我们通过其子类实现即可
     // 一.  1.关键字搜索
            String keywords = (String) searchMap.get("keywords");
            Criteria criteria = null;
            //2.判断关键字是否为空
            if (keywords!=null&&!"".equals(keywords)){
                //不等于空
                //输入关键字条件条件
                criteria = new Criteria("item_keywords").is(keywords);
            }else{
                //关键字为空,查询所有 通过一个表达式  *:*
                criteria = new Criteria().expression("*:*");
            }
    
            //3.将查询的条件添加到criteria中
            query.addCriteria(criteria);
            //4.这个条件已经封装了,query需要查询的所有条件
            HighlightPage<TbItem> page = solrTemplate.queryForHighlightPage(query, TbItem.class);
            //当前页的信息
            List<TbItem>  content = page.getContent();
            Map<String,Object> resultMap = new HashMap<>();
            resultMap.put("rows",content);
            return resultMap;
        }
    }
    
    接口方法;
    /**
     * 搜索接口的方法
     */
    public interface SearchService {
        //商品的搜索参数   通过map格式,Object是不同的类型,包括品牌,规格,分类 价格区间 每页显示条数 每页记录数
        public Map<String,Object> search(Map searchMap);
    }

    controller层的实现

    @RestController
    @RequestMapping("search")
    public class SearchController {
    
        @Reference
        private SearchService searchService;
    
        /**
         * 搜索功能那的实现
         * @param searchMap
         * @return
         */
        @RequestMapping("searchMap")
        Map <String,Object> search(@RequestBody Map searchMap){
          return  searchService.search(searchMap);
        }
    }
    

    前台代码的实现:

    controller层

    app.controller("searchController",function ($scope,$controller,searchService) {
    
        //控制器继承代码
        $controller("baseController",{$scope:$scope});
    
        //构建封装搜索条件的对象
        $scope.searchMap={
          keywords:"",
            category:"",
            brand:"",
            spec:{},//规格属性,需要规格名称和规格选项
            price:"",
            sort:"ASC",
            sortField:"",
            pageNo:1,//当前页
            pageSize:60//每页记录数
        };
        //商品搜索
        $scope.search=function () {
            searchService.search($scope.searchMap).success(function (response) {
                $scope.resultMap=response;
                //构建分页工具条
                //
            })
        }
        
    })

    service:

    //服务层
    app.service('searchService',function($http){
    
        //搜索功能的实现
        this.search=function(searchMap){
            return $http.post('itemsearch/search.do',searchMap);
        }
    });
    

    注意:一定要把方法里面的参数写对

    二.实现搜索高亮显示

    思路分析:我们通过在一个搜索关键字上添加前缀和后缀,以此来显示关键字的高亮,我们把业务模块都写在service层

    我们通过分析,在返回值的resultMap中的title没有设置高亮的样式,那么我们继续往上面找,如上图所示,我们通过遍历highlighted来获取,注意判断里面的list集合是否为空,得到值后直接进行赋值操作

    后端代码实现:

    //当前页的信息
            List<TbItem>  content = page.getContent();
            for (TbItem item : content) {
                //处理高亮结果
                List<HighlightEntry.Highlight> highlights = page.getHighlights(item);
                //判断highlightsshif为空
                if (highlights!=null&& highlights.size()>0){
                    //获取高亮结果集
                    List<String> snipplets = highlights.get(0).getSnipplets();
                    //在判断是否为空
                    if (snipplets!=null&& snipplets.size()>0){
                        item.setTitle(snipplets.get(0));
                    }
                }
            }

    注意出现这个问题,html代码直接没有被解析而是直接显示在页面上,这是angularjs出于安全考虑,防止hmtl注入,我们将设置权限

    我们写一个filter的过滤器

    定义过滤器,处理字符串为HTML标签
    //使用方式:<em ng-bind-html="带标签的数据 | trustHtml"></em>
    app.filter("trustHtml",function($sce){
    	return function(data){
    		return $sce.trustAsHtml(data);
    	};
    });

    页面处理:

    通过angularjs的一个指令实现:

    <em ng-bind-html="entity.title | trustHtml"></em>

    效果如图:

    三.过滤添加条件

    思路分析:在这我们只涉及一些写死的数据提交,也可以通过查询实现,分类,品牌,以及规格的显示,通过模板关联品牌和规格,注意:规格我们需要提交精确的其值,那么我们需要判断要对那个条件进行封装

    1.组合查询条件的实现  

    前端代码实现:

     //组合条件的查询
        $scope.addFilterCondition=function (key,value) {
            if (key=="category" || key=="price" || key=="brand"){
                $scope.searchMap[key]=value;
            }else{
                $scope.searchMap.spec[key]=value;
            }
    
            //调用查询方法
            $scope.search();
        }

    后台实现:

     //3.将查询的条件添加到criteria中
            query.addCriteria(criteria);
            //三.1.构建分类的查询条件
            String category = (String) searchMap.get("category");
            //2.判断查询条件是否为空
            if (category!=null&&!"".equals(category)){
                //构建分类查询条件
                Criteria categoryCriteria = new Criteria("item_category").is(category);
                //构建过滤条件查询
                FilterQuery filterQuery = new SimpleFilterQuery(categoryCriteria);
                //将查询的条件添加到query 中
                query.addFilterQuery(filterQuery);
            }
            //四.构建品牌查询条件
            String brand = (String)searchMap.get("brand");
            //判断是否为空
            if (brand!=null&&!"".equals(brand)){
                //构建品牌查询提哦案件
                Criteria brandCriteria = new Criteria("item_brand").is(brand);
                //构建过滤条件查询
                FilterQuery filterQuery  = new SimpleFilterQuery(brandCriteria);
                //将查询的条件添加到总的query 中
                query.addFilterQuery(filterQuery);
            }

    2.规格的功能的实现,由于我们分装的是一个实体类 spec  所以我们也没有必要在创建一个实体类,我们通过Map格式封装

    后台代码实现:

    //五.  封装规格
            //获取spec的对象格式
            Map<String,String> specMap = (Map<String, String>) searchMap.get("spec");
            //判断map的结果是否Wie空
            if (specMap!=null){
                //通过遍历它的key值,获得value值,就是规格选项的值
                for (String key: specMap.keySet()){
                    //构建品牌查询提哦案件
                    Criteria specificationCriteria = new Criteria("item_spec_"+key).is(specMap.get(key));
                    //构建过滤条件查询
                    FilterQuery filterQuery  = new SimpleFilterQuery(specificationCriteria);
                    //将查询的条件添加到总的query 中
                    query.addFilterQuery(filterQuery);
                }
            }

    3.根据价格实现功能

    思路:我们要对字符串进行判断处理,splic("-")   然后通过这个第一个值  0 和最后一个值*  判断

     //六.价格的条件查询
            String price = (String)searchMap.get("price");
            //分析:
            /*
            ('price','0-500')
            ('price','500-1000')
            ('price','1000-1500')
            ('price','2000-3000')
              ('price','3000-*')分析可知通过分隔符分开,然后分割后成为一个数组,最后
              通过临界值判断
             */
            //判断是否为空
            if (price!=null&&!"".equals(price)){
                String[] split = price.split("-");
                //通过价格临界值判断
                if (!"0".equals(split[0])){
                    //构建查询条件  大于0这个区间
                    Criteria priceCriteria= new Criteria("item_price").greaterThanEqual(split[0]);
                    //构建过滤条件查询
                    FilterQuery filterQuery  = new SimpleFilterQuery(priceCriteria);
                    //将查询的条件添加到总的query 中
                    query.addFilterQuery(filterQuery);
                }
    
                //通过* 判断  小于等于1的这个区间
                if (!"*".equals(split[1])){
                    //构建查询条件
                    Criteria priceCriteria= new Criteria("item_price").lessThanEqual(split[1]);
                    //构建过滤条件查询
                    FilterQuery filterQuery  = new SimpleFilterQuery(priceCriteria);
                    //将查询的条件添加到总的query 中
                    query.addFilterQuery(filterQuery);
                }
            }

     

    四.排序功能实现

    需求:

    实现如下图:

    查询操作实现,新品和价格排序

     

     

     

     

    这个是上一个模块移除的内容,点击已经添加的条件,实现移除的功能

    思路:我们通过点击传过来的的key值,删除value注意还要判断是spec,因为这个是对象,我们通过这个delete   方法删除这个key以及value

    //移除查询条件操作  //通过key移除值
    $scope.removeSearchItem=function (key) {
    
        if (key=="category" || key=="price" || key=="brand"){
            $scope.searchMap[key]="";
        }else{
          delete  $scope.searchMap.spec[key];
        }
        //调用查询方法
        $scope.search();
    }

    五.搜索分页功能实现

    思想:通过传过来pageNo当前页,pageSize总页数,作为条件,后台做处理,然后返回给前台

     

    后台代码:

     //八.构建分页查询
            Integer pageNo = (Integer) searchMap.get("pageNo");
            Integer pageSize = (Integer) searchMap.get("pageSize");
            query.setOffset((pageNo-1)*pageSize);//设置分页起始值
            query.setRows(pageSize);//设置总页数
    //最后还的封装一下
     Map<String,Object> resultMap = new HashMap<>();
            resultMap.put("rows",content);
            resultMap.put("totalpagee",page.getTotalPages());//总页数
            resultMap.put("pageNo",pageNo);//当前页
            return resultMap;

    前台代码实现:

       //构建分页工具条代码
        buildPageLabel=function(){
            $scope.pageLabel = [];// 新增分页栏属性,存放分页的页面
            var maxPageNo = $scope.resultMap.totalPages;// 得到最后页码
    
            // 定义属性,显示省略号
            $scope.firstDot = true;
            $scope.lastDot = true;
    
            var firstPage = 1;// 开始页码
            var lastPage = maxPageNo;// 截止页码
    
            if ($scope.resultMap.totalPages > 5) { // 如果总页数大于5页,显示部分页码
                if ($scope.resultMap.pageNo <= 3) {// 如果当前页小于等于3
                    lastPage = 5; // 前5页
                    // 前面没有省略号
                    $scope.firstDot = false;
    
                } else if ($scope.searchMap.pageNo >= lastPage - 2) {// 如果当前页大于等于最大页码-2
                    firstPage = maxPageNo - 4; // 后5页
                    // 后面没有省略号
                    $scope.lastDot = false;
                } else {// 显示当前页为中心的5页
                    firstPage = $scope.searchMap.pageNo - 2;
                    lastPage = $scope.searchMap.pageNo + 2;
                }
            } else {
                // 页码数小于5页  前后都没有省略号
                $scope.firstDot = false;
                $scope.lastDot = false;
            }
            // 循环产生页码标签
            for (var i = firstPage; i <= lastPage; i++) {
                $scope.pageLabel.push(i);
            }
        }
    
    
        //分页查询
        $scope.queryForPage=function(pageNo){
            $scope.searchMap.pageNo=pageNo;
    
            //执行查询操作
            $scope.search();
    
        }
    
        //分页页码显示逻辑分析:
        // 1,如果页面数不足5页,展示所有页号
        // 2,如果页码数大于5页
        // 1) 如果展示最前面的5页,后面必须有省略号.....
        // 2) 如果展示是后5页,前面必须有省略号
        // 3) 如果展示是中间5页,前后都有省略号
    
        // 定义函数,判断是否是第一页
        $scope.isTopPage = function() {
            if ($scope.searchMap.pageNo == 1) {
                return true;
            } else {
                return false;
            }
        }
        // 定义函数,判断是否最后一页
        $scope.isLastPage = function() {
            if ($scope.searchMap.pageNo == $scope.resultMap.totalPages) {
                return true;
            } else {
                return false;
            }
        }
    

    六.总结

     

    展开全文
  • 首页跳转到搜索页面及搜索实现分析 上节课我们一起学习了将数据从数据库导入到索引库,这节课我们一起学习下首页跳转到搜索页面。 我们要访问taotao-portal-web工程就要先启动redis服务(大家根据自己使用的情况启动...

    首页跳转到搜索页面及搜索实现分析

    上节课我们一起学习了将数据从数据库导入到索引库,这节课我们一起学习下首页跳转到搜索页面。

    我们要访问taotao-portal-web工程就要先启动redis服务(大家根据自己使用的情况启动,使用的是单机版就启动单机版服务器,使用的是集群就启动集群版服务器)。

    启动好taotao-portal-web工程之后,我们访问淘淘商城首页,如下图所示。

    该首页对应的后台代码如下所示,在index.jsp当中是没有搜索相关代码的,那么这部分代码在哪儿呢?其实是在header.jsp当中。

    header.jsp页面的代码如下图所示,可以看到在搜索框中输入搜索搜索关键词之后,点回车即可触发search方法。但我们发现在整个header.jsp当中并没有search方法,那么search方法在哪儿呢?其实是在页面引用的src="/js/base-v1.js"当中。

    base-v1.js中有search这个方法,如下图所示,它指定的访问端口是8082,我们要把它改成8085,,因为8085对应着我们的taotao-search-web工程。

    我们使用tomcat插件启动的方式启动taotao-search-web工程,启动方法是在工程上右键-------->Run As------->Maven Install,会看到如下图所示弹出框,在Goals一栏输入"clean tomcat7:run"。点击"Apply",然后点击"Run"就可以启动该工程了。

    我们在淘淘商城首页的搜索框中输入"手机"然后点击搜索按钮,会看到如下图所示界面,可以看到已经正确跳转到搜索工程了,只是我们的搜索功能还没完成因此现在访问是报404的。

    下面我们来分析一下搜索功能的实现,我们首先把搜索工程的静态资源,大家可以到http://download.csdn.net/detail/u012453843/9830293这个地址进行下载。下载解压之后放到搜索工程当中,如下图所示。

    我们商品搜索页面只需进行查询和分页即可,我们现在不用像京东做的这么复杂,练习嘛,我们就不使用过滤条件、排序等功能了。我们只实现查询和分页功能。

    我们再到search.jsp页面查看一下代码,可以看到搜索页面由总页数totalPages和搜索出来的内容列表itemList组成,而且可能会用到查询总数量。这些我们应该放到一个pojo当中。

    由于搜索服务和搜索工程都需要用到这个pojo,因此我们还是把这个pojo放到taotao-common当中比较合适。如下图所示。注意类一定要实现序列化接口!!—SearchResult.java

    使用SolrJ查询索引库

    我们有必要在工程中写查询索引库的代码前先进行必要的测试。我们先到Solr服务页面去简单进行查询,如下图所示,在搜索条件"q"这一栏,我们输入"手机",分页参数的每页显示条数我们随便写一个数,这里我写的是3,也就是一次只查询三条数据,由于在查询条件一栏没有指定搜索域,因此默认搜索域一定要指定,否则无法查询出数据,我们在solr服务器的schema.xml文件当中配置了复制域"item_keywords",我们的默认搜索域便使用复制域"item_keywords"。我们知道在天猫或者京东,当搜索出结果后关键字是高亮显示的,我们也应该有高亮显示效果,我们点击"hl"前面的复选框就会展开如下图关于高亮显示的配置,让我们指定要高亮显示的字段以及高亮显示的html代码(在关键字前面和后面都有,比如手机)。点击"Execute Query"按钮便可以查询到如下图右侧的数据,上面是查询出来的商品列表信息,下面是高亮信息"highlighting"。

    大家应该注意到下图高亮数据部分有一条查询结果是空的(“143771131488369”:{}),这是因为这条数据的item_title字段是"121"并没有关键字"手机",但是在商品分类中有"手机",于是认为该商品也符合搜索条件(复制域的意思是把各个字段的值都复制到"item_keywords"当中,查询的时候,只要满足其中一个字段的条件便认为是满足条件),但由于"item_title"字段没有"手机",因此显示的高亮结果是"143771131488369":{}

    针对上面的查询条件,我们使用java测试代码来跑一遍,代码如下所示。queryDocumentPhone()

    在这里插入图片描述
    在这里插入图片描述

    搜索服务Dao实现

    我们先来看下上节课我们一起学习的solrj查询测试代码,如下图所示,可以看到,代码的上半部分是比较灵活的,根据用户查询条件的不同而不同,但是红色圈住的那部分代码则是固定的,不论什么查询条件都要执行下面那段代码,因此我们可以考虑将下面那段代码封装起来,写到Dao层。—就是查询、将查询结果封装的那些

    我们根据在公司的实际操作场景来写,在实际开发中一般都是要有接口和实现类的,因此我们在taotao-search-interface工程新建一个com.taotao.search.dao包,并在该包下新建一个接口类SearchDao,如下图所示。

    我们在taotao-search-service工程添加一个com.taotao.search.dao.impl包并在该包下新建SearchDao的实现类SearchDaoImpl,如下图所示。

    这样,我们的Dao层便写完了。

    搜索功能Service实现

    首先我们在taotao-search-interface工程新建一个接口类SearchService,并在接口类中添加一个接口,如下图所示。

    接着,我们到taotao-search-service工程添加一个实现类SearchServiceImpl,并实现SearchService接口,如下图所示。

    在上面的实现类中使用注解的方式注入SearchDao,能不能注入成功呢?答案是不行的,因为我们在taotao-search-service的spring配置文件中配置的扫描包的范围是com.taotao.search.service,而com.taotao.search.dao不在这个范围内,从而无法将dao这个包下的接口类扫描到spring容器当中,我们也无法注入成功,解决方法有两种,第一种是把扫描包的范围扩大,由原来的com.taotao.search.service改为com.taotao.search,这样com.taotao.search.dao和com.taotao.search.service两个包就都可以被扫描进spring容器当中了。第二种方法是在已有的扫描包后面再加一个扫描范围com.taotao.search.dao,中间以","分隔(base-package=“com.taotao.search.service,com.taotao.search.dao”)。

    写完了Service,下面我们便要发布服务,我们在taotao-search-service工程的applicationContext-service.xml文件中暴露搜索接口,如下图所示。

    商品搜索功能Controller实现

    在taotao-search-web工程需要添加对Search服务的引用,如下图所示。

    下面我们写一个Controller,SearchController

    下面对Controller当中代码做下解释并需要在配置文件配置下每页显示数量。
    首先,@RequestMapping("/search")的值为何是"/search",这要从搜索页面代码说起,在search.jsp页面当中是没有搜索代码的,搜索代码在commons/header.jsp当中。

    我们打开commons目录下的header.jsp页面,可以看到当我们在搜索框中输入搜索条件并按回车后会触发search方法,这个search方法并不在这个header.jsp当中,而是在引用的base-v1.js当中。

    我们到base-v1.js当中,找到search方法,在search方法中,我们可以看到要访问的页面是search.html,我们的taotao-search-web工程拦截的就是以".html"结尾的文件,因此没有问题,既然要请求search.html,我们当然要拦截的是"search"了。另外,大家在这里也看到了,请求后面的参数是以"q"来携带的,这个变量名字与我们Controller当中定义的参数名称"queryString"不一致,名称不一致的情况需要指定映射关系,于是便有了@RequestParam(“q”) String queryString。

    我们从上图的请求当中可以看到也没有每页显示多少条的参数,而这个参数必须是灵活可配置的,因此最好写到配置文件当中。如下图所示。

    search方法中的Model参数是为了向页面回显数据用的,我们可以看下搜索页面都需要回显哪些数据,可以看到有四个值需要回显,这也刚好对应着我们在Controller类中所回显的四个变量。

    方法最后返回逻辑视图"search"经过spring自动添加后缀.jsp,于是便去访问这个search.jsp页面,并把四个变量也都带过来了,这样,页面便可以正常显示数据了。

    好了,至此,我们的Controller便完成了。

    搜索功能测试

    经过前面几节的学习,我们把搜索相关代码写完了,这节我们一起测试下搜索功能。

    首先我们要保证zookeeper、redis、image、solr服务都开启。

    接着,我们把taotao-common工程重新打包到本地maven仓库,然后把taotao-search工程也打包到本地maven仓库。

    下面我们依次启动taotao-manager、taotao-content、taotao-search三个服务,然后启动taotao-manager-web、taotao-portal-web、taotao-search-web三个系统。

    启动完之后,我们到淘淘商城首页,如下图所示,我们在搜索框中输入"手机"并按回车进行搜索。

    搜索结果如下图,可以看到,搜索结果乱码了。

    那么,我们怎么解决乱码问题呢?我们可以直接在Controller当中解决,如下图所示。工程默认的编码是"iso8859-1",现在需要把它修改为"utf-8"。

    修改完之后,我们重启taotao-search-web工程,然后我们再在淘淘商城首页的搜索框中输入"手机"然后回车进行查询,便可以看到如下图所示界面,可以看到搜索出结果了,只是由于数据库中存储的图片地址都是测试数据,是无法访问的,因此都没有图片。

    我们可以在淘淘商城后台添加一个关于手机的商品,如下图所示,输入完商品信息后点击"提交"。

    我们到数据库最后一页,可以看到我们刚才添加的手机信息,如下图所示。

    我们到搜索工程,在搜索框中输入"小米"然后回车即可搜索小米的商品,如下图所示,可以看到并没有我们刚才添加的手机,这是什么原因呢?其实很简单,是因为我们刚才添加的商品并没有在索引库中,因此我们需要把数据库中的商品与索引库重新同步一下。

    我们到淘淘商城后台进行同步数据库数据到索引库,如下图所示。点击"导入数据到索引库。由于数据量较大,导入需要几十秒的时间。

    导入成功后,我们再到搜索工程进行搜索,发现这时成功搜索出该手机了!

    但是到这里依然会有个问题,我们在后台添加商品的时候可以指定多张图片,我们试下多张图片的情况下,还能不能正常搜索出带图片的手机。如下图所示,我们添加一款金立手机并上传了三张图片。

    添加完金立M2017手机之后,我们还需要重新将数据库中的数据与索引库进行同步,这里就不啰嗦怎么操作了。同步之后,我们在搜索工程输入"金立M2017"然后回车进行搜索,发现搜索到了结果但是图片无法正常显示。如下图所示。

    这是什么原因造成的呢?其实也很简单,我们看下数据库中存储的数据格式,我们刚添加的手机image字段存储的信息如下所示,可以看到是三张图片地址中间以","分隔拼成的字符串,这样的字符串在页面是无法正常显示的,我们需要把图片进行拆分,显示其中的一张才行。

    http://192.168.156.13:8888/group1/M00/00/00/wKicDVkHzTiAVCl3AADZzN2RODM244.jpg,http://192.168.156.13:8888/group1/M00/00/00/wKicDVkHzTiAVZaFAABmf6hpOQM129.jpg,http://192.168.156.13:8888/group1/M00/00/00/wKicDVkHzTiAMvRzAAADTv6ZwbQ551.jpg

    我们到SearchDaoImpl当中进行处理,如下图所示。
    修改后的SearchDaoImpl.java类的代码如下

    修改了taotao-search-service,我们便要重启taotao-search工程,重启之后,我们重新搜索"金立M2017"便可以看到图片了,如下图所示。

    下面还有功能没有完善,那就是下一页、上一页,当我们点击上图的下一页时,会看到如下图所示界面,说明请求就不正确,因为它默认访问的端口是8082,而我们的搜索工程的端口是8085,因此我们需要找到控制下一页、上一页功能的js并做下修改。

    我们在search.jsp页面当中可以看到引用了脚本search_main.js,这个js就控制了我们的上一页、下一页的操作。

    我们打开search_main.js文件,在文件中搜索8082,只有一处,我们把8082,修改为8085,如下图所示。

    下面我们到搜索工程再点击"下一页",会看到如下图所示界面,发现已经正常到第2页了,说明上一页、下一页功能也正常了!我们的搜索完成了!!!

    总结:

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    运行

    错误1
    2019-09-01 11:42:24,078 [http-bio-8085-exec-2] [org.springframework.web.servlet.PageNotFound]-[WARN] No mapping found for HTTP request with URI [/] in DispatcherServlet with name ‘taotao-search-web’

    错误2
    javax.el.PropertyNotFoundException: Property ‘images’ not found on type com.taotao.common.pojo.SearchItem

    严重: Servlet.service() for servlet [taotao-search-web] in context with path [] threw exception [An exception occurred processing JSP page /WEB-INF/jsp/search.jsp at line 47

    改为image属性

    错误3
    javax.el.PropertyNotFoundException: Property ‘0’ not found on type java.lang.String

    严重: Servlet.service() for servlet [taotao-search-web] in context with path [] threw exception [An exception occurred processing JSP page /WEB-INF/jsp/search.jsp at line 47

    改为image,因为这里我们只取得图片数组中的第一张展示出来

    使用SolrJ管理Solr集群

    首先,大家需要搭建Solr集群,关于Solr集群的搭建http://blog.csdn.net/u012453843/article/details/70767178这篇博客进行了非常详细的说明,大家可以参考搭建。

    刚搭建好的Solr集群,是没有任何索引数据的,如下图所示。

    下面我们新建一个测试类TestSolrCloud,然后写一个测试方法

    搜索功能切换到集群

    Solr单机版使用的是HttpSolrServer,集群版用的是CloudSolrServer,这两个类都是SolrServer的子类,我们在Dao层使用的SolrServer进行操作,从而我们可以不用改Dao层代码,只需要在Spring容器中切换单机版或集群版Solr即可。

    现在我们到applicationContext-solr.xml文件当中配置一下Solr集群

    由于还没有将数据库中的数据导入到集群版的索引库,因此我们需要到淘淘商城后台重新导入到索引库,不过在此之前,我们最好把上节课测试添加的那条数据给删除掉。如下图所示,我们点击"Documents",在Document Type类型中选择XML,在Document(s)中输入删除语句,然后点击提交按钮即可完成删除操作。

    下面我们把数据库中的数据导入到集群版索引库(整个过程耗费的时间比较长,一两分钟左右),如下图所示。

    导入成功后,我们访问淘淘商城首页,并在搜索框中输入"金立M2017"并按回车进行搜索,发现可以正常搜索到结果,如下图所示。说明我们切换到集群版后,代码完全没问题。

    展开全文
  • 壹立方商城----实现商品搜索功能

    千次阅读 2018-10-20 12:56:28
    然后商品搜索页面就会展示,所有搜索到的商品(图片无法显示,是因为图片地址失效了,可以显示的是我自己添加的) 搜索时,会有关键字分词+高亮 还有分页效果 2.功能分析 我们在e3-portal-web首页展示中分析,...
  • 首先我们在taotao-search-web工程中需要添加对搜索服务的引用,如下图所示。 为了方便大家复制,现将springmvc.xml配置文件的内容贴出。&lt;?xml version="1.0" encoding="UTF-8"?&gt; ...
  • 首先我们在taotao-search-web工程中需要添加对搜索服务的引用,如下图所示。为了方便大家复制,现将springmvc.xml配置文件的内容贴出。 <?xml version="1.0" encoding="UTF-8"?> <bea...
  • 在taotao-search-web工程需要添加对Search服务的引用,如下图所示。  springmvc.xml文件所有代码如下: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmln
  • 商品详情页静态化功能实现 1.详情页静态化操作 2.填充基础数据 Spu、List<Sku> 3.规格切换 搜索页面渲染 1.数据展示 2.搜索条件展示 3.实现条件搜索控制 1.Thymeleaf 1.1 介绍 thymeleaf是一个XML/XHTML/...
  • 实现主页、按条件搜索商品商品详情、评论商品、收藏、商品后台管理等等完整功能,可用作毕业设计,提供对应毕业设计报告范文可用作参考修改,工程文档以及完整报告私聊扣扣, 三一2625138,附带环境安装和程序运行...
  • 系统完成的主要功能有:用户设置功能、发布信息功能、信息管理功能搜索信息功能,留言功能,及系统管理功能,具体业务功能包括如下: 用户注册:学生可利用自已学号进行注册,注册实现实名制。 用户登录:只有登录...
  • 用户端(A类页面)大概功能有:商品分类列表、首页全文检索(全网站搜索商品、分类等),首页图片轮播,首页分类下的商品推荐,加入购物车,购物车选中结算,分类下的商品列表、商品详情展示等等。 管理端(B类页面)...

空空如也

空空如也

1 2 3 4 5 ... 8
收藏数 159
精华内容 63
热门标签
关键字:

web商品搜索功能实现