本文介紹如何使用 Neo4j 建立電商平台的圖資料函式庫,並利用 Python 模擬客戶購買行為。首先,我們會建立產品、品牌和型別節點,並定義它們之間的關係。接著,我們會建立客戶節點,並記錄他們的購買行為。最後,我們會使用 Python 和 Cypher 查詢語言操作 Neo4j 資料函式庫,實作新增客戶、新增購買記錄等功能,並驗證資料的正確性。這個架構允許我們根據客戶的購買歷史推薦相關產品,並能彈性地新增或修改節點和邊緣以滿足未來的業務需求。

為電子商務平台設計圖資料函式庫與資料處理流程

建立 Neo4j 圖資料函式庫

為了建立一個能夠支援客戶瀏覽、比較和購買產品的電子商務後端系統,我們首先需要設定一個新的 Neo4j 圖資料函式庫。以下是具體的步驟:

  1. 開啟 Neo4j Desktop,並選擇新增本地 DBMS。
  2. 為新的資料函式庫命名,例如 Store DB
  3. 設定一個密碼,例如 testpython。注意,在實際的生產環境中,應使用密碼管理系統來保護資料函式庫密碼。
  4. 點選「Create」建立新的圖資料函式庫。
  5. 資料函式庫建立完成後,點選「Start」啟動資料函式庫,並等待其載入完成。
  6. 開啟 Neo4j Browser 視窗,並在命令列中執行 :server user add 來新增管理員使用者。
  7. 新增一個使用者名稱和密碼,例如 admintestpython,並賦予 PUBLICadmin 角色。

設計圖資料函式庫的 Schema

在建立資料函式庫之後,我們需要決定要儲存哪些型別的資料。首先,讓我們來看看產品的 CSV 檔案:

productID,name,brand,mainType,subType
1001,WX100 In-ear Headphones,Hans,Audio,Bluetooth Headphones
1002,WX-B210 Bluetooth Headphones,Hans,Audio,Bluetooth Headphones
1003,Hans-3 Bluetooth Headphones,Hans,Audio,Bluetooth Headphones
...

從這個檔案中,我們可以看到每個產品都有一個唯一的 ID、名稱、品牌、主型別和子型別。因此,我們可以設計出三種節點型別:ProductBrandType,並使用 HAS_TYPEHAS_BRAND 關係來連線它們。

圖資料函式庫 Schema 設計範例

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center
skinparam rectangleBackgroundColor #F5F5F5
skinparam rectangleBorderColor #333333
skinparam arrowColor #333333

title 圖資料函式庫 Schema 設計範例

rectangle "HAS_BRAND" as node1
rectangle "HAS_TYPE" as node2
rectangle "SUBTYPE_OF" as node3

node1 --> node2
node2 --> node3

@enduml

此圖示展示了產品、品牌和型別之間的關係。我們可以使用這種結構來儲存產品資訊,並根據客戶的購買記錄來推薦相關產品。

內容解密:

  • Product 節點代表個別產品,包含產品 ID、名稱等屬性。
  • Brand 節點代表品牌,包含品牌名稱等屬性。
  • Type 節點代表產品型別,包含主型別和子型別等屬性。
  • HAS_BRAND 關係用於連線產品和其對應的品牌。
  • HAS_TYPE 關係用於連線產品和其對應的型別。
  • SUBTYPE_OF 關係用於表示子型別和主型別之間的隸屬關係。

儲存客戶資訊

為了儲存客戶資訊,我們可以建立 Customer 節點,並包含客戶 ID、姓名和地址等屬性。同時,我們可以使用 PURCHASED 關係來連線客戶和他們購買的產品,並在關係上儲存購買日期和時間等屬性。

客戶節點與購買關係範例

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center
skinparam rectangleBackgroundColor #F5F5F5
skinparam rectangleBorderColor #333333
skinparam arrowColor #333333

title 客戶節點與購買關係範例

rectangle "客戶節點" as n1
rectangle "購買關係範例" as n2

n1 --> n2

@enduml

此圖示展示了客戶和產品之間的購買關係。我們可以在 PURCHASED 關係上儲存額外的屬性,例如購買日期和時間。

內容解密:

  • Customer 節點代表個別客戶,包含客戶 ID、姓名和地址等屬性。
  • PURCHASED 關係用於連線客戶和他們購買的產品,並包含購買日期和時間等屬性。

資料管線開發

擴充套件圖資料函式庫架構

圖6.2展示了一個擴充套件的資料函式庫架構,包含了產品和客戶的資訊。這個架構將產品和客戶的特徵轉換為有向異質圖(directed heterogeneous graph)。由於客戶可以多次購買同一件產品,因此節點之間可能會有多條邊緣,這使得我們的網路被歸類別為多重圖(multigraph)。

這個圖資料函式庫架構具有足夠的彈性,可以輕鬆地新增或修改節點和邊緣,以滿足不斷變化的業務需求。與傳統的關聯式資料函式庫相比,異質圖資料函式庫架構可以更靈活地處理複雜的資料關係。

新增靜態產品資訊

為了開始建立圖資料函式庫,我們需要將產品資訊匯入資料函式庫中。首先,將products.csv檔案移動到Neo4j的匯入資料夾中。然後,使用Cypher查詢語言將CSV檔案中的資料匯入Neo4j資料函式庫。

LOAD CSV WITH HEADERS FROM 'file:///products.csv' AS row
MERGE (p:Product {productID: toInteger(row.productID), name: row.name})
MERGE (b:Brand {brandName: row.brand})
MERGE (m:Type {typeName: row.mainType})
MERGE (s:Type {typeName: row.subType})
MERGE (p)-[:HAS_BRAND]->(b)
MERGE (p)-[:HAS_TYPE]->(s)
MERGE (s)-[:HAS_TYPE]->(m)

內容解密:

  1. 使用LOAD CSV WITH HEADERS函式讀取products.csv檔案,並將每一行資料暫存於row變數中。
  2. 使用MERGE函式新增或匹配ProductBrandType節點,並設定其屬性。
  3. 使用MERGE函式新增或匹配節點之間的關係,包括HAS_BRANDHAS_TYPE
  4. 這裡使用MERGE而不是CREATE,以避免建立重複的節點。

驗證產品資料

為了確認產品資料已正確匯入Neo4j資料函式庫,我們可以使用Cypher查詢語言執行一些簡單的查詢。

MATCH (n:Product {productID:1001})
RETURN n.productID, n.name

內容解密:

  1. 使用MATCH函式查詢productID為1001的Product節點。
  2. 使用RETURN函式傳回該節點的productIDname屬性。

模擬客戶互動

線上零售商可以透過圖資料函式庫來儲存客戶的購買行為和其他互動資訊。我們的目標是建立一個產品推薦系統,利用客戶的購買記錄來推薦相關產品。

圖表說明

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center
skinparam rectangleBackgroundColor #F5F5F5
skinparam rectangleBorderColor #333333
skinparam arrowColor #333333

title 圖表說明

rectangle "購買" as node1
rectangle "屬於" as node2

node1 --> node2

@enduml

此圖示展示了客戶、產品、品牌和型別之間的關係。客戶可以購買產品,產品屬於特定的品牌和型別,型別又屬於特定的主型別。

圖表內容解密:

  1. 客戶節點與產品節點之間的邊緣表示客戶購買了該產品。
  2. 產品節點與品牌節點之間的邊緣表示產品屬於該品牌。
  3. 產品節點與型別節點之間的邊緣表示產品屬於該型別。
  4. 型別節點與主型別節點之間的邊緣表示型別屬於該主型別。

設計資料函式庫結構與資料處理流程

在真實世界的應用中,客戶會與應用程式互動並將資料儲存到資料函式庫中。然而,在本例中,我們將模擬一些假客戶並模擬他們向資料函式庫傳送的查詢。在實際的軟體開發團隊中,建立假客戶和客戶互動可能是新資料管道測試框架的一部分。

這些型別的查詢交易通常由發布/訂閱訊息服務(pub/sub)或類別似服務處理,而這些服務通常由雲端運算服務管理。這些交易通常發生在多台機器之間,這對機器的設定、安全性和許可權都有影響,這是一個獨立的主題,即DevOps領域。在本章中,我們的假客戶將使用Python生成,並透過Python向Neo4j傳送查詢以模擬購買行為。所有這些都將在本機上進行,這使我們能夠專注於圖資料建模和網路分析。

建立客戶節點

首先,我們需要建立客戶節點。在我們的結構中,客戶具有productID、名稱和地址。Python有一些不錯的工具可以用於生成假資料,我們將在本章中使用Faker函式庫。

使用Faker生成假資料

Faker函式庫提供了多種方法來模擬不同型別的資料,從簡單的電話號碼到完整的假個人資訊。對於我們的用途,我們需要生成名稱和地址以賦予客戶。

  1. 匯入Faker類別:從faker函式庫匯入Faker類別,並建立一個Faker例項,將其指定給變數fake。我們可以選擇性地設定Faker函式庫的隨機種子,以便在Python指令碼執行之間保持一致的隨機生成資料。
from faker import Faker
fake = Faker()
Faker.seed(0)
  1. 生成名稱和地址:使用案例項化的fake物件生成名稱和地址,只需呼叫類別的name()和address()方法即可。
name = fake.name()
address = fake.address()
print(name)
print(address)

建立客戶類別

現在我們可以建立假資料,讓我們將這些資料附加到客戶身上。一個合理的方法是建立一個Customer類別,它為每個人儲存一個customerID、名稱和地址屬性。

class Customer:
    def __init__(self, customerID):
        self.customerID = customerID
        self.name = fake.name()
        self.address = fake.address()

測試客戶類別

讓我們測試新的類別,透過生成一個客戶。為了測試目的,我們可以建立一個具有任意ID(9999)的客戶,並列印預出Customer物件的屬性。

new_customer = Customer(9999)
print(new_customer.customerID)
print(new_customer.name)
print(new_customer.address)

連線Neo4j並新增客戶

為了將客戶資訊傳遞給圖資料函式庫,我們需要從Python存取Neo4j,就像在第5章《與圖資料函式庫合作》中所做的那樣。

建立Neo4j連線類別

當例項化時,這個類別使用傳遞給它的憑據建立與Neo4j的連線。它還可以建立與Neo4j的會話,並使用query()方法執行查詢,以及使用close()關閉連線。

from neo4j import GraphDatabase

class Neo4jConnect:
    def __init__(self, uri, user, password):
        self.driver = GraphDatabase.driver(uri, auth=(user, password))

    def close(self):
        self.driver.close()

    def query(self, query):
        session = self.driver.session()
        result = session.run(query)
        return result

新增客戶到Neo4j

我們現在準備好生成並新增客戶到Neo4j。這可以被視為客戶註冊我們的線上零售應用程式或網站。

def add_customer(c, connection):
    query = f'MERGE (:Customer {{customerID: toInteger({c.customerID}), ' \
            f'name: "{c.name}", ' \
            f'address: "{c.address}"}})'
    connection.query(query)

測試新增客戶功能

讓我們試試這個新方法,透過建立另一個示例客戶。使用我們的Customer類別,我們可以生成一個新的Customer物件,具有我們需要的屬性。與所有從Python到Neo4j的Cypher查詢一樣,我們然後使用Neo4jConnect建立連線,然後呼叫新的add_customer方法,使用我們的生成的Customer和我們的Neo4j連線,這處理了向我們的圖資料函式庫寫入一個節點。最後,我們使用connection.close()關閉會話。

test_customer = Customer(9999)
connection = Neo4jConnect('bolt://localhost:7687', 'admin', 'testpython')
add_customer(test_customer, connection)
connection.close()

驗證結果

回到Neo4j瀏覽器,我們可以透過多種方式檢查節點是否正確新增,但為了更直觀地呈現,讓我們開啟Neo4j桌面版的Store DB資料函式庫。使用一些非常簡單的Cypher來匹配所有型別為Customer的節點,我們可以將我們的客戶傳回到瀏覽器。

MATCH (c:Customer) RETURN c

我們應該看到傳回了一個節點。點選那個Customer節點應該在Neo4j瀏覽器視窗的右側顯示節點屬性,確認我們的屬性已經正確設定。接下來,我們現在可以向圖中新增客戶,但是目前它們仍然與資料函式庫中的其他節點斷開連線。根據我們的結構,我們需要建立關係來表示產品購買,但我們仍然需要一個過程來模擬這些交易。

程式碼詳細解析:

此段程式碼主要分為幾個部分:首先是匯入必要的函式庫,包括用於生成假資料的Faker以及用於與Neo4j互動的neo4j。然後定義了一個Customer類別,用於建立客戶物件,每個客戶具有唯一的ID、名稱和地址。接著,定義了一個Neo4jConnect類別,用於建立與Neo4j的連線、執行Cypher查詢以及關閉連線。最後,定義了一個add_customer函式,用於將客戶資訊新增到Neo4j中。

內容解密:

  1. 匯入必要的函式庫:程式碼首先匯入了必要的函式庫,包括Faker用於生成假資料,以及neo4j用於與Neo4j圖資料函式庫互動。
  2. 定義Customer類別:定義了一個Customer類別,該類別具有一個初始化方法__init__,該方法接受一個customerID引數,並使用Faker生成名稱和地址。
  3. 定義Neo4jConnect類別:定義了一個Neo4jConnect類別,用於管理與Neo4j的連線,包括建立連線、執行查詢和關閉連線。
  4. 定義add_customer函式:定義了一個add_customer函式,該函式接受一個Customer物件和一個Neo4jConnect物件,使用Cypher查詢將客戶資訊新增到Neo4j中。
  5. 測試功能:程式碼最後測試了上述功能,透過建立一個客戶物件,建立與Neo4j的連線,並將客戶新增到資料函式庫中。

此段程式碼展示瞭如何使用Python與Neo4j互動,包括建立連線、建立節點以及執行Cypher查詢。這對於需要在應用程式中使用圖資料函式庫的開發者來說是非常有用的。

建立客戶購買行為的資料管道

在前面的章節中,我們已經成功建立了一個客戶節點並將其新增到Neo4j資料函式庫中。現在,我們將繼續建立一個方法來新增客戶購買行為,類別似於我們之前定義的 add_customer() 方法。

新增客戶購買行為的方法

我們將命名這個方法為 add_purchase(),並且需要更多的引數來正確地模擬購買行為。這個函式將接受一個客戶物件 c、產品ID、當前時間和一個Neo4j連線物件。我們將再次使用 f-strings 來組裝Cypher查詢。

當客戶選擇一個產品並購買時,為了在圖中模擬這個行為,我們需要根據正確的客戶節點的 customerID 屬性和匹配的產品節點的 productID 進行匹配。這些ID是唯一的,因此可以用來匹配對應的節點。查詢的最後一步是使用 MERGE 在客戶和產品節點之間建立一個邊,並使用 PURCHASED 關係來描述這個邊,同時在 datetime 邊屬性上加上時間戳。

def add_purchase(c, productID, time, connection):
    query = f'MATCH (c:Customer {{customerID: toInteger({c.customerID})}}) ' \
            f'MATCH (p:Product {{productID: toInteger({productID})}}) ' \
            f'MERGE (c)-[:PURCHASED {{datetime:"{time}"}}]->(p)'
    connection.query(query)

內容解密:

  1. MATCH (c:Customer {{customerID: toInteger({c.customerID})}}) 這個Cypher查詢陳述式用於匹配具有特定 customerID 的客戶節點。
  2. MATCH (p:Product {{productID: toInteger({productID})}}) 這個陳述式用於匹配具有特定 productID 的產品節點。
  3. MERGE (c)-[:PURCHASED {{datetime:"{time}"}}]->(p) 這個陳述式用於在客戶和產品節點之間建立一個 PURCHASED 關係,並在關係上新增 datetime 屬性。
  4. connection.query(query) 將組裝好的Cypher查詢陳述式傳送到Neo4j資料函式庫執行。

測試新增客戶購買行為的方法

為了測試 add_purchase() 方法的行為,我們可以使用之前建立的客戶物件(ID為9999),並模擬一次購買行為。我們可以在 products.csv 原始資料中找到一個產品供客戶購買,或者直接在Neo4j中查詢。在這個例子中,我們將使用 productID 為1010的產品節點。

首先,我們從 datetime 模組匯入 datetime,以便能夠存取當前時間。然後,建立一個到資料函式庫的連線,並使用 add_purchase() 方法,將現有的 test_customer、產品ID(1010)、當前時間(使用 datetime.now() 取得)和Neo4j連線物件作為引數傳遞給它。

from datetime import datetime
connection = Neo4jConnect('bolt://localhost:7687', 'admin', 'testpython')
add_purchase(test_customer, 1010, datetime.now(), connection)
connection.close()

內容解密:

  1. from datetime import datetime 匯入 datetime 類別,以便能夠取得當前時間。
  2. connection = Neo4jConnect('bolt://localhost:7687', 'admin', 'testpython') 建立到Neo4j資料函式庫的連線。
  3. add_purchase(test_customer, 1010, datetime.now(), connection) 呼叫 add_purchase() 方法,模擬客戶購買行為。
  4. connection.close() 關閉資料函式庫連線。

驗證新增的邊

在執行完上述程式碼後,我們可以進入Neo4j Browser,查詢我們新建立的邊。我們可以使用Cypher查詢來查詢客戶和產品節點之間具有 PURCHASED 關係的邊,並傳回匹配的節點。

MATCH (c:Customer)-[:PURCHASED]->(p:Product) RETURN c, p

如果邊被正確新增,我們應該能夠在Neo4j Browser中看到兩個節點透過 PURCHASED 關係連線起來。點選這個邊,可以在右側的屬性面板中檢視其屬性,包括 datetime

測試多次購買行為

我們的圖結構設計允許客戶多次購買同一個產品,並且每次購買行為都會被記錄為一條獨立的邊。為了驗證這一點,我們可以再次執行相同的程式碼,讓 test_customer 第二次購買產品ID為1010的產品。

connection = Neo4jConnect('bolt://localhost:7687', 'admin', 'testpython')
add_purchase(test_customer, 1010, datetime.now(), connection)
connection.close()

執行完後,我們可以再次在Neo4j Browser中使用Cypher查詢來檢查是否成功增加了新的邊。

MATCH (c:Customer)-[purchase:PURCHASED]->(p:Product) RETURN c.name, p.name, purchase.datetime

我們應該能夠看到兩條邊被成功新增,它們具有不同的 datetime 值。這是因為在 add_purchase() 方法中,我們使用了 MERGE 陳述式,並且在模式中指定了 datetime 屬性。因此,即使客戶和產品節點之間的關係已經存在,只要 datetime 不同,就會被視為不同的模式,從而建立新的邊。

清理測試資料

在驗證完我們的 add_customer()add_purchase() 方法工作正常後,我們可以清理測試過程中新增到圖中的測試客戶和購買記錄。可以使用以下Cypher查詢來刪除客戶節點及其相關的關係。

MATCH (c:Customer) DETACH DELETE c