站内全文搜索 - CSDN
精华内容
参与话题
  • Coreseek发布了3.2.14版本和4.1版本,其中的3.2.14版本是2010年发布的,它是基于Sphinx0.9.9搜索引擎的。而4.1版本是2011年发布的,它是基于Sphinx2.0.2的。Sphinx从0.9.9到2.0.2还是有改变了很多的,有很多功能,...
  • 全文检索首先对要搜索的文档进行分词,然后形成索引,通过查询索引来查询文档。全文检索是目前搜索引擎,大数据搜索的关键技术。全文检索系统可实现亚秒级的检索速度以及每秒上百次的并发检索支持。 需求: 实现...

    全文检索首先对要搜索的文档进行分词,然后形成索引,通过查询索引来查询文档。全文检索是目前搜索引擎,大数据搜索的关键技术。全文检索系统可实现亚秒级的检索速度以及每秒上百次的并发检索支持。

    需求:

    实现淘宝京东等电商网站中商品信息搜索功能,可以根据关键词(分词)、分类、商品简介,详情等搜索商品信息,可以根据相关度,价格,销量等做排序。

    比如,我们搜索“实惠的老人机”

    要求:

    分词检索:比如我们输入:“实惠的老人机”,有效分词:  “实惠” ,“老人机”,"“老人", "机”, 无效分词:“的“, 默认按照商品标题,描述,详情,分类等相关度的高低进行排序(我们不学百度高竞价,以搜索关键词相关度为默认排序),并可以按照用户所选条件进行排序。

    解决方案:

    1.数据库的Like模糊搜索。(不采用)

    数据库Like模糊搜索是典型的顺序扫描方法,数据量大就搜索得特别慢。最重要的是无法进行分词检索。

    2.数据库的全文索引。(不推荐)

    目前各大数据库的全文索引支持性还不是很好。以MySQL为例子:MySQL不支持中文全文检索。虽然支持英文的全文检索但表的存储引擎要是MyISAM,默认存储引擎InnoDB不支持全文索引(MYSQL5.6以上的InnoDB支持全文索引)。但不支持中文分词,所以我们需要自行扩展。

    3.采用Apache的顶级开源项目Solr构建索引库构建全文检索引擎,它是基于Lucene的全文搜索服务器。为了扩容和应对高并发,我们还要搭建SolrCloud集群,这里我们搭建最小的集群模块:2台Solr,各有1台备份机,需要4台Servlet服务容器。

    什么是lucene?

    Lucene是Apache的一个全文检索引擎工具包,通过lucene可以让程序员快速开发一个全文检索功能。

    Lucene实现全文检索的流程

    想必小伙伴们都用过下面这个Everything文件搜索工具,接下来我们先用Lucene来实现一个"乞丐版的Everything"文件检索系统。

    乞丐版的Everything

    我在”C:\Users\hp\Desktop\测试“下放置了以下文件,现在让我们用Lucene进行文件检索,要求检索文件标题和文件内容中含有相关分词的文件并按照搜索关键词出现的频次降序排列。

    // 创建索引
    	@Test
    	public void testIndex() throws Exception {
    		// 创建一个indexwriter对象
    		Directory directory = FSDirectory.open(new File("C:\\Users\\hp\\Desktop\\index")); //索引库存储路径
    		// Directory directory = new RAMDirectory();//保存索引到内存中 (内存索引库)
    		Analyzer analyzer = new StandardAnalyzer();// 官方推荐的标准分词器
    		IndexWriterConfig config = new IndexWriterConfig(Version.LATEST, analyzer);
    		IndexWriter indexWriter = new IndexWriter(directory, config);
    		// 1)指定索引库的存放位置Directory对象
    		// 2)指定一个分析器,对文档内容进行分析
    		// 第三步:创建field对象,将field添加到document对象中。
    		File f = new File("C:\\Users\\hp\\Desktop\\测试"); //源文件目录
    		File[] listFiles = f.listFiles();
    		for (File file : listFiles) {
    			// 创建document对象
    			Document document = new Document();
    			// 文件名称
    			String file_name = file.getName();
    			Field fileNameField = new TextField("fileName", file_name, Store.YES);
    			// 文件大小
    			long file_size = FileUtils.sizeOf(file);
    			Field fileSizeField = new LongField("fileSize", file_size, Store.YES);
    			// 文件路径
    			String file_path = file.getPath();
    			Field filePathField = new StoredField("filePath", file_path);
    			// 文件内容
    			String file_content = FileUtils.readFileToString(file);
    			Field fileContentField = new TextField("fileContent", file_content, Store.YES);
    
    			document.add(fileNameField);
    			document.add(fileSizeField);
    			document.add(filePathField);
    			document.add(fileContentField);
    			// 使用indexwriter对象将document对象写入索引库,此过程进行索引创建。并将索引和document对象写入索引库
    			indexWriter.addDocument(document);
    
    		}
    		// 关闭IndexWriter对象。
    		indexWriter.close();
    	}

    运行结果:成功创建了索引库。

    我们搜索文件名有"apache"的相关文件:

    // 搜索索引
    	@Test
    	public void testSearch() throws Exception {
    		// 第一步:创建一个Directory对象,也就是索引库存放的位置
    		Directory directory = FSDirectory.open(new File("C:\\Users\\hp\\Desktop\\index"));
    		// 第二步:创建一个indexReader对象,需要指定Directory对象
    		IndexReader indexReader = DirectoryReader.open(directory);
    		// 第三步:创建一个indexsearcher对象,需要指定IndexReader对象
    		IndexSearcher indexSearcher = new IndexSearcher(indexReader);
    		// 第四步:创建一个TermQuery对象,指定查询的域和查询的关键词
    		Query query = new TermQuery(new Term("fileName", "apache"));
    		// 第五步:执行查询。
    		TopDocs topDocs = indexSearcher.search(query, 10);
    		System.out.println("搜索到相关文件数:" + topDocs.scoreDocs.length);
    		// 第六步:返回查询结果。遍历查询结果并输出。
    		ScoreDoc[] scoreDocs = topDocs.scoreDocs;
    		for (ScoreDoc scoreDoc : scoreDocs) {
    			int doc = scoreDoc.doc;
    			Document document = indexSearcher.doc(doc);
    			// 文件名称
    			String fileName = document.get("fileName");
    			System.out.println("文件名称:" + fileName);
    			// 文件内容
    			String fileContent = document.get("fileContent");
    			System.out.println("文件内容:" + fileContent);
    			// 文件大小
    			String fileSize = document.get("fileSize");
    			System.out.println("文件大小:" + fileSize);
    			// 文件路径
    			String filePath = document.get("filePath");
    			System.out.println("文件路径:" + filePath);
    			System.out.println("------------");
    		}
    		// 第七步:关闭IndexReader对象
    		indexReader.close();
    	}

    结果:

    现在我们搜索——文件名“语录”,文件内容含有"斗地主"的文件

    // 搜索索引:搜索文件名“语录”,文件内容含有"斗地主"的文件
    	@Test
    	public void testMultSearch() throws Exception {
    		// 第一步:创建一个Directory对象,也就是索引库存放的位置
    		Directory directory = FSDirectory.open(new File("C:\\Users\\hp\\Desktop\\index"));
    		// 第二步:创建一个indexReader对象,需要指定Directory对象
    		IndexReader indexReader = DirectoryReader.open(directory);
    		// 第三步:创建一个indexsearcher对象,需要指定IndexReader对象
    		IndexSearcher indexSearcher = new IndexSearcher(indexReader);
    		// 第四步:创建一个Query对象,指定查询的域和查询的关键词
    		// 参数1: 默认查询的域
    		// 参数2:采用的分析器
    		QueryParser queryParser = new QueryParser("fileName", new StandardAnalyzer());
    		// *:* 域:值
    		Query query = queryParser.parse("fileName:语录 AND fileContent:斗地主");
    		// 第五步:执行查询。
    		TopDocs topDocs = indexSearcher.search(query, 10);
    		System.out.println("搜索到相关文件数:" + topDocs.scoreDocs.length);
    		// 第六步:返回查询结果。遍历查询结果并输出。
    		ScoreDoc[] scoreDocs = topDocs.scoreDocs;
    		for (ScoreDoc scoreDoc : scoreDocs) {
    			int doc = scoreDoc.doc;
    			Document document = indexSearcher.doc(doc);
    			// 文件名称
    			String fileName = document.get("fileName");
    			System.out.println("文件名称:" + fileName);
    			// 文件内容
    			String fileContent = document.get("fileContent");
    			System.out.println("文件内容:" + fileContent);
    			// 文件大小
    			String fileSize = document.get("fileSize");
    			System.out.println("文件大小:" + fileSize);
    			// 文件路径
    			String filePath = document.get("filePath");
    			System.out.println("文件路径:" + filePath);
    			System.out.println("------------");
    		}
    		// 第七步:关闭IndexReader对象
    		indexReader.close();
    	}

    结果:

    目前,我们用的是apache提供的标准的分词器,但其实标准分词器对中文的分词bu't不太友好。具体可以写个C++的文件指针测试一下。

    // 用C++的文件指针查看标准分析器的分词效果
    	@Test
    	public void testTokenStream() throws Exception {
    		// 创建一个标准分析器对象
    //		 Analyzer analyzer = new StandardAnalyzer();
    		// Analyzer analyzer = new CJKAnalyzer();
    		// Analyzer analyzer = new SmartChineseAnalyzer();
    		Analyzer analyzer = new IKAnalyzer();
    		// 获得tokenStream对象
    		// 第一个参数:域名,可以随便给一个
    		// 第二个参数:要分析的文本内容
    		// TokenStream tokenStream = analyzer.tokenStream("test",
    		// "The Spring Framework provides a comprehensive programming and configuration model.");
    		TokenStream tokenStream = analyzer.tokenStream("test", 
    				"高富帅从过去到未来,一直致力于电动车核心技术的研发,全面保障电动车使用安全,全面提升电动车续航能力,全面优化电动车防盗体验");
    		// 添加一个引用,可以获得每个关键词
    		CharTermAttribute charTermAttribute = tokenStream.addAttribute(CharTermAttribute.class);
    		// 添加一个偏移量的引用,记录了关键词的开始位置以及结束位置
    		OffsetAttribute offsetAttribute = tokenStream.addAttribute(OffsetAttribute.class);
    		// 将指针调整到列表的头部
    		tokenStream.reset();
    		// 遍历关键词列表,通过incrementToken方法判断列表是否结束
    		while (tokenStream.incrementToken()) {
    			// 关键词的起始位置
    			System.out.println("start->" + offsetAttribute.startOffset());
    			// 取关键词
    			System.out.println(charTermAttribute);
    			// 结束位置
    			System.out.println("end->" + offsetAttribute.endOffset());
    		}
    		tokenStream.close();
    	}
    

    结果:

    这个不是分词,是拆字呀,明显无法满足要求。这里我们引入第三方的IKAnalyzer中文分词器。注意:创建索引库和查询索引的分词器要保持一致。

    查看IKAnalyzer的分词效果:

    比拆字的好一些,但还是不够理想,以前”高富帅“不是一个词,但现在是一个词,我们需要扩展一下字典库。我们将”高富帅“作为一个分词。

    结果完美:

    在了解了Lucene以后,我们可以开始在Linux上搭建Solr服务了。

    什么是solr

    Solr 是Apache下的一个顶级开源项目,采用Java开发,它是基于Lucene的全文搜索服务器。Solr可以独立运行在Jetty(Jetty是Solr内置的一个轻量级Servlet容器)、Tomcat等这些Servlet容器中。
    Solr的目标是打造一款企业级的搜索引擎系统,它是基于Lucene一个搜索引擎服务,可以独立运行,通过Solr可以非常快速的构建企业的搜索引擎,通过Solr也可以高效的完成站内搜索功能。

    搭建步骤

    第一步:把solr 的压缩包上传到Linux系统。
    第二步:解压solr。


    第三步:安装Tomcat,解压缩即可。
    第四步:把solr部署到Tomcat下。
    第五步:解压缩war包。可以启动Tomcat解压。


    第六步:把/root/solr-4.10.3/example/lib/ext目录下的所有的jar包,添加到solr工程中,并加入配置文件到solr工程中。
    [root@localhost ext]# pwd
    /root/solr-4.10.3/example/lib/ext
    [root@localhost ext]# cp * /usr/local/apache-tomcat-8.5.30/webapps/solr/WEB-INF/lib/


    第七步:创建一个solrhome。/example/solr目录就是一个solrhome。复制此目录到/usr/local/solr/solrhome
    [root@localhost example]# pwd
    /root/solr-4.10.3/example
    [root@localhost example]# cp -r solr /usr/local/solr/solrhome
    [root@localhost example]# 
    第八步:关联solr及solrhome。需要修改solr工程的web.xml文件。需要删除这里的注释,并将地址改成我们真实的地址。

    原来的:

    更改后:

    第九步:启动Tomcat。

    访问地址:http://192.168.25.128:8080/solr/#/,访问成功表示solr搭建成功,这个后台管理系统我们平时只是拿来测试索引库。

    Solr的使用

    配置业务域

    添加文档必须有id域,其他域 必须在solr的schema.xml中定义。

    假设我们有这么三张表:商品表,商品详情描述表和商品分类表。

    CREATE TABLE `tb_item` (
      `id` bigint(20) NOT NULL COMMENT '商品id,同时也是商品编号',
      `title` varchar(100) NOT NULL COMMENT '商品标题',
      `sell_point` varchar(500) DEFAULT NULL COMMENT '商品卖点',
      `price` bigint(20) NOT NULL COMMENT '商品价格,单位为:分',
      `num` int(10) NOT NULL COMMENT '库存数量',
      `barcode` varchar(30) DEFAULT NULL COMMENT '商品条形码',
      `image` varchar(500) DEFAULT NULL COMMENT '商品图片',
      `cid` bigint(10) NOT NULL COMMENT '所属类目,叶子类目',
      `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '商品状态,1-正常,2-下架,3-删除',
      `created` datetime NOT NULL COMMENT '创建时间',
      `updated` datetime NOT NULL COMMENT '更新时间',
      PRIMARY KEY (`id`),
      KEY `cid` (`cid`),
      KEY `status` (`status`),
      KEY `updated` (`updated`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='商品表';
    
    CREATE TABLE `tb_item_desc` (
      `item_id` bigint(20) NOT NULL COMMENT '商品ID',
      `item_desc` text COMMENT '商品描述',
      `created` datetime DEFAULT NULL COMMENT '创建时间',
      `updated` datetime DEFAULT NULL COMMENT '更新时间',
      PRIMARY KEY (`item_id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='商品描述表';
    
    CREATE TABLE `tb_item_cat` (
      `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '类目ID',
      `parent_id` bigint(20) DEFAULT NULL COMMENT '父类目ID=0时,代表的是一级的类目',
      `name` varchar(50) DEFAULT NULL COMMENT '类目名称',
      `status` int(1) DEFAULT '1' COMMENT '状态。可选值:1(正常),2(删除)',
      `sort_order` int(4) DEFAULT NULL COMMENT '排列序号,表示同级类目的展现次序,如数值相等则按名称次序排列。取值范围:大于零的整数',
      `is_parent` tinyint(1) DEFAULT '1' COMMENT '该类目是否为父类目,1为true,0为false',
      `created` datetime DEFAULT NULL COMMENT '创建时间',
      `updated` datetime DEFAULT NULL COMMENT '创建时间',
      PRIMARY KEY (`id`),
      KEY `parent_id` (`parent_id`,`status`) USING BTREE,
      KEY `sort_order` (`sort_order`)
    ) ENGINE=InnoDB AUTO_INCREMENT=1183 DEFAULT CHARSET=utf8 COMMENT='商品类目';

    我们需要商品标题,商品卖点,商品价格,商品图片,分类名称,商品描述做检索,需要在schema.xml中定义以下业务域:

    1. 商品Id:使用schema.xml中的id域
    2. 商品标题
    3. 商品卖点
    4. 商品价格
    5. 商品图片
    6. 分类名称
    7. 商品描述

    创建对应的业务域。

    创建步骤:

    第一步:把中文分析器添加到solr工程中。
        1、把IKAnalyzer2012FF_u1.jar添加到solr工程的lib目录下。
        2、把扩展词典、配置文件放到solr工程的WEB-INF/classes目录下。


    第二步:配置一个FieldType,制定使用IKAnalyzer。
        修改schema.xml文件。


        修改Solr的schema.xml文件,添加FieldType,使中文分词器生效:

    <!--添加中文分词器-->
    <fieldType name="text_ik" class="solr.TextField">
      <analyzer class="org.wltea.analyzer.lucene.IKAnalyzer"/>
    </fieldType>
    


    第三步:配置业务域,type制定使用自定义的FieldType。
       设置业务系统Field。

    <field name="item_title" type="text_ik" indexed="true" stored="true"/>
    <field name="item_sell_point" type="text_ik" indexed="true" stored="true"/>
    <field name="item_price"  type="long" indexed="true" stored="true"/>
    <field name="item_image" type="string" indexed="false" stored="true" />
    <field name="item_category_name" type="string" indexed="true" stored="true" />
    <field name="item_desc" type="text_ik" indexed="true" stored="false" />
    
    <field name="item_keywords" type="text_ik" indexed="true" stored="false" multiValued="true"/>
    <copyField source="item_title" dest="item_keywords"/>
    <copyField source="item_sell_point" dest="item_keywords"/>
    <copyField source="item_category_name" dest="item_keywords"/>
    <copyField source="item_desc" dest="item_keywords"/>

    第四步:重启tomcat。

    测试使用solrJ管理索引库

    写个单元测试类:

    往索引库中添加一条记录:

    /**
     * 
     * @ClassName: TestSolrManager
     * @Description: 测试solrJ管理索引库
     * @Company: xx有限公司
     * @author WuJie
     * @date 2018年8月14日
     */
    public class TestSolrManager {
    
    	@Test
    	public void add() throws Exception {
    		// 1.创建solrserver 建立连接 需要指定地址
    		SolrServer solrServer = new HttpSolrServer("http://192.168.25.128:8080/solr");
    		// 2.创建solrinputdocument
    		SolrInputDocument document = new SolrInputDocument();
    		// 3.向文档中添加域
    		document.addField("id", "wujie");
    		document.addField("item_title", "Hello World, Sorl!");
    		// 4.将文档提交到索引库中
    		solrServer.add(document);
    		// 5.提交
    		solrServer.commit();
    	}
    
    }

    用管理后台查询一下:

    一切正常。我们将测试数据删除。

    现在,我们正式开始编写开始提出的——电商站内搜索商品的需求。

    首先,我们需要将数据库中的商品相关数据导入到索引库中。这里的业务逻辑是:一旦商品相关数据发送更新,需要及时更新索引库。

    创建之前提到的相关业务域的POJO:

    /**
     * 搜索的商品数据POJO
     */
    public class SearchItem implements Serializable {
    	
    	private static final long serialVersionUID = 1L;
    	
    	private Long id;//商品的id 
    	private String title;//商品标题
    	private String sell_point;//商品卖点
    	private Long price;//价格
    	private String image;//商品图片的路径
    	private String category_name;//商品分类名称
    	private String item_desc;//商品的描述
    	
    	public Long getId() {
    		return id;
    	}
    	public void setId(Long id) {
    		this.id = id;
    	}
    	public String getTitle() {
    		return title;
    	}
    	public void setTitle(String title) {
    		this.title = title;
    	}
    	public String getSell_point() {
    		return sell_point;
    	}
    	public void setSell_point(String sell_point) {
    		this.sell_point = sell_point;
    	}
    	public Long getPrice() {
    		return price;
    	}
    	public void setPrice(Long price) {
    		this.price = price;
    	}
    	public String getImage() {
    		return image;
    	}
    	public void setImage(String image) {
    		this.image = image;
    	}
    	public String getCategory_name() {
    		return category_name;
    	}
    	public void setCategory_name(String category_name) {
    		this.category_name = category_name;
    	}
    	public String getItem_desc() {
    		return item_desc;
    	}
    	public void setItem_desc(String item_desc) {
    		this.item_desc = item_desc;
    	}
    	
    	public String[] getImages(){
    		if(this.getImage()!=null){
    			String[] split = this.getImage().split(",");
    			return split;
    		}
    		return null;
    	}
    
    }

    定义mapper接口和mapper映射文件(用的MyBaits3.0):

    由于要关联查询3张表,而多表查询用不了MyBaits逆向工程,只能自己写接口和映射文件。

    SearchItemMapper.java

    //导入所有的商品数据到索引库中
    	ResultPoJo importAllItems() throws Exception;
    	
    	//根据查询条件查询
    	SearchResult search(String queryString,Integer page,Integer rows) throws Exception;

    SearchItemMapper.xml

    <?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.freesub.search.mapper.SearchItemMapper">
    	<select id="getSearchItemList" resultType="com.freesub.pojo.SearchItem">
    		SELECT
    		a.id,
    		a.title,
    		a.sell_point,
    		a.price,
    		a.image,
    		b.`name` category_name,
    		c.item_desc
    		FROM
    		tb_item a,
    		tb_item_cat b,
    		tb_item_desc c
    		WHERE
    		a.cid = b.id
    		AND a.id = c.item_id
    		AND a.`status` = 1
    	</select>
    </mapper>

    SearchItemServiceImpl.class

    @Service
    public class SearchItemServiceImpl implements SearchItemService {
    
    	@Autowired
    	private SearchItemMapper mapper;
    
    	@Autowired
    	private SolrServer solrServer;
    
    	@Autowired
    	private SearchDao searchDao;
    
    	@Override
    	public ResultPoJo importAllItems() throws Exception {
    		// 1、查询所有商品数据
    		List<SearchItem> searchItemList = mapper.getSearchItemList();
    		// 2、创建一个SolrServer对象。
    		// 3、为每个商品创建一个SolrInputDocument对象。
    		for (SearchItem searchItem : searchItemList) {
    			SolrInputDocument document = new SolrInputDocument();
    			// 4、为文档添加域
    			document.addField("id", searchItem.getId());
    			document.addField("item_title", searchItem.getTitle());
    			document.addField("item_sell_point", searchItem.getSell_point());
    			document.addField("item_price", searchItem.getPrice());
    			document.addField("item_image", searchItem.getImage());
    			document.addField("item_category_name", searchItem.getCategory_name());
    			document.addField("item_desc", searchItem.getItem_desc());
    			// 5、向索引库中添加文档
    			solrServer.add(document);
    		}
    
    		// 提交修改
    		solrServer.commit();
    
    		return ResultPoJo.ok();
    	}
    
    	@Override
    	public SearchResult search(String queryString, Integer page, Integer rows) throws Exception {
    		// 1、创建一个SolrQuery对象。
    		SolrQuery query = new SolrQuery();
    		// 2、设置查询条件
    		query.setQuery(queryString);
    		// 3、设置分页条件
    		query.setStart((page - 1) * rows);
    		query.setRows(rows);
    		// 4、需要指定默认搜索域。
    		query.set("df", "item_title");
    		// 5、设置高亮
    		query.setHighlight(true);
    		query.addHighlightField("item_title");
    		query.setHighlightSimplePre("<em style=\"color:red\">");
    		query.setHighlightSimplePost("</em>");
    		// 6、执行查询,调用SearchDao。得到SearchResult
    		SearchResult result = searchDao.search(query);
    		// 7、需要计算总页数。
    		long recordCount = result.getRecordCount();
    		long pageCount = recordCount / rows;
    		if (recordCount % rows > 0) {
    			pageCount++;
    		}
    		result.setPageCount(pageCount);
    		// 8、返回SearchResult
    		return result;
    	}
    
    }

    SearchDao.class

    /**
     * 由于搜索功能只在搜索工程中用到,可以不写接口,只写类;我们设计接口主要是为了方便不同服务之间相互调用
     * 
     * @ClassName: SearchDao
     * @Description: 从索引库中搜索商品的dao
     * @Company: xx有限公司
     * @author WuJie
     * @date 2018年8月14日
     */
    
    @Repository
    public class SearchDao {
    
    	@Autowired
    	private SolrServer server;
    
    	/*
    	 * 根据查询的条件查询商品的结果集
    	 */
    	public SearchResult search(SolrQuery query) throws Exception {
    		SearchResult searchResult = new SearchResult();
    		// 1.创建solrserver对象 由spring管理 注入
    		// 2.直接执行查询
    		QueryResponse response = server.query(query);
    		// 3.获取结果集
    		SolrDocumentList results = response.getResults();
    		// 设置searchresult的总记录数
    		searchResult.setRecordCount(results.getNumFound());
    		// 4.遍历结果集
    		// 定义一个集合
    		List<SearchItem> searchItems = new ArrayList<>();
    		// 取高亮
    		Map<String, Map<String, List<String>>> highlighting = response.getHighlighting();
    		for (SolrDocument solrDocument : results) {
    			// 将solrdocument的属性 一个个的设置到 searchItem中
    			SearchItem item = new SearchItem();
    			item.setCategory_name(solrDocument.get("item_category_name").toString());
    			item.setId(Long.parseLong(solrDocument.get("id").toString()));
    			item.setImage(solrDocument.get("item_image").toString());
    			// item.setItem_desc(item_desc);
    			item.setPrice((Long) solrDocument.get("item_price"));
    			item.setSell_point(solrDocument.get("item_sell_point").toString());
    			// 取高亮
    			List<String> list = highlighting.get(solrDocument.get("id")).get("item_title");
    			// 判断list是否为空
    			String gaoliangstr = "";
    			if (null != list && list.size() > 0) {
    				// 有高亮
    				gaoliangstr = list.get(0);
    			} else {
    				gaoliangstr = solrDocument.get("item_title").toString();
    			}
    
    			item.setTitle(gaoliangstr);
    			// searchItem 封装到SearchResult中的itemList 属性中
    			searchItems.add(item);
    		}
    		// 5.设置SearchResult 的属性
    		searchResult.setItemList(searchItems);
    		return searchResult;
    	}
    }

    SearchController.class

    @Controller
    public class SearchController {
    
    	@Value("${ITEM_ROWS}")
    	private Integer ITEM_ROWS;
    
    	@Autowired
    	private SearchItemService service;
    
    	/**
    	 * 根据条件搜索商品的数据
    	 * 
    	 * @param page
    	 * @param queryString
    	 * @return
    	 */
    	@RequestMapping("/search")
    	public String search(@RequestParam(defaultValue = "1") Integer page, @RequestParam(value = "q") String queryString, Model model) throws Exception {
    		// 1.引入
    		// 2.注入
    		// 3.调用
    		// 处理乱码:
    		queryString = new String(queryString.getBytes("iso-8859-1"), "utf-8");
    		SearchResult result = service.search(queryString, page, ITEM_ROWS);
    		// 4.设置数据传递到jsp中
    		model.addAttribute("query", queryString);
    		model.addAttribute("totalPages", result.getPageCount());// 总页数
    		model.addAttribute("itemList", result.getItemList());
    		model.addAttribute("page", page);
    		return "search";
    	}
    }

    最终效果:

    我们搜索“给力的电动车”,结果:

    再测试一波:

    我们搜索“价廉物美的手机”:

    结果,搜索到一波功能机:

    IK中文分词器也存在诸多缺陷,我们还需要对其进行进一步优化。要想完善分词库,提高句子精度,得从词性分析,词&句子情感(情感识别),还有知识的提取,以及依存文法构建等一些东西来实现,已经属于AI人工智能的领域。

    展开全文
  • 用compass实现站内全文搜索引擎(一)

    千次阅读 2013-09-12 22:14:32
    Compass是一个强大的,事务的,高性能的对象/搜索引擎映射(OSEM:object/search engine mapping)与一个Java持久层框架.Compass包括:  * 搜索引擎抽象层(使用Lucene搜索引荐), * OSEM (Object/Search Engine ...

    Compass是一个强大的,事务的,高性能的对象/搜索引擎映射(OSEM:object/search engine mapping)与一个Java持久层框架.Compass包括: 
    * 搜索引擎抽象层(使用Lucene搜索引荐),
    * OSEM (Object/Search Engine Mapping) 支持,
    * 事务管理,
    * 类似于Google的简单关键字查询语言,
    * 可扩展与模块化的框架,
    * 简单的API.

    如果你需要做站内搜索引擎,而且项目里用到了hibernate,那用compass是你的最佳选择。

    本文用到compass2.14和IK中文分词包,另外将会使用注解来实现

    废话不说,先给出关键的实现代码

    假如现在有个需求,需要根据关键字搜索出文章,
    Java代码  收藏代码
    1. @Searchable(alias="article")   
    2. public class Article {  
    3.   
    4.     private Long ID; // 标识ID  
    5.     private String content; // 正文  
    6.     private String title; // 文章标题  
    7.     private Date createTime; // 创建时间  
    8.   
    9.     @SearchableId  
    10.     public Long getID() {  
    11.         return ID;  
    12.     }  
    13.   
    14.     public void setID(Long id) {  
    15.         ID = id;  
    16.     }  
    17.   
    18.     @SearchableProperty(index = Index.TOKENIZED, store = Store.YES)  
    19.     public String getContent() {  
    20.         return content;  
    21.     }  
    22.   
    23.     public void setContent(String content) {  
    24.         this.content = content;  
    25.     }  
    26.   
    27.   
    28.   
    29.     @SearchableProperty(index = Index.TOKENIZED, store = Store.YES)  
    30.     public String getTitle() {  
    31.         return title;  
    32.     }  
    33.   
    34.     public void setTitle(String title) {  
    35.         this.title = title;  
    36.     }  
    37.   
    38.   
    39.     @SearchableProperty(index = Index.TOKENIZED, store = Store.YES)  
    40.     public Date getCreateTime() {  
    41.         return createTime;  
    42.     }  
    43.   
    44.     public void setCreateTime(Date createTime) {  
    45.         this.createTime = createTime;  
    46.     }  
    47.   
    48.   
    49.   
    50.       
    51.   
    52.   
    53. }  


    简单解释一下:
    @Searchable(alias="article")表示这个是可以搜索实体,别人为article.
    @SearchableId  这个是实体搜索的标识ID,和hibernate里的概念差不多,用来区分索引文件里的实体索引。
    @SearchableProperty(index = Index.TOKENIZED, store = Store.YES) 表示这个属性存入索引文件,而且是在分词后在存入.
    展开全文
  • 如下图所示: 二、修改模板,在需要添加搜索框的地方添加如下代码:Html代码 form action="http://localhost:8080/ZCMS/Search/Result.jsp"> input name="query" type="text" id="query" />

    一、确认“站点管理”-“站点列表”中的相应站点的“自动生成索引“选“是”。如下图所示:


    二、修改模板,在需要添加搜索框的地方添加如下代码:

    Html代码 复制代码
    1. <form action="http://localhost:8080/ZCMS/Search/Result.jsp">  
    2.     <input name="query" type="text" id="query" />     
    3.     <input name="site" type="hidden" id="site" value="${Site.ID}"/>     
    4.     <input type="submit" value="搜索" />               
    5. </form>  
    <form action="http://localhost:8080/ZCMS/Search/Result.jsp">
    	<input name="query" type="text" id="query" />  
    	<input name="site" type="hidden" id="site" value="${Site.ID}"/>  
    	<input type="submit" value="搜索" />			
    </form>
    


    其中form的action属性请参照ZCMS部署后的实际URL进行修改/
    注意:form的method不能为post

    三、重新生成模板相关的静态化文件。或者在“站点管理”-“栏目与专题”中选中左边树的根节点“文档库”,在右边区域点击“生成全站”按钮。生成后通过浏览器访问相关页面。如下图所示:


    四、在文本框中输入要搜索的关键字,点击“搜索”按钮,得到搜索结果,如下图所示:
     

    展开全文
  • 站内全文搜索

    2019-06-29 03:32:14
    全文搜索 • solr安装 • solr中文分词 • solr数据库导入 • solr数据查询 • solrj接口调用 0 1:安装solr:官网下载解压;http://lucene.apache.org/solr/ 启动方法:bin目录下打开cmd; ...

    • 全文搜索

    • solr安装
    • solr中文分词
    • solr数据库导入
    • solr数据查询
    • solrj接口调
    0
     
    1:安装solr:官网下载解压;http://lucene.apache.org/solr/
    启动方法:bin目录下打开cmd;
    solr -e cloud -noprompt
    2:solr的分词:
     

    转载于:https://www.cnblogs.com/liguo-wang/p/9631774.html

    展开全文
  • 为解决以上问题,边缘工作室经过长期的调研,不断摸索、反复实验,厚积薄发,并根据当前趋势开发出了“易搜索-智能全文检索站内搜索引擎”,简称为YSS,使以上问题迎刃而解。YSS主要有以下特点: 1、如你所需,他是...
  • 基于Lucene/XML的站内全文检索解决方案:WebLucene 作者:车东 发表于:2003-05-06 18:05 最后更新于:2007-07-16 11:07 版权声明:可以任意转载,转载时请务必以超链接形式标明文章原始出处和作者信息及本声明。 ...
  • 站内全文搜索php源代码

    热门讨论 2020-07-30 23:32:53
    php文本站内全文检索说明: 1。文件结构: search.php和template.php(界面风格文件,可随意修改),search0.php是search.php的加强版。 2。特点 1)无需mysql支持,无需建立索引,无需设置路径,放在哪级目录下...
  • 版权声明:可以任意转载,转载时请务必以超链接形式标明文章原始出处和作者信息及本声明http://www.chedong.com/tech/weblucene.html 作者: 车东 Email: chedongATbigfoot.com/chedongATchedong.com关键词:Lucene...
  • 认真思考评估站内全文搜索方案

    千次阅读 2006-10-22 01:27:00
    对于网站的站内全文检索功能,从前我的做法基本上是实行自动摘录一定长度的文体,然后根本这一个域使用like方式实现全文搜索。也由于这种搜索不是经常需要的,所以也不太舍得为之花太大的资源,无论是处理什么站点,...
  • 如果索引的文档主要是Word/PDF/静态网页等,在内部的公共文件服务器上使用Google的桌面搜索: http://in.tra.net:4664/ 然后在这台服务器上架设一台反相代理服务器,代理桌面搜索的...
  • 内容摘要:为Lucene做一个通用XML接口一直是我最大的心愿:更方便的在WEB应用中嵌入全文检索功能 提供了XML的数据输入接口:适合将原有基于各种数据库的数据源导入到全文索引中,保证了数据源的平台无关性;...
  • NULL 博文链接:https://aa00aa00.iteye.com/blog/1030043
1 2 3 4 5 ... 20
收藏数 12,871
精华内容 5,148
热门标签
关键字:

站内全文搜索