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://xxx.xxx.xxx.xxx:3306/test" 
        user="user" password="user"/>
    <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 
* Description: 空间搜索实现类
* Date: 2019年11月01日
*
* * @author xrw *

*/ @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 
* Description: solr查询方法封装类
* Date: 2019年11月1日
*
*/
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; } /** * 获取得到对象化的数据列表
* 使用场景:
* 1.当没有过滤显示字段数组,转换对象的Class注解有@Field关联字段值进行映射,返回对象数据列表。
* 2.当有过滤显示字段数组并且没有过滤显示字段别名数组,过滤字段后,转换对象的Class注解有@Field关联字段值进行映射,返回对象数据列表。 *
* 3.当有过滤显示字段数组并且有过滤显示字段别名数组,不需要转换对象的Class注解有@Field字段:
* 1)长度顺序一致,即都设置有别名情况下,设置对应的别名,并且能找到与别名名称相同的转换对象Class的属性名,通过反射直接赋值。
* 2)顺序一致,但长度不一致,即有部分设置别名,有部分没有设置别名,设置对应的别名,并且能找到与别名名称相同的转换对象Class的属性名, * 通过反射直接赋值,找不到相同的属性名则不做赋值处理。
* * @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(); } /** * 设置过滤查询参数
* 例如权重条件设置: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,通过反射设置得到对应的转换对象列表
* 1.主要用于当已经设置过滤返回对象和别名后得到的docs文档列表,根据转换对象的Class,设置与文档key一致的属性值
* 2.当转换对象的Class注解有@Field("otherFields"),把那些没有设置到属性里的值,全部加入到注解有@Field( * "otherFields")的Map对象中
* 3.如果没有找到注解有@Field("otherFields")的Map,那些没有设置到属性里的值全部丢弃掉
* * @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 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
//            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://xxx.xxx.xxx.xxx:8983/solr
server.port=8023
wedOrigin.name=*

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

你可能感兴趣的:(solr,solr,springboot,mysql)