我们知道QueryElevationComponent组件可以实现竞价排名的功能,但是很不方便,需要在xml中配置。但大多数情况下,我们需要在我们的管理后台录入数据进行设置。
那么需求来了,可否我将竞价排名的数据录入数据库,使用接口让solr实时更新竞价排名的数据。经过研究源码,是可以实现的。只需要修改如下Handler即可:
org.apache.solr.handler.component.QueryElevationComponent
所以,我们下载solr的源码对该类进行修改,找到方法loadElevationMap修改为以下代码:
//load up the elevation map private Map<String, ElevationObj> loadElevationMap(Config cfg) throws IOException { XPath xpath = XPathFactory.newInstance().newXPath(); Map<String, ElevationObj> map = new HashMap<String, ElevationObj>(); //第一步:计算elevate/query节点的配置 NodeList nodes = (NodeList) cfg.evaluate("elevate/query", XPathConstants.NODESET); for (int i = 0; i < nodes.getLength(); i++) { Node node = nodes.item(i); String qstr = DOMUtil.getAttr(node, "text", "missing query 'text'"); NodeList children = null; try { children = (NodeList) xpath.evaluate("doc", node, XPathConstants.NODESET); } catch (XPathExpressionException e) { throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "query requires '<doc .../>' child"); } ArrayList<String> include = new ArrayList<String>(); ArrayList<String> exclude = new ArrayList<String>(); for (int j = 0; j < children.getLength(); j++) { Node child = children.item(j); String id = DOMUtil.getAttr(child, "id", "missing 'id'"); String e = DOMUtil.getAttr(child, EXCLUDE, null); if (e != null) { if (Boolean.valueOf(e)) { exclude.add(id); continue; } } include.add(id); } ElevationObj elev = new ElevationObj(qstr, include, exclude); if (map.containsKey(elev.analyzed)) { throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Boosting query defined twice for query: '" + elev.text + "' (" + elev.analyzed + "')"); } map.put(elev.analyzed, elev); } //第二步:计算elevate/interface节点的配置,使用接口配置elevate nodes = (NodeList) cfg.evaluate("elevate/interface", XPathConstants.NODESET); NodeList urlNodes = null; Node node = null; Node urlNode = null; ObjectMapper objectMapper = new ObjectMapper(); Map<String,Object> tempMap = null; List elevates = null; for (int i = 0; i < nodes.getLength(); i++) { ArrayList<String> include = new ArrayList<String>(); ArrayList<String> exclude = new ArrayList<String>(); node = nodes.item(i); try { urlNodes = (NodeList) xpath.evaluate("url", node, XPathConstants.NODESET); } catch (XPathExpressionException e) { throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "query requires '<url .../>' child"); } for(int j = 0;j < urlNodes.getLength();j++){ urlNode = urlNodes.item(j); String url = DOMUtil.getAttr(urlNode, "value", "missing 'value'"); elevates = objectMapper.readValue(new URL(url), List.class); if(elevates != null && elevates.size() != 0){ int size = elevates.size(); for(int k = 0;k < size;k++){ tempMap = (Map<String,Object>)elevates.get(k); String q = (String)tempMap.get("q");//关键词 ArrayList<String> ids = (ArrayList<String>)tempMap.get("ids");//需要排在前面的docId ArrayList<String> eids = (ArrayList<String>)tempMap.get("eids");//需要排除在结果集外的docId include.addAll(ids); exclude.addAll(eids); ElevationObj elev = new ElevationObj(q, include, exclude); if (map.containsKey(elev.analyzed)) { throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Boosting query defined twice for query: '" + elev.text + "' (" + elev.analyzed + "')"); } map.put(elev.analyzed, elev); } } } } //返回map return map; }
将编译后的class替换solr-core-xxx.jar中的class,即可。
在data目录新建elevate.xml,配置如下:
<elevate> <query text="tomcat"> <doc id="697321" /> <doc id="180008" /> </query> <interface> <url value="http://www.itdaan.com/getelevate.html"/> </interface> </elevate>
其中,interface就是我们可以配置的接口,直接可以返回需要竞价排名的数据。其中,接口返回的JSON数据如下:
[{"q":"nginx","ids":["697321","11298","150076","285875"],"eids":[]}]
q:表示关键词。ids:表示需要排名提前的docId。eids:需要过滤的docId。