本文基於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=*