Skip to content

Conversation

@jacobmolby
Copy link
Contributor

Summary

This PR adds a new counts plugin that generates code for efficiently loading relationship counts, and allows MySQL insert queries to properly execute loaders.

Features

New Counts Plugin

Adds the ability to load counts of to-many relationships without fetching the full related data. This is useful for displaying "X has 5 posts" without loading all posts.

PreloadCount - Loads counts via correlated subquery in a single query:

pilots, err := models.Pilots(
    models.PreloadCount.Pilot.Jets(
        models.SelectWhere.Jet.Active.EQ(true), // optional filtering
    ),
).All(ctx, db)

// SQL: SELECT *, (SELECT count(*) FROM jets WHERE jets.pilot_id = pilots.id AND jets.active = true) AS "__count_Jets" FROM pilots

fmt.Printf("Pilot has %d jets\n", *pilots[0].C.Jets)

ThenLoadCount - Loads counts via separate query after main query:

pilots, err := models.Pilots(
    models.ThenLoadCount.Pilot.Jets(),
).All(ctx, db)

InsertThenLoadCount - Loads counts after insert:

pilot, err := models.Pilots.Insert(
    &models.PilotSetter{Name: omit.From("John")},
    models.InsertThenLoadCount.Pilot.Jets(),
).One(ctx, db)

The InsertThenLoadCount could easily be expanded to UpdateThenLoadCount as well if we want to. I didn't generate it since MySQL update queries doesn't support returning (.One() or All()). However I can make that change as well if want to.

Models now include a C struct with *int64 pointers for each to-many relationship count.

MySQL Insert Loader Support

Added support InsertThenLoad and InsertThenLoadCount for MySQL. Previously the issue was that bob.Exec in insertAll where running the loaders causing issues with nil. Now we save and clear loaders before executing the INSERT, then transfer them to the SELECT query that retrieves the inserted row.

Changes

  • New plugin: gen/plugins/counts.go
  • New templates: gen/templates/counts/
  • Updated: gen/templates.go, gen/plugins/plugins.go - Register counts plugin
  • Updated: gen/templates/models/table/001_types.go.tpl - Add C struct block
  • Fixed: dialect/mysql/table.go - Properly handle loaders in insert flow

Configuration

plugins:
  counts:
    disabled: false  # Enable/disable the counts plugin

Breaking Changes

None. The C struct is added to models but doesn't affect existing functionality.


I had difficulties figuring out how the generated queries are tested against a DB? Can you point me in the right direction for this, since I would like to include some actual query tests.

@stephenafamo
Copy link
Owner

First of all, I must say a big thank you for such a well thought out and high quality contribution.

I had difficulties figuring out how the generated queries are tested against a DB? Can you point me in the right direction for this, since I would like to include some actual query tests.

To do this, test templates should be included too, which will generate test code, and these tests are run when testing generation.

@jacobmolby
Copy link
Contributor Author

First of all, I must say a big thank you for such a well thought out and high quality contribution.

I had difficulties figuring out how the generated queries are tested against a DB? Can you point me in the right direction for this, since I would like to include some actual query tests.

To do this, test templates should be included too, which will generate test code, and these tests are run when testing generation.

Do you have an example of where this has been done?

@stephenafamo
Copy link
Owner

See gen/templates/factory/bobfactory_main.bob_test.go.tpl, and any file ending in _test.go.tpl

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants