本文件所描述的內容屬於神策分析的高級使用功能,涉及較多技術細節,適用於對相關功能有經驗的用戶參考。如果對文件內容有疑惑,請諮詢您的數據諮詢顧問取得一對一的協助。

自1.11 版本開始,神策分析已經支援虛擬屬性功能,同時還支援使用第三方的維度表來對已接入的事件和屬性進行進一步的擴展,該功能可以大大的增強神策分析對於復雜業務需求的處理能力。
1.17 版本以前,建立虛擬屬性和維度表均需要在部署神策系統的伺服器上操作,1.17 及之後版本,支援在中繼資料管理內建立虛擬屬性。

請先 ssh 到部署了神策服務的任意一台電腦,在 sa_cluster 帳戶下執行建立虛擬屬性或者維度表的指令。

1. 虛擬屬性

所謂虛擬屬性,是指在數據入庫之後透過 SQL 運算式對已有的事件屬性和用戶屬性進行二次加工,產生一個新的屬性值。

注意:

  1. events 表中的 time 和 event 屬於特殊欄位,目前支援對 time 欄位二次加工,具體可參考應用場景 5 舉例。不支援對 event 欄位二次加工提取虛擬屬性。
  2. 建立虛擬屬性時 SQL 運算式最長不能超過 1024 個字元。

1.1. 應用場景 1:屬性抽取

建立虛擬屬性需要用到 sa_view_tools 這個工具。例如我們現在有一個事件屬性是 $url,希望從 $url 中抽取出 q= 的屬性,作為 search_keyword 來進行分析,那麼我們可以這麼做:

~/sa/web/bin/sa_view_tools.sh external_property add \
-p default \
-n search_keyword \
-c '搜尋關鍵字' \
-e "parse_url(events.\$url, 'QUERY', 'q')" \
-t STRING
BASH
spadmin external_view external_property add \
-p default \
-n search_keyword \
-c '搜尋關鍵字' \
-e "parse_url(events.\$url, 'QUERY', 'q')" \
-t STRING
BASH

其中,-p 是神策系統的專案名稱, -n 是外部屬性在神策系統中的英文名稱,-c 是中文名稱,-e 是對應的 SQL 運算式,-t 表示數據型別。

我們在 -e 參數中使用了一個 parse_url 的 SQL 函數來進行參數提取,這樣在分析的時候使用 search_keyword 屬性即可實作相關的分析需求。

1.2. 應用場景 2:屬性合併

假設我們在埋點的時候埋了兩個屬性:item_id 和 item_id_1,但實際上它們是一個含義,希望在使用的時候進行合併,也可以使用虛擬屬性功能來定義:

~/sa/web/bin/sa_view_tools.sh external_property add \
-p default \
-n new_item_id \
-c 'Item Id' \
-e "coalesce(events.item_id, events.item_id_1)" \
-t STRING
BASH
spadmin external_view external_property add \
-p default \
-n new_item_id \
-c 'Item Id' \
-e "coalesce(events.item_id, events.item_id_1)" \
-t STRING
BASH

這裡的 coalesce 是一個標準 SQL 函數,回傳所有參數中第一個非 NULL 的值,這樣我們在使用的時候只要使用 new_item_id 來進行分析就可以達到屬性合併的目的。

1.3. 應用場景 3:高精度小數

預設情況下,神策分析的 NUMBER 型別只支援小數點之後 3 位,如果需要支援高精度型別,可以使用擴展屬性的方式來實作。具體做法如下:

  • 將需要支援高精度的內容以字串的型別來傳送給神策分析,以避免精度遺失,以 Java SDK 為例:
Map<String, Object> properties = new HashMap<String, Object>();
properties.put("big_number", "123.12312345");
sa.track(distinctId, true, "TestBigNumber", properties);
JAVA
  • 建立一個高精度型別的虛擬屬性
# 這裡假設傳入的原始字串屬性為 big_number,建立的高精度屬性為 big_number_decimal
# decimal 參數中的 38 表示總的數據位數,16 表示小數點之後的位數
~/sa/web/bin/sa_view_tools.sh external_property add \
-p default \
-n big_number_decimal \
-c '高精度數字' \
-e 'cast(events.big_number as decimal(38,16))' \
-t number
BASH
# 這裡假設傳入的原始字串屬性為 big_number,建立的高精度屬性為 big_number_decimal
# decimal 參數中的 38 表示總的數據位數,16 表示小數點之後的位數
spadmin external_view external_property add \
-p default \
-n big_number_decimal \
-c '高精度數字' \
-e 'cast(events.big_number as decimal(38,16))' \
-t number
BASH
  • 使用 big_number_decimal 屬性進行相關的分析

1.4. 應用場景 4:聯合去重

在神策分析中,我們支援對某個屬性進行去重數的計算,但是不直接支援對兩個或者更多的屬性進行去重。如果有這類需求,也可以使用虛擬屬性的方式來實作,即定義一個新的屬性,它的值是需要去重的多個屬性的組合。

例如,我們想計算不同用戶瀏覽不同商品的去重次數(即一個用戶瀏覽同一個商品不重複計數,但是瀏覽不同的商品需要計數),那麼可以定義一個虛擬屬性如下:

~/sa/web/bin/sa_view_tools.sh external_property add \
-p default \
-n user_and_product \
-c '用戶ID + 商品ID' \
-e "concat(cast(events.user_id as string), events.product_id)" \
-t STRING
BASH
spadmin external_view external_property add \
-p default \
-n user_and_product \
-c '用戶ID + 商品ID' \
-e "concat(cast(events.user_id as string), events.product_id)" \
-t STRING
BASH

然後在神策分析中,查看這個屬性的去重數即可得到對應的指標。

1.5. 應用場景 5:時間聚合

預設情況下,神策分析支援天、週、月等時間聚合方式,如果想要其它的時間聚合方式,也可以使用虛擬屬性實作。例如,如果想要按照 "週X" 來對數據進行分析,可以從 time 屬性中提取出一個 day_of_week 的屬性:

~/sa/web/bin/sa_view_tools.sh external_property add \
-p default \
-n day_of_week \
-e 'dayofweek(time)' \
-t NUMBER
BASH
spadmin external_view external_property add \
-p default \
-n day_of_week \
-e 'dayofweek(time)' \
-t NUMBER
BASH

dayofweek 函數求得的數值,1 表示星期天,7 表示星期六,2-6 表示星期一至星期五。類似的,也可以用 extract(hour from time) 運算式來提取時間中的小時部分。

1.6. 應用場景 6:用戶屬性

神策支援使用 users 表中的用戶屬性提取虛擬屬性,比如根據用戶屬性 Birthday ,得到用戶的年齡屬性,可參考如下範例:

~/sa/web/bin/sa_view_tools.sh external_property add \
-p default \
-n user_age \
-u true \
-c "用戶年齡" \
-e "cast (extract(year FROM now()) -extract(year FROM EPOCH_TO_TIMESTAMP(users.Birthday)) as int)" \
-t NUMBER
BASH
spadmin external_view external_property add \
-p default \
-n user_age \
-u true \
-c "用戶年齡" \
-e "cast (extract(year FROM now()) -extract(year FROM EPOCH_TO_TIMESTAMP(users.Birthday)) as int)" \
-t NUMBER
BASH

注意:

  • 使用用戶屬性建立虛擬屬性時,需要增加 -u 參數,並且在運算式內用到的用戶屬性註明是用戶屬性。
  • 不支援使用 user 表中的用戶屬性和維度表關聯。

1.7. 更多應用

由於 external_property add 指令的 -e 參數支援任意的 Impala SQL 運算式,因此,可以很靈活的根據實際的業務需求來建立虛擬屬性。

2. 維度表

除了基於已經埋點的屬性來直接建立虛擬屬性之外,我們還可以結合第三方維度表來建立更複雜的虛擬屬性應用。

假設我們在神策分析中有一個 pay_order 事件,同時該事件有 product_id、product_name 等屬性。現在我們希望在分析的時候使用product 的更多其它維度來進行分析(例如product_manufacturer),但是這些維度並沒有在埋點的時候打入神策系統中,這個時候就可以引入維度表來滿足這個需求。

2.1. 使用 items 表作為維度表

注意:神策分析 1.14 及之後版本才支援此方式。之前的版本請參考 2.2 的方法。

如果希望啟用 items 表,首先需要透過 SDK 提供的 itemSet 介面進行 Item 資訊的上報。以 Java SDK 為例:

Map<String, Object> properties = new LinkedHashMap<>();
properties.put("product_name", "iPhone 8");
properties.put("product_manufacturer", "Apple");
properties.put("price", 998.88);
// 參數分別為 item_type、item_id、自定義屬性
sensorsAnalytics.itemSet("product", "T12345", properties);
JAVA

注意:item_type 可以用於區分不同的 item 類型,比如 movie、muisic 等,item_id 區分同一個 item 類型下面的不同的 Item。

然後,我們就可以在自定義查詢功能中使用這個表:

SELECT * FROM items LIMIT 10
SQL

接下來我們建立 items 和 events 表的關聯:

spadmin external_view external_dimension_table add \
-p default \
-t items \
-e "events.product_id = items.item_id AND items.item_type = 'product'"
BASH

注意:

  1. 這裡是使用 product_id 欄位進行關聯,同時限定了 item_type。如果同時存在多種 item_type,那麼需要建立多次不同的關聯,具體請參考 一張維度表使用不同的關聯條件
  2. 維度表關聯的欄位必須為主鍵,若不是主鍵且維度表數據有重複的情況下,會導致在神策分析查詢的數據增多。

  3. items 表關聯時,必須同時使用  item_id 和 item_type 進行關聯。若只指定 item_id ,沒有指定 item_type,item_id 有重複的情況下,會導致在神策分析查詢的數據增多。

最後,使用 items 表中的 product_manufacturer 來建立虛擬屬性即可:

spadmin external_view external_property add \
-p default \
-n product_manufacturer \
-c '產品製造商' \
-e items.product_manufacturer \
-t STRING
BASH

至此,我們已經可以在神策系統的所有分析功能中使用 pay_order 進行分析的時候,看到 product_manufacturer 屬性,並使用這個屬性進行任意的分析工作。

如果需要對維度表和虛擬屬性進行刪除、更新等管理操作,可以直接執行不帶參數的指令查看相關的幫助:

~/sa/web/bin/sa_view_tools.sh external_property
BASH
spadmin external_view external_property 
BASH

2.2. 自定義維度表

除了引用 items 表之外,我們也可以手動建立維度表。在這個例子中,我們使用一張 product_info 的維度表來作為例子。首先我們需要在 impala 中建立這樣一張表:

注意:這裡的維度表必須使用 Kudu 或者 HDFS 的 Parquet 文件格式來儲存,否則無法支援全部特性。

CREATE DATABASE dimensions;

CREATE TABLE dimensions.product_info (
	product_id STRING NOT NULL,
	product_manufacturer STRING NULL,
	PRIMARY KEY (product_id)
)
PARTITION BY HASH (product_id) PARTITIONS 3
STORED AS KUDU TBLPROPERTIES ('kudu.master_addresses'='${kudu_master_host}:7051');
SQL

可以透過此指令取得 kudu master 的網址。

#選擇 master_address 的 value
monitor_tools get_config -t client -m kudu
BASH
#選擇 master_address 的 value
spadmin config get client -m kudu
BASH

然後,我們需要準備好這張維度表的數據,通常應該是從其它業務資料庫或者資料倉儲中匯入進來。具體可以使用 impala-shell 匯入 SQL 檔案,或者 JDBC 等多種方式來進行,例如我們可以直接插入幾條數據:

INSERT INTO dimensions.product_info VALUES ('124', 'Xiaomi'), ('123', 'Apple');
SQL

如果需要插入的數據量比較大,建議使用批量檔案匯入的方式。首先需要建立一個文字格式的表,並指定分隔符號:

CREATE TABLE dimensions.raw_csv_product_info (
	product_id STRING,
	product_manufacturer STRING
) ROW FORMAT DELIMITED FIELDS TERMINATED by ',' LOCATION '/tmp/raw_csv_product_info/';
SQL

然後上傳已經準備好的逗號分開的文字數據檔案:

hdfs dfs -put data.csv /tmp/raw_csv_product_info/
BASH

更新 CSV 表,並執行 INSERT 把數據匯入 Kudu 即可。

REFRESH dimensions.raw_csv_product_info;
INSERT INTO dimensions.product_info SELECT * FROM dimensions.raw_csv_product_info;
SQL

在準備好維度表數據之後,我們用 sa_view_tools 工具來把該維度表加入神策系統中:

~/sa/web/bin/sa_view_tools.sh external_dimension_table add \
-p default \
-t dimensions.product_info \
-e 'events.product_id = dimensions.product_info.product_id'
BASH
spadmin external_view external_dimension_table add \
-p default \
-t dimensions.product_info \
-e 'events.product_id = dimensions.product_info.product_id'
BASH

其中,-p 是神策系統的專案名稱,-t 參數是維度表的完整名稱, -e 參數表示該維度表和事件表(events)的關聯關係,即 SQL 中進行 JOIN 的條件。

注意:維度表關聯的欄位必須為主鍵,若不是主鍵且維度表數據有重複的情況下,會導致在神策分析查詢的數據增多。

在定義了維度表之後,我們就可以把該維度表(即 product_info)中的具體欄位作為一個虛擬屬性加入神策系統中:

~/sa/web/bin/sa_view_tools.sh external_property add \
-p default \
-n product_manufacturer \
-c '產品製造商' \
-e dimensions.product_info.product_manufacturer \
-t STRING
BASH
spadmin external_view external_property add \
-p default \
-n product_manufacturer \
-c '產品製造商' \
-e dimensions.product_info.product_manufacturer \
-t STRING
BASH

至此,我們已經可以在神策系統的所有分析功能中使用 pay_order 進行分析的時候,看到 product_manufacturer 屬性,並使用這個屬性進行任意的分析工作。

2.3. 一張維度表使用不同的關聯條件

如果同一張維度表需要使用不同的關聯條件,那麼需要在新增維度表的時候使用别名。具體的方式為在原有的表面後面加上 #1 或者其它識別符號。例如:

~/sa/web/bin/sa_view_tools.sh external_dimension_table add \
-p default \
-t dimensions.product_info#1 \
-e 'events.item_id = dimensions.product_info#1.item_id'
BASH
spadmin external_view external_dimension_table add \
-p default \
-t dimensions.product_info#1 \
-e 'events.item_id = dimensions.product_info#1.item_id'
BASH

增加完成之後,後面在其它指令引用這張表時也需要使用 dimensions.product_info#1

3. 限制與規範

3.1. 查詢效能

由於關聯維度表需要使用JOIN,雖然神策的查詢引擎已經對這個類型的JOIN 做了一定程度的優化,但是相比直接使用原始的事件屬性依然會有比較顯著的效能降低,具體的效能和維度表的大小、JOIN 的條件等都有關係。因此,我們建議在直接使用事件屬性可以滿足需求的情況下,不要使用維度表;同時,應當確保維度表的行數在百萬以內,以盡量降低 JOIN 帶來的額外效能損耗。

3.2. 快取一致性

目前為止,神策系統的快取機制依然是基於事件數據的變更來實作的,這個機制中暫時沒有考慮到維度表的數據變化帶來的影響。因此,如果維度表的數據發生了變更(例如進行 Update 或者 Insert),查詢結果可能還會使用舊的快取數據,這個時候需要強制更新才能得到正確的結果。

4. 常見問題

4.1. 新增的虛擬屬性在頁面不可見

在增加完虛擬屬性之後,可能會發現在某些事件的屬性選擇介面無法看到對應的虛擬屬性,但是在自定義查詢裡卻可以看到。出現這種情況,是因為預設情況下虛擬屬性只會和包含了所有引用屬性的事件進行綁定。

例如,在應用場景 2 中,new_item_id 這個虛擬屬性引用了 item_id 和 item_id_1,所以也只有同時包含這兩個屬性的事件才能看到 new_item_id 屬性。

在 1.14 版本之後,如果有特殊需求,也可以在增加虛擬屬性時單獨指定一個關聯屬性,例如 item_name,這樣在任何包含了 item_name 的事件裡,都可以看到新的 new_item_id 屬性。例如:

spadmin external_view external_property add \
-p default \
-n new_item_id \
-c 'Item Id' \
-e "coalesce(events.item_id, events.item_id_1)" \
-r "item_name" \
-t STRING
BASH

其中,-r 不填寫是預設情況(這樣虛擬屬性只會和包含了所有引用屬性的事件進行綁定),-r 有具體屬性值的時候(設定關聯屬性-r 就是包含該屬性的事件會增加虛擬屬性),-r 為空("")的時候(這種情況下所有事件裡都可以看到這個虛擬屬性)

4.2. 新增虛擬屬性之後隱藏原屬性

例如在屬性合併的場景中,我們把 item_id 和 item_id_1 合併成了 new_item_id,這個時候可能希望隱藏舊的 item_id、item_id_1。那麼只需要直接在中繼資料管理中隱藏即可,引用的屬性隱藏之後並不會影響虛擬屬性的正常工作。

4.3. 如何建立日期(DATETIME)型別虛擬屬性

建立虛擬屬性是時間型別的欄位,要求建立虛擬屬性的運算式(-e)得到的結果是一個 bigint 型別的時間戳,再設定虛擬屬性型別 (-t) 為 DATETIME 型別。例如,原本記錄了一個 oldtimestamp 是數值型別,儲存的是時間戳,為了便於分析,可以使用虛擬屬性來新建一個 datetime 型別的屬性方便進行日期比較的分析。如果是上傳維度表再設定虛擬屬性,那麼要求上傳的維度表裡儲存的就是時間戳。

~/sa/web/bin/sa_view_tools.sh external_property add \
-p default \
-n newdate \
-c 'newdate' \
-e "cast(events.oldtimestamp as bigint)" \
-t DATETIME
BASH
spadmin external_view external_property add \
-p default \
-n newdate \
-c 'newdate' \
-e "cast(events.oldtimestamp as bigint)" \
-t DATETIME
BASH

4.4. 如何建立布林(BOOL)型別虛擬屬性

建立虛擬屬性是布林型別的欄位,要求建立虛擬屬性的運算式(-e)得到的結果是一個 bigint 型別且值為 0、1 ,再設定虛擬屬性類型 (-t) 為 BOOL 型別。例如,針對所有的付款記錄直接區分是否是“有效付款”和“無效付款” ,“無效付款”指得是付款金額值為 0 或者 null 的訂單,其他歸為“有效付款”。

~/sa/web/bin/sa_view_tools.sh external_property add \
-p default \
-n is_valueable_paid \
-c '是否有效付款' \
-e "cast((case when (events.ActualPaidAmount is not null or events.ActualPaidAmount=0) then '1' else '0' end) as bigint)" \
-t BOOL
BASH
spadmin external_view external_property add \
-p default \
-n is_valueable_paid \
-c '是否有效付款' \
-e "cast((case when (events.ActualPaidAmount is not null or events.ActualPaidAmount=0) then '1' else '0' end) as bigint)" \
-t BOOL
BASH

4.5. 如何建立串列(LIST) 型別虛擬屬性

維度表中的屬性類型對應 STRING 型別,虛擬屬性設定為 LIST 型別。 LIST 型別虛擬屬性建立舉例:

建立維度表的時候,設定 list_name 為 STRING 型別:

CREATE DATABASE dimensions;

CREATE TABLE dimensions.listceshi (
	list_id STRING NOT NULL,
	list_name STRING NULL,
	...
	PRIMARY KEY (list_id)
)
PARTITION BY HASH (product_id) PARTITIONS 3
STORED AS KUDU TBLPROPERTIES ('kudu.master_addresses'='${kudu_master_host}:7051');
SQL

傳值時如果 LIST 的值有多個值,需要用換行符號 \n 分隔:

INSERT INTO dimensions.listceshi VALUES ('124', 'apple\nbanana\npeer'), ('123', 'apple\nbanana\nbeer')
SQL

退出 impala,用工具建立好維度表和 events 表的關聯關係後,再建立 LIST 型別虛擬屬性:

~/sa/web/bin/sa_view_tools.sh external_property add \
-p default \
-n list_name \
-c '列表專案' \
-e dimensions.listceshi.list_name \
-t LIST
BASH
spadmin external_view external_property add \
-p default \
-n list_name \
-c '列表專案' \
-e dimensions.listceshi.list_name \
-t LIST
BASH

4.6. 測試維度表的關聯條件

注意:1.14 版本之後可用

對於比較複雜的關聯條件,建議先使用 impala-shell 執行 SQL 來進行測試,以確保結果的正確性。可以直接使用 JOIN 語法進行,例如:

/*sa(test_project)*/ 
SELECT dimensions.product_info.product_manufacturer FROM
events 
LEFT JOIN dimensions.product_info
ON events.product_id = dimensions.product_info.product_id
WHERE date = CURRENT_DATE()
LIMIT 100
SQL

4.7. 如何更新虛擬屬性/維度表

更新虛擬屬性

~/sa/web/bin/sa_view_tools.sh external_property update\
-p default \
-n new_item_id \
-c 'Item Id' \
-e "coalesce(events.item_id, events.item_id_1)" \
-r "item_name" \
-t STRING
BASH
spadmin external_view external_property update\
-p default \
-n new_item_id \
-c 'Item Id' \
-e "coalesce(events.item_id, events.item_id_1)" \
-r "item_name" \
-t STRING
BASH

其中,-p 是神策系統的專案名稱, -n 是外部屬性在神策系統中的英文名稱,-c 是中文名稱,-e 是對應的 SQL 運算式,-t 表示數據型別。

更新維度表

~/sa/web/bin/sa_view_tools.sh external_dimension_table update\
-p default \
-t dimensions.product_info \
-e 'events.product_id = dimensions.product_info.product_id'
BASH
spadmin external_view external_dimension_table update\
-p default \
-t dimensions.product_info \
-e 'events.product_id = dimensions.product_info.product_id'
BASH

其中,-p 是神策系統的專案名稱,-t 參數是維度表的完整名稱, -e 參數表示該維度表和事件表(events)的關聯關係,即 SQL 中進行 JOIN 的條件。

4.8. 如何刪除虛擬屬性/維度表

刪除虛擬屬性

~/sa/web/bin/sa_view_tools.sh external_property remove\
-p default \
-n new_item_id 
BASH
spadmin external_view external_property remove\
-p default \
-n new_item_id 
BASH

其中,-p 是神策系統的專案名稱, -n 是外部屬性在神策系統中的英文名稱。

刪除維度表與 events 表的關聯關係

~/sa/web/bin/sa_view_tools.sh external_dimension_table remove\
-p default \
-t dimensions.product_info
BASH
spadmin external_view external_dimension_table remove\
-p default \
-t dimensions.product_info
BASH

其中,-p 是神策系統的專案名稱,-t 參數是維度表的完整名稱。