• neo4j学习记录


    neo4j - note

    一、概述

    1.链接

    官网: https://neo4j.com/

    官网document: https://neo4j.com/docs/getting-started/current/

    官网下载: https://neo4j.com/download-center/

    官网指令操作指南: https://neo4j.com/docs/cypher-manual/current/clauses/

    文字教程: http://www.vue5.com/neo4j/neo4j_exe_environment_setup.html

    w3school: https://www.w3cschool.cn/neo4j/ 推荐这个文字教程

    B站教程: https://www.bilibili.com/video/BV1HQ4y1h78j/?spm_id_from=333.337.search-card.all.click 讲得不太好,可以看文字教程即可

    spring-data neo4j: https://spring.io/projects/spring-data-neo4j#learn

    spring-data neo4j: https://neo4j.com/developer/spring-data-neo4j/

    2.介绍

    百度百科的明星关系

    银行欺诈检测

    反洗钱

    供应链管理

    与MYSQL对比

    MYSQLNEO4J
    节点
    列和数据属性和数据
    约束关系
    数据模型

    属性图模型规则

    • 代表节点,关系和属性中的数据
    • 节点和关系都包含属性
    • 关系连接节点
    • 属性是键值对
    • 节点使用圆圈表示,关系用箭头键表示。
    • 关系有方向:单向和双向。
    • 每个关系包含“起始节点”或“从节点”和“到节点”或“结束节点”

    构件:

    • 节点
    • 属性
    • 关系
    • 标签
    • 数据浏览器,浏览器打开数据库

    二、使用

    1.环境搭建

    如果neo4j.4.X + 的版本要当前jvm环境是 jdk 11

    下载3.x的最高版本

    设置NEO4J_HOME = C: neo4j-community-2.1.3

    设置PATH = C: neo4j-community-2.1.3 bin;

    启动控制台

    neo4j console  
    
    • 1

    desktop客户端也可以操作,推荐,客户端可以安装不同版本的 server

    http://localhost:7474/

    初始用户名和密码 neo4j

    登录后会要求改密码 123456

    2.CQL

    CQL命令/条用法
    CREATE 创建创建节点,关系和属性
    MATCH 匹配检索有关节点,关系和属性数据
    RETURN 返回返回查询结果
    WHERE 哪里提供条件过滤检索数据
    DELETE 删除删除节点和关系
    REMOVE 移除删除节点和关系的属性
    ORDER BY以…排序排序检索数据
    SET 组添加或更新标签
    1. 创建 Create
    语法元素描述
    CREATE它是一个Neo4j CQL命令。
    它是我们要创建的节点名称。
    它是一个节点标签名称
    CREATE (dept:Dept)   无属性
    
    • 1

    语法说明:

    语法元素描述
    它是我们将要创建的节点名称。
    它是一个节点标签名称
    属性是键值对。 定义将分配给创建节点的属性的名称
    属性是键值对。 定义将分配给创建节点的属性的值
    带属性节点
    CREATE (
       :
       { 	
          :
          ........
          :
       }
    )
    
    CREATE (dept:Dept {deptno:10,dname:"Accounting",location:"Hyderabad" })
    CREATE (emp:Employee{id:123,name:"Lokesh",sal:35000,deptno:10})
    CREATE (m:Dept:Employee{id:138})  #创建m节点,对应多个标签
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    2. 查询Match
    • 从数据库获取有关节点和属性的数据
    • 从数据库获取有关节点,关系和属性的数据
    MATCH 
    (
       :
    )
    
    • 1
    • 2
    • 3
    • 4
    #获取25个节点
    MATCH (n) RETURN n LIMIT 25
    
    # 查询Dept下的内容
    MATCH (dept:Dept) return dept
    
    # 查询Employee标签下 id=123,name="Lokesh"的节点
    MATCH (p:Employee {id:123,name:"Lokesh"}) RETURN p
    
    ## 查询Employee标签下name="Lokesh"的节点,使用(where命令)
    MATCH (p:Employee)
    WHERE p.name = "Lokesh"
    RETURN p
    
    ## 返回lin的关联关系及节点
    match p=(:Employee{name:"lin"}) -[r:HAVE]->() return p 
    
    ## 查询neo4j帮我们自动生成的
    MATCH (n:Dept) where id(n)=42 RETURN n LIMIT 25
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    3. Return
    需要搭配查询语句match,不可单独用
    RETURN 
       .,
       ........
       .
       
    return n 返回整个节点
    return n.deptno 就返回n的属性  
    
    MATCH (dept: Dept) RETURN dept.deptno,dept.dname,dept.location
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    4. 关系基础-创建

    基于方向性,Neo4j关系被分为两种主要类型。

    • 单向关系
    • 双向关系

    在以下场景中,我们可以使用Neo4j CQL CREATE命令来创建两个节点之间的关系。 这些情况适用于Uni和双向关系。

    • 在两个现有节点之间创建无属性的关系
    • 在两个现有节点之间创建有属性的关系
    • 在两个新节点之间创建无属性的关系
    • 在两个新节点之间创建有属性的关系
    • 在具有WHERE子句的两个退出节点之间创建/不使用属性的关系
    CREATE (cc:CreditCard{id:"5001",number:"1234567890",cvv:"888",expiredate:"20/17"})
    CREATE (e:Customer{id:"1001",name:"Abc",dob:"01/10/1982"})
    
    • 1
    • 2
    MATCH (:),(:)
    CREATE  
    	()-[:{}]->()
    RETURN 
    
    • 1
    • 2
    • 3
    • 4
    (1) 现有节点之间创建无属性的关系

    客户指向信用卡

    MATCH (e:Customer),(cc:CreditCard) 
    CREATE (e)-[r:DO_SHOPPING_WITH ]->(cc) 
    
    • 1
    • 2

    查询

    MATCH (e)-[r:DO_SHOPPING_WITH ]->(cc)  RETURN r   #返回单纯的关系
    
    MATCH p=()-[r:DO_SHOPPING_WITH]->() RETURN p  #返回这个关系所连接的所有节点
    
    • 1
    • 2
    • 3
    (2) 现有节点之间创建有属性的关系
    MATCH (cust:Customer{name:"lin"}),(cc:CreditCard) 
    CREATE (cust)-[r:DO_SHOPPING_WITH_PROP{shopdate:"12/12/2014",price:55000}]->(cc) 
    RETURN r
    
    • 1
    • 2
    • 3
    MATCH (cust:Customer{name:"lin"}),(cc:CreditCard),(r:Relation) 
    where r.form=cust.name
    CREATE (cust)-[r:DO_SHOPPING_WITH_PROP{shopdate:"12/12/2014",price:55000}]->(cc) 
    RETURN r
    
    • 1
    • 2
    • 3
    • 4
    (3) 新节点之间创建无属性的关系
    # LIKES关系标签  like关系名称,一般写r即可
    CREATE (fb1:FaceBookProfile1)-[like:LIKES]->(fb2:FaceBookProfile2) 
    
    • 1
    • 2

    查询关系

    MATCH (fb1:FaceBookProfile1)-[like:LIKES]->(fb2:FaceBookProfile2) 
    RETURN like
    
    MATCH (e)-[r:LIKES ]->(cc)  RETURN r
    
    • 1
    • 2
    • 3
    • 4
    (4) 新节点之间创建有属性的关系
    CREATE (video1:YoutubeVideo1{title:"Action Movie1",updated_by:"Abc",uploaded_date:"10/10/2010"})
    -[movie:ACTION_MOVIES{rating:1}]->
    (video2:YoutubeVideo2{title:"Action Movie2",updated_by:"Xyz",uploaded_date:"12/12/2012"}) 
    
    • 1
    • 2
    • 3
    MATCH p=()-[r:ACTION_MOVIES]->() RETURN p LIMIT 25
    
    MATCH (video1)-[r:ACTION_MOVIES]->(video2) 
    RETURN video1,video2
    
    • 1
    • 2
    • 3
    • 4
    (5) 检索关系节点的详细信息 用这个
    MATCH (cust)-[r:DO_SHOPPING_WITH]->(cc) 
    RETURN cust,cc
    
    • 1
    • 2
    5. where子句

    AND RO NOT

    MATCH (emp:Employee) 
    WHERE emp.name = 'Abc' OR emp.name = 'Xyz'
    RETURN emp
    
    • 1
    • 2
    • 3
    # WHERE子句在两个现有节点之间创建了一个NEW关系
    MATCH (cust:Customer),(cc:CreditCard) 
    WHERE cust.id = "1001" AND cc.id= "5001" 
    CREATE (cust)-[r:DO_SHOPPING_WITH{shopdate:"12/12/2014",price:55000}]->(cc) 
    RETURN r
    
    • 1
    • 2
    • 3
    • 4
    • 5
    6. delete删除(节点和关系)
    • 删除节点。
    • 删除节点及相关节点和关系。

    删除节点

    MATCH (e: Employee) DELETE e
    
    • 1

    删除节点和关系

    MATCH (cc: FaceBookProfile1)-[rel]-(c:FaceBookProfile2) 
    DELETE cc,c,rel
    
    • 1
    • 2

    删除关系

    MATCH (cc: CreditCard)-[rel]-(c:Customer) 
    DELETE rel
    
    • 1
    • 2
    7. remove删除(标签和属性)
    • 删除节点或关系的标签
    • 删除节点或关系的属性
    CREATE (book:Book {id:122,title:"Neo4j Tutorial",pages:340,price:250}) 
    
    MATCH (book : Book)
    RETURN book
    
    • 1
    • 2
    • 3
    • 4
    删除属性
    MATCH (book { id:122 })
    REMOVE book.price
    RETURN book
    
    类似于SQL 
    ALTER TABLE BOOK REMOVE COLUMN PRICE;
    SELECT * FROM BOOK WHERE ID = 122;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    删除多标签节点 中的一个标签
    MATCH (m:Employee) where m.id=138
    REMOVE m:Dept
    
    • 1
    • 2
    • 3
    8. set子句更新

    向现有节点或关系添加新属性

    MATCH (n:YoutubeVideo1)
    set n.updated_by='lin'
    return n
    
    • 1
    • 2
    • 3
    9. roder/limit/union/skip

    order by

    MATCH (emp:Employee)
    RETURN emp.empid,emp.name,emp.salary,emp.deptno
    ORDER BY emp.name DESC
    
    • 1
    • 2
    • 3

    union 它将两组结果中的公共行组合并返回到一组结果中。 它不从两个节点返回重复的行。

    MATCH (cc:CreditCard)
    RETURN cc.id as id,cc.number as number,cc.name as name,
       cc.valid_from as valid_from,cc.valid_to as valid_to
    UNION
    MATCH (dc:DebitCard)
    RETURN dc.id as id,dc.number as number,dc.name as name,
       dc.valid_from as valid_from,dc.valid_to as valid_to
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    union all 它结合并返回两个结果集的所有行成一个单一的结果集。它还返回由两个节点重复行。

    MATCH (cc:CreditCard)
    RETURN cc.id as id,cc.number as number,cc.name as name,
       cc.valid_from as valid_from,cc.valid_to as valid_to
    UNION ALL
    MATCH (dc:DebitCard)
    RETURN dc.id as id,dc.number as number,dc.name as name,
       dc.valid_from as valid_from,dc.valid_to as valid_to
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    limit skip

    MATCH (emp:Employee) 
    RETURN emp
    LIMIT/SKIP 2
    
    • 1
    • 2
    • 3
    10.合并
    MERGE = CREATE + MATCH
    
    • 1

    MERGE命令检查该节点在数据库中是否可用。 如果它不存在,它创建新节点。 否则,它不创建新的

    MATCH  (gp2:GoogleProfile2) 
    RETURN gp2.Id,gp2.Name
    
    • 1
    • 2
    11.NULL值 / IN
    MATCH (e:Employee) 
    WHERE e.id IS NULL
    RETURN e.id,e.name,e.sal,e.deptno
    
    • 1
    • 2
    • 3
    MATCH (e:Employee) 
    WHERE e.id IN [123,124]
    RETURN e.id,e.name,e.sal,e.deptno
    
    • 1
    • 2
    • 3

    3.函数

    字符串函数
    描述
    UPPER它用于将所有字母更改为大写字母。
    LOWER它用于将所有字母改为小写字母。
    SUBSTRING它用于获取给定String的子字符串。
    REPLACE它用于替换一个字符串的子字符串。
    MATCH (e:Employee) 
    RETURN e.id,UPPER(e.name),e.sal,e.deptno
    
    MATCH (e:Employee) 
    RETURN e.id,SUBSTRING(e.name,0,2),e.sal,e.deptno
    
    • 1
    • 2
    • 3
    • 4
    • 5
    聚合函数
    聚集功能描述
    COUNT它返回由MATCH命令返回的行数。
    MAX它从MATCH命令返回的一组行返回最大值。
    MIN它返回由MATCH命令返回的一组行的最小值。
    SUM它返回由MATCH命令返回的所有行的求和值。
    AVG它返回由MATCH命令返回的所有行的平均值。
    MATCH (e:Employee) RETURN COUNT(*)
    
    MATCH (e:Employee) 
    RETURN MAX(e.sal),MIN(e.sal)
    
    MATCH (e:Employee) 
    RETURN SUM(e.sal),AVG(e.sal)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    关系函数
    描述
    STARTNODE它用于知道关系的开始节点。
    ENDNODE它用于知道关系的结束节点。
    ID它用于知道关系的ID。
    TYPE它用于知道字符串表示中的一个关系的TYPE。
    MATCH (a)-[movie:ACTION_MOVIES]->(b) 
    RETURN STARTNODE(movie)
    
    MATCH (a)-[movie:ACTION_MOVIES]->(b) 
    RETURN ENDNODE(movie)
    
    MATCH (a)-[movie:ACTION_MOVIES]->(b) 
    RETURN ID(movie),TYPE(movie)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    4.索引

    CREATE INDEX ON : ()
    
    CREATE INDEX ON :Customer (name)
    
    • 1
    • 2
    • 3
    DROP INDEX ON : ()
    
    DROP INDEX ON :Customer (name)
    
    • 1
    • 2
    • 3

    5.UNIQUE约束

    CREATE CONSTRAINT ON (cc:CreditCard)
    ASSERT cc.number IS UNIQUE
    
    • 1
    • 2
    DROP CONSTRAINT ON (cc:CreditCard)
    ASSERT cc.number IS UNIQUE
    
    • 1
    • 2

    三、代码运用

    1. spring data neo4j 5.x

    参考spring官网

    
                org.springframework.boot
                spring-boot-starter-data-neo4j
                 2.3.6.RELEASE
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    新版本替换了下面的注解

        
          org.springframework.data
          spring-data-neo4j
          5.3.5.RELEASE
          compile
        
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    使用5.x版本 是下面的Old 如果是6.x版本关系不好理解,且官网文档写不清楚关系如何连接

    OldNew
    org.neo4j.ogm.annotation.NodeEntityorg.springframework.data.neo4j.core.schema.Node
    org.neo4j.ogm.annotation.GeneratedValueorg.springframework.data.neo4j.core.schema.GeneratedValue
    org.neo4j.ogm.annotation.Idorg.springframework.data.neo4j.core.schema.Id
    org.neo4j.ogm.annotation.Propertyorg.springframework.data.neo4j.core.schema.Property
    org.neo4j.ogm.annotation.Relationshiporg.springframework.data.neo4j.core.schema.Relationship
    (1) node
    package com.lin.neo4j.domain;
    
    import lombok.Builder;
    import lombok.Data;
    import org.neo4j.ogm.annotation.GeneratedValue;
    import org.neo4j.ogm.annotation.Id;
    import org.neo4j.ogm.annotation.NodeEntity;
    import org.neo4j.ogm.annotation.Property;
    
    @Data
    @Builder
    @NodeEntity("Dept") // 标签:默认就是类名
    public class Dept {
        @Id // 声明主键
        @GeneratedValue  // 声明该字段为neo4j自动生成的映射的字段
        private Long id;
    
        @Property("deptno") // 如果名称不一样可用
        private Integer deptno;
    
        private String dname;
    
        private String location;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    @Data
    @NodeEntity
    public class Person {
        @Id
        @GeneratedValue
        private Long id;
    
        private String name;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    import lombok.Data;
    import org.neo4j.ogm.annotation.EndNode;
    import org.neo4j.ogm.annotation.GeneratedValue;
    import org.neo4j.ogm.annotation.Id;
    import org.neo4j.ogm.annotation.RelationshipEntity;
    import org.neo4j.ogm.annotation.StartNode;
    
    /**
     * 部门关系
     */
    @Data
    @RelationshipEntity(type = "DeptRelationShip")
    public class DeptRelationShip {
        @Id
        @GeneratedValue
        private Long id;
    
        private String name;
    
        @StartNode
        private Dept startNode;
    
        @EndNode
        private Person endNode;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    (2) repository
    import com.lin.neo4j.domain.Dept;
    
    import org.springframework.data.neo4j.annotation.Query;
    import org.springframework.data.neo4j.repository.Neo4jRepository;
    import org.springframework.stereotype.Repository;
    
    import java.util.List;
    
    @Repository
    public interface DeptRepository extends Neo4jRepository {
    
        @Query(value = "create (d:Dept {deptno:{0},dname:{1},location:{2}})")
        void create(Integer deptno, String dname, String location);
    
        Dept findByDeptno(Integer deptno);
    
        List findByDeptnoOrderByDeptnoAsc(Integer deptno);
    
    }
    
    @Repository
    public interface DeptRelationRepository extends Neo4jRepository {
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
      List findByEmailAddressAndLastname(EmailAddress emailAddress, String lastname);
    
      // Enables the distinct flag for the query
      List findDistinctPeopleByLastnameOrFirstname(String lastname, String firstname);
      List findPeopleDistinctByLastnameOrFirstname(String lastname, String firstname);
    
      // Enabling ignoring case for an individual property
      List findByLastnameIgnoreCase(String lastname);
      // Enabling ignoring case for all suitable properties
      List findByLastnameAndFirstnameAllIgnoreCase(String lastname, String firstname);
    
      // Enabling static ORDER BY for a query
      List findByLastnameOrderByFirstnameAsc(String lastname);
      List findByLastnameOrderByFirstnameDesc(String lastname);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    Page findByLastname(String lastname, Pageable pageable);
    
    Slice findByLastname(String lastname, Pageable pageable);
    
    List findByLastname(String lastname, Sort sort);
    
    List findByLastname(String lastname, Pageable pageable);
    
    
    Sort sort = Sort.by("firstname").ascending()
      .and(Sort.by("lastname").descending());
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    limit

    User findFirstByOrderByLastnameAsc();
    
    User findTopByOrderByAgeDesc();
    
    Page queryFirst10ByLastname(String lastname, Pageable pageable);
    
    Slice findTop3ByLastname(String lastname, Pageable pageable);
    
    List findFirst10ByLastname(String lastname, Sort sort);
    
    List findTop10ByLastname(String lastname, Pageable pageable);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    组合结果

    interface PersonRepository extends Repository {
      Streamable findByFirstnameContaining(String firstname);
      Streamable findByLastnameContaining(String lastname);
    }
    
    Streamable result = repository.findByFirstnameContaining("av")
      .and(repository.findByLastnameContaining("ea"));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    (3) operate实践
    @SpringBootTest(classes = Application.class)
    @RunWith(SpringRunner.class)
    public class NodeTest {
        @Autowired
        private DeptRepository deptRepository;
    
        @Test
        public void testCreate() {
           deptRepository.create(6,"技术部","深圳");
           deptRepository.save(Dept.builder().deptno(3).dname("人力资源").location("广州").build());
        }
    
        @Test
        public void testDelete() {
            // 这里的删除有关系也能删除掉
            deptRepository.deleteById(40L);
        }
    
        @Test
        public void testDeptQuery() {
            final Iterable all = deptRepository.findAll();
            all.forEach(e-> System.out.println(e));
            System.out.println(deptRepository.findById(40L));
        }
    
        @Test
        public void testDeptNativeQuery() {
            Dept deptn = deptRepository.findByDeptno(4);
            System.out.println(deptn);
            System.out.println(deptRepository.findByDeptno(4));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    @SpringBootTest(classes = Application.class)
    @RunWith(SpringRunner.class)
    public class RelationTest {
        @Autowired
        private DeptRepository deptRepository;
    
        @Autowired
        private DeptRelationRepository deptRelationRepository;
    
        @Test
        public void testRelation() {
            DeptRelationShip deptRelationShip = new DeptRelationShip();
            deptRelationShip.setName("归属");
    
            Dept dept = deptRepository.findById(86L).orElse(null);
            deptRelationShip.setStartNode(dept);
    
            Person person = new Person();
            person.setName("霖");
    //        person.setId(106L);
            deptRelationShip.setEndNode(person);
    
            // 这里的保存关系 会连带保存person新节点,如果person设置id且该id已存在,则不新建person节点
            deptRelationRepository.save(deptRelationShip);
        }
    
        @Test
        public void testRelationQuery() {
            Iterable deptRelationAll = deptRelationRepository.findAll();
            deptRelationAll.forEach(e-> System.out.println(e));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
  • 相关阅读:
    SharePoint Integrator Delphi版
    这几个好用的去图片水印免费软件,你们知道吗?
    Java开发从入门到精通(五):JDK9-JDK16 新特性
    证件照尺寸修改、图片背景换色、照片大小压缩…几个在线图片编辑、处理网站推荐
    申论~~~
    矩阵结构下需要的文化导向
    C语言:union类型
    操作系统的发展与分类
    (01)ORB-SLAM2源码无死角解析-(56) 闭环线程→计算Sim3:理论推导(1)求解s,t
    SpringBoot整合Mongodb
  • 原文地址:https://blog.csdn.net/qq_40301202/article/details/127433329