Include join table, but not the actual association

I have a HABTM model

List has_and_belongs_to_many meditations

I want to output a json list of lists & the meditation_ids (but no info about the actual meditations)

  "lists": [
      "id": 10,
      "name": "Default",
      "default": true,
      "meditation_ids": []
      "id": 11,
      "name": "Secondary List",
      "default": false,
      "meditation_ids": [

I’m getting lists with

@lists = List.includes(:meditations).all

this avoids the n+1 query - but it unnecesarily loads the meditations

**HABTM_Meditations Load (0.1ms)** **SELECT "lists_meditations".* FROM "lists_meditations" WHERE "lists_meditations"."list_id" IN ($1, $2)** [["list_id", 10], ["list_id", 11]]

**Meditation Load (0.2ms)** **SELECT "meditations".* FROM "meditations" WHERE "meditations"."id" IN ($1, $2, $3)** [["id", 6], ["id", 13], ["id", 4]]

Is there a way to perform the HABTM_Meditations load, without the followup Meditation Load ?

Not with the HABTM join, because the join table doesn’t have a model to give you the accessors you’d need to load them with a normal association. If you changed to a has_many through: association, then you could ask for the ids from that association.

has_many :ommms
has_many :meditations, through: :ommms

Now you could ask for :ommm_ids (the join model, not the model it’s used to join through to) and you’d only send one query.



What about:

lists = List.joins('INNER JOIN lists_meditations AS lm ON lm.list_id =').select('lists.*', 'lm.meditation_id')

And then from what you get back, in the attributes will be a bunch of ‘meditation_id’! In fact, you could create a cute hash showing how each list relates to its multiple meditations like this:

lists_hash = lists.each_with_object( { |h, k| h[k] = [] }) { |list, s| s[list] << list['meditation_id'] }
1 Like

Thanks Lorin

I went with just using has_many through: rather than habtm which lets me just call the join table as the include.

I try to keep away from writing sql directly if I can…

1 Like