solr 基本原理 配置,solr java 初级应用

由于公司开发的电商平台,在进行商品搜索时,需要使用搜索引擎,这里简单说明一下,自己在使用和搭建过程的心得和经验:仅供参考,如有误请指出
solr版本:4.10.1

搜索引擎的基本原理:

1.通过分词创建索引

a.根据存储数据对应的字段进行分词,得到分词库;

Solr/Lucene采用的是一种反向索引,所谓反向索引:就是从关键字到文档的映射过程,保存这种映射这种信息的索引称为反向索引。

b.形成索引和分词库后,此时的索引就类似于我们使用的新华字典的比划偏旁,通过索引对应的文档编号,找到对应的文档。

由于我们环境中已经安装了solr,solr中已经存在一个core(collection1),需要在此基础上添加一个core.
在solr安装目录下,找到example/solr,然后新建一个文件夹,例如:collection2;
在collection2中新建conf和data,用来存放配置文件和数据;

可以直接将原来已经存在的collection1中的conf所有东西copy过去,然后修改schema.xml和solrconfig.xml这两个文件。

注:安装完成之后,会自动生成一个solr的集合,建议自己试着重新创建一个core.配置文件可以参考安装之后的demo进行配置。

schema.xml:
schema.xml这个配置文件可以在你下载solr包的安装解压目录的\solr\example\solr\collection1\conf中找到,它就是solr模式关联的文件。
打开这个配置文件,你会发现有详细的注释,主要进行存储字段的配置。模式组织主要分为三个重要配置(types,fileds,其他配置);


types定义将会使用到的域类型:






  
    
    
    
    
    

    

    
  
  
    
    
    
    

    

    
  



    
    
    
这里使用的是IK分词器

fileds 域,类似于数据库里面的字段
而声明类型就需要用到上面的types

   











其他配置:
uniqueKey: 唯一键,这里配置的是上面出现的fileds,一般是id、url等不重复的。在更新、删除的时候可以用到。
在做add(document)操作时,如果里面已经存在一个id相同的文档,则后面添加会将已经存在的覆盖。

defaultSearchField:默认搜索属性,如q=solr就是默认的搜索那个字段

solrQueryParser:查询转换模式,是并且还是或者(AND/OR必须大写)


solrconfig.xml基本上可以直接使用。

主要说一下在开发过程中的使用,这些配置都比价简单,也能在网上找到对应的案例。


安装和创建好solr对应的core之后我们进行简单的应用


1.在使用之前,先获取solr连接:

注:此方法在SolrJUtil这个类下。

DEFAULT_URL="http://localhost:8080/solr/collection1";
public static HttpSolrServer getServer() {
    synchronized (SolrJUtil.class) {
        if (server == null) {
            server = new HttpSolrServer(DEFAULT_URL);

            server.setMaxRetries(1); // defaults to 0. > 1 not recommended.
            server.setConnectionTimeout(5000); // 5 seconds to establish TCP
            server.setSoTimeout(1000); // socket read timeout
            server.setDefaultMaxConnectionsPerHost(100);
            server.setMaxTotalConnections(100);
            server.setFollowRedirects(false); // defaults to false
            server.setAllowCompression(true);
        }
    }
    return server;
}


2.获取数据,整理数据,对相应的字段进行分词,创建基本的分词库,将数据添加到solr document文档中


//部分伪代码片段
//步骤:
//1.获取solr core对应的连接
//2.删除集合中所有数据
//3.抓取和整理数据中获取的数据
//4.进行分词
//5.将分词构建成document存入solr中,将查询的其他数据构建成另外一个document存入solr中
//6.提交commit

public static boolean initDbIndex(Connection conn) {
	ResultSet result = null;
	HanyuPinyinOutputFormat spellFormat = new HanyuPinyinOutputFormat();
	int i = 0;
	try {
		
		server.deleteByQuery("*:*");
		
		Collection docs = new ArrayList();
		List uniqueData = new ArrayList();
		
		//此处从数据库获取你需要存储到搜索引擎里面的数据。
		//result = 从数据库抓取的结果集
		Map commentsCountMap = new HashMap();
		while (result.next()) {
			commentsCountMap.put(result.getLong("goodsId"), result.getInt("num"));
		}
		while (result.next()) {
			Long goodsId = result.getLong("goodsId");
			String goodsName = result.getString("goods_name");
			String coverUrl = result.getString("cover_url");
			String thumbUrl = result.getString("thumb_url");
			String categoryId = result.getString("cat_id");
			String categoryName = result.getString("cat_name");
			
			//得到分词集合,存储到solr中之后,主要用来做提示和引导,例如用户在搜索框输入鞋,然后下面提示鞋子,篮球鞋等。
			List suggestList = SolrJUtil.getFieldDefaultAnalysis("content",goodsName.toLowerCase());//手动分词
			List goodsIds = new ArrayList();
			for (String suggest : suggestList) {
				SolrInputDocument doc = null;
				if (!uniqueData.contains(suggest)) {// 不重复添加数据
					String pinyin = PinyinHelper.toHanyuPinyinString(suggest,spellFormat, "");
					doc = new SolrInputDocument();
					doc.addField("id", UUIDGenerator.getUUID());
					String trimSuggest = suggest.replaceAll(" ", "");//去除空格的中文索引
					String pinyin_Text = pinyin.replaceAll(" ", "");//拼音
					String firstSpellText = getFirstSpell(suggest);//拼音首字母
					doc.addField("suggest", suggest);//中文建议
					doc.addField("content", trimSuggest+" "+pinyin_Text+" "+ firstSpellText);//混合字段
					uniqueData.add(suggest);
				}
				if(!goodsIds.contains(goodsId)){
					String goodsName_py = PinyinHelper.toHanyuPinyinString(goodsName.replaceAll(" ", ""), spellFormat, "");
					if(doc == null){
						doc = new SolrInputDocument();
						//doc.addField("id", UUIDGenerator.getUUID());
					}
					doc.setField("id", goodsId);
					doc.addField("goodsId", goodsId);
					doc.addField("goodsName", goodsName);
					doc.addField("searchContent", goodsName.replaceAll(" ", "").toLowerCase()+goodsName_py);
					if(prices.size()>0){
						doc.addField("goodsPrice", prices.get(0));
					}
					if(minNums.size()>0){
						doc.addField("minOrderNum", minNums.get(0));
					}
					doc.addField("coverUrl", Global.IMG_SERVER_URL+coverUrl);
					doc.addField("thumbUrl", Global.IMG_SERVER_URL+thumbUrl);
					doc.addField("categoryId", categoryId);
					doc.addField("categoryName", categoryId+"_"+categoryName);
					goodsIds.add(goodsId);
				}
				if(doc != null){
					docs.add(doc);
				}
			}
		}
		try {
			if(docs.size()>0){
				UpdateResponse response = server.add(docs);
				server.commit();
				i = docs.size();
			}
		} catch (SolrServerException e) {
			e.printStackTrace();
			return false;
		} catch (IOException e) {
			e.printStackTrace();
			return false;
		}

	} catch (Exception e) {
		LOG.error("initDbIndex errror:"+e.getMessage());
		e.printStackTrace();
		return false;
	} finally {
		try {
			result.close();
			conn.close(); // 4、关闭数据库
		} catch (Exception e) {
			LOG.error("initDbIndex errror:"+e.getMessage());
			e.printStackTrace();
			return false;
		}
	}
	System.out.println("索引创建完毕,本次共更新" + i + "条数据!");
	LOG.error("索引创建完毕,本次共更新" + i + "条数据!");
	return true;
}


其他相关的工具类,主要用来进行分词.

获取指定字符串的分词,采用默认分词器


public static List getFieldDefaultAnalysis(String tokenField, String content) {
	FieldAnalysisRequest request = new FieldAnalysisRequest("/analysis/field");
	request.addFieldName(tokenField);// 字段名,随便指定一个支持中文分词的字段
	request.setFieldValue("");// 字段值,可以为空字符串,但是需要显式指定此参数
	request.setQuery(content);

	FieldAnalysisResponse response = null;
	try {
		response = request.process(server);
	} catch (Exception e) {
		e.printStackTrace();
	}

	List results = new ArrayList();
	Iterator it = response.getFieldNameAnalysis(tokenField).getQueryPhases().iterator();
	while (it.hasNext()) {
		AnalysisPhase pharse = (AnalysisPhase) it.next();
		List list = pharse.getTokens();
		for (TokenInfo info : list) {
			results.add(info.getText());
		}
	}

	return results;
}

获取汉语拼音首字母,英文字符不变

public static String getFirstSpell(String chinese) {
	StringBuffer pybf = new StringBuffer();
	char[] arr = chinese.toCharArray();
	HanyuPinyinOutputFormat defaultFormat = new HanyuPinyinOutputFormat();
	defaultFormat.setCaseType(HanyuPinyinCaseType.LOWERCASE);
	defaultFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
	for (int i = 0; i < arr.length; i++) {
		if (arr[i] > 128) {
			try {
				String[] temp = PinyinHelper.toHanyuPinyinStringArray(
						arr[i], defaultFormat);
				if (temp != null) {
					pybf.append(temp[0].charAt(0));
				}
			} catch (BadHanyuPinyinOutputFormatCombination e) {
				e.printStackTrace();
			}
		} else {
			pybf.append(arr[i]);
		}
	}
	return pybf.toString().replaceAll("\\W", "").trim();
}

上述有些方法是不需要的,例如不需要使用拼音分词等。就不需最后一个。


完成上述之后,solr中的数据基本就初始化和准备完毕了,这个时候需要使用程序来进行查询了:

public String productList() {
	SearchResult result = new SearchResult();
	HttpSolrServer server = null;
	server = SolrJUtil.getServer();
	SolrQuery query = new SolrQuery();
	keywords = keywords.trim();
	query.set("q", "searchContent:*" + StringUtils.escapeAllSpecialChars(keywords) + "*");
	query.set("spellcheck.q", StringUtils.escapeAllSpecialChars(keywords));
	query.set("qt", "/spell");// 请求到spell
	query.set("qf", "searchContent");// 查询字段
	query.set("fl", "goodsId,goodsName,goodsPrice,minOrderNum,productUnit,coverUrl,isProvideSample,"
			+ "transactions,countryCode,sourcePlace,productFeature,score,isEnquiry,isDiscount,fobMinPrice,fobMaxPrice,discount");// 返回字段

	query.setStart((page - 1) * Global.PAGE_SIZE);//分页查询起始位置
	query.setRows(Global.PAGE_SIZE);//分页大小
	QueryResponse rp = server.query(query);

	SolrDocumentList docList = rp.getResults();
	result.setTotalRows(docList.getNumFound());
	if (docList != null && docList.getNumFound() > 0) {
		//获取文档中的数据,同时封装到javaBean中
		String goodsName = (String) doc.getFieldValue("goodsName");
		result.setProducts(products);
		result.setSuccess(true);
	} else {
		SpellCheckResponse re = rp.getSpellCheckResponse();// 获取拼写检查的结果集
		if (re != null) {
			result.setSuccess(false);
			result.setSuggestStr(re.getFirstSuggestion(keywords.replaceAll(" ", "")));
		}
	}
}

这是普通的查询,如果想要加上高亮,因为一般在搜索结果中,都会对结果进行高亮:
query.setHighlight(true);

query.addHighlightField("companyName");

query.setHighlightSimplePre("");

query.setHighlightSimplePost("");

Map>> highlightMap = queryResponse.getHighlighting();

List companyNameList = highlightMap.get(idStr).get("companyName");

if (CollectionUtils.isNotEmpty(companyNameList)) {  

    //使用这个结果替换掉document中的值
    supplier.setCompanyName(companyNameList.get(0));
}

到此,基本上就已经介绍完了solr的配置,以及基本应用,可能讲述的有些凌乱。

注:上述代码都是伪代码,是提供一个步骤和思路。大家可以进行参考。

如果转载,请注明出处



你可能感兴趣的:(solr)