增加:综合浏览文件全文检索查询增加全字符匹配

master
guoxin 1 year ago
parent 660cf20896
commit 8475abc341
  1. 5
      shandan-browser/src/main/java/com/keyware/shandan/browser/controller/SearchController.java
  2. 31
      shandan-browser/src/main/java/com/keyware/shandan/browser/entity/FullSearchParam.java
  3. 2
      shandan-browser/src/main/java/com/keyware/shandan/browser/entity/SearchResultSort.java
  4. 57
      shandan-browser/src/main/java/com/keyware/shandan/browser/service/FileSearchService.java
  5. 99
      shandan-browser/src/main/resources/static/js/browser.js
  6. 3
      shandan-browser/src/main/resources/view/browser.html

@ -8,6 +8,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.keyware.shandan.bianmu.entity.DirectoryResource; import com.keyware.shandan.bianmu.entity.DirectoryResource;
import com.keyware.shandan.bianmu.entity.MetadataBasicVo; import com.keyware.shandan.bianmu.entity.MetadataBasicVo;
import com.keyware.shandan.bianmu.service.MetadataService; import com.keyware.shandan.bianmu.service.MetadataService;
import com.keyware.shandan.browser.entity.FullSearchParam;
import com.keyware.shandan.browser.entity.PageVo; import com.keyware.shandan.browser.entity.PageVo;
import com.keyware.shandan.browser.entity.SearchConditionVo; import com.keyware.shandan.browser.entity.SearchConditionVo;
import com.keyware.shandan.browser.entity.SearchResultSort; import com.keyware.shandan.browser.entity.SearchResultSort;
@ -139,10 +140,10 @@ public class SearchController {
*/ */
@AppLog(operate = "文件全文检索查询") @AppLog(operate = "文件全文检索查询")
@GetMapping("/full/file") @GetMapping("/full/file")
public Result<Object> searchFile(PageVo page, String search, String metaId, SearchResultSort sort) { public Result<Object> searchFile(FullSearchParam param) {
try { try {
return Result.of(fileSearchService.searchFile(page, search, metaId, sort)); return Result.of(fileSearchService.searchFile(param));
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
return Result.of(null, false, "Elasticsearch 请求异常"); return Result.of(null, false, "Elasticsearch 请求异常");

@ -0,0 +1,31 @@
package com.keyware.shandan.browser.entity;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 全文检索查询参数
*
* @author GuoXin
* @date 2023/9/9
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class FullSearchParam extends SearchResultSort {
/**
* 搜索的文本
*/
String search;
/**
* 搜索类型是否为匹配文本片段
*/
String searchType;
/**
* 资源ID
*/
String metaId;
/**
* 要查询的字段集合以英文逗号字符拼接
*/
String searchFields;
}

@ -15,7 +15,7 @@ import lombok.Setter;
*/ */
@Getter @Getter
@Setter @Setter
public class SearchResultSort { public class SearchResultSort extends PageVo<Object> {
//排序字段 //排序字段
private String field; private String field;
//排序方式 //排序方式

@ -4,8 +4,8 @@ import cn.hutool.core.util.ReflectUtil;
import com.keyware.shandan.bianmu.entity.DirectoryVo; import com.keyware.shandan.bianmu.entity.DirectoryVo;
import com.keyware.shandan.bianmu.enums.ReviewStatus; import com.keyware.shandan.bianmu.enums.ReviewStatus;
import com.keyware.shandan.bianmu.service.DirectoryService; import com.keyware.shandan.bianmu.service.DirectoryService;
import com.keyware.shandan.browser.entity.FullSearchParam;
import com.keyware.shandan.browser.entity.PageVo; import com.keyware.shandan.browser.entity.PageVo;
import com.keyware.shandan.browser.entity.SearchResultSort;
import com.keyware.shandan.common.constants.DirConstant; import com.keyware.shandan.common.constants.DirConstant;
import com.keyware.shandan.common.util.StreamUtil; import com.keyware.shandan.common.util.StreamUtil;
import com.keyware.shandan.common.util.StringUtils; import com.keyware.shandan.common.util.StringUtils;
@ -17,20 +17,18 @@ import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.Requests; import org.elasticsearch.client.Requests;
import org.elasticsearch.client.RestHighLevelClient; import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder; import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.builder.SearchSourceBuilder; import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder; import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortOrder; import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.annotations.FieldType; import org.springframework.data.elasticsearch.annotations.FieldType;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.io.IOException; import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
/** /**
@ -48,10 +46,15 @@ public class FileSearchService {
@Autowired @Autowired
private DirectoryService directoryService; private DirectoryService directoryService;
public PageVo searchFile(PageVo page, String text, String metaId, SearchResultSort sort) throws IOException { public PageVo searchFile(FullSearchParam param) throws IOException {
SearchRequest request = Requests.searchRequest("shandan"); SearchRequest request = Requests.searchRequest("shandan");
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.from(page.getPage() * page.getSize() - page.getSize()).size(page.getSize()); searchSourceBuilder.from(param.getPage() * param.getSize() - param.getSize()).size(param.getSize());
String metaId = param.getMetaId();
String text = param.getSearch();
String[] searchFields = param.getSearchFields().split(",");
BoolQueryBuilder builder = QueryBuilders.boolQuery(); BoolQueryBuilder builder = QueryBuilders.boolQuery();
if (!DirConstant.DIR_ROOT_ID.equals(metaId)) { if (!DirConstant.DIR_ROOT_ID.equals(metaId)) {
@ -61,19 +64,19 @@ public class FileSearchService {
builder.must(QueryBuilders.termsQuery("entityId", ids)); builder.must(QueryBuilders.termsQuery("entityId", ids));
} }
if (StringUtils.isNotBlank(text)) { if (StringUtils.isNotBlank(text)) {
BoolQueryBuilder should = QueryBuilders.boolQuery(); BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
should.should(QueryBuilders.matchQuery("fileName", text)); String[] includesCol = {"fileName", "entryStaff", "equipmentModel", "source", "taskCode", "taskNature", "troopCode", "targetNumber", "missileNumber", "themeTask", "remark", "text"};
should.should(QueryBuilders.matchQuery("entryStaff", text));
should.should(QueryBuilders.matchQuery("equipmentModel", text)); for (String colName : searchFields) {
should.should(QueryBuilders.matchQuery("source", text)); if (Arrays.asList(includesCol).contains(colName)) {
should.should(QueryBuilders.matchQuery("taskCode", text)); if ("phrase".equals(param.getSearchType())) {
should.should(QueryBuilders.matchQuery("taskNature", text)); boolQuery.should(QueryBuilders.matchPhraseQuery(colName, text));
should.should(QueryBuilders.matchQuery("troopCode", text)); } else {
should.should(QueryBuilders.matchQuery("targetNumber", text)); boolQuery.should(QueryBuilders.matchQuery(colName, text));
should.should(QueryBuilders.matchQuery("missileNumber", text)); }
should.should(QueryBuilders.matchQuery("remark", text)); }
should.should(QueryBuilders.matchQuery("text", text)); }
builder.must(should); builder.must(boolQuery);
} }
// 高亮显示设置 // 高亮显示设置
@ -87,23 +90,23 @@ public class FileSearchService {
searchSourceBuilder.query(builder).highlighter(highlightBuilder); searchSourceBuilder.query(builder).highlighter(highlightBuilder);
// 设置排序 // 设置排序
if (!sort.isEmpty()) { if (!param.isEmpty()) {
String sortField = sort.getField(); String sortField = param.getField();
// 使用反射获取文件实体类中字段上的注解设置的字段类型 // 使用反射获取文件实体类中字段上的注解设置的字段类型
Field field = ReflectUtil.getField(SysFile.class, sortField); Field field = ReflectUtil.getField(SysFile.class, sortField);
if (field != null) { if (field != null) {
org.springframework.data.elasticsearch.annotations.Field fieldAnno = field.getAnnotation(org.springframework.data.elasticsearch.annotations.Field.class); org.springframework.data.elasticsearch.annotations.Field fieldAnno = field.getAnnotation(org.springframework.data.elasticsearch.annotations.Field.class);
if(fieldAnno.type() != FieldType.Text if (fieldAnno.type() != FieldType.Text
&& fieldAnno.type() != FieldType.Object && fieldAnno.type() != FieldType.Object
&& fieldAnno.type() != FieldType.Auto && fieldAnno.type() != FieldType.Auto
&& fieldAnno.type() != FieldType.Nested && fieldAnno.type() != FieldType.Nested
&& fieldAnno.type() != FieldType.Ip && fieldAnno.type() != FieldType.Ip
&& fieldAnno.type() != FieldType.Attachment){ && fieldAnno.type() != FieldType.Attachment) {
if(fieldAnno.type() == FieldType.Keyword){ if (fieldAnno.type() == FieldType.Keyword) {
sortField = sortField.concat(".keyword"); sortField = sortField.concat(".keyword");
} }
searchSourceBuilder.sort(sortField, SortOrder.fromString(sort.getSort())); searchSourceBuilder.sort(sortField, SortOrder.fromString(param.getSort()));
}else { } else {
log.error("不支持的排序类型:{}:{}", sortField, fieldAnno.type()); log.error("不支持的排序类型:{}:{}", sortField, fieldAnno.type());
} }
} }
@ -113,7 +116,7 @@ public class FileSearchService {
// 设置排序 // 设置排序
SearchResponse response = esClient.search(request, RequestOptions.DEFAULT); SearchResponse response = esClient.search(request, RequestOptions.DEFAULT);
return PageVo.ofSearchHits(response.getHits(), page); return PageVo.ofSearchHits(response.getHits(), param);
} }
private List<String> getChildrenByDirId(String dirId) { private List<String> getChildrenByDirId(String dirId) {

@ -152,7 +152,7 @@ layui.use(['layer', 'listPage', 'globalTree', 'gtable', 'form', 'element', 'drop
if (_table.where.conditions && _table.where.conditions.length > 0) { if (_table.where.conditions && _table.where.conditions.length > 0) {
let formVal = form.val('search-form'); let formVal = form.val('search-form');
formVal = Object.assign(formVal, whereConditionToFormValue(_table.where)); formVal = Object.assign(formVal, whereConditionToFormValue(_table.where));
if(preciseQuery === 'eq'){ if (preciseQuery === 'eq') {
formVal['preciseQuery'] = 'eq'; formVal['preciseQuery'] = 'eq';
} }
form.val('search-form', formVal); form.val('search-form', formVal);
@ -211,45 +211,6 @@ layui.use(['layer', 'listPage', 'globalTree', 'gtable', 'form', 'element', 'drop
metaListTable.addTableRowEvent('theadSet1', () => tHeadSetLayer.show(resourceTableId)) metaListTable.addTableRowEvent('theadSet1', () => tHeadSetLayer.show(resourceTableId))
} }
function initFileSearchTable(id) {
const theadConfig = THeadSetLayer.filterConfigData(fileTableId)
dirFileTable = listPage.init({
table: {
id: 'dirFileTable',
toolbar: '#fileTableToolBar',
searchInput: 'fileSearchKeyInput',
searchFieldNames: 'search',
url: `${ctx}/search/full/file?metaId=${id}`,
height: 'full-110',
request: {pageName: 'page', limitName: 'size'},
defaultToolbar: [{title: '列表配置', layEvent: 'theadSet2', icon: 'layui-icon-cols'}],
autoSort: false,
limit: 30,
method: 'get',
minWidth: 80,
cols: THeadSetLayer.convertColumns(theadConfig, 'dirFileTable')
},
});
dirFileTable.gtable.on('sort', function (data) {
const {field, type} = data;
let sort = type;
if (!sort || sort === 'null') {
sort = undefined;
}
dirFileTable.table.where.field = field.trim();
dirFileTable.table.where.sort = sort;
dirFileTable.reloadTable({table: dirFileTable.table})
})
// 查看按钮监听
dirFileTable.addTableRowEvent('details-file', function (obj) {
openMaxLayerWithURL(`${ctx}/sys/file/view?fileId=${obj.id}`)
})
dirFileTable.addTableRowEvent('theadSet2', () => tHeadSetLayer.show(fileTableId))
}
/** /**
* 搜索条件模块初始化 * 搜索条件模块初始化
*/ */
@ -330,7 +291,7 @@ layui.use(['layer', 'listPage', 'globalTree', 'gtable', 'form', 'element', 'drop
setSearchKeyCondition(value, preciseQuery === 'eq' ? 'eq' : 'like'); setSearchKeyCondition(value, preciseQuery === 'eq' ? 'eq' : 'like');
}) })
function setSearchKeyCondition(value, logic){ function setSearchKeyCondition(value, logic) {
let where = metaListTable.where || {}; let where = metaListTable.where || {};
let conditions = where.conditions || []; let conditions = where.conditions || [];
conditions = conditions.filter(item => item.fieldName !== 'metadataName') conditions = conditions.filter(item => item.fieldName !== 'metadataName')
@ -480,6 +441,60 @@ layui.use(['layer', 'listPage', 'globalTree', 'gtable', 'form', 'element', 'drop
closeBtn: 1 closeBtn: 1
}); });
} }
/**
* 初始化文件全文检索查询列表
* @param id 目录ID
*/
function initFileSearchTable(id) {
const theadConfig = THeadSetLayer.filterConfigData(fileTableId);
let queryType = '';
dirFileTable = listPage.init({
table: {
id: 'dirFileTable',
toolbar: '#fileTableToolBar',
searchInput: 'fileSearchKeyInput',
searchFieldNames: 'search',
url: `${ctx}/search/full/file?metaId=${id}`,
height: 'full-110',
where: {searchFields: THeadSetLayer.filterConfigData(fileTableId).filter(conf=>conf.isShow).map(conf => conf.colName).join(',')},
request: {pageName: 'page', limitName: 'size'},
defaultToolbar: [{title: '列表配置', layEvent: 'theadSet2', icon: 'layui-icon-cols'}],
autoSort: false,
limit: 30,
method: 'get',
minWidth: 80,
cols: THeadSetLayer.convertColumns(theadConfig, 'dirFileTable'),
done: () => {
form.val('full-search-form', {queryType});
}
},
});
form.on('checkbox(query-type)', function ({value, othis}) {
queryType = othis.hasClass('layui-form-checked') ? value : '';
dirFileTable.table.where.searchType = queryType;
});
dirFileTable.gtable.on('sort', function (data) {
const {field, type} = data;
let sort = type;
if (!sort || sort === 'null') {
sort = undefined;
}
dirFileTable.table.where.field = field.trim();
dirFileTable.table.where.sort = sort;
dirFileTable.reloadTable({table: dirFileTable.table})
})
// 查看按钮监听
dirFileTable.addTableRowEvent('details-file', function (obj) {
openMaxLayerWithURL(`${ctx}/sys/file/view?fileId=${obj.id}`)
})
dirFileTable.addTableRowEvent('theadSet2', () => tHeadSetLayer.show(fileTableId))
}
}) })
/** /**
@ -521,7 +536,7 @@ class THeadSetLayer {
const {gtable, form, id} = this; const {gtable, form, id} = this;
gtable.init({ gtable.init({
id, id,
data: this.filterConfigData(tableId), data: THeadSetLayer.filterConfigData(tableId),
method: 'get', method: 'get',
toolbar: false, toolbar: false,
height: 'full', height: 'full',

@ -214,8 +214,9 @@
<div class="current-position">当前位置:<label></label></div> <div class="current-position">当前位置:<label></label></div>
<table class="layui-hide" id="dirFileTable" lay-filter="dirFileTable"></table> <table class="layui-hide" id="dirFileTable" lay-filter="dirFileTable"></table>
<script type="text/html" id="fileTableToolBar"> <script type="text/html" id="fileTableToolBar">
<div class="layui-btn-container"> <div class="layui-btn-container layui-form" lay-filter="full-search-form">
<div class="layui-layout-left" style="top:10px; left: 20px"> <div class="layui-layout-left" style="top:10px; left: 20px">
<input type="checkbox" name="queryType" title="全字符匹配" lay-skin="primary" value="phrase" lay-filter="query-type">
<input type="text" id="fileSearchKeyInput" name="searchKeyInput" <input type="text" id="fileSearchKeyInput" name="searchKeyInput"
autocomplete="off" class="layui-input layui-btn-sm" autocomplete="off" class="layui-input layui-btn-sm"
placeholder="请输入关键字查询" style="width: 330px"> placeholder="请输入关键字查询" style="width: 330px">