Just focusing on the database and ignoring the statistics issues, and assuming the 5 days you use is a design decision and not just ephemeral example, and that we can ignore anything with an incomplete record, then you can do it like this:
Set up with random data:
create table movie as select x as movie_id, y as release_day, floor(random()*1000)::int as showtime_count, floor(random()*100000)::int as ticket_sold_count
from generate_series(1,1000000) f(x), generate_series(1,6) g(y);
denormalize and index:
create materialized view release as
select movie_id,
cube(
array_agg(showtime_count order by release_day)||
array_agg(ticket_sold_count order by release_day)
) as datavector
from movie where
release_day <=5
group by movie_id
having count(release_day)=5 and
count(distinct release_day)=5;
create index on release using gist (datavector);
And use:
explain (analyze,buffers) select * from release order by datavector <-> '(230, 16, 178, 507, 366, 8798, 4724, 1736, 5765, 808)' limit 1;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------
Limit (cost=0.41..0.59 rows=1 width=44) (actual time=1.800..1.802 rows=1 loops=1)
Buffers: shared hit=95
-> Index Scan using release_datavector_idx on release (cost=0.41..176884.39 rows=999999 width=44) (actual time=1.798..1.798 rows=1 loops=1)
Order By: (datavector <-> '(230, 16, 178, 507, 366, 8798, 4724, 1736, 5765, 808)'::cube)
Buffers: shared hit=95
Planning Time: 0.091 ms
Execution Time: 1.841 ms
cube
extension address this and what would recommend if not postgres to use for this task?