这篇文章主要是关于solr和普通用户之间的桥梁SearchService,简单了解下整过工作流程.

为何要在Solr和用户之间加一层?

因为solr提供了url方式(REST风格)的API来进行增删改查,因此如果不加安全策略,别人在查询的同时可以修改你的数据,这是绝对不允许的。但是把solr的服务端口开放然后加安全策略的方式是不科学的,这个安全策略难以配置,并且漏洞很多,所以我们通过建立一个独立的web服务器来提供对外服务,solr服务器只对内网开放,这样就比较安全并容易控制。

在这里,我不直接提供传统意义上的web服务,而是采用WebService的模式,参照REST风格风格提供API服务。优点是可以应对各种不同平台。

这样做的另一个好处是能轻松应对SolrCloud的扩展和并发量的剧增,如果后期并发增加,可以扩展SearchService到多台web服务器,然后通过nginx做反向代理和负载均衡,将客户端的请求分散到不同的web服务器上。

用Servlet提供服务

dependencies {   
    testCompile group: 'junit', name: 'junit', version: '4.11' 
    testCompile 'org.slf4j:slf4j-simple:1.7.20'
    providedCompile 'javax.servlet:javax.servlet-api:3.0.1'  
    compile 'org.apache.solr:solr-solrj:5.5.0'  
    compile 'com.google.code.gson:gson:2.6.2' 
    compile 'com.squareup.okhttp:okhttp:2.7.5'
    }

定义API参数

参数 意义(取值范围) 默认值
keyword 关键词 null
point 坐标点 null
distance 距离 100KM
bound_type 界限方式{geofilt,bbox,linestring,polygon} null
boundary 范围 null
sort_order 排序方式 {score,distance} null
sight_type 景点类型 null
place 行政区划 null
page 页数 1
rows 每页记录条数 10
query_type 查询方式{near,key,map} near

Solrj的使用

1.先来构建出来SolrClient

代码位置:github中trip-search项目中SearchService/src/main/java/com/fliaping/trip/search/MySolrClient.java

public class MySolrClient {
    private static final String urlString = "http://localhost:8983/solr/trip";
    private static HttpSolrClient solrClient ;
    private static MySolrClient mySolrClient ;
    private MySolrClient(){}

    public static synchronized MySolrClient getInstance(){
        if (mySolrClient == null) mySolrClient = new MySolrClient();
        return mySolrClient;
    }
    public static HttpSolrClient getSolrClient(){
        if (solrClient == null){
            solrClient = new HttpSolrClient(urlString);
            solrClient.setSoTimeout(1000); // socket read timeout
            solrClient.setAllowCompression(true);  // allow Compression
        }
        return solrClient;
    }
}

2.设置查询参数,这里的内容较多,方法也各不相同,详细请参考 SolrJ Tutorial和官方 Using SolrJ

示例代码:

代码位置:github中trip-search项目中SearchService/src/main/java/com/fliaping/trip/search/DoQuery.java

solrQuery.setStart(start)
                .setRows(rows)
                .setQuery("{!geofilt}")
                .set("indent","true")
                .set("pt","22.5347168,113.971228") //当前坐标
                .set("sfield",Setting.sfield) //位置域
                .set("fl","_dist_:geodist(),*"); //返回结果中添加_dist_字段(到当前坐标的距离)

3.查询并得到结果

QueryResponse queryResponse = solrClient.query(solrQuery);

4.对结果进行自定义包装

代码位置:github中trip-search项目中SearchService/src/main/java/com/fliaping/trip/search/DoQuery.java

/**
     * 对查询结果包装成为客户端可用的格式
     * @param queryResponse solr的请求结果
     * @param hasFacet 是否需要facet
     * @return
     */
    private String wrapResult(QueryResponse queryResponse,boolean hasFacet){
        SightList sightList = new SightList();

        if(hasFacet){
            List<SightFacet> sightFacets = new ArrayList<SightFacet>();
            //整理facet数据
            List facet = queryResponse.getFacetFields();
            for (int i = 0; i < facet.size(); i++) {
                //取得每个facet的信息
                FacetField ff = (FacetField) facet.get(i);
                //System.out.println("name:"+ff.getName()+" valuecount:"+ff.getValueCount());

                SightFacet sightFacet = new SightFacet(); //facet对象
                sightFacet.setFacetName(ff.getName()); //设置facet的field名字
                List<SightFacet.Item> items = new ArrayList<SightFacet.Item>();
                //取得每个facet中的每个item
                List<FacetField.Count> countList =  ff.getValues();
                for (int j = 0; j < countList.size(); j++) {
                    FacetField.Count count = countList.get(j);
                    if (count.getCount() <= 0) continue; //facet中没有数据的,不加入结果集
                    //System.out.println("name:"+count.getName()+" count:"+count.getCount());
                    SightFacet.Item item = new SightFacet.Item(count.getName(),count.getCount());
                    items.add(item);
                }

                sightFacet.setFacetCount(items.size()); //设置facet中的个数
                sightFacet.setItems(items);   //设置facet中的items
                sightFacets.add(sightFacet);  //添加到facet列表中
            }
            sightList.setSightFacets(sightFacets);
        }


        //整理景点数据
        List<Sight> list = queryResponse.getBeans(Sight.class);
        SolrDocumentList documentList = queryResponse.getResults();

        sightList.setSights(list);
        sightList.setTime(queryResponse.getQTime());
        sightList.setTotalNum(documentList.getNumFound());
        sightList.setNowPage((int) (documentList.getStart()/documentList.size()) + 1);
        sightList.setTotalPage((int) (documentList.getNumFound()/documentList.size()) + 1);

        //美观型json
        Gson gson2 = new GsonBuilder().setPrettyPrinting().create();
        String json = gson2.toJson(sightList);

        //紧凑型json
        //String json = sightList.toJson();
        return json;
    }

三种查询方式

  • 附近查询、关键字查询、地图查询

根据参数判断执行的动作,通过query_type参数确定查询方式调用不同函数。

附近搜索

代码位置:github中trip-search项目中SearchService/src/main/java/com/fliaping/trip/search/DoQuery.java

/**
     * 附近搜索
     */
    public  void  nearQuery(){
        //http://localhost:9090/ss?query_type=near&distance=10&point=22.5347168,113.971228

        //设置查询Query
        solrQuery.setQuery("{!geofilt}");

        //设置距离distance
        int distance = notNull(request.getParameter(UrlP.distance.name()),3000); //距离默认值3000公里
        if (distance >= 0 && distance < 40076) { //判断距离值有效
            solrQuery.set("d",distance);
        }

        //设置坐标点
        String point = notNull(request.getParameter(UrlP.point.name()),"22.5347168,113.971228"); //默认坐标世界之窗
        solrQuery.set("pt",point);

        //设置排序
        solrQuery.setSort("geodist()", SolrQuery.ORDER.asc);

        //设置显示字段 fl
        solrQuery.setFields("_dist_:geodist()","*");


        //设置facet
        solrQuery.setFacet(true)
                .setFacetMissing(true)
                .set("facet.field","sight_type");


        System.out.println(solrQuery.toQueryString());


        try {
            QueryResponse queryResponse = solrClient.query(solrQuery);

            //对solr的查询结果,整合后返回
            respJson(wrapResult(queryResponse,false));
            System.out.println("distance:"+distance);

        } catch (SolrServerException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }


    }

关键词搜索

代码位置:github中trip-search项目中SearchService/src/main/java/com/fliaping/trip/search/DoQuery.java

public void keyQuery(){
        //http://localhost:8983/solr/trip/select?start=0&rows=10&sfield=sight_coordinate&fl=_dist_:geodist(),*&q=石河子&pt=44.3093867,86.0555942&sort=geodist() asc&facet=true&facet.missing=true&facet.field=sight_type
        //设置过滤
        //solrQuery.set("fq",request.getParameter(UrlP.keyword.name()));

        //设置关键字
        String keyword = notNull(request.getParameter(UrlP.keyword.name()),"*:*");
        solrQuery.setQuery(keyword);

        //设置坐标点
        String point = notNull(request.getParameter(UrlP.point.name()),"22.5347168,113.971228"); //默认坐标世界之窗
        solrQuery.set("pt",point);

        //设置排序
        String sortOrder = notNull(request.getParameter(UrlP.sort_order.name()),SortOrder.distance.get()); //默认距离排序


        if (SortOrder.distance.is(sortOrder)) { //距离排序
            solrQuery.setSort("geodist()", SolrQuery.ORDER.asc);

        }else if (SortOrder.score.is(sortOrder)){ //评分排序

            solrQuery.setSort("sight_score_ctrip", SolrQuery.ORDER.desc);

        }else if (SortOrder.price.is(sortOrder)){ //价格排序
            // TODO: 5/15/16 价格排序

        }else if (SortOrder.best.is(sortOrder)){ //综合排序
            // TODO: 5/15/16 综合排序

        }

        //设置过滤
        //设置景点类型过滤
        String sight_type = request.getParameter(UrlP.sight_type.name());
        if(sight_type != null){
            String[] type = sight_type.split(",");
            for (String item : type) {
                if (item != null){
                    solrQuery.addFilterQuery("sight_type:"+item);
                }
            }
        }

        //设置高亮
        /*solrQuery.setHighlight(true)
                .setHighlightSimplePre("<em>")
                .setHighlightSimplePost("</em>")
                .set("hl.fl","sight_intro");*/
        //设置facet
        solrQuery.setFacet(true)
                .setFacetMissing(true)
                .set("facet.field","sight_type");


        System.out.println(solrQuery.toQueryString());
        try {
            QueryResponse queryResponse = solrClient.query(solrQuery);
            //对solr的查询结果,整合后返回
            respJson(wrapResult(queryResponse,true));
        } catch (SolrServerException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }


    }

results matching ""

    No results matching ""