2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

【備忘録】Rails+MongoDBでDocumentNotFoundが出力された際のmongoid.yml設定

Last updated at Posted at 2018-12-24

Rails+MongoDBにて
RailsからDBアクセスした際、DBにレコードがなかった時に
本来はエラーメッセージを画面内に設定するなど、
次のアクションを起こしたいところを
エラー画面DocumentNotFoundが出た時の対策です。

ローカル環境の例

ログインを行い、DBにレコードがあった場合、
次画面遷移を行う。なければエラーメッセージを出力し
再度ログイン画面を読み込む。
という処理を行いたい想定です。

    def Login
        @user = User.find_by(name: params[:name])
        if @user && @user.authenticate(params[:password])
            # 次画面遷移
            redirect_to("/Hoge")
        else
       # エラーメッセージだす
            @errorMessage = "ログインに失敗"
            render("/top")
        end
    end

スクリーンショット 2018-12-15 18.14.22.png

↓存在しないユーザでログインする
スクリーンショット 2018-12-15 18.20.34.png

再度ログイン画面を読み込みたいところ、謎のDocumentNotFoudエラー発生です。。

原因

この例では、DBを検索して一件のみを取得する際に、find_byを使用して検索を行なっていますが、ここでエラーがおきています。
ググった結果、mongoDBのfind_byメソッドにエラーの設定がされています。
[MongoDB公式ドキュメントのfind_byのクエリメソッドの実装]
(https://github.com/mongodb/mongoid/blob/71c29a80599087008ecfbd057d3d049c2153c7ce/lib/mongoid/findable.rb#L113)
を確認すると

findable.rb
    # Find the first +Document+ given the conditions.
    # If a matching Document is not found and
    # Mongoid.raise_not_found_error is true it raises
    # Mongoid::Errors::DocumentNotFound, return null nil elsewise.
    #
    # @example Find the document by attribute other than id
    #   Person.find_by(:username => "superuser")
    #
    # @param [ Hash ] attrs The attributes to check.
    #
    # @raise [ Errors::DocumentNotFound ] If no document found
    # and Mongoid.raise_not_found_error is true.
    #
    # @return [ Document, nil ] A matching document.
    #
    # @since 3.0.0
    def find_by(attrs = {})
      result = where(attrs).find_first
      if result.nil? && Mongoid.raise_not_found_error
        raise(Errors::DocumentNotFound.new(self, attrs))
      end
      yield(result) if result && block_given?
      result
    end

コメントをみると、

 # @raise [ Errors::DocumentNotFound ] If no document found
 # and Mongoid.raise_not_found_error is true.

とあります。レコードが存在しなくMongoid.ymlのraise_not_found_errorがtrueの場合はDocumentNotFoundが出力されます。

対策(ローカル環境編)

config/mongoid.ymlの設定を見てみる

mongoid.yml
development:
  clients:
    default:
・・・
  options:
・・・
    # Raise an error when performing a #find and the document is not found.
    # (default: true)
    #raise_not_found_error: true

デフォルトではraise_not_found_errorはtrueに設定されているため、DocumentNotFoundが出力される・・・
ここをfalseにします。

#raise_not_found_error: false

また、設定ファイルを反映するためには
ローカルのサーバを再起動させる必要があります。

$ rails s

動作確認(ローカル環境)

スクリーンショット 2018-12-15 18.14.22.png
↓ログイン

スクリーンショット 2018-12-16 1.36.43.png

無事DocumentNotFoundにならなくなりました。

heroku環境の例と対策

次にローカル以外の環境の例です
ここではherokuでの例をあげます
スクリーンショット 2018-12-15 18.14.22.png
↓存在しないユーザでログイン

スクリーンショット 2018-12-15 18.20.48.png

ページが存在しないエラーになりました。
コマンドにてログを確認します。
(事前にherokuにログインが必要です$ heroku login)

$ heroku logs -t

2018-12-15T08:50:55.791954+00:00 app[web.1]: F, [2018-12-15T08:50:55.791887 #4] FATAL -- : [a510ff0f-8ab4-4197-a560-8ef1fc93e3b5] Mongoid::Errors::DocumentNotFound (
2018-12-15T08:50:55.791957+00:00 app[web.1]: message:
2018-12-15T08:50:55.791960+00:00 app[web.1]: Document not found for class Like with attributes {:user_id=>BSON::ObjectId('hoge'), :post_id=>BSON::ObjectId('piyo')}.
2018-12-15T08:50:55.791961+00:00 app[web.1]: summary:
2018-12-15T08:50:55.791963+00:00 app[web.1]: When calling Like.find_by with a hash of attributes, all attributes provided must match a document in the database or this error will be raised.
2018-12-15T08:50:55.791965+00:00 app[web.1]: resolution:
2018-12-15T08:50:55.791967+00:00 app[web.1]: Search for attributes that are in the database or set the Mongoid.raise_not_found_error configuration option to false, which will cause a nil to be returned instead of raising this error.):

この例では次画面遷移時にLikeというコレクションを読んでいますが、その際にMongoid::Errors::DocumentNotFoundが発生しています。原因がLike.find_byを実行時にレコードがなかったためにエラってます。対策としてデータを用意するか、mongoid.ymlのraise_not_found_errorオプションをfalseにすると良い。と書いています。

ここではデータがない場合も想定しているのでMongoid.ymlのraise_not_found_error:falseにします。
デフォルトではoptions自体がないので追記します。
(optionsの階層に注意してください。
productionの直下です。clientsの直下ではないです。)

mongid.yml
production:
 clients:
   default:
    uri: mongodb://heroku_9v5bk0vx:fuga@hage.mlab.com:37977/heroku_9v5bk0vx
    options:
      connect_timeout: 15
 options:
    raise_not_found_error: false

mongoid.ymlを設定後にherokuにデプロイ

動作確認(heroku環境)

スクリーンショット 2018-12-15 18.14.22.png
↓ログイン
スクリーンショット 2018-12-16 2.08.13.png

herokuでも無事DocumentNotFoundにならなくなりました。

環境

(今回のエラーはvarsionは関係ないですが載せておきます)

Rails 5.2.1
ruby 2.3.7p456
MongoDB v4.0.2

参照

Mongoidの隠れクエリメソッドfind_by!とraise_not_found_errorオプションの関係
ありがとうございます。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?