Optimize Postgresql query
The obvious alternative to the seq scan on the child table would be the nested loop in which it has to probe the child table by index 20,718 separate times.  (Actually 21,215 times, but of course the planner doesn't know that at the time it makes its decision).  Each one of those probes is going to go to an assumedly random leaf page in the index, and then follow up to a random spot in the child table itself, triggering a bunch of random IO.  If all that of that data is already found in the cache, this will be a good plan.  But if it actually has to hit the disk for each one this is going to be ungodly slow (especially since your disk seems to be powered by gerbils--your pre-edit plan where it took 22 seconds to seq scan the child table is simply horrible).

If you want to see the alternative plan, you can set enable_hashjoin = off before executing the query.  This is generally not a setting you would want to do in production, it is more for exploratory purposes.  But as a last resort, you could make this setting change locally just for running this query, then reset it.

If you like the nested loop because most of your data actually is in memory most of the time, you can signal this to the planner by reducing the value of "random_page_cost" to something closer to 1.  In my hands, lowering from the default of 4 down to 1.4 was enough to flip the plan from the seq scan to the nested loop. If most of your data is always in cache (not just the child table and its index) then it might be reasonable to make a global change to this parameter.  If you do that, however, you should have a plan to pre-warm your database in case you reboot the machine (such as this).  Getting the data back into cache the natural way is likely to be slow and painful.