2025-01-04 17:22:46 -07:00
|
|
|
-- Enable required extensions
|
|
|
|
create extension if not exists "uuid-ossp"; -- For UUID generation
|
|
|
|
create extension if not exists pg_cron; -- For scheduled jobs
|
|
|
|
|
|
|
|
-- Create the search_cache table
|
|
|
|
create table public.search_cache (
|
|
|
|
id uuid default uuid_generate_v4() primary key,
|
|
|
|
query text not null,
|
|
|
|
results jsonb not null,
|
|
|
|
location text not null,
|
|
|
|
category text not null,
|
|
|
|
created_at timestamp with time zone default timezone('utc'::text, now()) not null,
|
|
|
|
updated_at timestamp with time zone default timezone('utc'::text, now()) not null,
|
|
|
|
expires_at timestamp with time zone default timezone('utc'::text, now() + interval '7 days') not null
|
|
|
|
);
|
|
|
|
|
|
|
|
-- Create indexes
|
|
|
|
create index search_cache_query_idx on public.search_cache (query);
|
|
|
|
create index search_cache_location_category_idx on public.search_cache (location, category);
|
|
|
|
create index search_cache_expires_at_idx on public.search_cache (expires_at);
|
|
|
|
|
|
|
|
-- Enable RLS
|
|
|
|
alter table public.search_cache enable row level security;
|
|
|
|
|
|
|
|
-- Create policies
|
|
|
|
create policy "Allow public read access"
|
|
|
|
on public.search_cache for select
|
|
|
|
using (true);
|
|
|
|
|
|
|
|
create policy "Allow service write access"
|
|
|
|
on public.search_cache for insert
|
|
|
|
with check (true);
|
|
|
|
|
|
|
|
create policy "Allow service update access"
|
|
|
|
on public.search_cache for update
|
|
|
|
using (true)
|
|
|
|
with check (true);
|
|
|
|
|
|
|
|
create policy "Allow delete expired records"
|
|
|
|
on public.search_cache for delete
|
|
|
|
using (expires_at < now());
|
|
|
|
|
|
|
|
-- Create function to clean up expired records
|
|
|
|
create or replace function cleanup_expired_cache()
|
|
|
|
returns void
|
|
|
|
language plpgsql
|
|
|
|
security definer
|
|
|
|
as $$
|
|
|
|
begin
|
|
|
|
delete from public.search_cache
|
|
|
|
where expires_at < now();
|
|
|
|
end;
|
|
|
|
$$;
|
|
|
|
|
|
|
|
-- Create a manual cleanup function since pg_cron might not be available
|
|
|
|
create or replace function manual_cleanup()
|
|
|
|
returns void
|
|
|
|
language plpgsql
|
|
|
|
security definer
|
|
|
|
as $$
|
|
|
|
begin
|
|
|
|
delete from public.search_cache
|
|
|
|
where expires_at < now();
|
|
|
|
end;
|
|
|
|
$$;
|
|
|
|
|
|
|
|
-- Create a view for cache statistics
|
|
|
|
create or replace view cache_stats as
|
|
|
|
select
|
|
|
|
count(*) as total_entries,
|
|
|
|
count(*) filter (where expires_at < now()) as expired_entries,
|
|
|
|
count(*) filter (where expires_at >= now()) as valid_entries,
|
|
|
|
min(created_at) as oldest_entry,
|
|
|
|
max(created_at) as newest_entry,
|
|
|
|
count(distinct category) as unique_categories,
|
|
|
|
count(distinct location) as unique_locations
|
|
|
|
from public.search_cache;
|
|
|
|
|
|
|
|
-- Grant permissions to access the view
|
|
|
|
grant select on cache_stats to postgres;
|
|
|
|
|
|
|
|
-- Create table if not exists businesses
|
|
|
|
create table if not exists businesses (
|
|
|
|
id text primary key,
|
|
|
|
name text not null,
|
|
|
|
phone text,
|
|
|
|
email text,
|
|
|
|
address text,
|
|
|
|
rating numeric,
|
|
|
|
website text,
|
|
|
|
logo text,
|
|
|
|
source text,
|
|
|
|
description text,
|
|
|
|
latitude numeric,
|
|
|
|
longitude numeric,
|
|
|
|
last_updated timestamp with time zone default timezone('utc'::text, now()),
|
|
|
|
search_count integer default 1,
|
|
|
|
created_at timestamp with time zone default timezone('utc'::text, now())
|
|
|
|
);
|
|
|
|
|
|
|
|
-- Create indexes for common queries
|
|
|
|
create index if not exists businesses_name_idx on businesses (name);
|
|
|
|
create index if not exists businesses_rating_idx on businesses (rating desc);
|
|
|
|
create index if not exists businesses_search_count_idx on businesses (search_count desc);
|
|
|
|
create index if not exists businesses_last_updated_idx on businesses (last_updated desc);
|
|
|
|
|
|
|
|
-- Create tables if they don't exist
|
|
|
|
CREATE TABLE IF NOT EXISTS businesses (
|
|
|
|
id TEXT PRIMARY KEY,
|
|
|
|
name TEXT NOT NULL,
|
|
|
|
phone TEXT,
|
|
|
|
email TEXT,
|
|
|
|
address TEXT,
|
|
|
|
rating INTEGER,
|
|
|
|
website TEXT,
|
|
|
|
logo TEXT,
|
|
|
|
source TEXT,
|
|
|
|
description TEXT,
|
|
|
|
location JSONB,
|
|
|
|
place_id TEXT,
|
|
|
|
photos TEXT[],
|
|
|
|
opening_hours TEXT[],
|
|
|
|
distance JSONB,
|
|
|
|
last_updated TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
search_count INTEGER DEFAULT 0
|
|
|
|
);
|
|
|
|
|
|
|
|
CREATE TABLE IF NOT EXISTS searches (
|
|
|
|
id SERIAL PRIMARY KEY,
|
|
|
|
query TEXT NOT NULL,
|
|
|
|
location TEXT NOT NULL,
|
|
|
|
timestamp TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
results_count INTEGER
|
|
|
|
);
|
|
|
|
|
|
|
|
CREATE TABLE IF NOT EXISTS cache (
|
|
|
|
key TEXT PRIMARY KEY,
|
|
|
|
value JSONB NOT NULL,
|
|
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
expires_at TIMESTAMP WITH TIME ZONE NOT NULL
|
|
|
|
);
|
|
|
|
|
|
|
|
-- Create indexes
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_businesses_location ON businesses USING GIN (location);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_businesses_search ON businesses USING GIN (to_tsvector('english', name || ' ' || COALESCE(description, '')));
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_cache_expires ON cache (expires_at);
|
|
|
|
|
|
|
|
-- Set up RLS (Row Level Security)
|
|
|
|
ALTER TABLE businesses ENABLE ROW LEVEL SECURITY;
|
|
|
|
ALTER TABLE searches ENABLE ROW LEVEL SECURITY;
|
|
|
|
ALTER TABLE cache ENABLE ROW LEVEL SECURITY;
|
|
|
|
|
|
|
|
-- Create policies
|
|
|
|
CREATE POLICY "Allow anonymous select" ON businesses FOR SELECT USING (true);
|
|
|
|
CREATE POLICY "Allow service role insert" ON businesses FOR INSERT WITH CHECK (true);
|
|
|
|
CREATE POLICY "Allow service role update" ON businesses FOR UPDATE USING (true);
|
|
|
|
|
|
|
|
CREATE POLICY "Allow anonymous select" ON searches FOR SELECT USING (true);
|
|
|
|
CREATE POLICY "Allow service role insert" ON searches FOR INSERT WITH CHECK (true);
|
|
|
|
|
|
|
|
CREATE POLICY "Allow anonymous select" ON cache FOR SELECT USING (true);
|
|
|
|
CREATE POLICY "Allow service role all" ON cache USING (true);
|
|
|
|
|
|
|
|
-- Add place_id column to businesses table if it doesn't exist
|
|
|
|
ALTER TABLE businesses ADD COLUMN IF NOT EXISTS place_id TEXT;
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_businesses_place_id ON businesses(place_id);
|
|
|
|
|
|
|
|
-- Create a unique constraint on place_id (excluding nulls)
|
|
|
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_businesses_place_id_unique
|
|
|
|
ON businesses(place_id)
|
2025-01-06 21:24:54 -07:00
|
|
|
WHERE place_id IS NOT NULL;
|
|
|
|
|
|
|
|
CREATE TABLE IF NOT EXISTS businesses (
|
|
|
|
id TEXT PRIMARY KEY,
|
|
|
|
name TEXT NOT NULL,
|
|
|
|
address TEXT NOT NULL,
|
|
|
|
phone TEXT NOT NULL,
|
|
|
|
description TEXT NOT NULL,
|
|
|
|
website TEXT,
|
|
|
|
source TEXT NOT NULL,
|
|
|
|
rating REAL,
|
|
|
|
lat REAL,
|
|
|
|
lng REAL,
|
|
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
|
|
);
|
|
|
|
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_businesses_source ON businesses(source);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_businesses_rating ON businesses(rating);
|