import {
pgTable,
pgView,
pgMaterializedView,
serial,
text,
varchar,
integer,
timestamp,
boolean,
} from 'drizzle-orm/pg-core';
import { sql, eq, desc, and, gte } from 'drizzle-orm';
// Tables
export const users = pgTable('users', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
email: varchar('email', { length: 255 }).notNull(),
createdAt: timestamp('created_at').defaultNow(),
});
export const posts = pgTable('posts', {
id: serial('id').primaryKey(),
title: varchar('title', { length: 255 }).notNull(),
content: text('content'),
authorId: integer('author_id').notNull(),
published: boolean('published').default(false),
views: integer('views').default(0),
createdAt: timestamp('created_at').defaultNow(),
});
export const comments = pgTable('comments', {
id: serial('id').primaryKey(),
postId: integer('post_id').notNull(),
userId: integer('user_id').notNull(),
content: text('content').notNull(),
createdAt: timestamp('created_at').defaultNow(),
});
// Standard view: Recent published posts with author info
export const recentPublishedPosts = pgView('recent_published_posts').as((qb) =>
qb.select({
id: posts.id,
title: posts.title,
authorId: posts.authorId,
authorName: users.name,
authorEmail: users.email,
views: posts.views,
createdAt: posts.createdAt,
})
.from(posts)
.innerJoin(users, eq(posts.authorId, users.id))
.where(
and(
eq(posts.published, true),
gte(posts.createdAt, sql`NOW() - INTERVAL '30 days'`)
)
)
.orderBy(desc(posts.createdAt))
);
// SQL-based view: User statistics
export const userStats = pgView('user_stats', {
userId: integer('user_id'),
userName: text('user_name'),
postCount: integer('post_count'),
commentCount: integer('comment_count'),
totalViews: integer('total_views'),
}).as(sql`
SELECT
u.id as user_id,
u.name as user_name,
COUNT(DISTINCT p.id) as post_count,
COUNT(DISTINCT c.id) as comment_count,
COALESCE(SUM(p.views), 0) as total_views
FROM users u
LEFT JOIN posts p ON u.id = p.author_id
LEFT JOIN comments c ON u.id = c.user_id
GROUP BY u.id, u.name
`);
// Materialized view: Popular posts (refreshed periodically)
export const popularPosts = pgMaterializedView('popular_posts')
.with({
fillfactor: 70,
})
.as((qb) =>
qb.select({
id: posts.id,
title: posts.title,
authorName: users.name,
views: posts.views,
commentCount: sql<number>`COUNT(${comments.id})`.as('comment_count'),
})
.from(posts)
.innerJoin(users, eq(posts.authorId, users.id))
.leftJoin(comments, eq(posts.id, comments.postId))
.where(eq(posts.published, true))
.groupBy(posts.id, posts.title, users.name, posts.views)
.having(sql`COUNT(${comments.id}) > 10`)
.orderBy(desc(posts.views))
);