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數據

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章