需求背景:
最近在搞数据研发平台,有个搜索相关的需求:用户可以通过Job名做模糊搜索,返回相关结果集,并且要携带出完整的目录结构,研发平台目录结构分四种: 根节点是工作空间,子节点有多层嵌套文件夹,文件夹里面是DAG工作流,工作流里是Job任务;需求是要返回完整的目录结构给前端。
解决方案:
这个问题就很有意思了,首先第一点就是树形结构了,因为是模糊搜索,返回的Job可能会多个,如果遍历的话,可能会出现多个完整目录结构,这时会需要做路径合并,难度很大;我改造了树形结构,下面直接上代码:
SearchNode:
定义当前节点的Key,和父类节点的key,都要保证唯一性;
text字段表示当前Node节点的其他字段,统一放入到Map集合中
children表示当前Node节点的子节点,结构是SearchNode的数组集合;
这里在做toString时直接转成JSON格式,且把Map集合中的数据也解出来,这里有个问题:
因为Map的value是Object类型,我这里是直接用正则匹配出数字类型的,不用加双引号,且如果是Boolean类型则也不加双引号;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class SearchNode {
private String key;
private String parentKey;
private Map<String,Object> text;
private List<SearchNode> children;
private String regex = "([1-9]\\d*\\.?\\d*)|(0\\.\\d*[1-9])";
public SearchNode(String key, String parentKey, Map<String,Object> text) {
this.key = key;
this.parentKey = parentKey;
this.text = text;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("{");
sb.append("\"key\":\"")
.append(key).append('\"');
sb.append(",\"parentKey\":\"")
.append(parentKey).append('\"');
text.forEach((k,v) -> {
sb.append(",\"").append(k).append("\":");
Pattern compile = Pattern.compile(regex);
Matcher matcher = compile.matcher(v.toString());
if (matcher.matches()) {
sb.append(v);
} else {
if (v.equals(true) || v.equals(false)) {
sb.append(v);
} else {
sb.append("\"").append(v).append("\"");
}
}
});
sb.append(",\"children\":")
.append(children);
sb.append('}');
return sb.toString();
}
}
SearchTree:
这里是用递归思想获取树形结构,但是我们的需求背景是要拼接成一个完成的目录结构,而不是多个结构,所以选用Set集合去重思路,把重复的SearchNode干掉
public class SearchTree {
private HashSet<SearchNode> searchNodeList = new HashSet<>();
public SearchTree(HashSet<SearchNode> searchNodeList) {
this.searchNodeList = searchNodeList;
}
//建立树形结构
public HashSet<SearchNode> builTree(){
HashSet<SearchNode> treeSearchNodes =new HashSet<SearchNode>();
for(SearchNode searchNodeNode : getRootNode()) {
searchNodeNode =buildChilTree(searchNodeNode);
treeSearchNodes.add(searchNodeNode);
}
return treeSearchNodes;
}
//递归,建立子树形结构
private SearchNode buildChilTree(SearchNode pNode){
List<SearchNode> chilSearchNodes =new ArrayList<SearchNode>();
for(SearchNode searchNodeNode : searchNodeList) {
if(searchNodeNode.getParentKey().equals(pNode.getKey())) {
chilSearchNodes.add(buildChilTree(searchNodeNode));
}
}
pNode.setChildren(chilSearchNodes);
return pNode;
}
//获取根节点
private List<SearchNode> getRootNode() {
List<SearchNode> rootSearchNodeLists =new ArrayList<SearchNode>();
for(SearchNode searchNodeNode : searchNodeList) {
if(searchNodeNode.getParentKey().equals("head")) {
rootSearchNodeLists.add(searchNodeNode);
}
}
return rootSearchNodeLists;
}
}
测试结果:
@SpringBootTest
public class SearchNodeTests {
@Test
public void contextLoads() {
HashSet<SearchNode> menuList= new HashSet<>();
HashMap<String, Object> map1 = new HashMap<>();
map1.put("local","湖北省");
HashMap<String, Object> map2 = new HashMap<>();
map2.put("local","山东省");
HashMap<String, Object> map3 = new HashMap<>();
map3.put("local","武汉市");
HashMap<String, Object> map4 = new HashMap<>();
map4.put("local","仙桃市");
HashMap<String, Object> map5 = new HashMap<>();
map5.put("local","青岛市");
HashMap<String, Object> map6 = new HashMap<>();
map6.put("local","武昌区");
HashMap<String, Object> map7 = new HashMap<>();
map7.put("local","洪山区");
HashMap<String, Object> map8 = new HashMap<>();
map8.put("local","洪山街道");
/*插入一些数据*/
menuList.add(new SearchNode("1","head",map1));
menuList.add(new SearchNode("7","head",map2));
menuList.add(new SearchNode("2","1",map3));
menuList.add(new SearchNode("3","1",map4));
menuList.add(new SearchNode("8","7",map5));
menuList.add(new SearchNode("4","2",map6));
menuList.add(new SearchNode("5","2",map7));
menuList.add(new SearchNode("6","4",map8));
/*让我们创建树*/
SearchTree menuTree =new SearchTree(menuList);
menuList=menuTree.builTree();
/*转为json看看效果*/
String jsonOutput= JSON.toJSONString(menuList);
System.out.println("树状数据:"+menuList.toString());
System.out.println("树里面所有的id"+ this.list);//获取树里面的所有节点
}
List<Menu> children=null;
//参数1:树结构 参数二:节点id
public List<Menu> recursion (Menu tree, int node){
if(tree.getId()==node){ //节点id=需要查询的id
children = tree.getChildren(); //获取children里面的数据返回
}else {
for(Menu bean:tree.getChildren()){ //反之递归children里面的数据
recursion(bean,node);
}
}
return children;
}
List<Long> list = new ArrayList<Long>();//初始化id集合
//获取树里面的所有节点,调用方法前记得清空list
public void getAllNode(Menu tree) {
list.add((long) tree.getId());//添加节点
if (tree.getChildren() != null) {//判断是否有子节点
for (Menu bean : tree.getChildren()) {//循环递归子节点
getAllNode(bean);
}
}
}
}