神策分析支持多种不同语言的 SDK,这些 SDK 虽然在外部提供的接口上有所不同,但是在内部实现上都使用统一的数据格式,在这里,我们对数据格式进行一个更加细致的描述。

如果您使用非 SDK 采集数据,需要按照本章节中的数据格式来构造数据。

  • 注意:这里描述的是底层数据传输格式的定义,和具体 SDK 的调用接口无关。

1. 数据整体格式

发送端使用 JSON 作为数据传输格式,本系统以 JSON 数据类型为基础,再加以特定的限制。

1.1. 事件数据举例

记录一个事件及关联的属性。

{
	"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
	}
}
CODE


对于上述字段的说明如下:

  • distinct_id:类型是字符串,对用户的标识,对未登录用户,可以填充设备标识、CookieID 等,对于登录用户,则应该填充注册账号;这里的例子,假设是一个匿名用户,所以填充的是一个设备 ID;
  • login_id、anonymous_id:类型是字符串,对用户的标识,对未登录用户,只有 anonymous_id,而无 login_id 信息;
  • time:类型是数值,事件发生的实际时间戳,精确到毫秒;
  • type:表示一条数据的具体操作(小结部分会详细介绍),track 表明是记录一个事件 ,这里假设是一个商品浏览行为;
  • event:事件名,需是合法的变量名,即不能以数字开头,且只包含:大小写字母、数字、下划线和 $,其中以 $ 开头的表明是系统的预置事件,自定义事件名请不要以 $ 开头,且 event 字段长度最大为 100;
  • project:这条数据所属项目名,若不指定该参数,则需要使用该字段时取值 default,即默认项目。指定的项目必须是系统中已经存在的项目,否则这条数据将无效,更多项目相关请参见多项目
  • time_free:可选字段,表示不根据事件发生时间过滤该事件。只要出现 time_free 这个 key 且 value 不为 null,将不再校验 time 是否在允许导入的时间范围内。导入历史数据时可能会用到该字段;
  • identities:全域用户关联业务中用户标识字段,可包含多个用户标识,具体可以参考全域用户关联
  • properties:这个事件的具体属性,以 dict 的形式存在。其中以 $ 开头的表明是系统的预置属性,它的类型和中文名已经预先定义好了。
    自定义属性名需要是合法的变量名,不能以数字开头,且只包含:大小写字母、数字、下划线,自定义属性不能以 $ 开头;
    同一个名称的 property,在不同 event 中,必须保持一致的定义和类型;
    同一个名称的 property 大小写不可以相同,如果已经存在小写属性就不可再导入对应大写属性(比如元数据中有 abc 属性名,不能再传 ABC,Abc 等属性名),否则数据会校验失败不入库。

    • $app_version:用户所使用的 App 的版本;
    • $wifi:这条事件发生时,用户是否在使用 wifi;
    • $province、$city:省、市,在没有填充这两个字段的时候,会根据 IP 进行解析;
    • $user_agent:可选参数。如果传入该参数,则解析 User-Agent,解析结果包括:设备制造商、设备型号、操作系统、操作系统版本、浏览器、浏览器版本、爬虫名称(如果是爬虫);
      目前是神策是通过 UA 判断并有一个默认的属性 $bot_name (爬虫名称),对于爬虫种类,不能提前把所有的种类都加进去,主流的爬虫神策都已识别,但是有两种情况无法判断:
      • 第一种:如果 UA 里没有标明、且会触发 JS 脚本的非法爬虫
      • 第二种:如果爬虫没有触发 JS 脚本,那么也不会触发 SDK 的事件采集,所以本身就不会被统计到
    • $screen_width、$screen_height:屏幕的宽和高;
    • product_id、product_name、product_classify、product_price:跟商品相关的一些具体属性。

1.2. 用户关联事件数据举例

这个数据是一个较为复杂的功能,请在使用前先阅读 标识用户,并在必要时联系我们的技术支持人员。


{
	"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
	}
}
CODE

这条数据表示,一个 Android ID 为 0f485d4d12345e5f 的用户,成功完成了注册,注册后的注册 ID 是 130xxxx1234。并且系统后台,会将 Android ID 为 0f485d4d12345e5f 的用户和注册 ID 为 130xxxx1234 的用户,当做同一个用户对待。

需要注意的是,此数据结构中的 distinct_id 和 original_id 为必须字段,其中 disitnct_id 与 login_id 的值相同,original_id 与 anonymous_id 的值相同。


注意:若需要使用该事件,首先需要确认神策系统当前项目的用户关联策略为 全域用户关联,若使用简易用户关联策略,该数据会被整条拒绝。

{
    "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",
    }
}
CODE

这条数据表示,将一个 Android ID 为 3335654b922c4686 和一个邮箱为 test@163.com 尝试进行关联,关联成功后,后续两个 ID 独立上报事件时,在神策系统中都会被当做同一个用户。

注意:若需要使用该事件,首先需要确认神策系统当前项目的用户关联策略为 全域用户关联,若使用简易用户关联策略,该数据会被整条拒绝。

{
    "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",
    }
}
CODE

这条数据表示,将邮箱为 test@163.com 从系统已有用户中解除关联,解除关联成功后,神策系统中,将没有用户关联 test@163.com 邮箱。

1.3. 用户数据举例

更新用户数据的相关操作,主要是用来更新、删除用户的属性

直接设置一个用户属性,如果属性的字段已存在则覆盖,不存在则自动创建。

{
	"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"
	}
}
CODE

与 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"
	}
}
CODE

增加或减少一个用户的某个 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
	}
}
CODE

向某个用户的某个数组类型的属性,追加一个或者多个值。如果本次上传的值,与系统中已存在的值有重复,默认是不会去重的。如果本次上传的值,有重复项,也不会去重的。

{
	"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": ["橘子","西瓜"]
	}
}
CODE

将某个用户的某些属性值设置为空,在上传的数据中,属性的值请设置为非 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
	}
}
CODE

删除一个用户的记录。

{
	"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":{
	}
}
CODE

1.4. 物品数据举例

物品表数据举例,主要是用来增加、删除、更新数据表的相关内容,数据举例如下

创建/更新物品表中的某一行记录,如果该记录已存在则覆盖,不存在则自动创建。

{
	"type":"item_set",
	"item_id":"12",
	"item_type":"dub",
	"project": "ebiz_test",
	"properties":{
		"title":"because of u",
		"sub_title":"st",
		"xxx":"xxx"
	}
}
CODE


删除物品表中的某一行记录。

{
  "type":"item_delete",
  "item_id":"16",
  "item_type":"dub",
  "project": "ebiz_test"
}
CODE


对上述字段的解释如下:

  • type:item_set 表明是创建/更新某一行记录,item_delete 表示删除某一行记录;
  • item_id:表示物品的 id;
  • item_type:表示物品的类型,区分不同的物品表。需是合法的变量名,即不能以数字开头,且只包含:大小写字母、数字、下划线和 $,且 item_type 字段长度最大为 100;
    • 注意,物品表是使用 item_id 和 item_type 作为联合主键;
  • project:这条数据所属项目名,若不指定该参数,则需要使用该字段时取值 default,即默认项目。指定的项目必须是系统中已经存在的项目,否则这条数据将无效,更多项目相关请参见 多项目
  • properties:上报的物品的具体属性,以 dict 的形式存在。属性名需要是合法的变量名,不能以数字开头,且只包含:大小写字母、数字、下划线;

1.5. 小结

数据中 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表,一行记录表示一个物品

2. 属性数据类型

2.1. 属性数据类型自动识别规则

当一个属性在系统中未预先定义,则属性首次导入时,系统会根据首次导入的值,决定该属性在系统中的数据类型。

JSON 中的类型示例值导入后系统识别的数据类型该类型在系统中的相关限制
Number12 或 12.0NUMBER(数值型)-9E15 到 9E15 小数点后最多保留3位
Booltrue 或 falseBOOL(布尔值)
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处理)

2.2. 属性数据类型转化规则

当一个属性在系统中创建,其对应的数据类型就已经确定,后续导入数据时若类型和系统中记录的类型不符,则尝试对数据进行类型转换,若无法转换或转换失败则 该数据会被整条拒绝

尝试进行的类型转换如下(空格表示不进行转换):

原始类型→

目标类型↓

数值型布尔值字符串字符串集合日期时间
数值型
true -> 1; false -> 0空字符串 "" 抛弃该属性; 其他按数值解析

布尔值0 -> false; 非 0 值 -> true
字符串"true"、"false"转换为布尔类型

字符串原值作为字符串原值作为字符串

原值作为字符串,如

["Hello","World"]

原值作为字符串
字符串集合




日期时间在一定区间内的按 UNIX 时间戳的秒或毫秒转换
多种日期时间格式模式串解析

  • 上述表格左侧的列对应目标类型,上方的行对应原始类型。目标类型对应元数据中的数据类型,原始类型是数据上传时的属性值类型
  • 什么时候该使用数值类型的属性:
    • 需要进行聚合运算(例如求和、均值)或者按区间分组的值,典型的比如价格、时长、年龄等。
    • 除非有特殊需求,否则各类 ID(例如订单 ID)不建议作为数值类型存储。

3. 导入数据的限制

3.1. 一般限制

  1. 事件变量名(event 的值)和 属性变量名(properties 中 key 取值)都需是合法的变量名,即不能以数字开头,且只包含:大小写字母、数字、下划线和 $,且事件变量名和属性变量名最大长度都为 100,自定义的事件名或者属性名不能以$ 开头;
  2. 变量名不能与系统中已经存在的虚拟事件、虚拟属性的变量名重复;
  3. 系统对变量名大小写处理有特殊要求,字母内容完全一致,但是大小写不一致的变量名会被拦截;
  4. 类型 type 字段的取值只能是上文列出几种(track, profile_* 等),并且大小写敏感;
  5. 属性 properties 字段必须存在,可以为空( {} );
  6. 自定义的事件或者属性的变量名不可以与系统保留字段重名,本节列出了保留字段。

3.2. 事件时间限制

导入不合理时间的用户事件将影响数据的准确性(如客户端时间错误导致导入未来的数据),故默认情况下对导入的事件时间进行了限制:

  1. 使用客户端 SDK (iOS、Android、Web、小程序等)导入的数据,服务端默认只接收事件发生时间在向前 10 天内和未来向后 1 小时内的数据(相比于系统当前时间);
  2. 使用后端语言 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 时间是准确的,也不会修复事件触发的时间。

3.3. 属性在不同表中的限制

对事件表的属性,一个属性,只能具有一种类型(不同的具体事件,同名属性类型也必须相同);

对用户表的属性,一个属性,只能具有一种类型;

对物品表的属性,一个属性,只能具有一种类型;

对于一个属性名,在事件表、用户表、物品表中可以具有不同的类型。

3.4. 属性长度限制

属性的数据类型,及特殊字段长度限制如下:

项目限制
数据类型 NUMBER-9E15 到 9E15 小数点后最多保留3位

数据类型 STRING

使用 UTF-8 编码后最大长度 1024 字节,超出后系统会进行截断,保留前 1024 字节内容,并正常入库
数据类型 LIST每个 LIST 中最多包含 500 个不大于 255 字节的字符串
用户标识($identity_login_id 等)最大长度 255 字节
distinct_id、original_id最大长度 255 字节

3.5. 属性数上限

单个项目 事件表 / 用户表 / 物品表 的属性建议合理设置,过多影响导入和查询性能,达到上限则会导致导入异常。

建议值硬上限
300 以内2000

3.6. 保留字段

为了保证查询时属性名不与系统变量名冲突,设置如下保留字段,请避免其作为事件名和属性名(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

  • event_id
  • event_bucket
  • day
  • week_id
  • month_id
  • _offset
  • sampling_group
  • _offset
  • first_id_type
  • second_id_type
  • generated_from
  • merged_to
  • item_type
  • item_id