神策分析支持多种不同语言的 SDK,这些 SDK 虽然在外部提供的接口上有所不同,但是在内部实现上都使用统一的数据格式,在这里,我们对数据格式进行一个更加细致的描述。
如果您使用非 SDK 采集数据,需要按照本章节中的数据格式来构造数据。
- 注意:这里描述的是底层数据传输格式的定义,和具体 SDK 的调用接口无关。
数据整体格式
发送端使用 JSON 作为数据传输格式,本系统以 JSON 数据类型为基础,再加以特定的限制。
事件数据举例
记录一个事件及关联的属性。
{
"distinct_id": "0f485d4daaadedae5f",
"anonymous_id":"0f485d4daaadedae5f",
"time": 1434556935000,
"type": "track",
"event": "ViewProduct",
"project": "ebiz_test",
"time_free": true, //建议在导入历史数据时使用,SDK 采集的实时数据不建议使用
"identities":{
"$identity_android_id":"0f485d4daaadedae5f"
},
"properties": {
"$app_version":"1.3",
"$wifi":true,
"$province":"湖南",
"$city":"长沙",
"$user_agent":"Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_2 like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) CriOS/58.0.3029.113 Mobile/14F89 Safari/602.1",
"$screen_width":320,
"$screen_height":568,
"product_id":12345,
"product_name":"苹果",
"product_classify":"水果",
"product_price":14.0
}
}
对于上述字段的说明如下:
用户关联事件数据举例
这个数据是一个较为复杂的功能,请在使用前先阅读 标识用户,并在必要时联系我们的技术支持人员。
track_signup
{
"distinct_id":"130xxxx1234",
"original_id":"0f485d4d12345e5f",
"login_id":"130xxxx1234",
"anonymous_id":"0f485d4d12345e5f",
"time":1434557935000,
"type":"track_signup",
"event":"$SignUp",
"project":"ebiz_test",
"identities":{
"$identity_android_id":"0f485d4d12345e5f",
"$identity_login_id":"130xxxx1234"
},
"properties":{
"$manufacturer":"Apple",
"$model":"iPhone5,2",
"$os":"iOS",
"$os_version":"7.0",
"$app_version":"1.3",
"$wifi":true,
"$ip":"180.79.35.65",
"$province":"湖南",
"$city":"长沙",
"$screen_width":320,
"$screen_height":568
}
}
这条数据表示,一个 Android ID 为 0f485d4d12345e5f 的用户,成功完成了注册,注册后的注册 ID 是 130xxxx1234。并且系统后台,会将 Android ID 为 0f485d4d12345e5f 的用户和注册 ID 为 130xxxx1234 的用户,当做同一个用户对待。
需要注意的是,此数据结构中的 distinct_id 和 original_id 为必须字段,其中 distinct_id 与 login_id 的值相同,original_id 与 anonymous_id 的值相同。
track_id_bind
注意:若需要使用该事件,首先需要确认神策系统当前项目的用户关联策略为 全域用户关联,若使用简易用户关联策略,该数据会被整条拒绝。
{
"time":1622199005123,
"type":"track_id_bind",
"distinct_id":"3335654b922c4686",
"anonymous_id":"3335654b922c4686",
"identities":{
"$identity_android_id":"3335654b922c4686",
"$identity_email":"test@163.com"
},
"event":"$BindID",
"properties":{
"$app_name":"Test",
"$device_id":"3335654b922c4686",
"$model":"Redmi Note 4X",
"$os_version":"7.0",
"$app_version":"1.0",
"$wifi":true,
"$network_type":"WIFI",
"$lib_method":"code"
}
}
这条数据表示,将一个 Android ID 为 3335654b922c4686 和一个邮箱为 test@163.com 尝试进行关联,关联成功后,后续两个 ID 独立上报事件时,在神策系统中都会被当做同一个用户。
track_id_unbind
注意:若需要使用该事件,首先需要确认神策系统当前项目的用户关联策略为 全域用户关联,若使用简易用户关联策略,该数据会被整条拒绝。
{
"time":1622199169262,
"type":"track_id_unbind",
"distinct_id":"3335654b922c4686",
"anonymous_id":"3335654b922c4686",
"identities":{
"$identity_email":"test@163.com"
},
"event":"$UnbindID",
"properties":{
"$app_name":"test",
"$device_id":"3335654b922c4686",
"$model":"Redmi Note 4X",
"$os_version":"7.0",
"$app_version":"1.0",
"$wifi":true,
"$network_type":"WIFI",
"$lib_method":"code"
}
}
这条数据表示,将邮箱为 test@163.com 从系统已有用户中解除关联,解除关联成功后,神策系统中,将没有用户关联 test@163.com 邮箱。
用户数据举例
更新用户数据的相关操作,主要是用来更新、删除用户的属性。
profile_set
直接设置一个用户属性,如果属性的字段已存在则覆盖,不存在则自动创建。
{
"distinct_id": "12345",
"login_id": "12345",
"anonymous_id": "0f485d4da1111fe5f",
"type": "profile_set",
"time": 1435290195610,
"project": "ebiz_test",
"identities": {
"$identity_android_id": "0f485d4da1111fe5f",
"$identity_login_id": "12345"
},
"properties": {
"$province": "湖南",
"FavoriteFruits": ["苹果", "香蕉", "芒果"],
"Age": 33,
"$city": "长沙",
"IncomeLevel": "3000~5000",
"$name": "小明",
"Gender": "男",
"$signup_time": "2015-06-26 11:43:15.610"
}
}
profile_set_once
与 profile_set 数据不同,如果对应的属性字段已存在,则这条记录会被忽略而不会覆盖已有数据,如果属性不存在则会自动创建。
因此,profile_set_once 比较适用于为用户设置首次激活时间、首次注册时间等只在首次设置时有效的属性。
{
"distinct_id": "12345",
"login_id": "12345",
"anonymous_id": "0f485d4da1111fe5f",
"type": "profile_set_once",
"time": 1435290195610,
"project": "ebiz_test",
"identities": {
"$identity_android_id": "0f485d4da1111fe5f",
"$identity_login_id": "12345"
},
"properties": {
"$province": "湖南",
"FavoriteFruits": ["苹果", "香蕉", "芒果"],
"Age": 33,
"$city": "长沙",
"IncomeLevel": "3000~5000",
"$name": "小明",
"Gender": "男",
"$signup_time": "2015-06-26 11:43:15.610"
}
}
profile_increment
增加或减少一个用户的某个 <数值 Number> 类型的属性值,比如给用户属性 age 的值加 1。
如果用户表(users 表)中不存在这个用户,则会在用户表中自动创建该用户的记录,并设置该用户相应的属性值,在默认值 0 的基础上增加上传数据中的值。
{
"distinct_id": "12345",
"login_id": "12345",
"anonymous_id": "0f485d4da1111fe5f",
"type": "profile_increment",
"time": 1435290200354,
"project": "ebiz_test",
"identities": {
"$identity_android_id": "0f485d4da1111fe5f",
"$identity_login_id": "12345"
},
"properties": {
"age": 1
}
}
profile_append
向某个用户的某个数组类型的属性,追加一个或者多个值。如果本次上传的值,与系统中已存在的值有重复,默认是不会去重的。如果本次上传的值,有重复项,也不会去重的。
{
"distinct_id": "12345",
"login_id": "12345",
"anonymous_id": "0f485d4da1111fe5f",
"type": "profile_append",
"time": 1437280200354,
"project": "ebiz_test",
"identities": {
"$identity_android_id": "0f485d4da1111fe5f",
"$identity_login_id": "12345"
},
"properties": {
"FavoriteFruits": ["橘子", "西瓜"]
}
}
profile_unset
将某个用户的某些属性值设置为空,在上传的数据中,属性的值请设置为非 null 的任何值,例如 true。
{
"distinct_id": "12345",
"login_id": "12345",
"anonymous_id": "0f485d4da1111fe5f",
"type": "profile_unset",
"time": 1437280200354,
"project": "ebiz_test",
"identities": {
"$identity_android_id": "0f485d4da1111fe5f",
"$identity_login_id": "12345"
},
"properties": {
"Age": true,
"FavoriteFruits": true
}
}
profile_delete
删除一个用户的记录。
{
"distinct_id": "12345",
"login_id": "12345",
"anonymous_id": "0f485d4da1111fe5f",
"type": "profile_delete",
"time": 1437290200354,
"project": "ebiz_test",
"identities": {
"$identity_android_id": "0f485d4da1111fe5f",
"$identity_login_id": "12345"
},
"properties": {
}
}
物品数据举例
物品表数据举例,主要是用来增加、删除、更新数据表的相关内容,数据举例如下:
item_set
创建/更新物品表中的某一行记录,如果该记录已存在则覆盖,不存在则自动创建。
{
"type":"item_set",
"item_id":"12",
"item_type":"dub",
"project": "ebiz_test",
"properties":{
"title":"because of u",
"sub_title":"st",
"xxx":"xxx"
}
}
item_delete
删除物品表中的某一行记录。
{
"type":"item_delete",
"item_id":"16",
"item_type":"dub",
"project": "ebiz_test"
}
对上述字段的解释如下:
- type:item_set 表明是创建/更新某一行记录,item_delete 表示删除某一行记录;
- item_id:表示物品的 id;
- item_type:表示物品的类型,区分不同的物品表。需是合法的变量名,即不能以数字开头,且只包含:大小写字母、数字、下划线和 $,且 item_type 字段长度最大为 100;
- 注意,物品表是使用 item_id 和 item_type 作为联合主键;
- project:这条数据所属项目名,若不指定该参数,则需要使用该字段时取值 default,即默认项目。指定的项目必须是系统中已经存在的项目,否则这条数据将无效,更多项目相关请参见 多项目;
- properties:上报的物品的具体属性,以 dict 的形式存在。属性名需要是合法的变量名,不能以数字开头,且只包含:大小写字母、数字、下划线;
小结
数据中 type 字段表示一条数据的具体操作,是记录用户的一次行为、更新用户的属性、或者是创建一条物品记录。故每条数据中必须要有 type 字段,如果缺失该字段,则数据会被系统整条拒绝无法入库。
type 以及对应操作见下表:
type 类型 |
对应的操作 |
track |
数据导入 events 表,一行记录表示一个事件 |
track_signup |
数据导入 events 表,同时 users 表中会记录事件对应登录 ID 和匿名 ID |
track_id_bind、track_id_unbind |
数据导入 events 表,同时 users 中会增加或删除对应的 ID |
profile_* |
数据导入 users 表,一行记录表示一个用户 |
item_* |
数据导入 items 表,一行记录表示一个物品 |
属性数据类型
属性数据类型自动识别规则
当一个属性在系统中未预先定义,则属性首次导入时,系统会根据首次导入的值,决定该属性在系统中的数据类型。
JSON 中的类型 |
示例值 |
导入后系统识别的数据类型 |
该类型在系统中的相关限制 |
Number |
12 或 12.0 |
<数值 Number> |
-9E15 到 9E15 小数点后最多保留 3 位 |
Bool |
true 或 false |
<布尔 Bool> |
无 |
String |
"SensorsData" |
<字符串 String> |
使用 UTF-8 编码后最大长度 1024 字节,超出后系统会进行截断,保留前 1024 字节内容,并正常入库 |
List |
["橘子","西瓜"] |
<集合 List> |
默认是字符串元素的数组(传入的字符串不会去重),最大元素个数为 500,其中每个元素使用 UTF-8 编码后最大长度 255 字节。
如果需要调整 List 具体是数组还是集合,请联系神策技术支持。若 append 导致超过最大元素个数时,新入库的元素会淘汰最早入库的元素。
|
String |
- "2015-06-19 17:51:21.234"
- "2015-06-19 17:51:21"
- "2015-06-19"
|
<日期时间 Datetime> |
建议使用第一种,其中 SSS 为毫秒;年取值范围是 [1900, 2199]
- yyyy-MM-dd HH:mm:ss.SSS
- yyyy-MM-dd HH:mm:ss
- yyyy-mm-dd (时分秒按 00:00:00 处理)
|
属性数据类型转化规则
当一个属性在系统中创建,其对应的数据类型就已经确定,后续导入数据时若类型和系统中记录的类型不符,则尝试对数据进行类型转换,若无法转换或转换失败则 该数据会被整条拒绝。
尝试进行的类型转换如下(空格表示不进行转换):
原始类型 > 目标类型 |
数值型 |
布尔值 |
字符串 |
字符串集合 |
日期时间 |
数值型 |
|
true > 1; false > 0 |
空字符串 "" 抛弃该属性; 其他按数值解析 |
|
|
布尔值 |
0 > false; 非 0 值 > true |
|
字符串"true"、"false"转换为布尔类型 |
|
|
字符串 |
原值作为字符串 |
原值作为字符串 |
|
原值作为字符串,如 ["Hello","World"] |
原值作为字符串 |
字符串集合 |
|
|
|
|
|
日期时间 |
在一定区间内的按 UNIX 时间戳的秒或毫秒转换 |
|
多种日期时间格式模式串解析 |
|
|
- 上述表格左侧的列对应目标类型,上方的行对应原始类型。目标类型对应元数据中的数据类型,原始类型是数据上传时的属性值类型
- 什么时候该使用数值类型的属性:
- 需要进行聚合运算(例如求和、均值)或者按区间分组的值,典型的比如价格、时长、年龄等。
- 除非有特殊需求,否则各类 ID(例如订单 ID)不建议作为数值类型存储。
导入数据的限制
一般限制
- 事件变量名(event 的值)和属性变量名(properties 中 key 取值)都需是合法的变量名,即不能以数字开头,且只包含:大小写字母、数字、下划线和 $,且事件变量名和属性变量名最大长度都为 100,自定义的事件名或者属性名不能以 $ 开头;
- 变量名不能与系统中已经存在的虚拟事件、虚拟属性的变量名重复;
- 系统对变量名大小写处理有特殊要求,字母内容完全一致,但是大小写不一致的变量名会被拦截;
- 类型 type 字段的取值只能是上文列出几种(track, profile_* 等),并且大小写敏感;
- 属性 properties 字段必须存在,可以为空({});
- 自定义的事件或者属性的变量名不可以与系统保留字段重名,本节列出了保留字段。
事件时间限制
导入不合理时间的用户事件将影响数据的准确性(如客户端时间错误导致导入未来的数据),故默认情况下对导入的事件时间进行了限制:
- 使用客户端 SDK(iOS、Android、Web、小程序等)导入的数据,服务端默认只接收事件发生时间在向前 10 天内和未来向后 1 小时内的数据(相比于系统当前时间);
- 使用后端语言 SDK(如 Java、Python 等)或导入工具(如 LogAgent 等),默认只能导入事件时间当前向前 2 年内和未来向后 1 小时内的数据;
注意:
- 如果希望导入上述默认时间窗口之外的数据,可以联系值班同学修改窗口限制,或在数据中添加 time_free 字段(见本文档事件数据样例)。
关于事件时间的修正机制:
- 因为 App 端只能使用客户端的时间作为事件发生的时间,如果客户端时间不准确,会导致采集端数据有异常,因此神策默认开启时间修正机制:App 端发生事件时的时间 time 的值为 t1,发送数据时的时间 _flush_time 的值为 t2(客户端时间,且 _flush_time 不入库),服务端接收到数据的时间 $receive_time 时间为 t3(服务端时间),如果 t3 - t2 > 60s 或 t2 > t3,则认为客户端的时间不准确,会对事件触发时间进行修正,修正后事件时间 t1'=t1+(t3-t2)。以下场景不会修正事件发生的时间:
- 如果数据是延迟上报(比如数据在发送之前用户强杀 App,导致部分数据未及时发送,会先缓存在本地,待下次打开 App,网络正常时会重新尝试发送本地的缓存数据),发送数据时的 _flush_time 时间是准确的,也不会修复事件触发的时间。
属性在不同表中的限制
对事件表的属性,一个属性,只能具有一种类型(不同的具体事件,同名属性类型也必须相同);
对用户表的属性,一个属性,只能具有一种类型;
对物品表的属性,一个属性,只能具有一种类型;
对于一个属性名,在事件表、用户表、物品表中可以具有不同的类型。
属性长度限制
属性的数据类型,及特殊字段长度限制如下:
项目 |
限制 |
数据类型 <数值 Number> |
-9E15 到 9E15 小数点后最多保留 3 位 |
数据类型 <字符串 String> |
使用 UTF-8 编码后最大长度 1024 字节,超出后系统会进行截断,保留前 1024 字节内容,并正常入库 |
数据类型 <集合 List> |
每个 LIST 中最多包含 500 个不大于 255 字节的字符串 |
用户标识($identity_login_id 等) |
最大长度 255 字节 |
distinct_id、original_id |
最大长度 255 字节 |
属性数上限
单个项目 事件表 / 用户表 / 物品表 的属性建议合理设置,过多影响导入和查询性能,达到上限则会导致导入异常。
保留字段
为了保证查询时属性名不与系统变量名冲突,设置如下保留字段,请避免其作为事件名和属性名(properties 中的 key)使用:
保留前缀 |
保留字段 |
额外保留字段 |
事件表 |
用户表 |
物品表 |
- $
- identity_
- user_tag
- user_group
- segment_
|
- user_id
- distinct_id
- original_id
- time
- properties
- id
- first_id
- second_id
- users
- events
- event
- date
- datetime
- project
|
- event_id
- event_bucket
- day
- week_id
- month_id
- _offset
- sampling_group
|
- _offset
- first_id_type
- second_id_type
- generated_from
- merged_to
|
|