0

私は Scala の初心者で、Scala と Play 2.1 を使用して Web アプリケーションを作成することに初めて挑戦しています。

リアクティブ Mongo とリアクティブ Mongo Play プラグインを使用して、MongoDB への User オブジェクトのリポジトリとなる UserRepository クラスを作成しようとしています。

ここが私が理解していないことです。次のリポジトリ クラスを検討してください。

@Singleton
class UserRepository @Inject() (db: DB) {
    import scala.concurrent.ExecutionContext.Implicits.global

    val collection:JSONCollection = db.collection("users")

    def find(terms: (String, JsValueWrapper)*): Future[List[User]] = {
      find(Json.obj(terms:_*))
    }

    //Problematic method that sometimes works sometimes not
    def findOne(terms: (String, JsValueWrapper)*): Future[Option[User]] = {
       findOne(Json.obj(terms:_*))
    }

    def find(selector: JsObject): Future[List[User]] = {
       collection.find(selector).cursor[User].toList()
    }

    def findOne(selector: JsObject): Future[Option[User]] = {
       collection.find(selector).one[User]
    }

    def findByEmailAndPassword(email: String, password: String): Future[Option[User]] = {
       //problematic method works fine here
       findOne("email" -> email, "password" -> password)
    }

} 

私は、vararg タプルを使用して Mongo セレクター オブジェクトを記述する find メソッドを作成したいと考えていました。リポジトリ クラス自体の findByEmailAndPassword メソッドでそれを使用すると、正常に動作します。

アップデート:

タプルを 1 つだけ指定すると (どこから呼び出されたかに関係なく)、findOne の呼び出しはキー -> 値の形式では機能しません。これは機能します:

findOne(("email", "example@test.com"))
findOne("email" -> "example@test.com", "password" -> "foo")
val term: (String, JsValueWrapper) = "email" ->  "example@test.com"
findOne(term)

これは機能しません:

findOne("email" -> "example@test.com")

なんで?

更新 2:

それ以来、メソッドを次のように更新しました (したがって、リポジトリ クラスの外部に Writes および Reads オブジェクトを提供できます)。

def findOne[V](terms: (String, V)*)(implicit reads: Reads[T], writes: Writes[V]): Future[Option[T]] = {
  val objMap = terms.map(entry => (entry._1, Json.toJsFieldJsValueWrapper(entry._2)))
  findOne(Json.obj(objMap:_*))
}

今回のアップデート後、エラーは発生しなくなりました。そもそもなぜうまくいかなかったのかはまだわかりませんが。

元の質問:

ただし、次のようにコントローラーでリポジトリを使用すると:

@Singleton
class TestController @Inject() (val userRepository: UserRepository) extends Controller {

  def testFind = Action { implicit request =>
    Async {
      //This call to findOne doesn't work
      val query = userRepository.findOne("email" -> "test@example.com")

      //This call works fine:
      //val query = userRepository.findOne(Json.obj("email" -> "test@example.com"))
      query.map(
        userOption => userOption.map(u => Ok("Found user: " + u.toString)).getOrElse(Ok("No user found.")))
      }
    }
}

Controller クラスを呼び出すuserRepository.findOne("email" -> "test@example.com")と、機能しません。しかし、リポジトリ クラスで同じメソッドに同様の呼び出しを行うと、問題なく動作します (たとえば、findByEmailAndPassword は問題なく動作します)。コントローラーから呼び出されたときに発生するエラーは次のとおりです。

Overloaded method value [findOne] cannot be applied to ((String, String))

暗黙的な変換の問題だと思いましたが、コントローラー クラスにはリポジトリ クラスと同じインポートがすべてありますが、メソッドがコントローラーで使用されている場合、文字列は JsValueWrapper に変換されません。

findOne(terms*) メソッドを宣言する際の代替ソリューションも歓迎します。

4

1 に答える 1

0

内部的に findOne(term: (String, JsValueWrapper)*) に委任する別の findOne(term: (String, JsValueWrapper)) メソッドを追加で定義する必要があるかもしれません。

于 2013-10-02T19:42:30.820 に答える