# 数据预处理模块

> 本文档所描述的内容属于神策分析的高级使用功能，涉及较多技术细节，适用于对相关功能有经验的用户参考。如果对文档内容有疑惑，请咨询您的数据咨询顾问获取一对一的协助。

## 1. 概述

神策分析从 1.6 开始为用户开放自定义“数据预处理模块”，即为 SDK 等方式接入的数据（不包括批量导入工具方式）提供一个简单的 ETL 流程，使数据接入更加灵活。该功能仅支持私有部署版。

可以使用“数据预处理模块”处理的数据来源包括：

* SDK（各语言 SDK 直接发送的数据，包括可视化埋点的数据。使用 LoggingConsumer 将数据写到文件再使用批量导入工具除外）;
* LogAgent;
* FormatImporter;

数据预处理模块的工作流程如下：

```
                       数据预处理模块
服务端接收到 SDK 数据 -------------------> 导入神策分析的数据
```

数据预处理模块可以：

* 修改数据内容: 添加、删除、修改字段值，修改事件名等;
* 抛弃数据: 处理结果可直接返回 `null` 将一条数据抛弃;
* 添加数据: 由一条数据衍生出多条数据;

&#x20;**由于数据预处理模块可以对数据进行较大改变，请谨慎使用！**&#x20;

&#x20;**处理函数抛异常将导致整条数据被抛弃，请谨慎处理，例如空指针异常。**&#x20;

关于实现“数据预处理模块”可参考 [sensorsdata/ext-processor-sample](https://github.com/sensorsdata/ext-processor-sample) 。

~~**新嵌入神策 App 端 SDK 后，将老用户的设备激活过滤** 的例子可参考~~ [~~sensorsdata/ext-processor-identify-old-users~~](https://github.com/sensorsdata/ext-processor-identify-old-users)~~;~~

~~**在服务端记录激活设备，解决重装后激活数重复计算** 的例子可参考~~ [~~sensorsdata/ext-processor-find-new-user~~](https://github.com/sensorsdata/ext-processor-find-new-user)~~;~~

以上两个示例的功能已经集成到神策分析，示例的代码仅供参考。

本文当将提供一些场景举例。

## 2. 修改数据内容

### 2.1 解析 URL 参数

当一些字段不方便在客户端解析，可通过“数据预处理模块”在服务端解析。

例如 SDK 发来一条数据，传入“数据预处理模块”时格式如下：

```
{
    "distinct_id": "2b0a6f51a3cd6775",
    "time": 1434556935000,
    "type": "track",
    "event": "ViewProduct",
    "project": "sample_project",
    "ip": "123.123.123.123",
    "properties": {
        ...
        "$referrer":"http://www.kbyte.cn/view?title=abc&act=click",
        ...
    }
}
```

现在需将 `$referrer` 中的 `title`，`act` 等字段解析出来并设置为单独字段。

这个需求可通过实现一个“数据预处理模块”来实现，将数据处理成如下结果并返回：

```
{
    "distinct_id": "2b0a6f51a3cd6775",
    "time": 1434556935000,
    "type": "track",
    "event": "ViewProduct",
    "project": "sample_project",
    "properties": {
        ...
        "$referrer":"http://www.kbyte.cn/view?title=abc&act=click",
        "source_title":"abc",
        "source_act":"click",
        ...
    }
}
```

### 2.2 JOIN 外部字段

当期望的数据中有一部分需要在后端才能获取到时，可通过“数据预处理模块” JOIN 补全数据。

例如 SDK 发来一条浏览商品数据，其中包含字段 product\_id (商品 ID)，传入数据如下：

```
{
    "distinct_id": "2b0a6f51a3cd6775",
    "time": 1434556935000,
    "type": "track",
    "event": "ViewProduct",
    "project": "sample_project",
    "ip": "123.123.123.123",
    "properties": {
        ...
        "product_id":"ABCDE-12345",
        ...
    }
}
```

现在需要根据 product\_id (商品 ID) 在服务端查询到商品的中文名，且中文名英文名映射关系存储在 redis 中。

这时可以实现“数据预处理模块”，在处理过程中查询 redis，将查询结果填入数据：

```
{
    "distinct_id": "2b0a6f51a3cd6775",
    "time": 1434556935000,
    "type": "track",
    "event": "ViewProduct",
    "project": "sample_project",
    "properties": {
        ...
        "product_id":"ABCDE-12345",
        "product_name":"唐诗三百首",
        ...
    }
}
```

### 2.3 字段类型修改

神策分析的属性数据类型一旦确定，就无法再修改，可通过“数据预处理模块”对字段进行转换处理。

例如“用户 ID”是一个字符串类型的字段，但某 SDK 误将字段设置为数值类型：

```
{
    "distinct_id": "2b0a6f51a3cd6775",
    "time": 1434556935000,
    "type": "track",
    "event": "ViewProduct",
    "project": "sample_project",
    "ip": "123.123.123.123",
    "properties": {
        ...
        "account_id":1234,
        ...
    }
}
```

这时可实现“数据预处理模块”，将数据转换成：

```
{
    "distinct_id": "2b0a6f51a3cd6775",
    "time": 1434556935000,
    "type": "track",
    "event": "ViewProduct",
    "project": "sample_project",
    "properties": {
        ...
        "account_id":"1234",
        ...
    }
}
```

## 3. 抛弃数据

当一些数据符合期望抛弃的条件，可以直接返回 `null` 将其抛弃。

例如抛弃指定 ip 发来的数据。

## 4. 添加数据

“数据处理模块”输入为一条数据，输出可以是多条数据。

返回多条数据时，返回值需要是一个 JSON 数组，数组中每一个元素都为一条符合 **神策分析** 数据格式的数据。

例如，当用户触发了 ViewProduct 事件后，我们标记其为 VIP 用户，输入数据为：

```
{
    "distinct_id": "2b0a6f51a3cd6775",
    "time": 1434556935000,
    "type": "track",
    "event": "ViewProduct",
    "project": "sample_project",
    "ip": "123.123.123.123",
    "properties": {
        ...
    }
}
```

可以返回多条数据：

```
[
    {
        "distinct_id":"2b0a6f51a3cd6775",
        "time":1434556935000,
        "type":"track",
        "event":"ViewProduct",
        "project": "sample_project",
        "properties":{
            ...
        }
    },
    {
        "distinct_id":"2b0a6f51a3cd6775",
        "type":"profile_set",
        "time":1434556935000,
        "project": "sample_project",
        "properties":{
            "is_vip":true
        }
    }
]
```

## 5. 用户识别并预处理

如果一个 App 在使用神策分析之前已经有很多用户，现在开始使用神策分析并嵌入了客户端 SDK 如 Android 和 iOS SDK，由于如下原因，可能会将老用户误识别为新用户：

嵌入 SDK 后 App 启动时，发送 $AppStart 事件，其中一个属性 $is\_first\_time 用于标记是否第一次使用，这个值是根据 SDK 内的变量判断的，如果老用户的 App 升级到刚嵌入 SDK 的版本，直接使用初始值会导致认为这个用户是新安装的用户。

使用数据预处理模块解决该问题可参考 [sensorsdata/ext-processor-identify-old-users](https://github.com/sensorsdata/ext-processor-identify-old-users)。
