solr8学习进阶(二)整合springboot实现空间资源搜索

本文基于2019.10.25的solr最新版本solr-8.2.0

目标

实现对mysql数据库中的资源数据的圆选、框选。圆选及在地图上画圆,将圆心坐标以及半径作为参数传给后端,后端返回坐标处于圆内的资源数据,用于前端将资源标绘。框选即在地图上选择多个点组成多边形,将每个角的坐标传给后端,后端返回坐标在多边形(框)内的资源数据,用于前端将资源进行标绘。

solr导入MySql数据

上一篇已经讲过了,这里不再重复,不清楚的见solr8学习进阶(一)MySql整合
data-config.xml文件配置:

<?xml version="1.0" encoding="UTF-8" ?>
<dataConfig>
    <!-- 数据库信息 -->
    <dataSource type="JdbcDataSource" 
        driver="com.mysql.jdbc.Driver" 
        url="jdbc:mysql://192.168.1.160:3306/emplus_eos" 
        user="emt" password="chinaemt"/>
    <document>
        <!-- document实体 name为数据库表;pk为managed-schema文件中定义的<uniqueKey>id</uniqueKey> -->
        <entity name="app_db_disaster_shelter" pk="id" 
        	<!--  这里只查询数据库中我所需要的字段 -->
        	query="SELECT `i_id` as id,
  				`vc_name` as vcName,
  				`d_longitude` as x,
				`d_latitude` as y,
 				`vc_address` as vcAddress,
  				`vc_remark` as vcRemark,
  				concat( d_longitude,' ',d_latitude) as GEO
  				FROM app_db_disaster_shelter where sys_i_status=0"
  			<!-- deltaQuery、deletedPkQuery、deltaImportQuery三个方法是用于定时增量同步数据库,这里暂时不讲解,留到后面  -->
  			deltaQuery="SELECT `i_id` AS `id` FROM `app_db_disaster_shelter` where sys_i_status=0 and 
  			 	sys_dt_last_update>'${dataimporter.last_index_time}'"
			deletedPkQuery="SELECT `i_id` AS `id` FROM `app_db_disaster_shelter` where sys_i_status!='0'"
			deltaImportQuery="SELECT `i_id` as id,
  				`vc_name` as vcName,
  				`d_longitude` as x,
  				`d_latitude` as y,
  				`vc_address` as vcAddress,
  				`vc_remark` as vcRemark,
  				concat( d_longitude,' ',d_latitude) as GEO
  				from app_db_disaster_shelter where sys_i_status=0  and i_id='${dataimporter.delta.id}'">
            <!-- 数据库字段映射solr字段 也可以不写,写出来是为了结构清晰,数据库字段和core的属性一一对应 -->
            <field column="i_id" name="id"/>
            <field column="vc_name" name="vcName"/>
            <!-- <field column="i_area_id" name="iAreaId"/> -->
            <!-- <field column="vc_dept" name="vcDept"/> -->
            <field column="d_longitude" name="x"/>
            <field column="d_latitude" name="y"/>
            <field column="vc_address" name="vcAddress"/>
            <!-- <field column="vc_traffic" name="vcTraffic"/> -->
            <!-- <field column="vc_duty_tel" name="vcDutyTel"/> -->
            <!-- <field column="vc_fax" name="vcFax"/> -->
            <!-- <field column="vc_dept_address" name="vcDeptAddress"/> -->
            <!-- <field column="vc_group" name="vcGroup"/> -->
            <!-- <field column="dt_update_time" name="dtUpdateTime"/> -->
            <field column="vc_remark" name="vcRemark"/>
            <!-- <field column="dt_use_time" name="dtUseTime"/> -->
            <!-- <field column="vc_basic_situation" name="vcBasicSituation"/> -->
            <!-- <field column="d_area" name="dArea"/> -->
            <!-- <field column="i_dept_id" name="iDeptId"/> -->
            <!-- <field column="i_operate_id" name="iOperateId"/> -->
            <!-- <field column="i_status" name="iStatus"/> -->
            <!-- <field column="i_extend1" name="iExtend1"/> -->
            <!-- <field column="i_extend2" name="iExtend2"/> -->
            <!-- <field column="vc_extend1" name="vcExtend1"/> -->
            <!-- <field column="vc_extend2" name="vcExtend2"/> -->
            <!-- <field column="vc_extend3" name="vcExtend3"/> -->
            <!-- <field column="sys_i_status" name="sysIStatus"/> -->
            <!-- <field column="sys_dt_create" name="sysDtCreate"/> -->
            <!-- <field column="sys_i_create_user" name="sysICreateUser"/> -->
            <!-- <field column="sys_dt_last_update" name="sysDtLastUpdate"/> -->
            <!-- <field column="sys_i_last_update_user" name="sysILastUpdateUser"/> -->
            <!-- <field column="sys_vc_remark" name="sysVcRemark"/> -->
            <!-- <field column="i_datasync_unit_id" name="iDatasyncUnitId"/> -->
            <!-- <field column="dt_datasync_time" name="dtDatasyncTime"/> -->
            <!-- <field column="i_datasync_id" name="iDatasyncId"/> -->
            <!-- <field column="origin" name="origin"/> -->
        </entity>
    </document>
</dataConfig>

managed-schema文件配置如下:

<!-- name为属性,要和data-config.xml文件中定义的field的name属性一致;type从fieldType的name属性中选择,stored-是否存储;required-是否必须;multiValued-是否多值 -->
<field name="id" type="string" indexed="true" stored="true" required="true" multiValued="false" />
<field name="vcName" type="text_cn" indexed="true" stored="true" required="false" multiValued="false"/>
<field name="x" type="string" indexed="true" stored="true" required="false" multiValued="false"/>
<field name="y" type="string" indexed="true" stored="true" required="false" multiValued="false"/>
<field name="vcAddress" type="string" indexed="true" stored="true" required="false" multiValued="false"/>
<field name="vcRemark" type="string" indexed="true" stored="true" required="false" multiValued="false"/>
<field name="GEO" type="location_rpt" indexed="true" stored="true" required="false" multiValued="false"/>

注意:

  • 除了id属性以外,其他属性的required应定义为false,否则数据库字段数据为空时solr会报错
  • GEO属性由经度、纬度组合而成,格式为 (经度 纬度),用于圆选或框选的计算

然后就是启动solr了,导入数据了,前面都说过了

话不多说,直接上代码

controller层

/**
 * Project Name: solr
 * Package Name:com.solr.demo.contraller
 * File Name:AppDbResourseController.java
 * Data:2019年11月1日
 */
package com.solr.demo.contraller;

import com.solr.demo.DO.AppDbResourse;
import com.solr.demo.base.AppError;
import com.solr.demo.base.ResponseJSON;
import com.solr.demo.enums.CoreName;
import com.solr.demo.model.SearchParam;
import com.solr.demo.service.SearchService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author xrw
 * @create 2019/11/01
 * @description 提供solr的appDbMaterialAddress这个core的读写操作API
 */
@RestController
@RequestMapping("/solr/EmplusResources")
public class AppDbResourseController {
    Logger logger = LoggerFactory.getLogger(AppDbResourseController.class);
    @Autowired
    SearchService searchService;

    /**
     * 应急资源圆选:根据圆心处经纬度、半径查找圈内所有应急资源
     * @param x 经度
     * @param y 纬度
     * @param radius 半径,单位公里
     * @param b_appDbDisasterShelter 是否查找庇护场所
     * @param b_appDbMaterialAddress 是否查找物资库
     * @param b_appDbProtectionobject 是否查找防护目标
     * @param b_appDbRisk 是否查找风险隐患
     * @param b_appDbTeam 是否查找救援队伍
     * @return
     */
    @RequestMapping("/searchAppDbResourseByPoint")
    public ResponseJSON searchAppDbResourseByPoint(@RequestParam("x") Double x,
                                                   @RequestParam("y") Double y,
                                                   @RequestParam("radius") Double radius,
                                                   @RequestParam("appDbDisasterShelter") Boolean b_appDbDisasterShelter,
                                                   @RequestParam("appDbMaterialAddress") Boolean b_appDbMaterialAddress,
                                                   @RequestParam("appDbProtectionobject") Boolean b_appDbProtectionobject,
                                                   @RequestParam("appDbRisk") Boolean b_appDbRisk,
                                                   @RequestParam("appDbTeam") Boolean b_appDbTeam) {
        SearchParam searchParam;
        //参数不为空时,构建查询对象
        if (x != null && y != null && radius != null) {
            searchParam = new SearchParam(x, y, radius);
        } else {//为空时返回错误码
            return new ResponseJSON(AppError.PARAM_NULL_ERROR);
        }
        Map<String, Object> reSoureseMap = new HashMap<>();
        //是否查找庇护场所
        if (b_appDbDisasterShelter) {
            List<AppDbResourse> appDbDisasterShelter = searchService.searchAppDbResourseByPoint(searchParam, CoreName.appDbDisasterShelter.getCoreName());
            reSoureseMap.put("appDbDisasterShelter", appDbDisasterShelter);
        }
        //是否查找物资库
        if (b_appDbMaterialAddress) {
            List<AppDbResourse> appDbMaterialAddress = searchService.searchAppDbResourseByPoint(searchParam, CoreName.appDbMaterialAddress.getCoreName());
            reSoureseMap.put("appDbMaterialAddress", appDbMaterialAddress);
        }
        //是否查找防护目标
        if (b_appDbProtectionobject) {
            List<AppDbResourse> appDbProtectionobject = searchService.searchAppDbResourseByPoint(searchParam, CoreName.appDbProtectionobject.getCoreName());
            reSoureseMap.put("appDbProtectionobject", appDbProtectionobject);
        }
        //是否查找风险隐患
        if (b_appDbRisk) {
            List<AppDbResourse> appDbRisk = searchService.searchAppDbResourseByPoint(searchParam, CoreName.appDbRisk.getCoreName());
            reSoureseMap.put("appDbRisk", appDbRisk);
        }
        //是否查找救援队伍
        if (b_appDbTeam) {
            List<AppDbResourse> appDbTeam = searchService.searchAppDbResourseByPoint(searchParam, CoreName.appDbTeam.getCoreName());
            reSoureseMap.put("appDbTeam", appDbTeam);
        }
        ResponseJSON responseJSON = new ResponseJSON();
        responseJSON.put("data", reSoureseMap);
        logger.info("searchAppDbResourseByPoint success!");
        return responseJSON;
    }

    /**
     * 应急资源框选:用一组闭环的坐标画一个多边形,将处于多边形内的应急资源返回给前端
     * @param points 多边形坐标参数;参数示例:POLYGON((经度 纬度,经度 纬度,......));备注:(第一组经纬度和最后一组相同)
     * @param b_appDbDisasterShelter 是否查询庇护场所
     * @param b_appDbMaterialAddress 是否查询救援物资库
     * @param b_appDbProtectionobject 是否查询防护目标
     * @param b_appDbRisk 是否查询救援队伍
     * @param b_appDbTeam 是否查询风险隐患
     * @return
     */
    @RequestMapping("/searchAppDbResourseByPolygon")
    public ResponseJSON searchAppDbResourseByPolygon(@RequestParam("points") String points,
                                                     @RequestParam("appDbDisasterShelter") Boolean b_appDbDisasterShelter,
                                                     @RequestParam("appDbMaterialAddress") Boolean b_appDbMaterialAddress,
                                                     @RequestParam("appDbProtectionobject") Boolean b_appDbProtectionobject,
                                                     @RequestParam("appDbRisk") Boolean b_appDbRisk,
                                                     @RequestParam("appDbTeam") Boolean b_appDbTeam) {
        SearchParam searchParam;
        //参数不为空时,构建查询对象
        if (points != null) {
            searchParam = new SearchParam(points);
        } else {//为空时返回错误码
            return new ResponseJSON(AppError.PARAM_NULL_ERROR);
        }
        Map<String, Object> reSoureseMap = new HashMap<>();
        //是否查找庇护场所
        if (b_appDbDisasterShelter) {
            List<AppDbResourse> appDbDisasterShelter = searchService.searchAppDbResourseByPolygon(searchParam, CoreName.appDbDisasterShelter.getCoreName());
            reSoureseMap.put("appDbDisasterShelter", appDbDisasterShelter);
        }
        //是否查找物资库
        if (b_appDbMaterialAddress) {
            List<AppDbResourse> appDbMaterialAddress = searchService.searchAppDbResourseByPolygon(searchParam, CoreName.appDbMaterialAddress.getCoreName());
            reSoureseMap.put("appDbMaterialAddress", appDbMaterialAddress);
        }
        //是否查找防护目标
        if (b_appDbProtectionobject) {
            List<AppDbResourse> appDbProtectionobject = searchService.searchAppDbResourseByPolygon(searchParam, CoreName.appDbProtectionobject.getCoreName());
            reSoureseMap.put("appDbProtectionobject", appDbProtectionobject);
        }
        //是否查找风险隐患
        if (b_appDbRisk) {
            List<AppDbResourse> appDbRisk = searchService.searchAppDbResourseByPolygon(searchParam, CoreName.appDbRisk.getCoreName());
            reSoureseMap.put("appDbRisk", appDbRisk);
        }
        //是否查找救援队伍
        if (b_appDbTeam) {
            List<AppDbResourse> appDbTeam = searchService.searchAppDbResourseByPolygon(searchParam, CoreName.appDbTeam.getCoreName());
            reSoureseMap.put("appDbTeam", appDbTeam);
        }
        ResponseJSON responseJSON = new ResponseJSON();
        responseJSON.put("data", reSoureseMap);
        logger.info("searchAppDbResourseByPolygon success!");
        return responseJSON;
    }
}

Service层

Service:

import com.solr.demo.DO.AppDbResourse;
import com.solr.demo.model.SearchParam;
import java.util.List;

public interface SearchService {
    List<AppDbResourse> searchAppDbResourseByPoint(SearchParam searchParam,String m_coreName);
    List<AppDbResourse> searchAppDbResourseByPolygon(SearchParam searchParam,String m_coreName);
}

ServiceImp:

package com.solr.demo.service.serviceImp;

import com.solr.demo.DO.AppDbResourse;
import com.solr.demo.model.SearchParam;
import com.solr.demo.service.SearchService;
import com.solr.demo.SolrClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * ClassName: SearchSerivceImpl <br/>
 * Description: 空间搜索实现类 <br/>
 * Date: 2019年11月01日<br/>
 * <br/>
 *
 * @author xrw
 * <p>
 */
@Service(value = "searchSerivceImpl")
public class SearchSerivceImpl implements SearchService {
    Logger logger = LoggerFactory.getLogger(SearchService.class);
    String coreName; // core名称

    @Override
    public List<AppDbResourse> searchAppDbResourseByPoint(SearchParam searchParam, String m_coreName) {
        this.coreName = m_coreName;
        SolrClient solrClient = SolrClient.getInstance(); // 获取solr实例
        // 圆选条件,如有其筛选条件,必须放在此条件前面,否则其它条件不起作用
        String criteria = "{!geofilt sfield=GEO pt=" + searchParam.getY() + "," + searchParam.getX() + " d="
                + searchParam.getRadius() + "}";
        String[] solrAttribute = new String[]{"id", "vcName", "x", "y", "vcAddress", "vcRemark", "GEO"}; // solr中的数据属性名
        String[] beanAttribute = new String[]{"id", "vcName", "x", "y", "vcAddress", "vcRemark", "GEO"}; // dto中的属性名

        List<AppDbResourse> list = solrClient.getBeans(criteria, coreName, Integer.MAX_VALUE, 0, null, AppDbResourse.class,
                solrAttribute, beanAttribute, null);
        return list;
    }

    @Override
    public List<AppDbResourse> searchAppDbResourseByPolygon(SearchParam searchParam, String m_coreName) {
        this.coreName = m_coreName;
        SolrClient solrClient = SolrClient.getInstance(); // 获取solr实例
        //searchParam.getPolygon() 的示例
        // String test = "POLYGON((0 0,0 90,112 90,112 0,0 0))";
        String criteria = " GEO:\"IsWithin(" + searchParam.getPolygon() + ")\" "; // 查询条件
//        logger.info(criteria);
        String[] solrAttribute = new String[]{"id", "vcName", "x", "y", "vcAddress", "vcRemark", "GEO"}; // solr中的数据属性名
        String[] beanAttribute = new String[]{"id", "vcName", "x", "y", "vcAddress", "vcRemark", "GEO"}; // dto中的属性名
        List<AppDbResourse> list = solrClient.getBeans(criteria, coreName, Integer.MAX_VALUE, 0, null, AppDbResourse.class,
                solrAttribute, beanAttribute, null);
        return list;
    }
}

SolrClient实现

import org.apache.commons.lang3.StringUtils;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrRequest;
import org.apache.solr.client.solrj.SolrServer;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.impl.HttpSolrServer;
import org.apache.solr.client.solrj.request.QueryRequest;
import org.apache.solr.client.solrj.response.FacetField;
import org.apache.solr.client.solrj.response.FacetField.Count;
import org.apache.solr.client.solrj.response.Group;
import org.apache.solr.client.solrj.response.GroupCommand;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.client.solrj.response.TermsResponse;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;
import org.apache.solr.common.SolrInputDocument;
import org.apache.solr.common.util.NamedList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

/**
 * ClassName: SolrClient <br/>
 * Description: solr查询方法封装类 <br/>
 * Date: 2019年11月1日 <br/>
 * <br/>
 */
public class SolrClient {
	private Logger log = LoggerFactory.getLogger(SolrClient.class);

    /**
     * SolrClient实例对象
     */
    private static SolrClient mInstance;

    /**
     * ID唯一标识
     */
    public static final String ID = "id";

    /**
     * solr服务哈希表,key为core名称,value为HttpSolrServer对象
     */
    private HashMap<String, SolrServer> solrServers = new HashMap<String, SolrServer>();

    /**
     * UTF-8编码
     */
    public static final String UTF8_CHARSET_NAME = "UTF-8";

    /**
     * solr的url key值
     */
    private static  String SOLR_URL_KEY = "sajRisk.solr_url";

    /**
     * 拆分词组URL,用于拆分词组
     */
    public static final String URL_SPLIT_WORDS = "splitwords";

    /**
     * 代码词组的URL,用于根据代码得到标准词组
     */
    public static final String URL_CODE_WORDS = "codewords";

    /**
     * 日期格式
     */
    private static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    /** 字段业务名称--标准地址名称 */
    public static final String FIELD_AC_STANDARD_ADDR_NAME = "AC_STANDARD_ADDR_NAME";

    /** 字段业务名称--标准地址名称(字符串类型),冗余字段类型,用于ALL查询 */
    public static final String FIELD_AC_STANDARD_ADDR_NAME_S = "AC_STANDARD_ADDR_NAME_S";

    /** 注解字段常量--otherFields */
    public static final String ANNOTATION_OTHERFIELDS = "otherFields";

    /**
     * 获取solrClient单一实例
     * 
     * @return
     */
    public static SolrClient getInstance() {
        if (mInstance == null) {
            synchronized (SolrClient.class) {
                if (mInstance == null) {
                    mInstance = new SolrClient();
                }
            }
        }
        return mInstance;
    }

    /**
     * 根据core名称得到SolrServer对象,如果不存在,就创建
     * 
     * @param core
     *            solr的core名称
     * @return
     */
    public SolrServer getSolrServer(String core) {
        if (solrServers.containsKey(core)) {
            return solrServers.get(core);
        }

        Properties props = new Properties();
        InputStream resourceStream = SolrClient.class.getClassLoader().getResourceAsStream("application.properties");
        try {
            props.load(resourceStream);
        } catch (IOException e1) {
            getLogger().error("SolrClient.getProperties() - Failed to load properties file", e1);
        }

        String solrCoreUrl = props.getProperty(SOLR_URL_KEY) + "/" + core;
        SolrServer solrServer;
        try {
            solrServer = new HttpSolrServer(solrCoreUrl);
            solrServer.ping();
            solrServers.put(core, solrServer);
            return solrServer;
        } catch (Throwable e) {
            log.error("SolrClient.getSolrServer() - Failed to connect:" + solrCoreUrl, e);
        }
        return null;
    }
    /**
     * 获取得到对象化的数据列表<br>
     * 使用场景:<br>
     * 1.当没有过滤显示字段数组,转换对象的Class注解有@Field关联字段值进行映射,返回对象数据列表。<br>
     * 2.当有过滤显示字段数组并且没有过滤显示字段别名数组,过滤字段后,转换对象的Class注解有@Field关联字段值进行映射,返回对象数据列表。
     * <br>
     * 3.当有过滤显示字段数组并且有过滤显示字段别名数组,不需要转换对象的Class注解有@Field字段:<br>
     * 1)长度顺序一致,即都设置有别名情况下,设置对应的别名,并且能找到与别名名称相同的转换对象Class的属性名,通过反射直接赋值。<br>
     * 2)顺序一致,但长度不一致,即有部分设置别名,有部分没有设置别名,设置对应的别名,并且能找到与别名名称相同的转换对象Class的属性名,
     * 通过反射直接赋值,找不到相同的属性名则不做赋值处理。<br>
     * 
     * @param criteria
     *            查询条件
     * @param coreName
     *            core的名称
     * @param rows
     *            查询条数
     * @param start
     *            开始查询的位置
     * @param sortParams
     *            排序参数,以逗号隔开。如id desc,name desc
     * @param clazz
     *            要转换对象的Class
     * @param fields
     *            过滤显示字段数组,如果设置,只显示数组内的字段,数组为空,显示全部
     * @param aliasFields
     *            过滤显示字段别名数组,数组的顺序与fields字段数组的顺序一一对应,长度可不相同,当不相同时,
     *            取回fields对应的字段名为别名,并且返回对象的属性名与别名一致。
     * @param filterQueryParams
     *            过滤查询条件,例如权重条件设置:a^2 b^0.3 c^1
     * @return
     */
    public <T extends Object> List<T> getBeans(String criteria, String coreName, int rows, int start, String sortParams,
            Class<T> clazz, String[] fields, String[] aliasFields, String filterQueryParams) {
        if (StringUtils.isNotEmpty(criteria) && StringUtils.isNotEmpty(coreName)) {
            SolrServer solrServer = getSolrServer(coreName);
            SolrQuery solrQuery = new SolrQuery().setQuery(criteria).setRows(rows).setStart(start);
            this.setSortParams(sortParams, solrQuery);
            this.setFieldListParams(fields, aliasFields, solrQuery);
            this.setFilterQueryParams(filterQueryParams, solrQuery);
            QueryRequest queryRequest = new QueryRequest(solrQuery);
            queryRequest.setMethod(SolrRequest.METHOD.POST);
            try {
                QueryResponse response = queryRequest.process(solrServer);

                if (response.getResults() != null) {
                    // 如果设置有别名,根据转换对象的Class,通过反射设置得到对应的转换对象列表,并返回
                    // 如果没有设置别名,返回solr原始getBeans方法得到的对象列表值。该getBeans方法是通过注解的方'式设置映射关系
                    if (fields != null && fields.length > 0 && aliasFields != null && aliasFields.length > 0) {
                        return this.getBeansByDocsAndClazz(response.getResults(), clazz);
                    } else {
                        return response.getBeans(clazz);
                    }
                }
            } catch (SolrServerException e) {
                e.printStackTrace();
            }
        }
        return Collections.emptyList();
    }
    /**
     * 设置过滤查询参数<br>
     * 例如权重条件设置:a^2 b^0.3 c^1
     *
     * @param params
     *            参数
     * @param solrQuery
     *            solr查询类
     */
    private void setFilterQueryParams(String params, SolrQuery solrQuery) {
        if (StringUtils.isNotEmpty(params)) {
            String[] paramPairs = params.trim().split(" *  *");
            for (String paramPair : paramPairs) {
                solrQuery.addFilterQuery(paramPair);
            }
        }
    }

    /**
     * 设置过虑字段参数,如果有别名,则设置别名
     *
     * @param fields
     *            过滤显示字段数组,如果设置,只显示数组内的字段,数组为空,显示全部
     * @param aliasFields
     *            过滤显示字段别名数组,数组的顺序与fields字段数组的顺序一一对应,长度可不相同,当不相同时,
     *            取回fields对应的字段名为别名
     * @param solrQuery
     *            solr查询类
     */
    private void setFieldListParams(String[] fields, String[] aliasFields, SolrQuery solrQuery) {
        if (fields == null || fields.length <= 0) {
            return;
        }

        // 判断字段别名数组不为空且长度与字段数组相等
        if (aliasFields != null && aliasFields.length > 0) {
            StringBuilder fieldSb = new StringBuilder();
            // 别名的格式是: 别名:真实名
            for (int i = 0; i < fields.length; i++) {
                fieldSb.delete(0, fieldSb.length());
                if (aliasFields.length - 1 >= i) {
                    if (StringUtils.isEmpty(aliasFields[i])) {
                        aliasFields[i] = fields[i];
                    }
                    fieldSb.append(aliasFields[i]).append(":").append(fields[i]);
                } else {
                    fieldSb.append(fields[i]).append(":").append(fields[i]);
                }
                fields[i] = fieldSb.toString();
            }
        }
        solrQuery.setFields(fields);
    }
    /**
     * 根据solr文档列表和转换对象的Class,通过反射设置得到对应的转换对象列表<br>
     * 1.主要用于当已经设置过滤返回对象和别名后得到的docs文档列表,根据转换对象的Class,设置与文档key一致的属性值<br>
     * 2.当转换对象的Class注解有@Field("otherFields"),把那些没有设置到属性里的值,全部加入到注解有@Field(
     * "otherFields")的Map对象中<br>
     * 3.如果没有找到注解有@Field("otherFields")的Map,那些没有设置到属性里的值全部丢弃掉<br>
     *
     * @param docs
     *            solr文档对象列表
     * @param clazz
     *            要转换对象的Class
     * @return
     */
    private <T extends Object> List<T> getBeansByDocsAndClazz(List<SolrDocument> docs, Class<T> clazz) {
        // solr文档对象列表为空,直接返回空列表
        if (docs == null || docs.size() <= 0) {
            return Collections.emptyList();
        }

        // 得到所有属性列表
        Field[] declaredFields = clazz.getDeclaredFields();
        // 对象实例
        T obj = null;
        // 其他字段值map
        Map<String, String> otherFieldValueMap = null;
        // solr字段Object对象变量
        Object fieldValueObj = null;
        // solr字段字符串变量
        String fieldValueStr = null;
        // 返回列表
        List<T> rtnList = new ArrayList<T>();
        // 是否有相同的字段名称
        boolean hasSameFieldName = false;

        for (SolrDocument doc : docs) {
            // fieldValueMap = doc.getFieldValueMap();
            try {
                hasSameFieldName = false;
                otherFieldValueMap = new HashMap<String, String>();
                // 创建实例
                obj = clazz.newInstance();
                // 循环反射得到的字段列表,比较字段名是否一致,一致的话则赋值给对象。
                for (Entry<String, Object> entry : doc.entrySet()) {
                    fieldValueObj = entry.getValue();

                    for (Field field : declaredFields) {
                        // 字段名一致
                        if (field.getName().equals(entry.getKey())) {
                            field.setAccessible(true);

                            // 类型转换,如果是solr文档对象是日期类型,并且与clazz属性类型不一致,则做日期格式转换
                            if (fieldValueObj instanceof Date) {
                                if (field.getType() == Date.class) {
                                    field.set(obj, fieldValueObj);
                                } else {
                                    field.set(obj, dateFormat.format(fieldValueObj));
                                }
                            } else {
                                // 除了日期类型之外,其他类型solr对象与bean对象属性类型一致,按原类型设置值
                                if (fieldValueObj.getClass() == field.getType()) {
                                    field.set(obj, fieldValueObj);
                                } else {
                                    field.set(obj, fieldValueObj.toString());
                                }
                            }
                            hasSameFieldName = true;
                            break;
                        }
                    }

                    if (!hasSameFieldName) {
                        // 那些没有找到相同属性名的值,全部加入Map对象中
                        if (fieldValueObj instanceof Date) {
                            fieldValueStr = dateFormat.format(fieldValueObj);
                        } else {
                            // 除了日期类型之外,其他类型按字符串类型设置值
                            fieldValueStr = fieldValueObj.toString();
                        }
                        otherFieldValueMap.put(entry.getKey(), fieldValueStr);
                    }

                } // end-for (Entry<String, Object> entry : doc.entrySet())

                // 通过反射,设置其他字段值map到对象实例
                setOtherFieldValueMap(declaredFields, obj, otherFieldValueMap);

                rtnList.add(obj);
            } catch (InstantiationException e) {
                // 出现异常,记录日志,不直接抛出中断流程
                String error = "通过转换得到对应的转换对象列表方法时,出现InstantiationException异常!";
                log.error(error, e);
            } catch (IllegalAccessException e) {
                // 出现异常,记录日志,不直接抛出中断流程
                String error = "通过转换得到对应的转换对象列表方法时,出现IllegalAccessException异常!";
                log.error(error, e);
            }
        }
        return rtnList;
    }
    /**
     * 通过反射,设置其他字段值map到对象实例
     *
     * @param declaredFields
     *            所有属性字段的列表
     * @param obj
     *            要转换对象Class的对象实例
     * @param otherFieldValueMap
     *            其他字段值map
     * @return
     */
    private <T extends Object> T setOtherFieldValueMap(Field[] declaredFields, T obj,
            Map<String, String> otherFieldValueMap) {

        for (Field field : declaredFields) {
            if (field.isAnnotationPresent(org.apache.solr.client.solrj.beans.Field.class)
                    && field.getType() == Map.class) {

                org.apache.solr.client.solrj.beans.Field annotationField = field
                        .getAnnotation(org.apache.solr.client.solrj.beans.Field.class);

                // 注解的字段名是否为otherFields,则把除了有设置别名之外的需要返回的字段值,赋值给该字段值上
                if (ANNOTATION_OTHERFIELDS.equals(annotationField.value())) {
                    try {
                        field.setAccessible(true);
                        field.set(obj, otherFieldValueMap);
                    } catch (IllegalArgumentException e) {
                        // 出现异常,记录日志,不直接抛出中断流程
                        String error = "通过反射设置其他字段值map到对象实例方法时,出现InstantiationException异常!";
                        log.error(error, e);
                    } catch (IllegalAccessException e) {
                        // 出现异常,记录日志,不直接抛出中断流程
                        String error = "通过反射设置其他字段值map到对象实例方法时,出现IllegalAccessException异常!";
                        log.error(error, e);
                    }
                    break;
                }
            }
        }

        return obj;
    }
}

Model

package com.solr.demo.model;

public class SearchParam {

    //经度
    private double x;
    //纬度
    private double y;
    //半径
    private double radius;

    private String Polygon;

    public SearchParam(double x, double y, double radius) {
        this.x = x;
        this.y = y;
        this.radius = radius;
    }

    public SearchParam(String polygon) {
        this.Polygon = polygon;
    }

    public double getX() {
        return x;
    }

    public void setX(double x) {
        this.x = x;
    }

    public double getY() {
        return y;
    }

    public void setY(double y) {
        this.y = y;
    }

    public double getRadius() {
        return radius;
    }

    public void setRadius(double radius) {
        this.radius = radius;
    }

    public String getPolygon() {
        return Polygon;
    }

    public void setPolygon(String polygon) {
        Polygon = polygon;
    }
}

Enum

package com.solr.demo.enums;

public enum CoreName {
    //庇护场所
    appDbDisasterShelter("appDbDisasterShelter"),
    //物资库
    appDbMaterialAddress("appDbMaterialAddress"),
    //防护目标
    appDbProtectionobject("appDbProtectionobject"),
    //救援队伍
    appDbTeam("appDbTeam"),
    //风险隐患
    appDbRisk("appDbRisk");
    private String coreName;
    CoreName(String coreName) {
        this.coreName = coreName;
    }
    public String getCoreName() {
        return coreName;
    }
}

DO

这里提供一个抽象的资源实体类,具体的core的实体自己建

package com.solr.demo.DO;

/**
 * @author xrw
 * @create 2019/11/01
 * 资源抽象实体实体
 */
public class AppDbResourse {

    private String id;
    private String vcName;
    private String x;
    private String y;
    private String vcAddress;
    private String vcRemark;
    private String GEO;

    public String getid() { return id; }

    public void setid(String id) { this.id = id; }

    public String getVcName() { return vcName; }

    public void setVcName(String vcName) { this.vcName = vcName; }

    public String getX() { return x; }

    public void setX(String x) { this.x = x; }

    public String getY() { return y; }

    public void setY(String y) { this.y = y; }

    public String getVcAddress() { return vcAddress; }

    public void setVcAddress(String vcAddress) { this.vcAddress = vcAddress; }

    public String getVcRemark() { return vcRemark; }

    public void setVcRemark(String vcRemark) { this.vcRemark = vcRemark; }

    public String getGEO() { return GEO; }

    public void setGEO(String GEO) { this.GEO = GEO; }


    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        AppDbResourse that = (AppDbResourse) o;

        if (id != null ? !id.equals(that.id) : that.id != null) return false;
        if (vcName != null ? !vcName.equals(that.vcName) : that.vcName != null) return false;
        if (x != null ? !x.equals(that.x) : that.x != null) return false;
        if (y != null ? !y.equals(that.y) : that.y != null) return false;
        if (vcAddress != null ? !vcAddress.equals(that.vcAddress) : that.vcAddress != null) return false;
        if (vcRemark != null ? !vcRemark.equals(that.vcRemark) : that.vcRemark != null) return false;
        if (GEO != null ? !GEO.equals(that.GEO) : that.GEO != null) return false;

        return true;
    }

    @Override
    public int hashCode() {
        int result = id != null ? id.hashCode() : 0;
        result = 31 * result + (vcName != null ? vcName.hashCode() : 0);
        result = 31 * result + (x != null ? x.hashCode() : 0);
        result = 31 * result + (y != null ? y.hashCode() : 0);
        result = 31 * result + (vcAddress != null ? vcAddress.hashCode() : 0);
        result = 31 * result + (vcRemark != null ? vcRemark.hashCode() : 0);
        result = 31 * result + (GEO != null ? GEO.hashCode() : 0);
        return result;
    }
}

允许跨域访问配置

package com.solr.demo.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {

//    @Value("${wedOrigin.name}")
//     private  String wn;
//    @Autowired
//    SysConfigService sysConfigService;

    //允许跨域
    @Bean
    public CorsFilter corsFilter() {
        CorsConfiguration corsConfiguration = new CorsConfiguration();
//     String [] result = wn.split(",");
//        for (int i=0;i<result.length;i++){
//            corsConfiguration.addAllowedOrigin(result[i]);
//        }
        corsConfiguration.addAllowedOrigin("*");
        corsConfiguration.addAllowedHeader("*");
        corsConfiguration.addAllowedMethod("*");
        corsConfiguration.setAllowCredentials(true);
        UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
        urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration);
        return new CorsFilter(urlBasedCorsConfigurationSource);
    }
}

基于fastJson的返回值封装类

package com.solr.demo.base;

import com.alibaba.fastjson.JSONObject;
import com.solr.demo.utils.PageWrapper;
import org.springframework.data.domain.Page;

/**
 * @author xrw
 * @create 2019/11/01
 * @description 定义返回数据的标准格式
 */
public class ResponseJSON extends JSONObject {
    private static final long serialVersionUID = -4919743721058849685L;

    public ResponseJSON(){
        super.put("errorCode", 0);
        super.put("msg","success");
    }

    public ResponseJSON(String errorCode, String msg) {
        super.put("errorCode", errorCode);
        super.put("msg", msg);
    }

    public ResponseJSON(AppError appError){
        super.put("errorCode",appError.errorCode);
        super.put("msg",appError.msg);
    }

    public ResponseJSON(AppError appError, Object data) {
        super.put("errorCode", appError.errorCode);
        super.put("msg", appError.msg);
        super.put("data", data);
    }

    public ResponseJSON(Object data) {
        super.put("errorCode", 0);
        super.put("msg", "SUCCESS");
        if (data == null) {
            super.put("data", "");
            return;
        }
        super.put("data", data);
    }

    private Object preHandleData(Object data) {
        if (data instanceof Page) {
            return new PageWrapper<>((Page) data);
        } else {
            return data;
        }
    }
}

接口异常类

package com.solr.demo.base;

/**
 * @author xrw
 * @create 2019/11/01
 * @description 请求异常返回错误数据
 */
public enum AppError {
    //============错误返回数据=============
    /**
     * 接口异常
     */
    PARAM_NULL_ERROR("20001","参数为空");

    public String errorCode;
    public String msg;

     AppError(String errorCode, String msg) {
        this.errorCode = errorCode;
        this.msg = msg;
    }
}

配置类

application.properties:

sajRisk.solr_url = http://192.168.1.229:8983/solr
server.port=8023
wedOrigin.name=*

上一篇:solr8学习进阶(一)MySql整合
下一篇:solr8学习进阶(三)定时增量导入MySql数据

发布了96 篇原创文章 · 获赞 45 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_26356861/article/details/102971888
今日推荐