Skip to content

Commit d9e3dcd

Browse files
authored
examples(cube-twitter-stream) (#5274)
* Example * Update * Update
1 parent 989d13b commit d9e3dcd

27 files changed

+3314
-0
lines changed

examples/twitter-stream/.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
node_modules/
2+
.cubestore/
3+
.DS_Store
4+
.env

examples/twitter-stream/README.md

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
```bash
2+
docker build \
3+
-t us-docker.pkg.dev/cube-devrel-team/cube-twitter-stream/producer \
4+
--platform linux/amd64 .
5+
6+
docker push \
7+
us-docker.pkg.dev/cube-devrel-team/cube-twitter-stream/producer
8+
```
9+
10+
```sql
11+
CREATE OR REPLACE STREAM TWEETS_4 (
12+
ID STRING KEY,
13+
LANG STRING,
14+
PUBLIC_METRICS MAP<STRING, INT>,
15+
REPLY_SETTINGS STRING,
16+
SOURCE STRING,
17+
TEXT STRING,
18+
AUTHOR MAP<STRING, STRING>,
19+
ENTITIES MAP<STRING, ARRAY<MAP<STRING, STRING>>>,
20+
CREATED_AT STRING
21+
) WITH (
22+
KAFKA_TOPIC='test_4',
23+
KEY_FORMAT='json',
24+
VALUE_FORMAT='json'
25+
);
26+
27+
CREATE OR REPLACE TABLE STATS_4
28+
AS SELECT
29+
ID,
30+
UNIX_TIMESTAMP(PARSE_TIMESTAMP(LATEST_BY_OFFSET(CREATED_AT), 'yyyy-MM-dd''T''HH:mm:ss.SSS''Z''')) AS CREATED_AT,
31+
COALESCE(LATEST_BY_OFFSET(LANG), 'UNDEFINED') AS LANGUAGE,
32+
COALESCE(LATEST_BY_OFFSET(PUBLIC_METRICS)['retweet_count'], 0) AS RETWEET_COUNT,
33+
COALESCE(LATEST_BY_OFFSET(PUBLIC_METRICS)['reply_count'], 0) AS REPLY_COUNT,
34+
COALESCE(LATEST_BY_OFFSET(PUBLIC_METRICS)['like_count'], 0) AS LIKE_COUNT,
35+
COALESCE(LATEST_BY_OFFSET(PUBLIC_METRICS)['quote_count'], 0) AS QUOTE_COUNT,
36+
COALESCE(LATEST_BY_OFFSET(REPLY_SETTINGS), 'UNDEFINED') AS REPLY_SETTINGS,
37+
COALESCE(LATEST_BY_OFFSET(SOURCE), 'UNDEFINED') AS SOURCE,
38+
LATEST_BY_OFFSET(TEXT) AS TEXT,
39+
LEN(LATEST_BY_OFFSET(TEXT)) AS TEXT_LENGTH,
40+
COALESCE(LATEST_BY_OFFSET(AUTHOR)['username'], 'UNDEFINED') AS AUTHOR_USERNAME,
41+
COALESCE(LATEST_BY_OFFSET(AUTHOR)['name'], 'UNDEFINED') AS AUTHOR_NAME,
42+
COALESCE(LATEST_BY_OFFSET(AUTHOR)['description'], 'UNDEFINED') AS AUTHOR_DESCRIPTION,
43+
COALESCE(LATEST_BY_OFFSET(AUTHOR)['location'], 'UNDEFINED') AS AUTHOR_LOCATION,
44+
COALESCE(LATEST_BY_OFFSET(AUTHOR)['verified'], 'UNDEFINED') AS AUTHOR_VERIFIED,
45+
COALESCE(ARRAY_LENGTH(LATEST_BY_OFFSET(ENTITIES)['mentions']), 0) AS MENTION_COUNT,
46+
COALESCE(ARRAY_LENGTH(LATEST_BY_OFFSET(ENTITIES)['hashtags']), 0) AS HASHTAG_COUNT,
47+
COALESCE(ARRAY_LENGTH(LATEST_BY_OFFSET(ENTITIES)['urls']), 0) AS URL_COUNT
48+
FROM TWEETS_4
49+
GROUP BY ID;
50+
51+
CREATE OR REPLACE STREAM HASHTAGS
52+
AS SELECT
53+
ID,
54+
CONCAT(ID, '_', EXPLODE(ENTITIES['hashtags'])['start']) AS TAG_ID,
55+
EXPLODE(ENTITIES['hashtags'])['tag'] AS TAG
56+
FROM TWEETS_4;
57+
58+
CREATE OR REPLACE STREAM MENTIONS
59+
AS SELECT
60+
ID,
61+
CONCAT(ID, '_', EXPLODE(ENTITIES['mentions'])['id']) AS MENTION_ID,
62+
EXPLODE(ENTITIES['mentions'])['id'] AS USER_ID,
63+
EXPLODE(ENTITIES['mentions'])['username'] AS USERNAME
64+
FROM TWEETS_4;
65+
66+
CREATE OR REPLACE STREAM WORDS
67+
AS SELECT
68+
ID,
69+
CONCAT(ID, '_', EXPLODE(SPLIT(LCASE(REGEXP_REPLACE(TEXT, '\W', ' ')), ' '))) AS WORD_ID,
70+
EXPLODE(SPLIT(LCASE(REGEXP_REPLACE(TEXT, '\W', ' ')), ' ')) AS WORD,
71+
LEN(EXPLODE(SPLIT(LCASE(REGEXP_REPLACE(TEXT, '\W', ' ')), ' '))) AS WORD_LENGTH
72+
FROM TWEETS_4;
73+
74+
SELECT * FROM stats;
75+
```
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"siteId": "c125c8b6-8ff2-479a-84b0-ab4216a08e9a"
3+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"name": "cube-twitter-stream-app",
3+
"version": "1.0.0",
4+
"license": "MIT",
5+
"scripts": {
6+
"dev": "parcel src/index.html",
7+
"build": "parcel build src/index.html"
8+
},
9+
"devDependencies": {
10+
"parcel": "^2.6.2",
11+
"process": "^0.11.10"
12+
},
13+
"dependencies": {
14+
"@cube-dev/example-wrapper": "^1.1.9",
15+
"@cubejs-client/core": "^0.30.29",
16+
"@cubejs-client/react": "^0.30.30",
17+
"@cubejs-client/ws-transport": "^0.30.64",
18+
"react": "^18.2.0",
19+
"react-dom": "^18.2.0",
20+
"twitter-api-sdk": "^1.1.0"
21+
}
22+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import * as classes from './Card.module.css'
2+
import { getTimestamp } from '../utils'
3+
import { useState, useEffect } from 'react'
4+
5+
function Card({ title, value, timestamp }) {
6+
const [ currentTimestamp, setCurrentTimestamp ] = useState(getTimestamp())
7+
8+
useEffect(() => {
9+
const interval = setInterval(() => {
10+
setCurrentTimestamp(getTimestamp())
11+
}, 1000)
12+
13+
return () => clearInterval(interval)
14+
}, [])
15+
16+
return <div className={classes.root}>
17+
<p>{title}</p>
18+
<p className={classes.value}>{value}</p>
19+
<p className={classes.timestamp}>{currentTimestamp - timestamp} secs ago</p>
20+
</div>
21+
}
22+
23+
export default Card
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
.root {
2+
border-radius: 12px;
3+
box-shadow: rgba(15, 20, 25, 0.12) 0px 2px 12px 0px;
4+
display: inline-block;
5+
padding: 12px 18px;
6+
margin: 0 18px 18px 0;
7+
}
8+
9+
.value {
10+
font-size: 1.5em;
11+
font-weight: bolder;
12+
margin: 0.25em 0 0.25em 0;
13+
}
14+
15+
.timestamp {
16+
opacity: 0.5;
17+
}
18+
19+
a {
20+
color: rgb(29, 155, 240);
21+
}
22+
23+
em {
24+
color: rgb(29, 155, 240);
25+
font-style: normal;
26+
opacity: 0.5;
27+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import * as classes from './Cards.module.css'
2+
3+
function SendTweetBlock({ children }) {
4+
return <div className={classes.root}>
5+
{children}
6+
</div>
7+
}
8+
9+
export default SendTweetBlock
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.root {
2+
margin: auto;
3+
width: 90%;
4+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import * as classes from './LiveIndicatorCard.module.css'
2+
// import { getTimestamp } from '../utils'
3+
4+
function LiveIndicatorCard() {
5+
// useEffect(() => {
6+
// const interval = setInterval(() => {
7+
// setCurrentTimestamp(getTimestamp())
8+
// }, 1000)
9+
10+
// return () => clearInterval(interval)
11+
// }, [])
12+
13+
return <div className={classes.root}>
14+
<p>Streaming...</p>
15+
<p className={classes.value}>
16+
<span className={classes.dot}>&#9679;</span>
17+
<span className={classes.dot}>&#9679;</span>
18+
<span className={classes.dot}>&#9679;</span>
19+
</p>
20+
<p className={classes.timestamp}>Real-time</p>
21+
</div>
22+
}
23+
24+
export default LiveIndicatorCard
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
.root {
2+
border-radius: 12px;
3+
box-shadow: rgba(15, 20, 25, 0.12) 0px 2px 12px 0px;
4+
display: inline-block;
5+
padding: 12px 18px;
6+
margin: 0 18px 18px 0;
7+
}
8+
9+
.value {
10+
font-size: 1.5em;
11+
font-weight: bolder;
12+
margin: 0.25em 0 0.25em 0;
13+
}
14+
15+
.timestamp {
16+
opacity: 0.5;
17+
}
18+
19+
@keyframes blink {
20+
0%, 100% { opacity: 0; }
21+
50% { opacity: 1; }
22+
}
23+
24+
.dot {
25+
animation: blink 2s infinite;
26+
color: rgb(29, 155, 240);
27+
margin-right: 0.1em;
28+
/* color: green; */
29+
}

0 commit comments

Comments
 (0)