Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
A
Amazon-Selection-Data
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abel_cjy
Amazon-Selection-Data
Commits
f17ce091
Commit
f17ce091
authored
Oct 28, 2026
by
chenyuanjie
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
pinterest板块-月计算
parent
af7581a4
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
260 additions
and
0 deletions
+260
-0
pinterest_month.py
Pyspark_job/doris_handle/pinterest_month.py
+260
-0
No files found.
Pyspark_job/doris_handle/pinterest_month.py
0 → 100644
View file @
f17ce091
"""
@Author : CT
@Description : Pinterest 月度物化表 Doris 落地脚本(纯 Doris SQL)
流程:
[Step 1] Doris 建表 selection.{site}_pinterest_month_{yyyy_mm}[_test]
[Step 2] Doris INSERT OVERWRITE:pinterest_detail 当月为主表,
LEFT JOIN 当月搜索词聚合 + 上月/去年同月 saves,产出词干与同环比
[Step 3] 更新 MySQL workflow_everyday 流程记录表(仅 formal 模式)
支持 formal / test 模式:
- formal:表名无后缀
- test :表名加 _test 后缀
执行示例:
spark-submit pinterest_month.py us 2026-09 # 默认 formal
spark-submit pinterest_month.py us 2026-09 formal
spark-submit pinterest_month.py us 2026-09 test # test 模式
"""
import
os
import
sys
sys
.
path
.
append
(
os
.
path
.
dirname
(
sys
.
path
[
0
]))
import
pandas
as
pd
from
utils.DorisHelper
import
DorisHelper
from
utils.common_util
import
CommonUtil
from
utils.db_util
import
DBUtil
,
DbTypes
def
_doris_connect
(
use_type
=
'selection'
):
"""统一 pymysql 连接,database='selection' 让 UDF(如 stem_en)与 selection 库可见"""
import
pymysql
conn_info
=
DorisHelper
.
get_connection_info
(
use_type
)
return
pymysql
.
connect
(
host
=
conn_info
[
'ip'
],
port
=
conn_info
[
'jdbc_port'
],
user
=
conn_info
[
'user'
],
password
=
conn_info
[
'pwd'
],
database
=
'selection'
,
charset
=
'utf8mb4'
,
autocommit
=
True
,
)
def
_exec_doris_sql
(
sql_list
,
use_type
=
'selection'
):
"""通过 pymysql 走 Doris jdbc_port 执行 DDL / INSERT 等"""
conn
=
_doris_connect
(
use_type
)
try
:
cur
=
conn
.
cursor
()
for
sql
in
sql_list
:
print
(
f
"[Doris SQL] {sql[:200]}{'...' if len(sql) > 200 else ''}"
)
cur
.
execute
(
sql
)
cur
.
close
()
finally
:
conn
.
close
()
# ============================================================
# [Step 1] selection 月物化表建表
# ============================================================
def
build_month_create_table_sql
(
table_name
,
date_info
):
"""构建 selection.{table_name} 建表语句;table_name 由外层拼接:{site}_pinterest_month_{yyyy_mm}[_test]"""
return
f
"""
CREATE TABLE IF NOT EXISTS `selection`.`{table_name}`
(
`pin_id` VARCHAR(64) NOT NULL COMMENT 'Pinterest Pin ID',
`search_term_arr` ARRAY<STRING> NULL COMMENT '关联搜索词数组',
`search_term_stem` STRING NULL COMMENT '搜索词词干(stem_en派生)',
`title` STRING NULL COMMENT '标题',
`title_stem` STRING NULL COMMENT '标题词干(stem_en派生)',
`annotations_arr` ARRAY<STRING> NULL COMMENT 'Pinterest语义标签数组',
`annotations_stem` STRING NULL COMMENT '语义标签词干(stem_en派生)',
`saves` INT NULL COMMENT '收藏数',
`saves_mom` DECIMAL(20,4) NULL COMMENT '收藏数环比变化率',
`saves_yoy` DECIMAL(20,4) NULL COMMENT '收藏数同比变化率',
`pin_created_at` DATETIME NULL COMMENT 'Pin发布时间(UTC)',
`domain` STRING NULL COMMENT '域名',
`tracked_link` STRING NULL COMMENT '跟踪链接',
`board_name` STRING NULL COMMENT 'Board名称',
`image_orig` STRING NULL COMMENT '原图地址',
`pin_crawl_date` DATETIME NULL COMMENT '抓取时间(updated_at)',
`reactions` INT NULL COMMENT '互动总数(total reactions)',
`reactions_mom` DECIMAL(20,4) NULL COMMENT '互动总数环比变化率',
`reactions_yoy` DECIMAL(20,4) NULL COMMENT '互动总数同比变化率',
`share_count` INT NULL COMMENT '分享数',
`repin_count` INT NULL COMMENT '再保存/转存数',
`comment_count` INT NULL COMMENT '评论数',
`creator_name` STRING NULL COMMENT '创作者名称',
`creator_link` STRING NULL COMMENT '创作者主页链接',
`pinner_full_name` STRING NULL COMMENT 'Pinner名称',
`pinner_link` STRING NULL COMMENT 'Pinner主页链接',
`board_url` STRING NULL COMMENT 'Board链接',
`link` STRING NULL COMMENT '外链',
INDEX idx_search_term_stem (`search_term_stem`) USING INVERTED PROPERTIES("parser" = "english") COMMENT '搜索词词干倒排索引',
INDEX idx_title_stem (`title_stem`) USING INVERTED PROPERTIES("parser" = "english") COMMENT '标题词干倒排索引',
INDEX idx_annotations_stem (`annotations_stem`) USING INVERTED PROPERTIES("parser" = "english") COMMENT '语义标签词干倒排索引'
) ENGINE=OLAP
UNIQUE KEY(`pin_id`)
COMMENT 'Pinterest月度物化表 {date_info}'
DISTRIBUTED BY HASH(`pin_id`) BUCKETS 32
PROPERTIES (
"replication_num" = "3",
"enable_unique_key_merge_on_write" = "true"
)
"""
# ============================================================
# [Step 2] selection 月物化表 INSERT OVERWRITE
# ============================================================
def
_rate_case_sql
(
cur_col
,
his_col
):
"""同环比 CASE 表达式,默认值规则:空空=空,0/空组合=0,空vs>0=-1000,>0vs空或0=1000"""
return
f
"""CASE WHEN {cur_col} IS NULL AND {his_col} IS NULL THEN NULL
WHEN COALESCE({cur_col}, 0) = 0 AND COALESCE({his_col}, 0) = 0 THEN 0
WHEN {cur_col} IS NULL AND {his_col} > 0 THEN -1000
WHEN {cur_col} > 0 AND COALESCE({his_col}, 0) = 0 THEN 1000
ELSE ROUND(({cur_col} - {his_col}) / {his_col}, 4) END"""
def
build_month_insert_overwrite_sql
(
site_name
,
table_name
,
date_info
):
"""构建 INSERT OVERWRITE 到 selection.{table_name} 的 SQL"""
last_month
=
CommonUtil
.
get_month_offset
(
date_info
,
-
1
)
last_year_month
=
CommonUtil
.
get_month_offset
(
date_info
,
-
12
)
return
f
"""
INSERT OVERWRITE TABLE `selection`.`{table_name}`
SELECT /*+ SET_VAR(batch_size=1024, parallel_pipeline_task_num=2, insert_timeout=14400, query_timeout=14400) */
d.pin_id,
st.search_term_arr,
stem_en(array_join(st.search_term_arr, ' ')) AS search_term_stem,
d.title,
stem_en(d.title) AS title_stem,
d.annotations_arr,
stem_en(array_join(d.annotations_arr, ' ')) AS annotations_stem,
d.saves,
{_rate_case_sql('d.saves', 'm.saves')} AS saves_mom,
{_rate_case_sql('d.saves', 'y.saves')} AS saves_yoy,
d.pin_created_at,
d.domain,
d.tracked_link,
d.board_name,
d.image_orig,
d.pin_crawl_date,
d.reactions,
{_rate_case_sql('d.reactions', 'm.reactions')} AS reactions_mom,
{_rate_case_sql('d.reactions', 'y.reactions')} AS reactions_yoy,
d.share_count,
d.repin_count,
d.comment_count,
d.creator_name,
d.creator_link,
d.pinner_full_name,
d.pinner_link,
d.board_url,
d.link
FROM (
-- 当月 pin 详情(主表)
SELECT pin_id,
title,
CAST(annotations AS ARRAY<STRING>) AS annotations_arr,
saves,
str_to_date(substr(pin_created_at, 6, 20), '
%
d
%
b
%
Y
%
H:
%
i:
%
s') AS pin_created_at,
domain,
tracked_link,
board_name,
image_orig,
updated_at AS pin_crawl_date,
reactions,
share_count,
repin_count,
comment_count,
creator_name,
CONCAT('https://www.pinterest.com/', NULLIF(TRIM(creator_username), ''), '/') AS creator_link,
pinner_full_name,
CONCAT('https://www.pinterest.com/', NULLIF(TRIM(pinner_username), ''), '/') AS pinner_link,
board_url,
link
FROM `selection`.`pinterest_detail`
WHERE site_name = '{site_name}' AND date_type = 'month' AND date_info = '{date_info}'
) d
LEFT JOIN (
-- 当月搜索词按 pin 聚合
SELECT pin_id, collect_set(search_term) AS search_term_arr
FROM `selection`.`pinterest_st_id_syn`
WHERE site_name = '{site_name}' AND date_type = 'month' AND date_info = '{date_info}'
GROUP BY pin_id
) st ON d.pin_id = st.pin_id
LEFT JOIN (
-- 上月 saves、reactions
SELECT pin_id, saves, reactions
FROM `selection`.`pinterest_detail`
WHERE site_name = '{site_name}' AND date_type = 'month' AND date_info = '{last_month}'
) m ON d.pin_id = m.pin_id
LEFT JOIN (
-- 去年同月 saves、reactions
SELECT pin_id, saves, reactions
FROM `selection`.`pinterest_detail`
WHERE site_name = '{site_name}' AND date_type = 'month' AND date_info = '{last_year_month}'
) y ON d.pin_id = y.pin_id
"""
# ============================================================
# [Step 3] 流程记录表更新(仅 formal 模式)
# ============================================================
def
modify_mission_record_status
(
site_name
,
date_info
,
table_name
,
result_type
):
"""流程记录表更新:仅 formal 模式才入库 mysql workflow_everyday"""
if
result_type
!=
'formal'
:
print
(
f
"[Step 3] result_type={result_type},跳过流程记录表更新"
)
return
record_table
=
'workflow_everyday'
engine_mysql
=
DBUtil
.
get_db_engine
(
db_type
=
DbTypes
.
mysql
.
name
,
site_name
=
'us'
)
date_type
=
'month'
status_remark
=
'pinterest月计算完毕'
select_sql
=
(
f
"select id from {record_table} where site_name='{site_name}' and date_type='{date_type}' "
f
"and report_date='{date_info}' and page='pinterest' and status_val=14 and is_end='是'"
)
df_is_finished
=
pd
.
read_sql
(
select_sql
,
engine_mysql
)
if
df_is_finished
.
empty
:
replace_sql
=
f
"""
replace into {record_table} (site_name, report_date, status, status_val, table_name, date_type, page, is_end, remark, export_db_type)
VALUES ('{site_name}', '{date_info}', '{status_remark}', 14, '{table_name}', '{date_type}', 'pinterest', '是', '{status_remark}', 'doris')
"""
DBUtil
.
exec_sql
(
'mysql'
,
'us'
,
replace_sql
)
print
(
f
"[Step 3] 流程记录表 workflow_everyday 已写入:{site_name} {date_info} table_name={table_name}"
)
else
:
print
(
f
"[Step 3] 流程记录表已存在该记录,跳过:date_type={date_type}"
)
def
main
(
site_name
,
date_info
,
result_type
=
'formal'
):
assert
result_type
in
(
'formal'
,
'test'
),
f
"不支持的 result_type:{result_type},仅支持 formal/test"
env_suffix
=
'_test'
if
result_type
==
'test'
else
''
month_table
=
f
"{site_name}_pinterest_month_{date_info.replace('-', '_')}{env_suffix}"
print
(
f
"启动:site={site_name}, date_info={date_info}, result_type={result_type}"
)
print
(
f
" selection 月物化表:selection.{month_table}"
)
# ===== [Step 1] Doris 建表 =====
print
(
f
"[Step 1] Doris 建表 selection.{month_table}"
)
_exec_doris_sql
([
build_month_create_table_sql
(
month_table
,
date_info
)])
# ===== [Step 2] Doris INSERT OVERWRITE =====
print
(
f
"[Step 2] Doris INSERT OVERWRITE selection.{month_table}"
)
_exec_doris_sql
([
build_month_insert_overwrite_sql
(
site_name
,
month_table
,
date_info
)])
# ===== [Step 3] 流程记录表更新(仅 formal 模式)=====
# TODO 待上线后打开
# modify_mission_record_status(site_name, date_info, month_table, result_type)
print
(
"success!"
)
if
__name__
==
"__main__"
:
site_name
=
sys
.
argv
[
1
]
date_info
=
sys
.
argv
[
2
]
result_type
=
sys
.
argv
[
3
]
if
len
(
sys
.
argv
)
>
3
else
'formal'
main
(
site_name
,
date_info
,
result_type
)
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment