# TikTok Data Field Mapping Documentation
Complete reference for mapping Bitable fields to aPaaS chart components.
## Table of Contents
1. [Overview](#overview)
2. [Bitable Data Structure](#bitable-data-structure)
3. [Field Mappings](#field-mappings)
4. [Data Transformations](#data-transformations)
5. [Calculated Fields](#calculated-fields)
6. [Chart-Specific Mappings](#chart-specific-mappings)
7. [Expression Reference](#expression-reference)
---
## Overview
This document provides detailed field mapping information for integrating TikTok analytics data from Bitable into aPaaS chart components.
### Project Context
- **Base app_token**: `C8kmbTsqoa6rBesTKRpl8nV8gHd`
- **Table ID**: `tblG4uuUvbwfvI9Z`
- **Table Name**: TikTok L'AURA - Candle
- **Record Count**: 150 videos
- **Data Source**: TikTok Analytics
---
## Bitable Data Structure
### API Response Format
When fetching data from Bitable, the response follows this structure:
```json
{
"code": 0,
"msg": "success",
"data": {
"has_more": false,
"page_token": "",
"total": 150,
"items": [
{
"record_id": "recXXXXXXXXXXXXXX",
"fields": {
"Date": 1698768000000,
"Views": 15234,
"Likes": 1823,
"Comments": 342,
"Shares": 189,
"Followers": 25678,
"Engagement_Rate": 15.2
}
}
]
}
}
```
### Path Structure
- **Root Data**: `response.data`
- **Items Array**: `response.data.items`
- **Individual Record**: `response.data.items[0]`
- **Record Fields**: `response.data.items[0].fields`
- **Specific Field**: `response.data.items[0].fields.Views`
---
## Field Mappings
### Core Fields
#### 1. Date
**Bitable Field**: `Date`
**Field Type**: Number (Unix timestamp in milliseconds)
**Sample Value**: `1698768000000`
**Data Type**: Timestamp
**Access Paths**:
```javascript
// In data request response
response.data.items[0].fields.Date
// In aPaaS expression
{{getTikTokData.data.items[0].fields.Date}}
```
**Transformations**:
```javascript
// Convert to JavaScript Date object
new Date(item.fields.Date)
// Format as YYYY-MM-DD
new Date(item.fields.Date).toISOString().split('T')[0]
// Format as readable string
new Date(item.fields.Date).toLocaleDateString('en-US', {
year: 'numeric',
month: 'short',
day: 'numeric'
})
// Format for chart axis (MMM DD)
new Date(item.fields.Date).toLocaleDateString('en-US', {
month: 'short',
day: 'numeric'
})
```
**Usage in Charts**:
- **X-Axis**: Time series charts (line, bar)
- **Filter**: Date range selection
- **Sort**: Chronological ordering
- **Group By**: Daily, weekly, monthly aggregations
---
#### 2. Views
**Bitable Field**: `Views`
**Field Type**: Number
**Sample Value**: `15234`
**Data Type**: Integer
**Access Paths**:
```javascript
// Single value
item.fields.Views
// In expression
{{getTikTokData.data.items[0].fields.Views}}
```
**Aggregations**:
```javascript
// Sum of all views
{{getTikTokData.data.items.reduce((sum, i) => sum + (i.fields.Views || 0), 0)}}
// Average views
{{getTikTokData.data.items.reduce((sum, i) => sum + (i.fields.Views || 0), 0) / getTikTokData.data.items.length}}
// Maximum views
{{Math.max(...getTikTokData.data.items.map(i => i.fields.Views || 0))}}
// Minimum views
{{Math.min(...getTikTokData.data.items.map(i => i.fields.Views || 0))}}
```
**Formatting**:
```javascript
// Compact notation (15.2K)
Intl.NumberFormat('en-US', {
notation: 'compact',
maximumFractionDigits: 1
}).format(value)
// With thousands separator (15,234)
Intl.NumberFormat('en-US').format(value)
// Custom format
value.toLocaleString('en-US')
```
**Usage in Charts**:
- **Metric Card**: Total views, average views
- **Line Chart**: Views trend over time
- **Bar Chart**: Daily/weekly views comparison
- **Table**: Individual video views
---
#### 3. Likes
**Bitable Field**: `Likes`
**Field Type**: Number
**Sample Value**: `1823`
**Data Type**: Integer
**Access Paths**:
```javascript
item.fields.Likes
{{getTikTokData.data.items[0].fields.Likes}}
```
**Aggregations**:
```javascript
// Total likes
{{getTikTokData.data.items.reduce((sum, i) => sum + (i.fields.Likes || 0), 0)}}
// Like rate (likes per view)
{{(item.fields.Likes / item.fields.Views * 100).toFixed(2)}}%
```
**Usage in Charts**:
- **Metric Card**: Total likes
- **Bar Chart**: Engagement breakdown
- **Pie Chart**: Engagement distribution
- **Table**: Per-video likes
---
#### 4. Comments
**Bitable Field**: `Comments`
**Field Type**: Number
**Sample Value**: `342`
**Data Type**: Integer
**Access Paths**:
```javascript
item.fields.Comments
{{getTikTokData.data.items[0].fields.Comments}}
```
**Aggregations**:
```javascript
// Total comments
{{getTikTokData.data.items.reduce((sum, i) => sum + (i.fields.Comments || 0), 0)}}
// Comment rate
{{(item.fields.Comments / item.fields.Views * 100).toFixed(2)}}%
```
**Usage in Charts**:
- **Bar Chart**: Engagement breakdown
- **Pie Chart**: Engagement distribution
- **Table**: Per-video comments
---
#### 5. Shares
**Bitable Field**: `Shares`
**Field Type**: Number
**Sample Value**: `189`
**Data Type**: Integer
**Access Paths**:
```javascript
item.fields.Shares
{{getTikTokData.data.items[0].fields.Shares}}
```
**Aggregations**:
```javascript
// Total shares
{{getTikTokData.data.items.reduce((sum, i) => sum + (i.fields.Shares || 0), 0)}}
// Share rate
{{(item.fields.Shares / item.fields.Views * 100).toFixed(2)}}%
```
**Usage in Charts**:
- **Bar Chart**: Engagement breakdown
- **Pie Chart**: Engagement distribution
- **Table**: Per-video shares
---
#### 6. Followers
**Bitable Field**: `Followers`
**Field Type**: Number
**Sample Value**: `25678`
**Data Type**: Integer
**Access Paths**:
```javascript
item.fields.Followers
{{getTikTokData.data.items[0].fields.Followers}}
```
**Special Handling**:
```javascript
// Get latest follower count (last record)
{{getTikTokData.data.items[getTikTokData.data.items.length - 1].fields.Followers}}
// Follower growth (last - first)
{{getTikTokData.data.items[getTikTokData.data.items.length - 1].fields.Followers -
getTikTokData.data.items[0].fields.Followers}}
```
**Usage in Charts**:
- **Metric Card**: Current followers, follower growth
- **Line Chart**: Follower trend
- **Table**: Followers at time of video
---
#### 7. Engagement_Rate
**Bitable Field**: `Engagement_Rate`
**Field Type**: Number (Percentage)
**Sample Value**: `15.2`
**Data Type**: Float
**Access Paths**:
```javascript
item.fields.Engagement_Rate
{{getTikTokData.data.items[0].fields.Engagement_Rate}}
```
**Aggregations**:
```javascript
// Average engagement rate
{{getTikTokData.data.items.reduce((sum, i) => sum + (i.fields.Engagement_Rate || 0), 0) /
getTikTokData.data.items.length}}
// Formatted with percentage
{{value.toFixed(2)}}%
```
**Usage in Charts**:
- **Metric Card**: Average engagement rate
- **Line Chart**: Engagement trend
- **Table**: Per-video engagement
---
## Data Transformations
### Date Transformations
```javascript
// Unix timestamp to Date object
const dateObj = new Date(timestamp);
// Date to ISO string (YYYY-MM-DD)
dateObj.toISOString().split('T')[0]
// Date to locale string
dateObj.toLocaleDateString('en-US', {
year: 'numeric',
month: 'short',
day: 'numeric'
})
// Date to short format (MMM DD)
dateObj.toLocaleDateString('en-US', {
month: 'short',
day: 'numeric'
})
// Get month name
dateObj.toLocaleDateString('en-US', { month: 'long' })
// Get week number
Math.ceil((dateObj.getDate() + 6 - dateObj.getDay()) / 7)
```
### Number Transformations
```javascript
// Compact notation (1.5K, 2.3M)
new Intl.NumberFormat('en-US', {
notation: 'compact',
maximumFractionDigits: 1
}).format(number)
// Standard with separators (15,234)
new Intl.NumberFormat('en-US').format(number)
// Percentage with 2 decimals
(number).toFixed(2) + '%'
// Round to nearest thousand
Math.round(number / 1000) * 1000
// Format as K/M
if (number >= 1000000) {
(number / 1000000).toFixed(1) + 'M'
} else if (number >= 1000) {
(number / 1000).toFixed(1) + 'K'
} else {
number.toString()
}
```
### Array Transformations
```javascript
// Map to array of values
items.map(i => i.fields.Views)
// Filter by condition
items.filter(i => i.fields.Views > 10000)
// Sort by field
items.sort((a, b) => b.fields.Views - a.fields.Views)
// Top N records
items.sort((a, b) => b.fields.Views - a.fields.Views).slice(0, 10)
// Group by month
items.reduce((groups, item) => {
const month = new Date(item.fields.Date).getMonth();
if (!groups[month]) groups[month] = [];
groups[month].push(item);
return groups;
}, {})
```
---
## Calculated Fields
### Total Engagement
**Description**: Sum of likes, comments, and shares
**Formula**:
```javascript
{{item.fields.Likes + item.fields.Comments + item.fields.Shares}}
```
**Usage**:
```javascript
// For single record
const totalEngagement = item.fields.Likes + item.fields.Comments + item.fields.Shares;
// Aggregate across all records
{{getTikTokData.data.items.reduce((sum, i) =>
sum + (i.fields.Likes || 0) + (i.fields.Comments || 0) + (i.fields.Shares || 0), 0)}}
```
### Engagement Per Follower
**Description**: Total engagement divided by followers
**Formula**:
```javascript
{{(item.fields.Likes + item.fields.Comments + item.fields.Shares) / item.fields.Followers * 100}}
```
**Formatted**:
```javascript
{{((item.fields.Likes + item.fields.Comments + item.fields.Shares) /
item.fields.Followers * 100).toFixed(2)}}%
```
### Like Rate
**Description**: Percentage of views that resulted in likes
**Formula**:
```javascript
{{item.fields.Likes / item.fields.Views * 100}}
```
**Formatted**:
```javascript
{{(item.fields.Likes / item.fields.Views * 100).toFixed(2)}}%
```
### Comment Rate
**Description**: Percentage of views that resulted in comments
**Formula**:
```javascript
{{item.fields.Comments / item.fields.Views * 100}}
```
### Share Rate
**Description**: Percentage of views that resulted in shares
**Formula**:
```javascript
{{item.fields.Shares / item.fields.Views * 100}}
```
### Average Engagement Rate
**Description**: Mean engagement rate across all videos
**Formula**:
```javascript
{{getTikTokData.data.items.reduce((sum, i) => sum + (i.fields.Engagement_Rate || 0), 0) /
getTikTokData.data.items.length}}
```
---
## Chart-Specific Mappings
### Metric Cards
#### Total Views Card
```javascript
{
"valueExpression": "{{getTikTokData.data.items.reduce((sum, item) => sum + (item.fields.Views || 0), 0)}}",
"format": "compact"
}
```
#### Total Likes Card
```javascript
{
"valueExpression": "{{getTikTokData.data.items.reduce((sum, item) => sum + (item.fields.Likes || 0), 0)}}",
"format": "compact"
}
```
#### Average Engagement Rate Card
```javascript
{
"valueExpression": "{{getTikTokData.data.items.reduce((sum, item) => sum + (item.fields.Engagement_Rate || 0), 0) / getTikTokData.data.items.length}}",
"suffix": "%",
"format": "0.00"
}
```
#### Total Followers Card
```javascript
{
"valueExpression": "{{getTikTokData.data.items[getTikTokData.data.items.length - 1].fields.Followers}}",
"format": "compact"
}
```
---
### Line Chart (Views & Engagement)
```javascript
{
"xAxis": {
"dataExpression": "{{getTikTokData.data.items.map(i => new Date(i.fields.Date))}}"
},
"series": [
{
"name": "Views",
"dataExpression": "{{getTikTokData.data.items.map(i => i.fields.Views)}}"
},
{
"name": "Engagement Rate",
"dataExpression": "{{getTikTokData.data.items.map(i => i.fields.Engagement_Rate)}}"
}
]
}
```
---
### Bar Chart (Engagement Breakdown)
```javascript
{
"xAxis": {
"dataExpression": "{{getTikTokData.data.items.slice(0, 30).map(i => new Date(i.fields.Date).toLocaleDateString())}}"
},
"series": [
{
"name": "Likes",
"dataExpression": "{{getTikTokData.data.items.slice(0, 30).map(i => i.fields.Likes)}}"
},
{
"name": "Comments",
"dataExpression": "{{getTikTokData.data.items.slice(0, 30).map(i => i.fields.Comments)}}"
},
{
"name": "Shares",
"dataExpression": "{{getTikTokData.data.items.slice(0, 30).map(i => i.fields.Shares)}}"
}
]
}
```
---
### Pie Chart (Engagement Distribution)
```javascript
{
"data": [
{
"name": "Likes",
"valueExpression": "{{getTikTokData.data.items.reduce((sum, i) => sum + (i.fields.Likes || 0), 0)}}"
},
{
"name": "Comments",
"valueExpression": "{{getTikTokData.data.items.reduce((sum, i) => sum + (i.fields.Comments || 0), 0)}}"
},
{
"name": "Shares",
"valueExpression": "{{getTikTokData.data.items.reduce((sum, i) => sum + (i.fields.Shares || 0), 0)}}"
}
]
}
```
---
### Table (Video Performance)
```javascript
{
"columns": [
{
"field": "Date",
"fieldPath": "fields.Date",
"format": "YYYY-MM-DD"
},
{
"field": "Views",
"fieldPath": "fields.Views",
"format": "0,0"
},
{
"field": "Likes",
"fieldPath": "fields.Likes",
"format": "0,0"
},
{
"field": "Comments",
"fieldPath": "fields.Comments",
"format": "0,0"
},
{
"field": "Shares",
"fieldPath": "fields.Shares",
"format": "0,0"
},
{
"field": "Engagement_Rate",
"fieldPath": "fields.Engagement_Rate",
"format": "0.00",
"suffix": "%"
}
]
}
```
---
## Expression Reference
### Common Patterns
#### Null Safety
```javascript
// Use || to provide default value
item.fields.Views || 0
// Use optional chaining (if supported)
item?.fields?.Views ?? 0
```
#### Array Operations
```javascript
// Sum
array.reduce((sum, item) => sum + item.value, 0)
// Average
array.reduce((sum, item) => sum + item.value, 0) / array.length
// Count
array.length
// Filter and count
array.filter(item => item.value > 1000).length
// Find max
Math.max(...array.map(item => item.value))
// Find min
Math.min(...array.map(item => item.value))
```
#### Conditional Logic
```javascript
// Ternary operator
value > 1000 ? 'High' : 'Low'
// Multiple conditions
value > 10000 ? 'Very High' : value > 5000 ? 'High' : value > 1000 ? 'Medium' : 'Low'
// Null coalescing
value ?? 'N/A'
```
#### String Formatting
```javascript
// Template literal
`${name}: ${value}`
// Concatenation
'Total: ' + value.toLocaleString()
// Truncate
text.length > 50 ? text.substring(0, 50) + '...' : text
```
---
## Best Practices
### 1. Always Handle Null Values
```javascript
// Bad
item.fields.Views
// Good
item.fields.Views || 0
item.fields.Views ?? 0
```
### 2. Use Descriptive Variable Names in Reduce
```javascript
// Bad
items.reduce((a, b) => a + b.fields.Views, 0)
// Good
items.reduce((totalViews, item) => totalViews + (item.fields.Views || 0), 0)
```
### 3. Format Numbers for Display
```javascript
// Bad (shows raw number)
{{sum}}
// Good (formatted)
{{Intl.NumberFormat('en-US').format(sum)}}
```
### 4. Validate Data Before Processing
```javascript
// Check if data exists
{{getTikTokData && getTikTokData.data && getTikTokData.data.items ?
getTikTokData.data.items.length : 0}}
```
### 5. Use Constants for Repeated Values
```javascript
// In data request configuration
const ITEMS = getTikTokData.data.items;
// Then use in expressions
{{ITEMS.reduce(...)}}
{{ITEMS.map(...)}}
```
---
## Troubleshooting
### Common Issues
#### Issue: "Cannot read property 'fields' of undefined"
**Cause**: Data not loaded or incorrect path
**Solution**: Check data source name and add null safety
```javascript
// Before
item.fields.Views
// After
item?.fields?.Views || 0
```
#### Issue: "NaN" displayed in metrics
**Cause**: Division by zero or null values
**Solution**: Add validation
```javascript
// Before
total / count
// After
count > 0 ? total / count : 0
```
#### Issue: Date shows as timestamp
**Cause**: Missing date transformation
**Solution**: Convert to Date object
```javascript
// Before
{{item.fields.Date}}
// After
{{new Date(item.fields.Date).toLocaleDateString()}}
```
#### Issue: Percentages show as decimals (0.15 instead of 15%)
**Cause**: Need to multiply by 100 and add %
**Solution**: Format properly
```javascript
// Before
{{rate}}
// After
{{(rate * 100).toFixed(2)}}%
```
---
## References
- [aPaaS Expression Syntax](https://apaas.feishu.cn/docs)
- [Bitable API Documentation](https://open.feishu.cn/document/server-docs/docs/bitable-v1/app-table-record/list)
- [JavaScript Array Methods](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)
- [Intl.NumberFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat)
- [Date Formatting](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleDateString)
---
**Version**: 1.0
**Last Updated**: 2025-12-09
**Maintained By**: Analytics Team