diff --git a/datastore/measurements.go b/datastore/measurements.go index acd07f9..f887b04 100644 --- a/datastore/measurements.go +++ b/datastore/measurements.go @@ -1,7 +1,6 @@ package datastore import ( - "fmt" "strings" "time" @@ -59,8 +58,7 @@ func (s *measurementsStore) List(opt *models.MeasurementListOptions) ([]*models. sql += " WHERE (" + strings.Join(conds, ") AND (") + ")" } - sql += fmt.Sprintf(" LIMIT $%v OFFSET $%v;", len(conds)+1, len(conds)+2) - vals = append(vals, opt.PerPageOrDefault(), opt.Offset()) + sql += ";" var measurements []*models.Measurement err := s.dbh.Select(&measurements, sql, vals...) diff --git a/datastore/strains.go b/datastore/strains.go index 18f8888..ac4208a 100644 --- a/datastore/strains.go +++ b/datastore/strains.go @@ -1,7 +1,6 @@ package datastore import ( - "fmt" "strings" "time" @@ -9,7 +8,7 @@ import ( ) func init() { - DB.AddTableWithName(models.Strain{}, "strains").SetKeys(true, "Id") + DB.AddTableWithName(models.StrainBase{}, "strains").SetKeys(true, "Id") } type strainsStore struct { @@ -18,7 +17,8 @@ type strainsStore struct { func (s *strainsStore) Get(id int64) (*models.Strain, error) { var strain models.Strain - if err := s.dbh.SelectOne(&strain, `SELECT * FROM strains WHERE id=$1;`, id); err != nil { + err := s.dbh.SelectOne(&strain, `SELECT s.*, array_agg(m.id) AS measurements FROM strains s LEFT OUTER JOIN measurements m ON m.strain_id=s.id WHERE s.id=$1 GROUP BY s.id;`, id) + if err != nil { return nil, err } if &strain == nil { @@ -31,9 +31,11 @@ func (s *strainsStore) Create(strain *models.Strain) (bool, error) { currentTime := time.Now() strain.CreatedAt = currentTime strain.UpdatedAt = currentTime - if err := s.dbh.Insert(strain); err != nil { + base := strain.StrainBase + if err := s.dbh.Insert(base); err != nil { return false, err } + strain.Id = base.Id return true, nil } @@ -42,13 +44,13 @@ func (s *strainsStore) List(opt *models.StrainListOptions) ([]*models.Strain, er opt = &models.StrainListOptions{} } - sql := `SELECT * FROM strains` + sql := `SELECT s.*, array_agg(m.id) AS measurements FROM strains s LEFT OUTER JOIN measurements m ON m.strain_id=s.id` var conds []string var vals []interface{} if opt.Genus != "" { - conds = append(conds, `species_id IN (SELECT s.id FROM species s + conds = append(conds, `s.species_id IN (SELECT s.id FROM species s INNER JOIN genera g ON g.id = s.genus_id WHERE lower(g.genus_name) = $1)`) vals = append(vals, opt.Genus) } @@ -57,8 +59,7 @@ func (s *strainsStore) List(opt *models.StrainListOptions) ([]*models.Strain, er sql += " WHERE (" + strings.Join(conds, ") AND (") + ")" } - sql += fmt.Sprintf(" LIMIT $%v OFFSET $%v;", len(conds)+1, len(conds)+2) - vals = append(vals, opt.PerPageOrDefault(), opt.Offset()) + sql += " GROUP BY s.id;" var strains []*models.Strain err := s.dbh.Select(&strains, sql, vals...) @@ -79,7 +80,8 @@ func (s *strainsStore) Update(id int64, strain *models.Strain) (bool, error) { } strain.UpdatedAt = time.Now() - changed, err := s.dbh.Update(strain) + + changed, err := s.dbh.Update(strain.StrainBase) if err != nil { return false, err } @@ -97,7 +99,7 @@ func (s *strainsStore) Delete(id int64) (bool, error) { return false, err } - deleted, err := s.dbh.Delete(strain) + deleted, err := s.dbh.Delete(strain.StrainBase) if err != nil { return false, err } diff --git a/datastore/strains_test.go b/datastore/strains_test.go index 063cb8f..dc3690b 100644 --- a/datastore/strains_test.go +++ b/datastore/strains_test.go @@ -12,17 +12,17 @@ import ( func insertStrain(t *testing.T, tx *modl.Transaction) *models.Strain { // clean up our target table tx.Exec(`DELETE FROM strains;`) - strain := newStrain(t, tx) - if err := tx.Insert(strain); err != nil { + s := newStrain(t, tx) + if err := tx.Insert(s); err != nil { t.Fatal(err) } - return strain + return &models.Strain{s, []int64(nil)} } -func newStrain(t *testing.T, tx *modl.Transaction) *models.Strain { +func newStrain(t *testing.T, tx *modl.Transaction) *models.StrainBase { // we want to create and insert a species (and genus) record too species := insertSpecies(t, tx) - return &models.Strain{ + return &models.StrainBase{ SpeciesId: species.Id, StrainName: "Test Strain", StrainType: "Test Type", @@ -67,11 +67,12 @@ func TestStrainsStore_Create_db(t *testing.T) { tx, _ := DB.Begin() defer tx.Rollback() - strain := newStrain(t, tx) + base_strain := newStrain(t, tx) + strain := models.Strain{base_strain, []int64(nil)} d := NewDatastore(tx) - created, err := d.Strains.Create(strain) + created, err := d.Strains.Create(&strain) if err != nil { t.Fatal(err) } diff --git a/models/measurements.go b/models/measurements.go index 4c88c57..98d1a32 100644 --- a/models/measurements.go +++ b/models/measurements.go @@ -17,7 +17,7 @@ import ( // combination of strain & characteristic, but not both. type Measurement struct { Id int64 `json:"id,omitempty"` - StrainId int64 `db:"strain_id" json:"strainId"` + StrainId int64 `db:"strain_id" json:"strain"` CharacteristicId int64 `db:"characteristic_id" json:"characteristicId"` TextMeasurementTypeId NullInt64 `db:"text_measurement_type_id" json:"textMeasurementTypeId"` TxtValue NullString `db:"txt_value" json:"txtValue"` diff --git a/models/measurements_test.go b/models/measurements_test.go index 857295a..6978527 100644 --- a/models/measurements_test.go +++ b/models/measurements_test.go @@ -59,7 +59,7 @@ func TestMeasurementService_Create(t *testing.T) { mux.HandleFunc(urlPath(t, router.CreateMeasurement, nil), func(w http.ResponseWriter, r *http.Request) { called = true testMethod(t, r, "POST") - testBody(t, r, `{"measurement":{"id":1,"strainId":1,"characteristicId":1,"textMeasurementTypeId":null,"txtValue":null,"numValue":1.23,"confidenceInterval":null,"unitTypeId":1,"notes":"a note","testMethodId":null,"createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z"}}`+"\n") + testBody(t, r, `{"measurement":{"id":1,"strain":1,"characteristicId":1,"textMeasurementTypeId":null,"txtValue":null,"numValue":1.23,"confidenceInterval":null,"unitTypeId":1,"notes":"a note","testMethodId":null,"createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z"}}`+"\n") w.WriteHeader(http.StatusCreated) writeJSON(w, want) @@ -128,7 +128,7 @@ func TestMeasurementService_Update(t *testing.T) { mux.HandleFunc(urlPath(t, router.UpdateMeasurement, map[string]string{"Id": "1"}), func(w http.ResponseWriter, r *http.Request) { called = true testMethod(t, r, "PUT") - testBody(t, r, `{"measurement":{"id":1,"strainId":1,"characteristicId":1,"textMeasurementTypeId":null,"txtValue":null,"numValue":4.56,"confidenceInterval":null,"unitTypeId":1,"notes":"a note","testMethodId":null,"createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z"}}`+"\n") + testBody(t, r, `{"measurement":{"id":1,"strain":1,"characteristicId":1,"textMeasurementTypeId":null,"txtValue":null,"numValue":4.56,"confidenceInterval":null,"unitTypeId":1,"notes":"a note","testMethodId":null,"createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z"}}`+"\n") w.WriteHeader(http.StatusOK) writeJSON(w, want) }) diff --git a/models/species.go b/models/species.go index e29beeb..e10ba8d 100644 --- a/models/species.go +++ b/models/species.go @@ -22,7 +22,7 @@ type SpeciesBase struct { type Species struct { *SpeciesBase - Strain NullSliceInt64 `db:"strains" json:"strains"` + Strains NullSliceInt64 `db:"strains" json:"strains"` } type SpeciesJSON struct { diff --git a/models/strains.go b/models/strains.go index 7ce60b1..6515683 100644 --- a/models/strains.go +++ b/models/strains.go @@ -12,7 +12,7 @@ import ( ) // A Strain is a subclass of species -type Strain struct { +type StrainBase struct { Id int64 `json:"id,omitempty"` SpeciesId int64 `db:"species_id" json:"species"` StrainName string `db:"strain_name" json:"strainName"` @@ -26,6 +26,11 @@ type Strain struct { DeletedAt NullTime `db:"deleted_at" json:"deletedAt"` } +type Strain struct { + *StrainBase + Measurements NullSliceInt64 `db:"measurements" json:"measurements"` +} + type StrainJSON struct { Strain *Strain `json:"strain"` } @@ -40,27 +45,30 @@ func (s *Strain) String() string { func NewStrain() *Strain { return &Strain{ - StrainName: "Test Strain", - StrainType: "Test Type", - Etymology: NullString{ - sql.NullString{ - String: "Test Etymology", - Valid: true, - }, - }, - AccessionBanks: "Test Accession", - GenbankEmblDdb: NullString{ - sql.NullString{ - String: "Test Genbank", - Valid: true, - }, - }, - IsolatedFrom: NullString{ - sql.NullString{ - String: "", - Valid: false, + &StrainBase{ + StrainName: "Test Strain", + StrainType: "Test Type", + Etymology: NullString{ + sql.NullString{ + String: "Test Etymology", + Valid: true, + }, + }, + AccessionBanks: "Test Accession", + GenbankEmblDdb: NullString{ + sql.NullString{ + String: "Test Genbank", + Valid: true, + }, + }, + IsolatedFrom: NullString{ + sql.NullString{ + String: "", + Valid: false, + }, }, }, + make([]int64, 0), } } diff --git a/models/strains_test.go b/models/strains_test.go index 5af252d..6a58a5e 100644 --- a/models/strains_test.go +++ b/models/strains_test.go @@ -55,7 +55,7 @@ func TestStrainService_Create(t *testing.T) { mux.HandleFunc(urlPath(t, router.CreateStrain, nil), func(w http.ResponseWriter, r *http.Request) { called = true testMethod(t, r, "POST") - testBody(t, r, `{"strain":{"id":1,"species":1,"strainName":"Test Strain","strainType":"Test Type","etymology":"Test Etymology","accessionBanks":"Test Accession","genbankEmblDdb":"Test Genbank","isolatedFrom":null,"createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z","deletedAt":null}}`+"\n") + testBody(t, r, `{"strain":{"id":1,"species":1,"strainName":"Test Strain","strainType":"Test Type","etymology":"Test Etymology","accessionBanks":"Test Accession","genbankEmblDdb":"Test Genbank","isolatedFrom":null,"createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z","deletedAt":null,"measurements":[]}}`+"\n") w.WriteHeader(http.StatusCreated) writeJSON(w, want) @@ -124,7 +124,7 @@ func TestStrainService_Update(t *testing.T) { mux.HandleFunc(urlPath(t, router.UpdateStrain, map[string]string{"Id": "1"}), func(w http.ResponseWriter, r *http.Request) { called = true testMethod(t, r, "PUT") - testBody(t, r, `{"strain":{"id":1,"species":1,"strainName":"Test Strain Updated","strainType":"Test Type Updated","etymology":"Test Etymology","accessionBanks":"Test Accession Updated","genbankEmblDdb":"Test Genbank Updated","isolatedFrom":null,"createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z","deletedAt":null}}`+"\n") + testBody(t, r, `{"strain":{"id":1,"species":1,"strainName":"Test Strain Updated","strainType":"Test Type Updated","etymology":"Test Etymology","accessionBanks":"Test Accession Updated","genbankEmblDdb":"Test Genbank Updated","isolatedFrom":null,"createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z","deletedAt":null,"measurements":[]}}`+"\n") w.WriteHeader(http.StatusOK) writeJSON(w, want) })