增加:综合浏览文件全文检索页面增自定义排序功能

master
Guo XIn 1 year ago
parent fe4a19682e
commit 5f1e778fd6
  1. 5
      shandan-browser/src/main/java/com/keyware/shandan/browser/controller/SearchController.java
  2. 26
      shandan-browser/src/main/java/com/keyware/shandan/browser/entity/SearchConditionVo.java
  3. 34
      shandan-browser/src/main/java/com/keyware/shandan/browser/entity/SearchResultSort.java
  4. 41
      shandan-browser/src/main/java/com/keyware/shandan/browser/service/FileSearchService.java
  5. 3
      shandan-browser/src/main/java/com/keyware/shandan/browser/service/MetadataSearchService.java
  6. 59
      shandan-browser/src/main/resources/static/js/browser.js

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

@ -10,7 +10,6 @@ import org.elasticsearch.search.sort.SortOrder;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
@ -35,7 +34,7 @@ public class SearchConditionVo extends PageVo implements Serializable {
//搜索条件集合
private List<Item> conditions = new ArrayList<>();
// 排序
private Sort sort = new Sort();
private SearchResultSort sort = new SearchResultSort();
//排序字段
@Deprecated
private String sortFiled;
@ -113,27 +112,4 @@ public class SearchConditionVo extends PageVo implements Serializable {
return " " + logicJoin + " \"" + table + "\".\"" + fieldName + "\" " + logicJudgement + " " + fieldValue;
}
}
/**
* 排序
*/
@Getter
@Setter
public static class Sort {
//排序字段
private String field;
//排序方式
private String sort;
/**
* @return order by sql语句
*/
public String getOrderBy() {
return isEmpty() ? Strings.EMPTY : " order by " + getField() + " " + getSort();
}
public boolean isEmpty() {
return StringUtils.isBlankAny(this.field, this.sort);
}
}
}

@ -0,0 +1,34 @@
package com.keyware.shandan.browser.entity;
import com.keyware.shandan.common.util.StringUtils;
import joptsimple.internal.Strings;
import lombok.Getter;
import lombok.Setter;
/**
* 排序
*
* @author GuoXin
* @date 2023/8/29
*/
@Getter
@Setter
public class SearchResultSort {
//排序字段
private String field;
//排序方式
private String sort;
/**
* @return order by sql语句
*/
public String getOrderBy() {
return isEmpty() ? Strings.EMPTY : " order by " + getField() + " " + getSort();
}
public boolean isEmpty() {
return StringUtils.isBlankAny(this.field, this.sort);
}
}

@ -1,12 +1,16 @@
package com.keyware.shandan.browser.service;
import cn.hutool.core.util.ReflectUtil;
import com.keyware.shandan.bianmu.entity.DirectoryVo;
import com.keyware.shandan.bianmu.enums.ReviewStatus;
import com.keyware.shandan.bianmu.service.DirectoryService;
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.util.StreamUtil;
import com.keyware.shandan.common.util.StringUtils;
import com.keyware.shandan.system.entity.SysFile;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
@ -17,10 +21,15 @@ import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.annotations.FieldType;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
@ -30,6 +39,7 @@ import java.util.List;
* @author Administrator
* @since 2021/12/21
*/
@Slf4j
@Service
public class FileSearchService {
@ -38,13 +48,13 @@ public class FileSearchService {
@Autowired
private DirectoryService directoryService;
public PageVo searchFile(PageVo page, String text, String metaId) throws IOException {
public PageVo searchFile(PageVo page, String text, String metaId, SearchResultSort sort) throws IOException {
SearchRequest request = Requests.searchRequest("shandan");
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.from(page.getPage() * page.getSize() - page.getSize()).size(page.getSize());
BoolQueryBuilder builder = QueryBuilders.boolQuery();
if(!DirConstant.DIR_ROOT_ID.equals(metaId)){
if (!DirConstant.DIR_ROOT_ID.equals(metaId)) {
List<String> ids = new ArrayList<>();
ids.add(metaId);
ids.addAll(getChildrenByDirId(metaId));
@ -75,13 +85,38 @@ public class FileSearchService {
highlightBuilder.preTags("<label style=\"color:red\">").postTags("</label>").field("*");
highlightBuilder.forceSource(true);
searchSourceBuilder.query(builder).highlighter(highlightBuilder);
// 设置排序
if (!sort.isEmpty()) {
String sortField = sort.getField();
// 使用反射获取文件实体类中字段上的注解设置的字段类型
Field field = ReflectUtil.getField(SysFile.class, sortField);
if (field != null) {
org.springframework.data.elasticsearch.annotations.Field fieldAnno = field.getAnnotation(org.springframework.data.elasticsearch.annotations.Field.class);
if(fieldAnno.type() != FieldType.Text
&& fieldAnno.type() != FieldType.Object
&& fieldAnno.type() != FieldType.Auto
&& fieldAnno.type() != FieldType.Nested
&& fieldAnno.type() != FieldType.Ip
&& fieldAnno.type() != FieldType.Attachment){
if(fieldAnno.type() == FieldType.Keyword){
sortField = sortField.concat(".keyword");
}
searchSourceBuilder.sort(sortField, SortOrder.fromString(sort.getSort()));
}else {
log.error("不支持的排序类型:{}:{}", sortField, fieldAnno.type());
}
}
}
request.source(searchSourceBuilder);
// 设置排序
SearchResponse response = esClient.search(request, RequestOptions.DEFAULT);
return PageVo.ofSearchHits(response.getHits(), page);
}
private List<String> getChildrenByDirId(String dirId){
private List<String> getChildrenByDirId(String dirId) {
DirectoryVo dir = directoryService.getById(dirId);
return StreamUtil.as(directoryService.childrenLists(dir, ReviewStatus.SUBMITTED)).map(DirectoryVo::getId).toList();
}

@ -12,6 +12,7 @@ import com.keyware.shandan.bianmu.mapper.DirectoryResourceMapper;
import com.keyware.shandan.bianmu.service.DirectoryService;
import com.keyware.shandan.browser.entity.PageVo;
import com.keyware.shandan.browser.entity.SearchConditionVo;
import com.keyware.shandan.browser.entity.SearchResultSort;
import com.keyware.shandan.common.util.StreamUtil;
import com.keyware.shandan.common.util.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
@ -101,7 +102,7 @@ public class MetadataSearchService {
});
// 排序设置
SearchConditionVo.Sort sort = condition.getSort();
SearchResultSort sort = condition.getSort();
if(sort != null && sort.getSort() != null ){
// 通过反射获取资源类字段注解中的数据库表相关的列名
Field field = ReflectUtil.getField(DirectoryResource.class, sort.getField().trim());

@ -18,7 +18,7 @@ layui.use(['layer', 'listPage', 'globalTree', 'gtable', 'form', 'element', 'drop
let metaListTable, dirFileTable, tagSelector, tags = [], conditions = [];
const unchecked_tags = new Set();
// 初始化
// 初始化
initDirectoryTree();
@ -144,17 +144,18 @@ layui.use(['layer', 'listPage', 'globalTree', 'gtable', 'form', 'element', 'drop
limit: 30,
cols: [[
{field: 'id', title: 'ID', hide: true},
{field: 'resourceName', title: '数据名称', width: 300,sort: true},
{field: 'resourceComment', title: '中文注释/描述',sort: true},
{field: 'directoryPath', title: '资源路径',sort: true},
{field: 'themeTask', title: '主题任务', hide: true,sort: true},
{field: 'resourceName', title: '数据名称', width: 300, sort: true},
{field: 'resourceComment', title: '中文注释/描述', sort: true},
{field: 'directoryPath', title: '资源路径', sort: true},
{field: 'themeTask', title: '主题任务', hide: true, sort: true},
{
field: 'dataSource',
title: '数据来源',
width: 160,
templet: (data) => DICT.getText("data_source", data.dataSource) || data.dataSource || '',sort: true
templet: (data) => DICT.getText("data_source", data.dataSource) || data.dataSource || '',
sort: true
},
{field: 'taskTime', title: '任务时间', width: 180, align: 'center',sort: true},
{field: 'taskTime', title: '任务时间', width: 180, align: 'center', sort: true},
{field: 'modifyTime', title: '注册时间', width: 160, align: 'center', hide: true},
{fixed: 'right', title: '操作', toolbar: '#rowToolBar', width: 100, align: 'center'}
]],
@ -173,13 +174,13 @@ layui.use(['layer', 'listPage', 'globalTree', 'gtable', 'form', 'element', 'drop
}
},
});
metaListTable.gtable.on('sort', function(data){
metaListTable.gtable.on('sort', function (data) {
const {field, type} = data;
let sort = type;
if(!sort || sort === 'null'){
if (!sort || sort === 'null') {
sort = undefined;
}
metaListTable.where.sort={field: field.trim(), sort: sort}
metaListTable.where.sort = {field: field.trim(), sort: sort}
beginSearch();
})
metaListTable.addTableRowEvent('export', function (obj) {
@ -229,32 +230,39 @@ layui.use(['layer', 'listPage', 'globalTree', 'gtable', 'form', 'element', 'drop
url: `${ctx}/search/full/file?metaId=${id}`,
height: 'full-110',
request: {pageName: 'page', limitName: 'size'},
autoSort: false,
limit: 30,
method: 'get',
cols: [[
{field: 'id', title: 'ID', hide: true},
{field: 'fileName', title: '文件名称', minWidth: 300, fixed: 'left'},
{
field: 'source',
title: '文件来源',
minWidth: 160,
sort: true,
templet: data => DICT.getText('data_source', data.source) || data.source || ''
},
{field: 'taskCode', title: '任务代号', minWidth: 160},
{field: 'taskCode', title: '任务代号', sort: true, minWidth: 160},
{
field: 'taskNature',
title: '任务性质',
minWidth: 160,
sort: true,
templet: data => DICT.getText('task_nature', data.taskNature) || data.taskNature || ''
},
{field: 'troopCode', title: '部队代号', minWidth: 160},
{field: 'missileNumber', title: '导弹编号', minWidth: 160},
{field: 'equipmentModel', title: '装备型号', minWidth: 160},
{field: 'troopCode', title: '部队代号', sort: true, minWidth: 160},
{field: 'missileNumber', title: '导弹编号', sort: true, minWidth: 160},
{field: 'equipmentModel', title: '装备型号', sort: true, minWidth: 160},
{
field: 'targetNumber',
title: '目标/靶标类型',
minWidth: 160,
sort: true,
templet: data => DICT.getText('target_type', data.targetNumber) || data.targetNumber || ''
},
{field: 'entryStaff', title: '录入人员', minWidth: 160},
{field: 'inputDate', title: '收文时间', width: 160, align: 'center'},
{field: 'entryStaff', title: '录入人员', sort: true, minWidth: 160},
{field: 'inputDate', title: '收文时间', sort: true, width: 160, align: 'center'},
{field: 'remark', title: '文件描述', width: 300},
{field: 'text', title: '文件内容', width: 300},
{fixed: 'right', title: '操作', toolbar: '#fileRowToolBar', width: 100, align: 'center'}
@ -262,6 +270,17 @@ layui.use(['layer', 'listPage', 'globalTree', 'gtable', 'form', 'element', 'drop
},
});
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}`)
@ -305,7 +324,7 @@ layui.use(['layer', 'listPage', 'globalTree', 'gtable', 'form', 'element', 'drop
// 监听条件标签删除事件
element.on('tabDelete(condition-tab)', function (data) {
let key = $(this).parent().data('key');
if(key === 'markTag'){
if (key === 'markTag') {
tags = [];
}
let formVal = form.val('search-form');
@ -427,7 +446,7 @@ layui.use(['layer', 'listPage', 'globalTree', 'gtable', 'form', 'element', 'drop
}
tag_htm += `<input type="checkbox" title="${tag.title}" value="${tag.id}" ${checked} lay-skin="primary">`;
}
if (tag_htm){
if (tag_htm) {
htm += `<li data-key="markTag">置标标签:${tag_htm}</li>`;
}
@ -438,9 +457,9 @@ layui.use(['layer', 'listPage', 'globalTree', 'gtable', 'form', 'element', 'drop
$('li[data-key="markTag"] .layui-form-checkbox').on('click', function () {
let val = $(this).prev().val();
if(unchecked_tags.has(val)){
if (unchecked_tags.has(val)) {
unchecked_tags.delete(val);
}else{
} else {
unchecked_tags.add(val);
}
beginSearch();