Neo4j圖資料分頁處理

來源:互聯網
上載者:User

Neo4j圖資料分頁處理

首先簡單介紹下Neo4j,Neo4j是一個高效能的NOSQL圖形資料庫,它將結構化資料存放區在網路上而不是表中,它是一個嵌入式的、基於磁碟的、具備完全的事務特性的Java持久化引擎,但是它將結構化資料存放區在網路(從數學角度叫做圖)而不是表中。Neo4j也可以被看做是一個高效能的圖引擎,該引擎具有成熟資料庫的所有特性。

Neo4j中涉及到幾個關鍵的實體物件,分別是Node(節點)、Relationship(關係)、Path(路徑)、Direction(關係方向)、RelationshipType(關聯類型)。朋友們可以將Relationship(關係)看做是連接線,一條連接線每端只能串連一個Node(節點),並且連接線兩端必須同時都串連有Node(節點);Relationship(關係)具有方向和類型特性。Node(節點)可以通過多Relationship(關係)與其他多個Node(節點)關聯,而且Node(節點)也可以是沒有任何串連的孤立節點。Path(路徑)包含多個Node和Relationship,是節點和關係的集合。就是筆者本人利用Neo4j構建的一個“射鵰英雄譜”局部關係圖:

neo4j_graph

3.1. Neo4j資料分頁檢索類型

Neo4j資料分頁檢索介面採用自身的Cypher檢索語句,通過構建Cypher分頁檢索語句,實現分頁處理。

Neo4j資料庫中不存在傳統的表的概念,一個資料庫可以視作一張圖。資料分頁檢索將針對Node和Relationship分別進行,不針對Path進行分頁檢索,因為基本上沒有什麼意義。按照檢索條件區分檢索類型,可以細分為以下幾種。

3.1.1 Node(節點)分頁檢索

1) 無條件檢索Cypher語句

--不根據屬性排序
START n=node(*) RETURN n SKIP 0 LIMIT 20

--根據屬性排序
START n=node(*) RETURN n ORDER BY n.NAME DESC SKIP 0 LIMIT 20

2) 根據Property屬性檢索Cypher語句

--根據屬性NAME值進行模糊檢索
START n=node(*) WHERE n.NAME=~'.*tom*' RETURN n SKIP 0 LIMIT 20

--根據屬性NAME值進行精確檢索
START n=node(*) WHERE n.NAME='tom' RETURN n SKIP 0 LIMIT 20

3) 根據Index索引檢索Cypher語句

--說明:N_INDEX為索引名稱,USER_NAME為索引Key名稱

--根據索引值進行模糊檢索
--模糊檢索的多種格式。
--1、*tom*表示USER_NAME中包含tom字串的
--2、*tom表示USER_NAME右側匹配tom字串的
--3、tom*表示USER_NAME左側匹配tom字串的
START n=node:N_INDEX('USER_NAME:*tom*') RETURN n SKIP 0 LIMIT 20

--根據索引值進行精確檢索
START n=node:N_INDEX (USER_NAME='tom') RETURN n SKIP 0 LIMIT 20

4) 根據Index索引和Property屬性檢索Cypher語句

--根據索引(模糊)和屬性(模糊)檢索
START n=node:N_INDEX('USER_NAME:*tom*') WHERE n.USER_TYPE=~'.*sys*' RETURN n SKIP 0 LIMIT 20

--根據索引(模糊)和屬性(精確)檢索
START n=node:N_INDEX('USER_NAME:*tom*') WHERE n.USER_TYPE ='system' RETURN n SKIP 0 LIMIT 20

--根據索引(精確)和屬性(模糊)檢索
START n=node:N_INDEX(USER_NAME='tom') WHERE n.USER_TYPE=~'.*sys*' RETURN n SKIP 0 LIMIT 20

--根據索引(精確)和屬性(精確)檢索
START n=node:N_INDEX(USER_NAME='tom') WHERE n.USER_TYPE ='system' RETURN n SKIP 0 LIMIT 20

5) 根據Label標籤檢索Cypher語句

--標籤內容為”中國”
START n=node(*) MATCH (n:中國) RETURN n SKIP 0 LIMIT 20

6) 根據Label標籤和Property屬性檢索Cypher語句

START n=node(*) MATCH (n:中國) WHERE n.USER_TYPE=’system’ RETURN n SKIP 0 LIMIT 20

3.1.2 Relationship(關係)分頁檢索

1) 無條件分頁檢索Cypher語句

--不根據屬性排序
START r=relationship(*) RETURN DISTINCT(r) SKIP 0 LIMIT 20

--根據屬性排序
START r=relationship(*) RETURN DISTINCT(r) ORDER BY r.NAME ASC SKIP 0 LIMIT 20

2) 根據Property屬性檢索Cypher語句

--根據屬性NAME值進行模糊檢索
START r=relationship(*) WHERE r.NAME=~'.*tom*' RETURN r SKIP 0 LIMIT 20

--根據屬性NAME值進行精確檢索
START r=relationship(*) WHERE r.NAME='tom' RETURN r SKIP 0 LIMIT 20

3) 根據Index索引檢索Cypher語句

--說明:R_INDEX為索引名稱,USER_NAME為索引Key名稱

--根據索引值進行模糊檢索
--模糊檢索的多種格式。
--1、*tom*表示USER_NAME中包含tom字串的
--2、*tom表示USER_NAME右側匹配tom字串的
--3、tom*表示USER_NAME左側匹配tom字串的
START r=relationship(*):R_INDEX('USER_NAME:*tom*') RETURN r SKIP 0 LIMIT 20

--根據索引值進行精確檢索
START r=relationship(*):R_INDEX (USER_NAME='tom') RETURN r SKIP 0 LIMIT 20

4) 根據Index索引和Property屬性檢索Cypher語句

--根據索引(模糊)和屬性(模糊)檢索
START r=relationship(*):R_INDEX('USER_NAME:*tom*') WHERE r.USER_TYPE=~'.*sys*' RETURN r SKIP 0 LIMIT 20

--根據索引(模糊)和屬性(精確)檢索
START r=relationship(*):R_INDEX('USER_NAME:*tom*') WHERE r.USER_TYPE ='system' RETURN r SKIP 0 LIMIT 20

--根據索引(精確)和屬性(模糊)檢索
START r=relationship(*):R_INDEX(USER_NAME='tom') WHERE r.USER_TYPE=~'.*sys*' RETURN r SKIP 0 LIMIT 20

--根據索引(精確)和屬性(精確)檢索
START r=relationship(*):R_INDEX(USER_NAME='tom') WHERE r.USER_TYPE ='system' RETURN r SKIP 0 LIMIT 20

5) 根據RelationshipType關聯類型檢索Cypher語句

--FRIEND為關聯類型字串
START n=node(*) MATCH n-[r:FRIEND]-() RETURN DISTINCT(r) SKIP 0 LIMIT 20

3.2. Neo4j資料分頁模型類

import java.io.Serializable;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import com.hnepri.common.util.LogInfoUtil;
/**
 * Description: 圖資料庫資料分頁模型類。<br>
 * 利用此類可分頁管理Node資料和Relationship資料等。
 * Copyright: Copyright (c) 2015<br>
 * Company: 河南電力科學研究院智能電網所<br>
 * @author shangbingbing 2015-11-01編寫
 * @version 1.0
 */
public class GraphPageModel implements Serializable {
    private static final long serialVersionUID = 330410716100946538L;
    private int pageSize = 10;
    private int pageIndex = 1;
    private int prevPageIndex = 1;
    private int nextPageIndex = 1;
    private int pageCount = 0;
    private int pageFirstRowIndex = 1;
    private boolean hasNextPage = true;
    private int totalCount = 0;
    private long startTime = System.currentTimeMillis();
    private long endTime = System.currentTimeMillis();
    private List<Node> nodeList = new ArrayList<Node>();
    private List<Relationship> relationshipList = new ArrayList<Relationship>();
    /**
    * 分頁物件建構函數
    * @param pageSize 每頁記錄數
    */
    public GraphPageModel(int pageSize) {
        this.pageSize = pageSize;
    }
    /**
    * 擷取分頁記錄數量
    * @return
    */
    public int getPageSize() {
        return pageSize;
    }
    /**
    * 擷取當前頁序號
    * @return
    */
    public int getPageIndex() {
        return pageIndex;
    }
    /**
    * 設定當前頁序號
    * @param pageIndex
    */
    public void setPageIndex(int pageIndex) {
        if(pageIndex <= 0) {
            pageIndex = 1;
        }
        this.pageIndex = pageIndex;
    }
    /**
    * 擷取分頁總數
    * @return
    */
    public int getPageCount() {
        if(this.getTotalCount() == 0) {
            this.pageCount = 0;
        } else {
            int shang = this.getTotalCount() / this.getPageSize();
            int yu = this.getTotalCount() % this.getPageSize();
            if(yu > 0) {
                shang += 1;
            }
            this.pageCount = shang;
        }
        return pageCount;
    }
    /**
    * 擷取每頁的第一行序號
    * @return
    */
    public int getPageFirstRowIndex() {
        this.pageFirstRowIndex = (this.pageIndex - 1) * this.getPageSize() + 1;
        return pageFirstRowIndex;
    }
    /**
    * 擷取上一頁序號
    * @return
    */
    public int getPrevPageIndex() {
        if(this.pageIndex > 1) {
            this.prevPageIndex = this.pageIndex - 1;
        } else {
            this.prevPageIndex = 1;
        }
        return prevPageIndex;
    }
    /**
    * 擷取下一頁序號
    * @return
    */
    public int getNextPageIndex() {
        if(this.pageIndex < this.pageCount) {
            this.nextPageIndex = this.pageIndex + 1;   
        } else {
            this.nextPageIndex = this.pageCount;
        }
        return nextPageIndex;
    }
    /**
    * 跳轉到下一頁
    */
    public void nextPage() {
        if(this.totalCount == 0 || this.getPageCount() == 0) {
            this.pageIndex = 1;
        } else {
            if(this.pageIndex < this.pageCount) {
                this.pageIndex = this.pageIndex + 1;   
            } else {
                this.pageIndex = this.pageCount;
            }
        }
    }
    /**
    * 跳轉到上一頁
    */
    public void prevPage() {
        if(this.pageIndex > 1) {
            this.pageIndex = this.pageIndex - 1;
        } else {
            this.pageIndex = 1;
        }
    }
    /**
    * 擷取是否有下一頁
    * @return
    */
    public boolean isHasNextPage() {
        if(this.pageIndex < this.getPageCount()) {
            this.hasNextPage = true;
        } else {
            this.hasNextPage = false;
        }
        return hasNextPage;
    }
    /**
    * 擷取總記錄數   
    */
    public int getTotalCount() {
        return totalCount;
    }
    /**
    * 擷取總記錄數   
    * @param totalCount
    */
    public void setTotalCount(int totalCount) {
        this.totalCount = totalCount;
    }
    /**
    * 初始化起始時間(毫秒)
    */
    public void initStartTime() {
        this.startTime = System.currentTimeMillis();
    }
    /**
    * 初始化截止時間(毫秒)
    */
    public void initEndTime() {
        this.endTime = System.currentTimeMillis();
    }
    /**
    * 擷取毫秒格式的耗時資訊
    * @return
    */
    public String getTimeIntervalByMilli() {
        return String.valueOf(this.endTime - this.startTime) + "毫秒";
    }
    /**
    * 擷取秒格式的耗時資訊
    * @return
    */
    public String getTimeIntervalBySecond() {
        double interval = (this.endTime - this.startTime)/1000.0;
        DecimalFormat df = new DecimalFormat("#.##");
        return df.format(interval) + "秒";
    }
    /**
    * 列印時間資訊
    */
    public void printTimeInfo() {
        LogInfoUtil.printLog("起始時間:" + this.startTime);
        LogInfoUtil.printLog("截止時間:" + this.endTime);
        LogInfoUtil.printLog("耗費時間:" + this.getTimeIntervalBySecond());
    }
    /**
    * 擷取Node檢索結果清單
    * @return
    */
    public List<Node> getNodeList() {
        return nodeList;
    }
    /**
    * 擷取Relationship檢索結果清單
    * @return
    */
    public List<Relationship> getRelationshipList() {
        return relationshipList;
    }
}


模型類中,nodeList和relationshipList分別用來存放Node和Relationship;分頁檢索Node時,就通過getNodeList()讀取Node資訊;分頁檢索Relationship時,就通過getRelationshipList()讀取Relationship資訊。


3.3. Neo4j資料分頁介面方法
首先,我們先設計一個通用的執行Cypher檢索語句的介面方法,將檢索結果(主要指Node、Relationship和Path對象)封轉進Propertyies列表中。


/**
 * 執行Cypher檢索語句,將檢索結果封裝進Properties列表中。
 * @param query cypher檢索語句
 * @param params cypher檢索語句參數集合
 * @return
 */
public List<Properties> executeQuery(String query, Map<String,Object> params) {
    List<Properties> propertiesList = new ArrayList<Properties>();
    if(StringUtils.isBlank(query)) {
        return propertiesList;
    }
    ExecutionEngine executionEngine = new ExecutionEngine(this.getGraphDatabaseService());
    ExecutionResult result = null;
    if(params == null || params.size() == 0) {
        result = executionEngine.execute(query);
    } else {
        result = executionEngine.execute(query, params);
    }
    for (Map<String, Object> row : result ) {
        Properties properties = new Properties();
        for ( Entry<String, Object> column : row.entrySet()){
            properties.put(column.getKey(), column.getValue());
        }
        propertiesList.add(properties);
    }
    return propertiesList;
}

下面以無條件分頁檢索Node資訊為例,講述下介面方法的設計思路。具體代碼如下:

/**
 * 分頁檢索Node資訊。
 * @param pageModel 分頁模型對象,不可為空。
 * @param orders 排序屬性欄位。
 * @return
 */
public GraphPageModel queryNodes(GraphPageModel pageModel, GOrderBy ... orders) {
    if(pageModel == null) {
        pageModel = new GraphPageModel(10);
    }
    pageModel.getNodeList().clear();
    pageModel.getRelationshipList().clear();
   
    //計算總行數
    String query = "START n=node(*) RETURN count(*) AS NODE_COUNT";
    List<Properties> resultList = this.executeQuery(query);
    if(resultList == null || resultList.size() == 0) {
        return pageModel;
    }
    for(Properties properties : resultList) {
        int nodeCount = Integer.valueOf(properties.get("NODE_COUNT").toString());
        pageModel.setTotalCount(nodeCount);
    }
   
    //組織排序欄位資訊
    String strGOrderBy = "";
    if(orders != null && orders.length > 0) {
        strGOrderBy = "ORDER BY";
        for(GOrderBy order : orders) {
            strGOrderBy += String.format(" n.%s %s,", order.getPropertyName(), order.getOrderType().toUpperCase());
        }
        strGOrderBy = strGOrderBy.substring(0, strGOrderBy.length() - 1);
    }
   
    int skipCount = (pageModel.getPageIndex() - 1) * pageModel.getPageSize();
    int limitCount = pageModel.getPageSize();
    query = String.format("START n=node(*) RETURN n AS NODE_ENTRY %s SKIP %s LIMIT %s", strGOrderBy, skipCount, limitCount);
    List<Properties> list = this.executeQuery(query);
    for(Properties properties : list) {
        pageModel.getNodeList().add((Node)properties.get("NODE_ENTRY"));
    }
   
    return pageModel;
}

Neo4j 的詳細介紹:請點這裡
Neo4j 的:請點這裡

推薦閱讀:

Neo4j產生測試資料

Neo4j運行原理

Neo4j High Availability 配置

Neo4J圖資料庫實踐系列

圖資料庫實踐系列 (一)--Neo4J簡介與安裝

圖資料庫實踐系列 (二)--Neo4J空間資料儲存

圖資料庫實踐系列 (三)--Neo4j Spatial的REST整合

本文永久更新連結地址:

相關文章

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.