- pgSchema tables for auth, money, house, ref, games, cs, animaldex, site, meta (lists excluded via schemaFilter; runtime list tables are managed by meta.*) - drizzle/ baseline matches the reorged DB exactly (catalog diff = 0 on rehearsal) - db/reorg: one-time SQL that moves the flat public schema (rehearsed on a prod copy) - scripts/db-migrate.ts: migrator with --init to record the baseline and a guard against running the baseline on a non-empty, unrecorded database
91 lines
2.5 KiB
TypeScript
91 lines
2.5 KiB
TypeScript
import {
|
|
index,
|
|
integer,
|
|
primaryKey,
|
|
text,
|
|
timestamp,
|
|
uniqueIndex,
|
|
bigint
|
|
} from 'drizzle-orm/pg-core';
|
|
import { csSchema } from './schemas';
|
|
import { id } from './helpers';
|
|
|
|
// A Steam market hash name identifies a listing, including wear and special variants.
|
|
export const csMarketItems = csSchema.table(
|
|
'items',
|
|
{
|
|
id: id(),
|
|
marketHashName: text('market_hash_name').notNull(),
|
|
name: text('name').notNull(),
|
|
iconUrl: text('icon_url'),
|
|
itemType: text('item_type'),
|
|
firstSeenAt: timestamp('first_seen_at', { withTimezone: true }).notNull().defaultNow(),
|
|
lastSeenAt: timestamp('last_seen_at', { withTimezone: true }).notNull().defaultNow()
|
|
},
|
|
(table) => [
|
|
uniqueIndex('items_hash_idx').on(table.marketHashName),
|
|
index('items_name_idx').on(table.name)
|
|
]
|
|
);
|
|
|
|
// Keep source observations intact. Aggregation happens only when the chart is requested.
|
|
export const csPriceObservations = csSchema.table(
|
|
'price_observations',
|
|
{
|
|
id: bigint('id', { mode: 'number' }).primaryKey().generatedAlwaysAsIdentity(),
|
|
itemId: integer('item_id')
|
|
.notNull()
|
|
.references(() => csMarketItems.id, { onDelete: 'cascade' }),
|
|
source: text('source').notNull(),
|
|
currency: text('currency').notNull(),
|
|
observedAt: timestamp('observed_at', { withTimezone: true }).notNull(),
|
|
priceCents: integer('price_cents').notNull(),
|
|
volume: integer('volume'),
|
|
kind: text('kind').notNull(),
|
|
importedAt: timestamp('imported_at', { withTimezone: true }).notNull().defaultNow()
|
|
},
|
|
(table) => [
|
|
uniqueIndex('price_observations_unique_idx').on(
|
|
table.itemId,
|
|
table.source,
|
|
table.currency,
|
|
table.observedAt,
|
|
table.kind
|
|
),
|
|
index('price_observations_lookup_idx').on(
|
|
table.itemId,
|
|
table.source,
|
|
table.currency,
|
|
table.observedAt
|
|
),
|
|
index('price_observations_time_idx').on(table.observedAt)
|
|
]
|
|
);
|
|
|
|
export const csItemDimensions = csSchema.table(
|
|
'item_dimensions',
|
|
{
|
|
itemId: integer('item_id')
|
|
.notNull()
|
|
.references(() => csMarketItems.id, { onDelete: 'cascade' }),
|
|
key: text('key').notNull(),
|
|
value: text('value').notNull()
|
|
},
|
|
(table) => [
|
|
primaryKey({ name: 'item_dimensions_pkey', columns: [table.itemId, table.key] }),
|
|
index('item_dimensions_filter_idx').on(table.key, table.value, table.itemId)
|
|
]
|
|
);
|
|
|
|
export const csPriceStreams = csSchema.table(
|
|
'price_streams',
|
|
{
|
|
source: text('source').notNull(),
|
|
currency: text('currency').notNull(),
|
|
kind: text('kind').notNull()
|
|
},
|
|
(table) => [
|
|
primaryKey({ name: 'price_streams_pkey', columns: [table.source, table.currency, table.kind] })
|
|
]
|
|
);
|