Your Question

How can I find one record that may or may not be present and neither case is an error?

The document you expected this should be explained

https://gorm.io/docs/query.html

Expected answer

Documentation suggests using Limit(1).Find(&foo).

What I would like to do is something like this:

var record *MyStruct
err := db.Where("foo = 1").Limit(1).Find(record).Error // DOESN'T WORK: returns 'invalid value' error
if record == nil {
  doSomething()
} else {
  doSomethingElse()
}

This is clean and very clear. No error is raised if the record is missing and the record is simply nil as you would expect, because it hasn't been found.

Unfortunately it seems this is not possible because if I try this I get invalid value errors.

I have found two other ways to do it, both of which are suboptimal:

Method 1:

record := &MyStruct{}
err := db.Where("foo = 1").First(&record).Error // logs "not found" Error even though nothing is broken if it doesn't exist
if err == gorm.ErrRecordNotFound {
  doSomething()
} else {
  doSomethingElse()
}

This suboptimal because gorm logs a not found message at level Error every time we hit record not found, even though this is completely normal and does not indicate anything is broken. It fills our logs with useless errors.

Method 2

record := &MyStruct{ID: -1}
err := db.Where("foo = 1").Limit(1).Find(&record).Error
if record.ID == -1 { // relies on hacky "magic" flag value
  doSomething()
} else {
  doSomethingElse()
}

This doesn't log useless errors if the record is missing, but is still suboptimal because it relies on "magic" numbers. It's really messy to put the ID as -1 and it isn't even consistent because not all of our database tables have an ID primary key. In some cases there may not be any value at all that is safe to use as a magic flag value.

Perhaps I have missed something. What is the correct way to handle this (quite common) use case?

Comment From: jinzhu

record := &MyStruct{} result := db.Where("foo = 1").Limit(1).Find(&record) result.RowsAffected == 1 result.Error == nil

Comment From: LouisSayers

So basically we're saying do:

func MyStructFind(fooId uint) (*MyStruct, error) {
  var record MyStruct
  result := db.Where("foo = ?", fooId).Limit(1).Find(&record)

  if RowsAffected == 0 { // nil
    return nil, result.Error
  } else {
    return &record, result.Error 
  }
}

I also find this really tedious to do and it seems like it's something that's commonly sought after. It'd be much nicer to be able to do:

func MyStructFind(fooId uint) (*MyStruct, error) {
  var record *MyStruct
  result := db.Where("foo = ?", fooId).Limit(1).FindOrNil(record)

  return record, result.Error 
}

Comment From: NicklasWallgren

FirstOrNil would be really helpful.

Comment From: tpjg

To disable logging for one statement, use a transaction, e.g.

  tx := db.Begin(&sql.TxOptions{ReadOnly: true, Isolation: sql.LevelDefault})
  l := tx.Logger
  tx.Logger = logger.Discard

  record := &MyStruct{}
  err := tx.Where("foo = 1").First(&record).Error 
  tx.Commit()
  tx.Logger = l
  if errors.Is(err, gorm.ErrRecordNotFound) {
    doSomething()
  } else {
    doSomethingElse()
  }

Comment From: Lykr

To be honest, treating nil result as an error is a stupid design.