数据格式

最后更新于:2019-01-03 16:12:26

神策分析支持多种不同语言的 SDK,这些 SDK 虽然在外部提供的接口上有所不同,但是在内部实现上都使用统一的数据格式,并且 FormatImporterLogAgentBatchImporterHDFSImporter 都支持直接导入以文件形式存储的符合要求格式的数据,在这里,我们对数据格式进行一个更加细致的描述。

注意:这里描述的是底层数据传输格式的定义,和具体 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。

FAQ: track_signup 的作用是什么?

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 一般限制

  1. 事件名(event 的值)和 属性名(properties 中 key 取值)都需是合法的变量名,即不能以数字开头,且只包含:大小写字母、数字、下划线和$;
  2. 类型 type 字段的取值只能是上文列出几种(track, profile_set 等),并且大小写敏感;
  3. 属性 properties 字段必须存在,可以为空( {} );
  4. 事件 time 字段允许的范围是 1000000000000(2001-09-09 09:46:40) ~ 10000000000000(2286-11-21 01:46:40);
  5. 本节 7.5 列出了保留属性名;

7.2 事件时间限制

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

  1. 使用客户端 SDK (iOS、Android)导入的数据,服务端默认只接收事件发生时间在接收时间向前 10 天内和未来向后 1 小时内的数据;
  2. 使用后端语言 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 的 trackprofile 接口。

属性值含义:true ,表示这条数据中的 distinct_id 字段的值为一个真实的 ID(例如客户的业务 ID),如果在这条数据进入数据库之前,此真实 ID 未和设备 ID 绑定,(绑定操作,前端可参考 login 方法,以 JavaScript SDK 为例。服务端可参考 tracksignup 方法,以 Java SDK 为例,历史数据导入可参考本文档的 tracksignup 接口。),那么这个真实 ID 会自关联,导致之后不能再和设备 ID 绑定。
属性值含义:false,表示这条数据中的 distinct_id 字段的值为一个设备 ID(即客户注册登陆之前的标识用户的 ID),之后此设备 ID 还可以和一个真实 ID 绑定。