数据格式
最后更新于:2019-01-03 16:12:26
神策分析支持多种不同语言的 SDK,这些 SDK 虽然在外部提供的接口上有所不同,但是在内部实现上都使用统一的数据格式,并且 FormatImporter 、LogAgent 、BatchImporter 和 HDFSImporter 都支持直接导入以文件形式存储的符合要求格式的数据,在这里,我们对数据格式进行一个更加细致的描述。
注意:这里描述的是底层数据传输格式的定义,和具体 SDK 的调用接口无关
注意:$is_login_id 参数说明
1. 数据整体格式
日志文件是一行一个 JSON,物理上对应一条数据,逻辑上对应一个描述了用户行为的事件,或是描述一个或多个用户属性的 Profile 操作。
2. track:
记录一个 Event 及关联的 Properties。
数据样例:
{
"distinct_id": "123456",
"time": 1434556935000,
"type": "track",
"event": "ViewProduct",
"project": "ebiz_test",
"time_free": true,
"properties": {
"$is_login_id":true, //此参数请慎重使用,详细介绍请参考文档底部 8.1 $is_login_id 参数说明
"$app_version":"1.3",
"$wifi":true,
"$ip":"180.79.35.65",
"$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":640,
"product_id":12345,
"product_name":"苹果",
"product_classify":"水果",
"product_price":14.0
}
}
对于上述字段的说明如下:
- distinct_id:
类型是字符串
,对用户的标识,对未登录用户,可以填充设备标识、CookieID 等,对于登录用户,则应该填充注册账号;这里的例子,我们假设是一个未注册用户,所以填充的是一个设备编号; - time:
类型是数值
,事件发生的实际时间戳,精确到毫秒; - type:track 表明是记录一个 Event ,这里我们假设是一个商品浏览行为;
- event:事件名,需是合法的变量名,即不能以数字开头,且只包含:大小写字母、数字、下划线和 $;
- project:这条数据所属项目名,若不指定该参数,则需要使用该字段时取值
default
,即默认项目。指定的项目必须是系统中已经存在的项目,否则这条数据将无效,更多项目相关请参见多项目; - time_free:可选字段,表示不根据事件发生时间过滤该事件。只要出现 time_free 这个 key 且 value 不为 null,将不再校验 time 是否在允许导入的时间范围内。导入历史数据时可能会用到该字段;
- properties:这个 Event 的具体属性,以 dict 的形式存在。其中以$开头的表明是系统的保留字段,它的类型和中文名已经预先定义好了,自定义属性请不要以 $ 开头;同一个名称的 property,在不同 event 中,必须保持一致的定义和类型;同一个名称的 property 大小写敏感,如果已经存在小写属性就不可再导入对应大写属性,反之亦然。
- $is_login_id:distinct_id 对应的是否是一个注册 ID;
- $app_version:用户所使用的 App 的版本;
- $wifi:这条事件发生时,用户是否在使用 wifi;
- $ip:用户使用设备的 IP。若数据中出现 $ip,且数据中没有 $province 或 $city 字段,将使用该 IP 解析出省市信息填入缺失字段;
- $province、$city:省、市,在没有填充这两个字段的时候,会根据 IP 进行解析;
- $user_agent:可选参数。如果传入该参数,则解析 User-Agent,解析结果包括:设备制造商、设备型号、操作系统、操作系统版本、浏览器、浏览器版本、爬虫名称(如果是爬虫);
- $screen_width、$screen_height:屏幕的宽和高;
- product_id、product_name、product_classify、product_price:跟商品相关的一些具体属性。
3. track_signup:
这个接口是一个较为复杂的功能,请在使用前先阅读相关说明,并在必要时联系我们的技术支持人员。
数据样例:
{
"distinct_id":"12345",
"original_id":"2b0a6f51a3cd6775",
"time": 1434557935000,
"type": "track_signup",
"event": "$SignUp",
"project": "ebiz_test",
"properties": {
"$manufacturer":"Apple",
"$model": "iPhone 5",
"$os":"iOS",
"$os_version":"7.0",
"$app_version":"1.3",
"$wifi":true,
"$ip":"180.79.35.65",
"$province":"湖南",
"$city":"长沙",
"$screen_width":320,
"$screen_height":640
}
}
这条数据表示,一个匿名 ID 为 2b0a6f51a3cd6775 的用户,成功完成了注册,注册后的注册 ID 是 12345。并且系统后台,会将 distinct_id 为 2b0a6f51a3cd6775 的用户和 distinct_id 为 12345 的用户,当做同一个用户对待。需要注意的是,此接口中的 original_id 为必须字段,表示与注册 ID 进行关联的匿名 ID。
4. Profile 相关操作
Profile 相关操作,主要是用来设置用户的 Profile 的,提供了如下一系列接口:
4.1 profile_set:
直接设置一个用户的 Profile,如果用户或者 Profile 的字段已存在,则覆盖,不存在则自动创建。
数据样例:
{
"distinct_id": "12345",
"type": "profile_set",
"time": 1435290195610,
"project": "ebiz_test",
"properties": {
"$province":"湖南",
"FavoriteFruits": [
"苹果","香蕉","芒果"
],
"Age":33,
"$city":"长沙",
"IncomeLevel": "3000~5000",
"$name": "小明",
"Gender":"男",
"$signup_time": "2015-06-26 11:43:15.610"
}
}
4.2 profile_set_once
直接设置一个用户的 Profile。与 profile_set 接口不同的是,如果用户或者 Profile 的字段已存在,则这条记录会被忽略而不会覆盖已有数据,如果 Profile 不存在则会自动创建。因此,profile_set_once 比较适用于为用户设置首次激活时间、首次注册时间等只在首次设置时有效的属性。
数据样例:
{
"distinct_id": "12345",
"type": "profile_set_once",
"time": 1435290195610,
"project": "ebiz_test",
"properties": {
"$province":"湖南",
"FavoriteFruits": [
"苹果","香蕉","芒果"
],
"Age":33,
"$city":"长沙",
"IncomeLevel": "3000~5000",
"$name": "小明",
"Gender":"男",
"$signup_time": "2015-06-26 11:43:15.610"
}
}
4.3 profile_increment:
增加或减少一个用户的某个 Numeric 类型的 Profile,如果用户不存在则自动创建, Profile 不存在则默认为 0。
数据样例:
{
"distinct_id": "12345",
"type": "profile_increment",
"time": 1435290200354,
"project": "ebiz_test",
"properties": {
"Age": 1
}
}
4.4 profile_delete:
删除一个用户的整个 Profile。
数据样例:
{
"distinct_id": "12345",
"type": "profile_delete",
"time": 1437290200354,
"project": "ebiz_test",
"properties":{
}
}
4.5 profile_append:
向某个用户的某个数组类型的 Profile 添加一个或者多个值。
数据样例:
{
"distinct_id": "12345",
"type": "profile_append",
"time": 1437280200354,
"project": "ebiz_test",
"properties": {
"FavoriteFruits": [
"橘子","西瓜"
]
}
}
4.6 profile_unset:
将某个用户的某些属性值设置为空。另外,为了与其它接口保持一致,在提交的数据上,属性的值请设置为非 null 的任何值,例如 true。
数据样例:
{
"distinct_id":"12345",
"type":"profile_unset",
"time":1437280200354,
"project": "ebiz_test",
"properties":{
"Age":true,
"FavoriteFruits":true
}
}
5. 属性数据类型
5.1 注意问题
- 发送端使用 JSON 作为数据传输格式,本系统以 JSON 数据类型为基础再加以额外限制,定义了若干种数据类型,但 不与 JSON 类型完全等价,详见后文属性类型说明。
- (table_name, property_name) 二元组唯一确定一个属性,table_name 为数据表的表名,如 users、events,可在自定义 SQL 查询中看到。这意味着同表同名属性类型必须相同,而不同表的同名属性类型可以不同。消息类型与所写入的数据表的关系如下表:
消息类型 | 目标数据表 |
---|---|
track | events |
profile_* | users |
track_signup | 事件信息被写入 events 表,users 表中会记录 first_id 和 second_id |
- 一个属性的类型由首次导入时的类型决定,后续导入时若类型不符,则尝试对数据进行类型转换,若无法转换或转换失败则输入数据会被整条拒绝。尝试进行的类型转换如下(空格不进行转换):
目标类型\原始类型 | 数值型 | 布尔值 | 字符串 | 字符串集合 | 日期时间 |
---|---|---|---|---|---|
数值型 | true -> 1; false -> 0 | 空字符串 "" 抛弃该属性; 其他按数值解析 | |||
布尔值 | 0 -> false; 非 0 值 -> true | 字符串"true"、"false"转换为布尔类型 | |||
字符串 | 原值作为字符串 | 原值作为字符串 | 原值作为字符串 | 原值作为字符串 | |
字符串集合 | |||||
日期时间 | 在一定区间内的按 UNIX 时间戳的秒或毫秒转换 | 多种日期时间格式模式串解析 |
- 什么时候该使用数值类型的属性:
- 需要进行聚合运算(例如求和、均值)或者按区间分组的值,典型的比如价格、时长、年龄等。
- 除非有特殊需求,否则各类 ID(例如订单 ID)不建议作为数值类型存储。
5.2 神策分析属性数据类型定义
神策分析数据类型 | 中文名 | 基础 JSON 类型 | 额外限制 | 示例 | 说明 |
---|---|---|---|---|---|
NUMBER | 数值型 | number | -9E15 到 9E15 小数点后最多保留3位 | 12 或 12.0 | |
BOOL | 布尔值 | bool | 无 | true 或 false | |
STRING | 字符串 | string | 使用 UTF-8 编码后最大长度 1024 字节,如需调整最大长度可以联系我们 | "SensorsData" | |
LIST | 字符串数组 | list | 字符串元素的数组,最大元素个数为 500,其中每个元素使用 UTF-8 编码后最大长度 255 字节;注意:1.12 之前的版本,在入库时会进行重排序和去重,1.12 及之后的版本默认保留原始数据的顺序。超过最大元素个数时,新入库的元素会淘汰最早入库的元素。 | ||
DATETIME | 日期时间 | string | yyyy-MM-dd HH:mm:ss.SSS 或 yyyy-MM-dd HH:mm:ss 或 yyyy-mm-dd (时分秒按00:00:00处理), 建议使用第一种,其中 SSS 为毫秒;年取值范围是 [1900, 2099] |
"2015-06-19 17:51:21.234" "2015-06-19 17:51:21" "2015-06-19" |
有些语言的时间格式化库不直接提供毫秒格式化,如 Python 提供 us 而不提供 ms,如需自行编写程序生成数据请注意这点。 |
DATE (仅 <= 1.10 版本) | 日期 | string | 格式为 yyyy-mm-dd,年取值范围是 [1900, 2099] | "2015-06-19" | 自 1.11 版本起,DATE 类型将作为 DATETIME 的一种格式存在(既时分秒为 00:00:00 的 DATETIME,且时分秒可省略),不再单设类型。 |
6. 预置属性
为了帮助使用者更方便地使用我们的产品,我们目前分别为 Event 和 Profile 提供了一些预置字段。
注意: JavaScript SDK 预置属性较多,这里没有全部列出,详细说明请参考相关文档 《JavaScript SDK》
Event 的预置字段有:
字段名称 | 类型 | 说明 | JS SDK 自动采集 | iOS SDK 自动采集 | Android SDK 自动采集 | 小程序 SDK 自动采集 | 服务端 SDK 自动采集 |
---|---|---|---|---|---|---|---|
$app_version | 字符串 | 应用的版本 | N | Y | Y | N | N |
$country | 字符串 | 国家 | Y | Y | Y | Y | N |
$city | 字符串 | 城市 | Y | Y | Y | Y | N |
$province | 字符串 | 省份 | Y | Y | Y | Y | N |
$lib | 字符串 | SDK类型,例如python、iOS等 | Y | Y | Y | Y | Y |
$lib_version | 字符串 | SDK版本 | Y | Y | Y | Y | Y |
$manufacturer | 字符串 | 设备制造商,例如Apple | Y | Y | Y | N | N |
$model | 字符串 | 设备型号,例如iphone6 | Y | Y | Y | Y | N |
$os | 字符串 | 操作系统,例如iOS | Y | Y | Y | Y | N |
$os_version | 字符串 | 操作系统版本,例如8.1.1 | Y | Y | Y | Y | N |
$screen_height | 数值 | 屏幕高度,例如1920 | Y | Y | Y | Y | N |
$screen_width | 数值 | 屏幕宽度,例如1080 | Y | Y | Y | Y | N |
$wifi | BOOL | 是否使用wifi,例如true | N | Y | Y | N | N |
$browser | 字符串 | 浏览器名,例如Chrome | Y | N | N | N | N |
$browser_version | 字符串 | 浏览器版本,例如Chrome 45 | Y | N | N | N | N |
$carrier | 字符串 | 运营商名称,例如ChinaNet | N | Y | Y | N | N |
$network_type | 字符串 | 网络类型,例如4G | N | Y | Y | Y | N |
$utm_matching_type | 字符串 | 渠道追踪匹配模式 | N | Y1 | Y1 | N | N |
$latest_referrer | 字符串 | 站外前向地址 | Y | N | N | N | N |
$latest_referrer_host | 字符串 | 站外前向域名 | Y | N | N | N | N |
$latest_utm_source | 字符串 | 最近广告系列来源 | Y | N | N | Y5 | N |
$ latest_utm_medium | 字符串 | 最近广告系列媒介 | Y | N | N | Y5 | N |
$ latest_utm_term | 字符串 | 最近广告系列字词 | Y | N | N | Y5 | N |
$ latest_utm_content | 字符串 | 最近广告系列内容 | Y | N | N | Y5 | N |
$ latest_utm_campaign | 字符串 | 最近广告系列名称 | Y | Y | N | Y5 | N |
$ latest_search_keyword | 字符串 | 最近一次搜索引擎关键词 | Y | N | N | N | N |
$ latest_traffic_source_type | 字符串 | 最近一次流量来源类型 | Y | N | N | N | N |
$is_first_day | 布尔值 | 是否首日访问 | Y | Y | Y | Y | N |
$device_id | 字符串 | 设备ID | Y6 | Y | Y | N | N |
Profile 的预置字段有:
字段名称 | 类型 | 说明 | JS SDK 自动采集 | iOS SDK 自动采集 | Android SDK 自动采集 | 小程序 SDK 自动采集 | 服务端 SDK 自动采集 |
---|---|---|---|---|---|---|---|
$city | 字符串 | 用户所在的城市 | N | N | N | N | N |
$province | 字符串 | 用户所在的省份 | N | N | N | N | N |
$name | 字符串 | 用户名 | N | N | N | N | N |
$signup_time | Datetime | 注册时间 | N | N | N | N | N |
$utm_matching_type | 字符串 | 渠道追踪匹配模式 | N | Y1 | Y1 | N | N |
$first_visit_time | Datetime | 首次访问时间 | Y3 | Y4 | Y4 | N | N |
$first_referrer | 字符串 | 首次前向地址 | Y3 | N | N | N | N |
$first_referrer_host | 字符串 | 首次前向域名 | Y3 | N | N | N | N |
$first_browser_language | 字符串 | 首次使用的浏览器语言 | Y3 | N | N | N | N |
$first_browser_charset | 字符串 | 首次浏览器字符类型(1.8支持) | Y3 | N | N | N | N |
$first_search_keyword | 字符串 | 首次搜索引擎关键词(1.8支持) | Y3 | N | N | N | N |
$first_traffic_source_type | 字符串 | 首次流量来源类型(1.8支持) | Y3 | N | N | N | N |
$utm_source | 字符串 | 首次广告系列来源 | Y2 | Y1 | Y1 | Y | N |
$utm_medium | 字符串 | 首次广告系列媒介 | Y2 | Y1 | Y1 | Y | N |
$utm_term | 字符串 | 首次广告系列字词 | Y2 | Y1 | Y1 | Y | N |
$utm_content | 字符串 | 首次广告系列内容 | Y2 | Y1 | Y1 | Y | N |
$utm_campaign | 字符串 | 首次广告系列名称 | Y2 | Y1 | Y1 | Y | N |
备注:
- 1:
$utm_
开头的相关属性,由 App 渠道追踪功能自动采集,请参考相关文档 《App 渠道追踪》。 - 2: 新用户首次访问时,需要开启 autoTrack 且URL中带有UTM参数,才会收集,请参考相关文档 《JavaScript SDK》。
- 3: 新用户首次访问时,开启 autoTrack才会收集,请参考相关文档 《JavaScript SDK》。
- 4: 新用户首次启动App,如果调用了trackInstallation接口会自动给这个属性赋值。
- 5: 小程序sdk1.3版本支持
- 6: 如果sdk初始化时候配置了is_track_device_id:true,就会采集
6.1 预置属性采集方式
- iOS 中,预置属性的采集方式大致如下:
NSMutableDictionary *p = [NSMutableDictionary dictionary];
UIDevice *device = [UIDevice currentDevice];
NSString *deviceModel = [self deviceModel];
struct CGSize size = [UIScreen mainScreen].bounds.size;
CTCarrier *carrier = [[[CTTelephonyNetworkInfo alloc] init] subscriberCellularProvider];
// Use setValue semantics to avoid adding keys where value can be nil.
[p setValue:[[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] forKey:@"$app_version"];
[p setValue:carrier.carrierName forKey:@"$carrier"];
[p addEntriesFromDictionary:@{
@"$lib": @"iOS",
@"$lib_version": [self libVersion],
@"$manufacturer": @"Apple",
@"$os": [device systemName],
@"$os_version": [device systemVersion],
@"$model": deviceModel,
@"$screen_height": @((NSInteger)size.height),
@"$screen_width": @((NSInteger)size.width),
}];
- Android 中,预置属性的采集方式大致如下:
{
deviceInfo.put("$lib", "Android");
deviceInfo.put("$lib_version", VERSION);
deviceInfo.put("$os", "Android");
deviceInfo.put("$os_version",
Build.VERSION.RELEASE == null ? "UNKNOWN" : Build.VERSION.RELEASE);
deviceInfo
.put("$manufacturer", Build.MANUFACTURER == null ? "UNKNOWN" : Build.MANUFACTURER);
deviceInfo.put("$model", Build.MODEL == null ? "UNKNOWN" : Build.MODEL);
try {
final PackageManager manager = mContext.getPackageManager();
final PackageInfo info = manager.getPackageInfo(mContext.getPackageName(), 0);
deviceInfo.put("$app_version", info.versionName);
} catch (final PackageManager.NameNotFoundException e) {
Log.e(LOGTAG, "Exception getting app version name", e);
}
final DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
deviceInfo.put("$screen_height", displayMetrics.heightPixels);
deviceInfo.put("$screen_width", displayMetrics.widthPixels);
TelephonyManager telephonyManager = (TelephonyManager) mContext.getSystemService(Context
.TELEPHONY_SERVICE);
String operatorString = telephonyManager.getSimOperator();
if (operatorString == null) {
// DO NOTHING
} else if (operatorString.equals("46000") || operatorString.equals("46002")) {
deviceInfo.put("$carrier", "中国移动");
} else if (operatorString.equals("46001")) {
deviceInfo.put("$carrier", "中国联通");
} else if (operatorString.equals("46003")) {
deviceInfo.put("$carrier", "中国电信");
} else {
deviceInfo.put("$carrier", "其他");
}
}
7. 限制
7.1 一般限制
- 事件名(event 的值)和 属性名(properties 中 key 取值)都需是合法的变量名,即不能以数字开头,且只包含:大小写字母、数字、下划线和$;
- 类型 type 字段的取值只能是上文列出几种(track, profile_set 等),并且大小写敏感;
- 属性 properties 字段必须存在,可以为空( {} );
- 事件 time 字段允许的范围是 1000000000000(2001-09-09 09:46:40) ~ 10000000000000(2286-11-21 01:46:40);
- 本节 7.5 列出了保留属性名;
7.2 事件时间限制
导入不合理时间的用户事件将影响数据的准确性(如客户端时间错误导致导入未来的数据),故默认情况下对导入的事件时间进行了限制:
- 使用客户端 SDK (iOS、Android)导入的数据,服务端默认只接收事件发生时间在接收时间向前 10 天内和未来向后 1 小时内的数据;
- 使用后端语言 SDK (如 Java、Python 等)或导入工具(如 LogAgent、BatchImporter、HdfsImporter),默认只能导入事件时间当前向前 2 年内和未来向后 1 小时内的数据;
如果希望导入上述默认时间窗口之外的数据,可以联系值班同学修改窗口限制,或在数据中添加 time_free
字段(见本文档 2. track 样例)。
7.3 同名属性同类型
- 对 Event 属性,一个属性名,只能具有一种类型(不同的具体事件,同名属性类型也必须相同);
- 对 Profile 属性,一个属性名,只能具有一种类型;
- 对于一个属性名,在 Event 和 Profile 中可以具有不同的类型;
7.4 属性长度限制
属性的数据类型,及特殊字段长度限制如下:
项目 | 限制 |
---|---|
数据类型 NUMBER | -9E15 到 9E15 小数点后最多保留3位 |
数据类型 STRING | 最大长度 1024 字节 |
数据类型 LIST | 每个 LIST 中最多包含 100 个不大于 255 字节的字符串 |
用户 distinct_id | 最大长度 255 字节 |
用户 original_id | 最大长度 255 字节 |
7.5 属性数上限
Event / Profile 的属性建议合理设置,过多影响导入和查询性能,达到上限则导致导入异常。
建议值 | 硬上限 |
---|---|
300 以内 | 2000 |
7.6 保留属性名
为了保证查询时属性名不与系统变量名冲突,设置如下保留属性名,请避免其作为属性名(properties 中的 key)使用:
date
datetime
distinct_id
event
events
first_id
id
original_id
device_id
properties
second_id
time
user_id
users
8. 常见问题
8.1 $is_login_id 参数说明(布尔类型)
常见使用场景:历史数据导入的 events 或者 users 数据,服务端 SDK 的 track 或 profile 接口。
属性值含义:true ,表示这条数据中的 distinct_id 字段的值为一个真实的 ID(例如客户的业务 ID),如果在这条数据进入数据库之前,此真实 ID 未和设备 ID 绑定,(绑定操作,前端可参考 login 方法,以 JavaScript SDK 为例。服务端可参考 tracksignup 方法,以 Java SDK 为例,历史数据导入可参考本文档的 tracksignup 接口。),那么这个真实 ID 会自关联,导致之后不能再和设备 ID 绑定。
属性值含义:false,表示这条数据中的 distinct_id 字段的值为一个设备 ID(即客户注册登陆之前的标识用户的 ID),之后此设备 ID 还可以和一个真实 ID 绑定。