【Neo4j系列】简化Neo4j数据库操作:一个基础工具类的开发之旅

作者:后端小肥肠

在Neo4j系列我打算写一个Neo4j同步关系数据库、增删改查及展示的基础小系统,这篇文件先分享系统里面的基础工具类,感兴趣的可以点个关注,看了文章的jym有更好的方法可以分享在评论区。

创作不易,未经允许严谨转载。

基础篇:

【Neo4j系列】Neo4j概念简介及整合SpringBoot_neo4j springboot-CSDN博客

目录

1. 前言

2. 与工具类适配的前端展示工具

2.1. neo4jd3.js 介绍

2.2. neo4jd3.js使用效果展示

3. Neo4j工具类包含功能简介

3.1. 编写 GraphDTO

3.2. 编写GraphNodeDTO

3.3. 编写GraphLinkDTO

3.4. 节点

3.4.1. 获取节点集合(无关系)

3.4.2. 获取单个节点

3.4.3. 获取所有节点(包含关系)

3.5. 获取所有标签

3.6. 数据库索引

3.6.1. 获取数据库索引

3.6.2. 创建 | 删除数据库索引

3.7. 获取所有关系

4. 工具类使用实战

4.1. 新增节点

4.2. 新增关系

4.3. 展示所有节点(包含关系)

5. 结语

6. 参考链接


1. 前言

Neo4j作为领先的图数据库,在处理复杂关系数据时展现出了卓越的性能和灵活性。然而,在实际开发中,我们常常会遇到一些重复性的任务和常见的挑战。为了提高开发效率,减少不必要的麻烦,一个强大而实用的工具类往往能够事半功倍。

在接下来的内容中,我们将详细介绍这个工具类的具体代码及核心功能。让我们一起探索如何让Neo4j开发变得更加高效和轻松吧!

如果这篇文章对你有帮助,别忘记动动小手点点关注哦~

2. 与工具类适配的前端展示工具

2.1. neo4jd3.js 介绍

neo4jd3.js 是一个基于 JavaScript 的库(源码地址:https://github.com/eisman/neo4jd3),它使用 D3.js 技术来可视化 Neo4j 图数据库中的数据。这种工具特别适合需要在 Web 环境中展示图形数据的应用场景。其核心优势包括:

  • 动态图形显示:用户可以看到图中的所有节点和边,并能通过拖拽和缩放来探索图中的元素。
  • 高度自定义:支持自定义节点和边的颜色、大小、形状和标签,使得每个项目都能够根据其具体需求来调整视图。
  • 交互性:提供点击节点查看详细信息,或是通过界面上的操作来进行图数据的动态查询和更新。

这些功能使得neo4jd3.js成为展示复杂关系和数据分析时的有力工具,帮助开发者和数据分析师更直观地理解和操作图数据库。

2.2. neo4jd3.js使用效果展示

下面是neo4jd3.js基础效果展示(无后端),先贴出html代码:

<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><title>Force</title><style>.nodetext {font-size: 12px;font-family: SimSun;fill: #000000;}.linetext {fill: #1f77b4;fill-opacity: 0.0;}.circleImg {stroke: #ff7f0e;stroke-width: 1.5px;}</style>
</head>
<body>
<h1>小肥肠知识图谱示例</h1>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script>var width = 900;var height = 800;var img_w = 77;var img_h = 80;var radius = 30; //圆形半径var svg = d3.select("body").append("svg").attr("width", width).attr("height", height);var edges = [];d3.json("xfc.json", function(error, root) {if (error) {return console.log(error);}console.log(root);root.edges.forEach(function(e) {var sourceNode = root.nodes.filter(function(n) {return n.id === e.source;})[0],targetNode = root.nodes.filter(function(n) {return n.id === e.target;})[0];edges.push({source: sourceNode,target: targetNode,relation: e.type});});console.log(edges);var force = d3.layout.force().nodes(root.nodes).links(edges).size([width, height]).linkDistance(200).charge(-1500).start();var defs = svg.append("defs");var arrowMarker = defs.append("marker").attr("id", "arrow").attr("markerUnits", "strokeWidth").attr("markerWidth", "15").attr("markerHeight", "15").attr("viewBox", "0 0 12 12").attr("refX", "17").attr("refY", "6").attr("orient", "auto");var arrow_path = "M2,2 L10,6 L2,10 L6,6 L2,2";arrowMarker.append("path").attr("d", arrow_path).attr("fill", "#ccc");var edges_line = svg.selectAll("line").data(edges).enter().append("line").attr("class", "line").style("stroke", "#ddd").style("stroke-width", 3).attr("marker-end", "url(#arrow)");var edges_text = svg.selectAll(".linetext").data(edges).enter().append("text").attr("class", "linetext").text(function(d) {return d.relation;}).style("fill-opacity", 1.0);var nodes_img = svg.selectAll("image").data(root.nodes).enter().append("circle").attr("class", "circleImg").attr("r", radius).attr("fill", function(d, i) {var defs = svg.append("defs").attr("id", "imgdefs");var catpattern = defs.append("pattern").attr("id", "catpattern" + i).attr("height", 1).attr("width", 1);catpattern.append("image").attr("x", -(img_w / 2 - radius)).attr("y", -(img_h / 2 - radius)).attr("width", img_w).attr("height", img_h).attr("xlink:href", d.labels);return "url(#catpattern" + i + ")";}).call(force.drag);var text_dx = -20;var text_dy = 20;var nodes_text = svg.selectAll(".nodetext").data(root.nodes).enter().append("text").attr("class", "nodetext").attr("dx", text_dx).attr("dy", text_dy).text(function(d) {var uservalue = d.properties.username || "";var personvalue = d.properties.person || "";var phonevalue = d.properties.phone || "";return uservalue + phonevalue + personvalue;});force.on("tick", function() {root.nodes.forEach(function(d) {d.x = d.x - img_w / 2 < 0 ? img_w / 2 : d.x;d.x = d.x + img_w / 2 > width ? width - img_w / 2 : d.x;d.y = d.y - img_h / 2 < 0 ? img_h / 2 : d.y;d.y = d.y + img_h / 2 + text_dy > height ? height - img_h / 2 - text_dy : d.y;});edges_line.attr("x1", function(d) { return d.source.x; });edges_line.attr("y1", function(d) { return d.source.y; });edges_line.attr("x2", function(d) { return d.target.x; });edges_line.attr("y2", function(d) { return d.target.y; });edges_text.attr("x", function(d) { return (d.source.x + d.target.x) / 2; });edges_text.attr("y", function(d) { return (d.source.y + d.target.y) / 2; });nodes_img.attr("cx", function(d) { return d.x; });nodes_img.attr("cy", function(d) { return d.y; });nodes_text.attr("x", function(d) { return d.x; });nodes_text.attr("y", function(d) { return d.y + img_w / 2; });});});
</script>
</body>
</html>

通过上述代码,用户可以在网页上看到一个动态的知识图谱,节点以图像的形式展示,节点之间的关系通过带箭头的线条表示,并且每条线上显示关系类型。用户可以通过拖动节点来重新排列图谱,从而更直观地理解数据之间的关联性。

xfc.json文件:

{"nodes": [{"id": "2","labels": "./image/kn.png","properties": {"person": "康娜1号"}},{"id": "58688","labels": "./image/kn2.png","properties": {"person": "康娜2号"}},{"id": "128386","labels": "./image/kn3.png","properties": {"person": "小肥肠"}},{"id": "200000","labels": "./image/kn4.png","properties": {"person": "人物4号"}},{"id": "300000","labels": "./image/kn5.png","properties": {"person": "人物5号"}},{"id": "400000","labels": "./image/kn6.png","properties": {"person": "人物6号"}},{"id": "500000","labels": "./image/kn7.png","properties": {"person": "人物7号"}},{"id": "600000","labels": "./image/kn8.png","properties": {"person": "人物8号"}}],"edges": [{"id": "23943","type": "know","source": "2","target": "58688","properties": {}},{"id": "94198","type": "know","source": "58688","target": "128386","properties": {}},{"id": "1000000","type": "know","source": "128386","target": "200000","properties": {}},{"id": "1100000","type": "know","source": "200000","target": "300000","properties": {}},{"id": "1200000","type": "know","source": "300000","target": "400000","properties": {}},{"id": "1300000","type": "know","source": "400000","target": "500000","properties": {}},{"id": "1400000","type": "know","source": "500000","target": "600000","properties": {}}]
}

效果展示:

效果还是不错的吧,请忽略我这个略丑的demo界面,之后的系统肯定会呈现一个完美的功能界面的,现在只是给大家打个样。 

3. Neo4j工具类包含功能简介

工具类的构建参考了github上的一个项目(https://github.com/MiracleTanC/Neo4j-KGBuilder),我在他的基础上做了稍微的修改,工具类包含返回节点集合、获取所有标签、获取数据库索引、删除索引、创建索引、返回关系、返回节点和关系、返回单个节点的信息等(后续文章会放出全部代码)。

ps:我的工具类是要适配前端的neo4jd3.js,所以工具类里面有数据格式的部分都是对应neo4jd3.js所要求的格式。

3.1. 编写 GraphDTO

@Data
@AllArgsConstructor
@NoArgsConstructor
public class GraphDTO {private List<GraphNodeDTO>nodes;private List<GraphLinkDTO>edges;
}

3.2. 编写GraphNodeDTO

@Data
@AllArgsConstructor
@NoArgsConstructor
public class GraphNodeDTO {private String id;private String labels;private HashMap<String,Object> properties;
}

3.3. 编写GraphLinkDTO

@Data
@AllArgsConstructor
@NoArgsConstructor
public class GraphLinkDTO {private String id;private String type;private String source;private String target;private HashMap<String,Object> properties;
}

3.4. 节点

3.4.1. 获取节点集合(无关系)
    public static List<GraphNodeDTO> getGraphNode(String cypherSql) {log.debug("Executing Cypher query: {}", cypherSql);try (Session session = neo4jDriver.session(SessionConfig.builder().withDefaultAccessMode(AccessMode.WRITE).build())) {return session.writeTransaction(tx -> {Result result = tx.run(cypherSql);return result.list().stream().flatMap(record -> record.fields().stream()).filter(pair -> pair.value().type().name().equals("NODE")).map(pair -> {Node node = pair.value().asNode();// label为节点对应图片String label = "";HashMap<String, Object> properties = new HashMap<>(node.asMap());return new GraphNodeDTO(String.valueOf(node.id()), label, properties);}).collect(Collectors.toList());});} catch (Exception e) {log.error("Error executing Cypher query: {}", cypherSql, e);return new ArrayList<>();}}

这段代码定义了一个名为 getGraphNode 的Java方法,它用于执行一个Neo4j Cypher查询来获取节点,并将结果封装为 GraphNodeDTO 对象的列表。该方法首先设置会话为写入模式,确保能执行可能涉及写入的查询。使用 session.writeTransaction 确保查询在事务中执行,这有助于处理可能的并发修改和保证数据一致性。

3.4.2. 获取单个节点
    public static GraphNodeDTO getSingleGraphNode(String cypherSql) {List<GraphNodeDTO> nodes = getGraphNode(cypherSql);if(CollectionUtils.isNotEmpty(nodes)){return nodes.get(0);}return null;}

 getSingleGraphNode 方法从数据库中执行指定的 Cypher 查询,获取查询结果中的第一个图谱节点。如果结果列表不为空,它会返回第一个节点;如果列表为空,则返回 null。这个方法简单明了,旨在快速获取一个节点或确认没有结果。

3.4.3. 获取所有节点(包含关系)
    public static GraphDTO getGraphNodeAndShip(String cypherSql) {log.debug("Executing Cypher query: {}", cypherSql);GraphDTO graphDTO = new GraphDTO();try (Session session = neo4jDriver.session(SessionConfig.builder().withDefaultAccessMode(AccessMode.WRITE).build())) {return session.writeTransaction(tx -> {Result result = tx.run(cypherSql);List<GraphNodeDTO> nodes = new ArrayList<>();List<GraphLinkDTO> relationships = new ArrayList<>();HashSet<String> uuids = new HashSet<>();result.list().stream().flatMap(record -> record.fields().stream()).forEach(pair -> {String type = pair.value().type().name();if ("NODE".equals(type)) {Node node = pair.value().asNode();String uuid = String.valueOf(node.id());if (uuids.add(uuid)) {HashMap<String, Object> properties = new HashMap<>(node.asMap());nodes.add(new GraphNodeDTO(uuid, node.labels().iterator().next(), properties));}} else if ("RELATIONSHIP".equals(type)) {Relationship relationship = pair.value().asRelationship();HashMap<String, Object> properties = new HashMap<>(relationship.asMap());relationships.add(new GraphLinkDTO(String.valueOf(relationship.id()), relationship.type(),String.valueOf(relationship.startNodeId()),String.valueOf(relationship.endNodeId()),properties));}});graphDTO.setNodes(nodes);graphDTO.setEdges(toDistinctList(relationships));return graphDTO;});} catch (Exception e) {log.error("Error executing Cypher query: {}", cypherSql, e);return new GraphDTO();}}

这段代码定义了一个名为 getGraphNodeAndShip 的Java方法,用于在Neo4j数据库中执行Cypher查询,从而获取并处理节点和关系数据,将结果封装成 GraphDTO 对象。它使用了写入事务来确保操作的稳定性,并在出现异常时进行了错误记录,最终返回一个包含节点和关系的复合数据对象。

3.5. 获取所有标签

public static List<HashMap<String, Object>> getGraphLabels() {String cypherSql = "CALL db.labels()";log.debug("Executing Cypher query: {}", cypherSql);try (Session session = neo4jDriver.session()) {return session.readTransaction(tx -> {Result result = tx.run(cypherSql);return result.list().stream().map(record -> {HashMap<String, Object> labelInfo = new HashMap<>();record.fields().forEach(pair -> {String key = pair.key();Value value = pair.value();if (key.equalsIgnoreCase("label")) {String label = value.asString().replace("\"", "");labelInfo.put(key, label);} else {labelInfo.put(key, value.asObject());}});return labelInfo;}).collect(Collectors.toList());});} catch (Exception e) {log.error("Error executing Cypher query for graph labels", e);return new ArrayList<>();}
}

这段代码用于从 Neo4j 数据库中检索所有图谱标签。它执行 CALL db.labels() 查询,并将结果转换为 List<HashMap<String, Object>> 格式。每个标签信息以 HashMap 的形式存储,其中包含标签的键和值。如果查询过程中发生异常,代码会记录错误并返回一个空列表。 

3.6. 数据库索引

3.6.1. 获取数据库索引
    public static List<HashMap<String, Object>> getGraphIndex() {String cypherSql = "CALL db.indexes()";log.debug("Executing Cypher query: {}", cypherSql);try (Session session = neo4jDriver.session()) {return session.readTransaction(tx -> {Result result = tx.run(cypherSql);return result.list().stream().map(record -> {HashMap<String, Object> indexInfo = new HashMap<>();record.fields().forEach(pair -> {String key = pair.key();Value value = pair.value();if (key.equalsIgnoreCase("labelsOrTypes")) {String objects = value.asList().stream().map(Object::toString).collect(Collectors.joining(","));indexInfo.put(key, objects);} else {indexInfo.put(key, value.asObject());}});return indexInfo;}).collect(Collectors.toList());});} catch (Exception e) {log.error("Error executing Cypher query for index information", e);return new ArrayList<>();}}

这个方法执行 Cypher 查询 CALL db.indexes() 来获取数据库索引信息。它记录查询语句,然后在 Neo4j 会话中执行查询并处理结果。对于每个索引信息记录,方法将其字段转换为 HashMap,特别处理 labelsOrTypes 字段,将其值转换为逗号分隔的字符串。最终,它将所有索引的 HashMap 收集到一个列表中并返回。如果出现异常,则记录错误并返回一个空列表。

3.6.2. 创建 | 删除数据库索引
    /*** 删除索引* @param label*/public static void deleteIndex(String label) {try (Session session = neo4jDriver.session()) {String cypherSql=String.format("DROP INDEX ON :`%s`(name)",label);session.run(cypherSql);} catch (Exception e) {log.error(e.getMessage());}}/*** 创建索引* @param label* @param prop*/public static void createIndex(String label,String prop) {try (Session session = neo4jDriver.session()) {String cypherSql=String.format("CREATE INDEX ON :`%s`(%s)",label,prop);session.run(cypherSql);} catch (Exception e) {log.error(e.getMessage());}}

这段代码包含两个方法,deleteIndexcreateIndex,分别用于在 Neo4j 数据库中删除和创建索引。deleteIndex 方法根据提供的标签删除该标签上的 name 索引,而 createIndex 方法在指定标签的指定属性上创建一个新索引。两者都使用 Neo4j 的 Cypher 查询语句执行操作,并在发生异常时记录错误信息。 

3.7. 获取所有关系

    public static List<GraphLinkDTO> getGraphRelationship(String cypherSql) {log.debug("Executing Cypher query: {}", cypherSql);try (Session session = neo4jDriver.session(SessionConfig.builder().withDefaultAccessMode(AccessMode.WRITE).build())) {return session.writeTransaction(tx -> {Result result = tx.run(cypherSql);return result.list().stream().flatMap(record -> record.fields().stream()).filter(pair -> pair.value().type().name().equals("RELATIONSHIP")).map(pair -> {Relationship relationship = pair.value().asRelationship();String id = String.valueOf(relationship.id());String source = String.valueOf(relationship.startNodeId());String target = String.valueOf(relationship.endNodeId());HashMap<String, Object> properties = new HashMap<>(relationship.asMap());return new GraphLinkDTO(id, relationship.type(), source, target, properties);}).collect(Collectors.toList());});} catch (Exception e) {log.error("Error executing Cypher query: {}", cypherSql, e);return new ArrayList<>();}}

这段代码定义了一个名为 getGraphRelationship 的方法,它执行一个Neo4j Cypher查询以获取关系数据,并将每个关系转换为 GraphLinkDTO 对象。方法在写入事务中执行查询,确保稳定性,并使用流处理来提取和封装关系的详细信息,包括ID、类型、起始节点、目标节点和属性。如果查询执行过程中发生异常,会记录错误并返回一个空列表

4. 工具类使用实战

4.1. 新增节点

参数:

代码:

    public GraphDTO createNode(AddNodeDTO addNodeDTO) {GraphDTO dataGraphDTO = new GraphDTO();String cypherSql = String.format("n.name='%s'", addNodeDTO.getProperties().get("name"));String buildNodeql = "";buildNodeql = String.format("create (n:`%s`) set %s return n", addNodeDTO.getDomain(), cypherSql);List<GraphNodeDTO> nodes = Neo4jUtil.getGraphNode(buildNodeql);dataGraphDTO.setNodes(nodes);return dataGraphDTO;}

4.2. 新增关系

参数:

代码:

    public GraphDTO createLink(AddLinkDTO addLinkDTO) {String cypherSql = String.format("MATCH (n:`%s`),(m:`%s`) WHERE id(n)=%s AND id(m) = %s "+ "CREATE (n)-[r:%s]->(m)" + "RETURN n,m,r", addLinkDTO.getDomain(), addLinkDTO.getDomain(), addLinkDTO.getSource(), addLinkDTO.getTarget(), addLinkDTO.getType());return   Neo4jUtil.getGraphNodeAndShip(cypherSql);}

4.3. 展示所有节点(包含关系)

    public GraphDTO getGraph(String domain) {GraphDTO graphDTO = new GraphDTO();if (!StringUtil.isBlank(domain)) {String nodeSql = String.format("MATCH (n:`%s`)  RETURN distinct(n) ", domain);List<GraphNodeDTO> graphNode = Neo4jUtil.getGraphNode(nodeSql);graphDTO.setNodes(graphNode);String relationShipSql = String.format("MATCH (n:`%s`)<-[r]-> (m)  RETURN distinct(r) ", domain);// m是否加领域List<GraphLinkDTO> graphRelation = Neo4jUtil.getGraphRelationship(relationShipSql);graphDTO.setEdges(graphRelation);}

效果图: 

5. 结语

在本文中介绍了面向neo4jd3.js的后端neo4j工具类。本工具不仅简化了图数据库的操作,还通过GraphDTO, GraphNodeDTO, 和 GraphLinkDTO等类,有效地封装和传输图数据。同时演示了如何使用这些工具执行常见的图操作,包括新增节点和关系,以及展示关系数据,下一章节将介绍如何在实际场景中实现neo4j与关系数据库实现数据同步,感兴趣的话可动动小手点点关注~

6. 参考链接

基于d3.js/neovis.js/neod3.js实现链接neo4j图形数据库的图像化显示功能_javascript技巧_脚本之家

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/403674.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

快讯 | OpenAI 找回场子:chatgpt-4o-latest 刷新多项AI跑分纪录

在数字化浪潮的推动下&#xff0c;人工智能&#xff08;AI&#xff09;正成为塑造未来的关键力量。硅纪元视角栏目紧跟AI科技的最新发展&#xff0c;捕捉行业动态&#xff1b;提供深入的新闻解读&#xff0c;助您洞悉技术背后的逻辑&#xff1b;汇聚行业专家的见解&#xff0c;…

微信支付代理商-自助提交资料源码之结算信息页面—微信支付商机版

一、支付代理上自助提交资料 一般在都在小程序完成提交 在网页中异常提示alert 但是很多小程序禁用了这个函数 并且不好看 那么久自定义一个组件每次直接调用 二、提示技术代码 function 未来之窗_VOS_通用提醒(msg){var 未来之窗内容message<cyberdiv style"font…

C++| QT图片调整透明度叠加

QT图片调整透明度叠加 实际效果界面UI放置控件设置布局界面自适应 代码项目工程的文件初始化按钮功能滑动条功能图片调整透明度叠加 实际效果 三个图片&#xff08;QLabel&#xff09;显示&#xff0c;两个按钮&#xff08;QPushButton&#xff09;加载图片&#xff0c;一个&a…

Spring 声明式事务 @Transactional

目录 一、添加依赖 二、Transactional 作用 三、Transactional详解 3.1 rollbackFor 3.2 事务隔离级别 3.3 Spring 事务传播机制 Spring 声明式事务 Transactional的使用很简单&#xff0c;只需要添加依赖&#xff0c;在需要的方法或者类上添加 Transactional注解即可。 …

AMBA-CHI协议详解(六)

AMBA-CHI协议详解&#xff08;一&#xff09; AMBA-CHI协议详解&#xff08;二&#xff09; AMBA-CHI协议详解&#xff08;三&#xff09; AMBA-CHI协议详解&#xff08;四&#xff09; AMBA-CHI协议详解&#xff08;五&#xff09; AMBA-CHI协议详解&#xff08;六&#xff09…

硬件面试经典 100 题(71~90 题)

71、请问下图电路的作用是什么&#xff1f; 该电路实现 IIC 信号的电平转换&#xff08;3.3V 和 5V 电平转换&#xff09;&#xff0c;并且是双向通信的。 上下两路是一样的&#xff0c;只分析 SDA 一路&#xff1a; 1&#xff09; 从左到右通信&#xff08;SDA2 为输入状态&…

Golang | Leetcode Golang题解之第349题两个数组的交集

题目&#xff1a; 题解&#xff1a; func intersection(nums1 []int, nums2 []int) (res []int) {sort.Ints(nums1)sort.Ints(nums2)for i, j : 0, 0; i < len(nums1) && j < len(nums2); {x, y : nums1[i], nums2[j]if x y {if res nil || x > res[len(re…

Python基础和变量使用

1. 基础了解 1.1 运行方式 Python有多种运行方式&#xff0c;以下是几种常见的执行Python代码的方法&#xff1a; 交互式解释器&#xff1a; 打开终端或命令提示符&#xff0c;输入python或python3&#xff08;取决于你的系统配置&#xff09;&#xff0c;即可进入Python交互…

设计模式---构建者模式(Builder Pattern)

构建者模式&#xff08;Builder Pattern&#xff09; 是一种创建型设计模式&#xff0c;旨在将复杂对象的构建过程与其表示分离。它允许使用相同的构建过程创建不同的表示。该模式通常用于构建复杂对象&#xff0c;这些对象由多个部分组成或具有多个可选属性。 构建者模式的核…

Python做统计图之美

Python数据分析可视化 案例效果图 import pandas as pd import matplotlib.pyplot as plt import matplotlib# 数据 data {"房型": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],"住宅类型": ["普通宅", "普通宅", "普通宅", &q…

ffmpeg开发者视频剪辑器

5G 时代的来临&#xff0c;加速了视频类作品的创作&#xff0c;由于现在的流量越来越便宜&#xff0c;网速越来越快&#xff0c;特别是流量无限用套餐&#xff0c;大家更愿意去看视频作品&#xff0c;特别是抖音的兴起&#xff0c;更是加速了小视频的流量。不会剪辑的我们该如何…

内网安全:跨域攻击

目录 获取域信息 利用域信任密钥获取目标域 利用krbtgt哈希值获取目标域 内网中的域林&#xff1a; 很多大型企业都拥有自己的内网&#xff0c;一般通过域林进行共享资源。根据不同职能区分的部门&#xff0c;从逻辑上以 主域和子域进行区分&#xff0c;以方便统一管理。在…

选择排序(直接选择排序与堆排序的比较)

选择排序 选择排序时间复杂度 1. 直接选择排序思考⾮常好理解&#xff0c;但是效率不是很好。实际中很少使用&#xff0c;思路是先进行遍历找到元最小的元素&#xff0c;然后与第一个进行交换 2. 时间复杂度&#xff1a;O&#xff08;&#xff09; 3. 空间复杂度&#…

openharmony 南向开发基础:ohos自定义子系统,自定义部件,调用hilog部件,hilog日志封装傻瓜式教程

openharmony 南向开发基础:ohos自定义子系统,自定义部件,调用hilog部件,hilog日志封装 自定义单部件 关于开源鸿蒙的南向教程不多,很多都是从官方文档上抄的的例子,官网的例子不是很适合入门,写的很粗糙,不适合傻瓜阅读,毕竟对于刚入行鸿蒙的新手而言,gn语法就是第一劝退魔咒…

vue 路由用法 router-view

通过router-view 点击子路由显示子路由关于我们的内容&#xff0c;点击关于信息显示关于信息内容。

map/set和unordered_map/unordered_set的区别及使用情况

map/set和unordered_map/unordered_set的区别 容器底层数据结构是否有序实现版本复杂度迭代器map/set红黑树有序C98O(logN&#xff09;双向迭代器unordered_map/unordered_set哈希表/散列表无序C11O(1)单向迭代器 unordered_set无序的&#xff08;VS下&#xff09; void uno…

【机器学习】探索数据矿藏:Python中的AI大模型与数据挖掘创新实践

&#x1f496; 前言&#xff1a;探索数据矿藏1. &#x1f4ca;数据获取与预处理&#xff1a;AI大模型的燃料1.1 &#x1f310;数据获取&#xff1a;多样性与规模并重1.2 &#x1f9f9;数据清洗与处理&#xff1a;提升数据质量1.3 &#x1f50d;特征工程&#xff1a;挖掘数据的深…

蓝牙音视频远程控制协议(AVRCP) command跟response介绍

零.声明 本专栏文章我们会以连载的方式持续更新&#xff0c;本专栏计划更新内容如下&#xff1a; 第一篇:蓝牙综合介绍 &#xff0c;主要介绍蓝牙的一些概念&#xff0c;产生背景&#xff0c;发展轨迹&#xff0c;市面蓝牙介绍&#xff0c;以及蓝牙开发板介绍。 第二篇:Trans…

[Qt][QSS][下]详细讲解

目录 1.样式属性0.前言1.盒模型(Box Model) 2.常用控件样式属性1.按钮2.复选框3.单选框4.输入框5.列表6.菜单栏7.注意 1.样式属性 0.前言 QSS中的样式属性⾮常多&#xff0c;不需要都记住&#xff0c;核⼼原则是⽤到了就去查 ⼤部分的属性和CSS是⾮常相似的 QSS中有些属性&am…

稚晖君发布5款全能人形机器人,开源创新,全能应用

8月18日&#xff0c;智元机器人举行“智元远征 商用启航” 2024年度新品发布会&#xff0c;智元联合创始人彭志辉主持并发布了“远征”与“灵犀”两大系列共五款商用人形机器人新品——远征A2、远征A2-W、远征A2-Max、灵犀X1及灵犀X1-W&#xff0c;并展示了在机器人动力、感知、…