Nebula Graph介紹和SpringBoot環境連接和查詢

来源:https://www.cnblogs.com/milton/archive/2022/10/12/16784098.html
-Advertisement-
Play Games

當前Nebula Graph的最新版本是3.2.1, Nebula Graph 的一些特點 1. 支持分散式. 相對於Neo4j, TigerGraph這些圖資料庫, Nebula 是面向分散式設計的, 因此對集群的支持比較完備, 在規模上上限要高很多. 在實際項目中存儲了180億的點邊, 這個數量... ...


Nebula Graph介紹和SpringBoot環境連接和查詢

轉載請註明來源 https://www.cnblogs.com/milton/p/16784098.html

說明

當前Nebula Graph的最新版本是3.2.1, 根據官方的文檔進行配置
https://docs.nebula-graph.io/3.2.1/14.client/4.nebula-java-client/

Nebula Graph 的一些特點

  1. 支持分散式. 相對於Neo4j, TigerGraph這些圖資料庫, Nebula 是面向分散式設計的, 因此對集群的支持比較完備, 在規模上上限要高很多. 在實際項目中存儲了180億的點邊, 這個數量對於Neo4j和TigerGraph是比較困難的.
  2. 支持圖空間. 各個圖空間的ID是互不幹擾的, 但是在同一個圖空間里ID的類型和長度必須一致. 註意這個一致約束的是所有的點和邊. Nebula 可以使用int64作為ID, 也可以用字元串, 但是字元串需要指定一個長度, 例如64個位元組. 相對於只能用長整數的Neo4j, ID設計上更自由靈活.
  3. 點對應的類型叫TAG, 邊對應的類型叫EDGE
    1. TAG和EDGE都會對應一組的屬性(map, 或者說dict)
    2. 一個點可以對多個TAG, 每個TAG一組屬性, 多組屬性. 項目中建議一開始不要用多TAG, 在整個圖結構穩定後, 再做合併
    3. 一個邊只對應一個EDGE, 一組屬性
  4. Nebula 用的是自定義的查詢語法 GQL, 和 cypher 語法基本一樣
  5. 除了點邊的ID和關聯關係外, 只有帶索引的屬性可以查詢. 這點和其它圖資料庫不一樣, 其它資料庫即使沒有索引, 慢是慢點但是不報錯, Nebula直接給你返回錯誤.
  6. 對於返回數量較大的查詢, Nebula會強制查詢必須帶limit
  7. Nebula 單節點穩定性是有問題的, 在3.2.1版本中觀察到偶爾會出現服務自行退出, 如果在生產環境使用, 需要有後臺監控進行心跳檢測和自動啟動

GQL 常用查詢

下麵列出一些常用的查詢

-- 列出圖空間
SHOW SPACES;

-- 列出tag(點類型)和edge(邊類型), 需要先 USE 一個圖空間
SHOW TAGS;
SHOW EDGES;

列出某一類型的點和邊

MATCH ()-[e:follow]-() RETURN e
MATCH (v:player) RETURN v

帶條件的查詢, 在結果數量較多時必須帶limit, 否則Nebula會報錯

match (v:ADDRESS)-[e]-() where id(v)==\"ADD:82388116\" return v,e limit 100

基礎配置和使用

在上面的鏈接中, 提供了最小的配置和測試代碼

pom.xml 增加包依賴

對於Nebula Graph 3.2.1, 需要使用3.0.0的版本. client的每個版本只能對應特定的一兩個服務端版本

<dependency>
	<groupId>com.vesoft</groupId>
	<artifactId>client</artifactId>
	<version>3.0.0</version>
</dependency>

Java調用

Java調用主要是三部分, 創建連接池, 創建會話, 執行查詢

創建 NebulaPool 連接池

連接到地址127.0.0.1, 埠9669, 連接池大小100. 註意地址和埠是一個列表, Nebula是支持集群的. 連接時不需要用戶和密碼

NebulaPool pool = new NebulaPool();
try {
	NebulaPoolConfig nebulaPoolConfig = new NebulaPoolConfig();
	nebulaPoolConfig.setMaxConnSize(100);
	List<HostAddress> addresses = Arrays.asList(new HostAddress("127.0.0.1", 9669));
	Boolean initResult = pool.init(addresses, nebulaPoolConfig);
	if (!initResult) {
		log.error("pool init failed.");
		return;
	}
} catch ()
//...

創建 Session 會話

創建會話時需要用戶名和密碼

Session session = pool.getSession("root", "nebula", false);

執行查詢

創建一個SPACE, 然後使用這個SPACE, 創建一個TAG person, 創建一個EDGE like

String createSchema = "CREATE SPACE IF NOT EXISTS test(vid_type=fixed_string(20)); "
		+ "USE test;"
		+ "CREATE TAG IF NOT EXISTS person(name string, age int);"
		+ "CREATE EDGE IF NOT EXISTS like(likeness double)";
ResultSet resp = session.execute(createSchema);
if (!resp.isSucceeded()) {
	log.error(String.format("Execute: `%s', failed: %s",
			createSchema, resp.getErrorMessage()));
	System.exit(1);
}

添加一個點記錄

String insertVertexes = "INSERT VERTEX person(name, age) VALUES "
		+ "'Bob':('Bob', 10), "
		+ "'Lily':('Lily', 9), "
		+ "'Tom':('Tom', 10), "
		+ "'Jerry':('Jerry', 13), "
		+ "'John':('John', 11);";
ResultSet resp = session.execute(insertVertexes);
if (!resp.isSucceeded()) {
	log.error(String.format("Execute: `%s', failed: %s",
			insertVertexes, resp.getErrorMessage()));
	System.exit(1);
}

查詢

String query = "GO FROM \"Bob\" OVER like "
		+ "YIELD $^.person.name, $^.person.age, like.likeness";
ResultSet resp = session.execute(query);
if (!resp.isSucceeded()) {
	log.error(String.format("Execute: `%s', failed: %s",
			query, resp.getErrorMessage()));
	System.exit(1);
}
printResult(resp);

在 SpringBoot 項目中使用 Nebula Graph

pom.xml 增加包依賴

<dependency>
	<groupId>com.vesoft</groupId>
	<artifactId>client</artifactId>
	<version>3.0.0</version>
</dependency>

Session工廠: NebulaSessionFactory.java

配合@Bean(destroyMethod = "close"), 創建一個工廠類, 接收pool並實現close()方法

public class NebulaSessionFactory {
    private final NebulaPool pool;
    private final String username;
    private final String password;

    public NebulaSessionFactory(NebulaPool pool, String username, String password) {
        this.pool = pool;
        this.username = username;
        this.password = password;
    }

    public Session getSession() {
        try {
            return pool.getSession(username, password, false);
        } catch (NotValidConnectionException|IOErrorException|AuthFailedException|ClientServerIncompatibleException e) {
            throw new RuntimeException("Nebula session exception", e);
        }
    }

    public void close() {
        pool.close();
    }
}

為什麼不直接將 NebulaPool 配置為Bean? 因為 Session 每次創建時需要帶用戶名密碼, 將密碼作為config註入到每個Service中肯定是大家都不願意看到的.

配置修改: application.yml

  • 這裡的值如果不打算使用profile配置, 可以直接寫入
  • hosts是逗號分隔的地址埠列表, 例如 10.22.33.33:9669,10.22.33.34:9669
myapp:
  nebula:
    hosts: @nebula.hosts@
    username: @nebula.username@
    password: @nebula.password@
    max-conn: @nebula.max-conn@

Spring啟動配置: NebulaGraphConfig.java

應用啟動時讀取配置, 創建 NebulaPool, 並實例化 NebulaSessionFactory, destroyMethod = "close", 這個表示在項目shutdown時會調用Bean的close方法釋放資源.

@Configuration
public class NebulaGraphConfig {

    @Value("${myapp.nebula.hosts}")
    private String hosts;
    @Value("${myapp.nebula.max-conn}")
    private int maxConn;
    @Value("${myapp.nebula.username}")
    private String username;
    @Value("${myapp.nebula.password}")
    private String password;

    @Bean(destroyMethod = "close")
    public NebulaSessionFactory nebulaSessionFactory() {
        List<HostAddress> hostAddresses = new ArrayList<>();
        String[] hostList = hosts.split(",[ ]*");
        for (String host : hostList) {
            String[] hostParts = host.split(":");
            if (hostParts.length != 2 || !hostParts[1].matches("\\d+")) {
                throw new RuntimeException("Invalid host name set for Nebula: " + host);
            }
            hostAddresses.add(new HostAddress(hostParts[0], Integer.parseInt(hostParts[1])));
        }
        NebulaPoolConfig poolConfig = new NebulaPoolConfig();
        poolConfig.setMaxConnSize(maxConn);
        NebulaPool pool = new NebulaPool();
        try {
            pool.init(hostAddresses, poolConfig);
        } catch (UnknownHostException e) {
            throw new RuntimeException("Unknown Nebula hosts");
        }
        return new NebulaSessionFactory(pool, username, password);
    }
}

Service調用

在 Service 中進行調用

@Service
@Slf4j
public class GraphServiceImpl implements GraphService {

    @Autowired
    private NebulaSessionFactory sessionFactory;

    @Override
    public <T> NebulaResult<T> query(String graphSpace, String gql) {
        Session session = null;
        try {
            log.info("GQL: {}", gql);
            session = sessionFactory.getSession();
            NebulaResult<Void> res = query(session, "USE " + graphSpace);
            if (!res.isSuccess() || res.getResults() == null || res.getResults().size() == 0) {
                log.error("Failed to use space:{}", graphSpace);
                return null;
            }
            if (!graphSpace.equals(res.getResults().get(0).getSpaceName())) {
                log.error("Failed to use space:{}, result:{}", graphSpace, res.getResults().get(0).getSpaceName());
                return null;
            }
            return query(session, gql);
        } catch (IOErrorException e) {
            log.error(e.getMessage(), e);
            return null;
        } finally {
            if (session != null) {
                session.release();
            }
        }
    }

    private <T> NebulaResult<T> query(Session session, String gql) throws IOErrorException {
        String json = session.executeJson(gql);
        return JacksonUtil.extractByType(json, new TypeReference<>() {});
    }
}

輔助類 NebulaResult.java 等

外層結構

這裡定義了 json 格式響應的外層結構

@Data
public class NebulaResult<T> implements Serializable {
    private List<Error> errors;
    private List<Result<T>> results;

    @JsonIgnore
    public boolean isSuccess() {
        return (errors != null && errors.size() == 1 && errors.get(0).getCode() == 0);
    }

    @Data
    public static class Error implements Serializable {
        private int code;
    }

    @Data
    @JsonIgnoreProperties(ignoreUnknown = true)
    @JsonInclude(JsonInclude.Include.NON_NULL)
    public static class Result<T> implements Serializable {
        private String spaceName;
        private List<Element<T>> data;
        private List<String> columns;
        private Error errors;
        private long latencyInUs;
    }

    @Data
    public static class Element<T> implements Serializable {
        private List<Meta<T>> meta;
        private List<Serializable> row;
    }

    @Data
    public static class Meta<T> implements Serializable {
        private String type;
        private T id;
    }
}

內層因為區分Edge和Vertex, 結構不一樣. 如果是混合返回的結果, 可以用 Serializable

String gql = "match (v:ADDR)-[e]-() where id(v)==\"ADD:123123\" return v,e limit 100";
        NebulaResult<Serializable> res = graphService.query("insurance", gql);
        log.info(JacksonUtil.compress(res));
        Assertions.assertThat(res).isNotNull();

對於邊, 需要使用結構化的ID

@Data
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
public class EdgeId implements Serializable {
    private int ranking;
    private int type;
    private String dst;
    private String src;
    private String name;
}

用這個結構進行查詢

NebulaResult<EdgeId> res3 = graphService.query("t_test1", "MATCH ()-[e:follow]-() RETURN e");

對於點, ID就是String

NebulaResult<String> res2 = graphService.query("t_test1", "MATCH (v:player) RETURN v");

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 最近谷歌官方宣稱由於使用人數少,關停了中國的翻譯服務,導致谷歌瀏覽器上的翻譯服務無法使用,當我們使用谷歌瀏覽器自帶翻譯功能時,會報出:無法翻譯此網頁,或者沒有翻譯反應。 在macOS下怎麼解決谷歌瀏覽器Chrome無法翻譯呢?下麵小編來教你快速解決。 1、打開終端(commond+空格 搜索“終端” ...
  • 一、CentOS 7.9 安裝 elasticsearch-7.8.1 地址 https://www.elastic.co https://www.elastic.co/cn/downloads/past-releases https://github.com/elastic https://git ...
  • 分離部署LNMP 環境說明: | 系統 | 主機名 | IP | 服務 | | | | | | | centos8 | nginx | 192.168.111.141 | nginx | | centos8 | mysql | 192.168.111.142 | mysql | | centos8 ...
  • nginx 一、nginx簡介 nginx(發音同engine x)是一款輕量級的Web伺服器/反向代理伺服器及電子郵件(IMAP/POP3)代理伺服器,併在一個BSD-like協議下發行。 nginx由俄羅斯的程式設計師Igor Sysoev所開發,最初供俄國大型的入口網站及搜尋引擎Rambler ...
  • Redis有3種實現持久化的方式:AOF日誌、RDB快照、混合持久化 Redis寫入AOF日誌的過程 Redis執行完寫操作命令後,將命令追加到server.aof_buf緩衝區 通過write()系統調用,將aof_buf緩衝區的數據寫入到AOF文件 數據被拷貝到了內核緩衝區page cache ...
  • 一、MySQL資料庫內置系統表 mysql5.7之後的版本自帶資料庫為 1.information_schema資料庫 這個庫在mysql中就是個信息資料庫,它保存著mysql伺服器所維護的所有其他資料庫的信息,包括了資料庫名,表名,欄位名等。在註入中,infromation_schema庫的作用無 ...
  • GreatSQL社區原創內容未經授權不得隨意使用,轉載請聯繫小編並註明來源。 GreatSQL是MySQL的國產分支版本,使用上與MySQL一致。 文章導讀: 什麼是Undo Log? Undo:意為撤銷或取消,以撤銷操作為目的,返回某個狀態的操作。 Undo Log:資料庫事務開始之前,會將要修改 ...
  • 摘要:本文重點介紹單個SQL語句持續執行慢的場景。 本文分享自華為雲社區《GaussDB(DWS) SQL性能問題案例集》,作者:黎明的風。 本文重點介紹單個SQL語句持續執行慢的場景。我們可以對執行慢的SQL進行單獨分析,SELECT、INSERT、UPDATE等語句都可以使用explain ve ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...