5 changed files with 588 additions and 6 deletions
-
55src/main/java/com/bw/search/controller/CharacterController.java
-
24src/main/java/com/bw/search/entity/TermEntity.java
-
28src/main/java/com/bw/search/service/CharacterService.java
-
322src/main/java/com/bw/search/service/impl/CharacterServiceImpl.java
-
165src/main/java/com/bw/search/service/impl/RagSearchServiceImpl.java
@ -0,0 +1,55 @@ |
|||||
|
package com.bw.search.controller; |
||||
|
|
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.web.bind.annotation.CrossOrigin; |
||||
|
import org.springframework.web.bind.annotation.GetMapping; |
||||
|
import org.springframework.web.bind.annotation.PostMapping; |
||||
|
import org.springframework.web.bind.annotation.RequestBody; |
||||
|
import org.springframework.web.bind.annotation.RequestMapping; |
||||
|
import org.springframework.web.bind.annotation.RequestParam; |
||||
|
import org.springframework.web.bind.annotation.RestController; |
||||
|
|
||||
|
import com.bw.search.common.Res; |
||||
|
import com.bw.search.entity.TermEntity; |
||||
|
import com.bw.search.service.CharacterService; |
||||
|
|
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
|
||||
|
/** |
||||
|
* 人物操作控制层 |
||||
|
* @author jian.mao |
||||
|
* @date 2026年1月6日 |
||||
|
* @description |
||||
|
*/ |
||||
|
@RestController |
||||
|
@CrossOrigin |
||||
|
@RequestMapping("/api") |
||||
|
@Slf4j |
||||
|
public class CharacterController { |
||||
|
|
||||
|
@Autowired |
||||
|
private CharacterService characterService; |
||||
|
/** |
||||
|
* 查询专家数据 |
||||
|
*/ |
||||
|
@GetMapping("/characters") |
||||
|
public Res<?> getCharacter( |
||||
|
@RequestParam(value = "page", defaultValue = "1", required = false) Integer page, |
||||
|
@RequestParam(value = "size", defaultValue = "10", required = false) Integer size) { |
||||
|
return characterService.getCharacter(page,size); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 根据条件查询人物数据 |
||||
|
* @param termEntity |
||||
|
* @return |
||||
|
*/ |
||||
|
@PostMapping("/charactersbyterm") |
||||
|
public Res<?> getCharacterByTerm(@RequestBody TermEntity termEntity){ |
||||
|
|
||||
|
|
||||
|
return characterService.getCharacterByTerm(termEntity); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
package com.bw.search.entity; |
||||
|
|
||||
|
import java.util.List; |
||||
|
|
||||
|
import lombok.Data; |
||||
|
|
||||
|
/** |
||||
|
* 查询条件 |
||||
|
* @author jian.mao |
||||
|
* @date 2026年1月14日 |
||||
|
* @description |
||||
|
*/ |
||||
|
@Data |
||||
|
public class TermEntity { |
||||
|
|
||||
|
private List<String> fullNames; |
||||
|
private List<String> affiliations; |
||||
|
private List<String> countrys; |
||||
|
private List<String> positions; |
||||
|
private List<String> researchFocus; |
||||
|
|
||||
|
private Integer size; |
||||
|
private Integer page; |
||||
|
} |
||||
@ -0,0 +1,28 @@ |
|||||
|
package com.bw.search.service; |
||||
|
|
||||
|
import com.bw.search.common.Res; |
||||
|
import com.bw.search.entity.TermEntity; |
||||
|
|
||||
|
/** |
||||
|
* 人物操作业务层 |
||||
|
* @author jian.mao |
||||
|
* @date 2026年1月6日 |
||||
|
* @description |
||||
|
*/ |
||||
|
public interface CharacterService { |
||||
|
|
||||
|
/** |
||||
|
* 获取人物业务层接口 |
||||
|
* @param page |
||||
|
* @param size |
||||
|
*/ |
||||
|
public Res<?> getCharacter(Integer page, Integer size); |
||||
|
|
||||
|
/** |
||||
|
* 获取人物数据 by 条件 |
||||
|
* @param termEntity |
||||
|
* @return |
||||
|
*/ |
||||
|
public Res<?> getCharacterByTerm(TermEntity termEntity); |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,322 @@ |
|||||
|
package com.bw.search.service.impl; |
||||
|
|
||||
|
import java.util.ArrayList; |
||||
|
import java.util.HashMap; |
||||
|
import java.util.List; |
||||
|
import java.util.Map; |
||||
|
|
||||
|
import javax.annotation.Resource; |
||||
|
|
||||
|
import org.apache.http.HttpResponse; |
||||
|
import org.apache.http.auth.AuthScope; |
||||
|
import org.apache.http.auth.UsernamePasswordCredentials; |
||||
|
import org.apache.http.client.CredentialsProvider; |
||||
|
import org.apache.http.client.methods.HttpPost; |
||||
|
import org.apache.http.entity.ContentType; |
||||
|
import org.apache.http.entity.StringEntity; |
||||
|
import org.apache.http.impl.client.BasicCredentialsProvider; |
||||
|
import org.apache.http.impl.client.CloseableHttpClient; |
||||
|
import org.apache.http.impl.client.HttpClients; |
||||
|
import org.apache.http.util.EntityUtils; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
|
||||
|
import com.alibaba.fastjson.JSONArray; |
||||
|
import com.alibaba.fastjson.JSONObject; |
||||
|
import com.bw.search.common.Res; |
||||
|
import com.bw.search.config.EsConfig; |
||||
|
import com.bw.search.entity.TermEntity; |
||||
|
import com.bw.search.service.CharacterService; |
||||
|
|
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
|
||||
|
/** |
||||
|
* 人物操作业务接口实现类 |
||||
|
* @author jian.mao |
||||
|
* @date 2026年1月6日 |
||||
|
* @description |
||||
|
*/ |
||||
|
@Service |
||||
|
@Slf4j |
||||
|
public class CharacterServiceImpl implements CharacterService { |
||||
|
|
||||
|
@Resource |
||||
|
private EsConfig esConfig; |
||||
|
|
||||
|
@Override |
||||
|
public Res<?> getCharacter(Integer page, Integer size) { |
||||
|
// TODO Auto-generated method stub |
||||
|
CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); |
||||
|
credentialsProvider.setCredentials( |
||||
|
AuthScope.ANY, |
||||
|
new UsernamePasswordCredentials(esConfig.getUsername(),esConfig.getPassword()) |
||||
|
); |
||||
|
|
||||
|
CloseableHttpClient httpClient = null; |
||||
|
try { |
||||
|
if (esConfig.getUsername() != null && !esConfig.getUsername().trim().equals("")) { |
||||
|
httpClient = HttpClients.custom() |
||||
|
.setDefaultCredentialsProvider(credentialsProvider) |
||||
|
.build(); |
||||
|
} else { |
||||
|
httpClient = HttpClients.custom().build(); |
||||
|
} |
||||
|
|
||||
|
// ================== 构建查询 DSL ================== |
||||
|
int from = (page - 1) * size; |
||||
|
|
||||
|
Map<String, Object> query = new HashMap<String, Object>(); |
||||
|
query.put("from", from); |
||||
|
query.put("size", size); |
||||
|
|
||||
|
// must 条件 |
||||
|
List<Map<String, Object>> mustList = new ArrayList<Map<String, Object>>(); |
||||
|
|
||||
|
Map<String, Object> bool = new HashMap<String, Object>(); |
||||
|
bool.put("must", mustList); |
||||
|
|
||||
|
Map<String, Object> queryBody = new HashMap<String, Object>(); |
||||
|
queryBody.put("bool", bool); |
||||
|
|
||||
|
query.put("query", queryBody); |
||||
|
|
||||
|
// sort |
||||
|
List<Map<String, Object>> sortList = new ArrayList<Map<String, Object>>(); |
||||
|
Map<String, Object> order = new HashMap<String, Object>(); |
||||
|
order.put("order", "desc"); |
||||
|
Map<String, Object> sortField = new HashMap<String, Object>(); |
||||
|
sortField.put("collectionTime", order); |
||||
|
sortList.add(sortField); |
||||
|
query.put("sort", sortList); |
||||
|
log.info("查询条件:{}",JSONObject.toJSONString(query)); |
||||
|
|
||||
|
// ================== 发起 HTTP 请求 ================== |
||||
|
StringBuffer host = new StringBuffer(); |
||||
|
host.append(esConfig.getHost()) |
||||
|
.append("/") |
||||
|
.append(esConfig.getIndex()) |
||||
|
.append("/_search"); |
||||
|
|
||||
|
HttpPost httpPost = new HttpPost(host.toString()); |
||||
|
httpPost.setHeader("Content-Type", "application/json"); |
||||
|
|
||||
|
StringEntity entity = new StringEntity( |
||||
|
JSONObject.toJSONString(query), |
||||
|
ContentType.APPLICATION_JSON |
||||
|
); |
||||
|
httpPost.setEntity(entity); |
||||
|
|
||||
|
HttpResponse response = httpClient.execute(httpPost); |
||||
|
int statusCode = response.getStatusLine().getStatusCode(); |
||||
|
String responseBody = EntityUtils.toString(response.getEntity(), "UTF-8"); |
||||
|
|
||||
|
if (statusCode != 200) { |
||||
|
log.error("ES 查询失败 status={}, body={}", statusCode, responseBody); |
||||
|
return Res.fail("ES 查询失败"); |
||||
|
} |
||||
|
|
||||
|
// ================== 解析返回 ================== |
||||
|
JSONObject json = JSONObject.parseObject(responseBody); |
||||
|
JSONObject hits = json.getJSONObject("hits"); |
||||
|
|
||||
|
Long total = hits.getJSONObject("total").getLong("value"); |
||||
|
|
||||
|
List<Map<String, Object>> list = new ArrayList<Map<String, Object>>(); |
||||
|
JSONArray hitList = hits.getJSONArray("hits"); |
||||
|
for (int i = 0; i < hitList.size(); i++) { |
||||
|
JSONObject source = hitList.getJSONObject(i).getJSONObject("_source"); |
||||
|
list.add(source); |
||||
|
} |
||||
|
|
||||
|
Map<String, Object> result = new HashMap<String, Object>(); |
||||
|
result.put("page", page); |
||||
|
result.put("size", size); |
||||
|
result.put("total", total); |
||||
|
result.put("list", list); |
||||
|
|
||||
|
return Res.ok(result); |
||||
|
|
||||
|
} catch (Exception e) { |
||||
|
return Res.fail("查询任务失败"); |
||||
|
} finally { |
||||
|
if (httpClient != null) { |
||||
|
try { |
||||
|
httpClient.close(); |
||||
|
} catch (Exception ignored) {} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public Res<?> getCharacterByTerm(TermEntity term) { |
||||
|
|
||||
|
CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); |
||||
|
credentialsProvider.setCredentials( |
||||
|
AuthScope.ANY, |
||||
|
new UsernamePasswordCredentials(esConfig.getUsername(), esConfig.getPassword()) |
||||
|
); |
||||
|
|
||||
|
CloseableHttpClient httpClient = null; |
||||
|
|
||||
|
try { |
||||
|
if (esConfig.getUsername() != null && !"".equals(esConfig.getUsername().trim())) { |
||||
|
httpClient = HttpClients.custom() |
||||
|
.setDefaultCredentialsProvider(credentialsProvider) |
||||
|
.build(); |
||||
|
} else { |
||||
|
httpClient = HttpClients.custom().build(); |
||||
|
} |
||||
|
|
||||
|
// ============ 分页 ============ |
||||
|
int page = term.getPage() == null || term.getPage() < 1 ? 1 : term.getPage(); |
||||
|
int size = term.getSize() == null || term.getSize() < 1 ? 10 : term.getSize(); |
||||
|
int from = (page - 1) * size; |
||||
|
|
||||
|
Map<String, Object> query = new HashMap<>(); |
||||
|
query.put("from", from); |
||||
|
query.put("size", size); |
||||
|
|
||||
|
// ============ bool ============ |
||||
|
List<Map<String, Object>> mustList = new ArrayList<>(); |
||||
|
List<Map<String, Object>> filterList = new ArrayList<>(); |
||||
|
|
||||
|
Map<String, Object> bool = new HashMap<>(); |
||||
|
bool.put("must", mustList); |
||||
|
bool.put("filter", filterList); |
||||
|
|
||||
|
// ================= fullName 模糊(包含) ================= |
||||
|
if (term.getFullNames() != null && !term.getFullNames().isEmpty()) { |
||||
|
List<Map<String, Object>> shouldList = new ArrayList<>(); |
||||
|
|
||||
|
for (String name : term.getFullNames()) { |
||||
|
if (name != null && !"".equals(name.trim())) { |
||||
|
Map<String, Object> match = new HashMap<>(); |
||||
|
Map<String, Object> field = new HashMap<>(); |
||||
|
field.put("fullName", name); |
||||
|
match.put("match", field); |
||||
|
shouldList.add(match); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (!shouldList.isEmpty()) { |
||||
|
Map<String, Object> shouldBool = new HashMap<>(); |
||||
|
shouldBool.put("should", shouldList); |
||||
|
shouldBool.put("minimum_should_match", 1); |
||||
|
|
||||
|
Map<String, Object> shouldQuery = new HashMap<>(); |
||||
|
shouldQuery.put("bool", shouldBool); |
||||
|
|
||||
|
mustList.add(shouldQuery); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// ================= affiliation 精确 ================= |
||||
|
if (needFilter(term.getAffiliations())) { |
||||
|
Map<String, Object> terms = new HashMap<>(); |
||||
|
terms.put("affiliation", term.getAffiliations()); |
||||
|
|
||||
|
Map<String, Object> termsQuery = new HashMap<>(); |
||||
|
termsQuery.put("terms", terms); |
||||
|
|
||||
|
filterList.add(termsQuery); |
||||
|
} |
||||
|
|
||||
|
// ================= country -> geographicInfo ================= |
||||
|
if (needFilter(term.getCountrys())) { |
||||
|
Map<String, Object> terms = new HashMap<>(); |
||||
|
terms.put("geographicInfo", term.getCountrys()); |
||||
|
|
||||
|
Map<String, Object> termsQuery = new HashMap<>(); |
||||
|
termsQuery.put("terms", terms); |
||||
|
|
||||
|
filterList.add(termsQuery); |
||||
|
} |
||||
|
|
||||
|
// ================= position ================= |
||||
|
if (needFilter(term.getPositions())) { |
||||
|
Map<String, Object> terms = new HashMap<>(); |
||||
|
terms.put("position", term.getPositions()); |
||||
|
|
||||
|
Map<String, Object> termsQuery = new HashMap<>(); |
||||
|
termsQuery.put("terms", terms); |
||||
|
|
||||
|
filterList.add(termsQuery); |
||||
|
} |
||||
|
// ================= researchFocus(数组精确匹配) ================= |
||||
|
if (needFilter(term.getResearchFocus())) { |
||||
|
Map<String, Object> terms = new HashMap<>(); |
||||
|
terms.put("researchFocus", term.getResearchFocus()); |
||||
|
Map<String, Object> termsQuery = new HashMap<>(); |
||||
|
termsQuery.put("terms", terms); |
||||
|
|
||||
|
filterList.add(termsQuery); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
Map<String, Object> queryBody = new HashMap<>(); |
||||
|
queryBody.put("bool", bool); |
||||
|
query.put("query", queryBody); |
||||
|
|
||||
|
// ================= 排序 ================= |
||||
|
List<Map<String, Object>> sortList = new ArrayList<>(); |
||||
|
Map<String, Object> order = new HashMap<>(); |
||||
|
order.put("order", "desc"); |
||||
|
Map<String, Object> sortField = new HashMap<>(); |
||||
|
sortField.put("collectionTime", order); |
||||
|
sortList.add(sortField); |
||||
|
query.put("sort", sortList); |
||||
|
|
||||
|
log.info("查询条件:{}",JSONObject.toJSONString(query)); |
||||
|
// ================= 请求 ES ================= |
||||
|
String url = esConfig.getHost() + "/" + esConfig.getIndex() + "/_search"; |
||||
|
HttpPost httpPost = new HttpPost(url); |
||||
|
httpPost.setHeader("Content-Type", "application/json"); |
||||
|
httpPost.setEntity(new StringEntity(JSONObject.toJSONString(query), ContentType.APPLICATION_JSON)); |
||||
|
|
||||
|
HttpResponse response = httpClient.execute(httpPost); |
||||
|
String responseBody = EntityUtils.toString(response.getEntity(), "UTF-8"); |
||||
|
|
||||
|
if (response.getStatusLine().getStatusCode() != 200) { |
||||
|
log.error("ES查询失败 body={}", responseBody); |
||||
|
return Res.fail("ES查询失败"); |
||||
|
} |
||||
|
|
||||
|
// ================= 解析返回 ================= |
||||
|
JSONObject json = JSONObject.parseObject(responseBody); |
||||
|
JSONObject hits = json.getJSONObject("hits"); |
||||
|
|
||||
|
Long total = hits.getJSONObject("total").getLong("value"); |
||||
|
|
||||
|
List<Map<String, Object>> list = new ArrayList<>(); |
||||
|
JSONArray hitList = hits.getJSONArray("hits"); |
||||
|
for (int i = 0; i < hitList.size(); i++) { |
||||
|
list.add(hitList.getJSONObject(i).getJSONObject("_source")); |
||||
|
} |
||||
|
|
||||
|
Map<String, Object> result = new HashMap<>(); |
||||
|
result.put("page", page); |
||||
|
result.put("size", size); |
||||
|
result.put("total", total); |
||||
|
result.put("list", list); |
||||
|
|
||||
|
return Res.ok(result); |
||||
|
|
||||
|
} catch (Exception e) { |
||||
|
log.error("ES查询异常", e); |
||||
|
return Res.fail("查询失败"); |
||||
|
} finally { |
||||
|
try { |
||||
|
if (httpClient != null) httpClient.close(); |
||||
|
} catch (Exception ignored) {} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
private boolean needFilter(List<String> list) { |
||||
|
if (list == null || list.isEmpty()) return false; |
||||
|
if (list.size() == 1 && "all".equalsIgnoreCase(list.get(0))) { |
||||
|
return false; |
||||
|
} |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
} |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue