2011-05-10 1 views
3

현재 웹 서비스 구현을 위해 Casbah와 MongoDB를 사용하고 있습니다. 지금까지 아무런 문제가 없습니다. 나는 Scala도 사용하고있다.Casbah & Rogue for MongoDB - 쿼리 기능

그러나 find/findOne 유형 쿼리를 많이 수행 할 때 Casbah보다 더 나은 점이 있는지 궁금했습니다.

나는 형식이 안전한 Scala 기반 DSL 인 Rogue를 통해보다 쉽게 ​​쿼리를 읽을 수있게되었습니다.

웹 서비스 프로젝트가 커지고 복잡해지면서 Rogue가 쿼리를 지원할 수 있도록 Rogue로 전환하는 것이 유용한 지 알고 싶었습니다.

계속 진행해야하는지 또는 더 나은 것으로 전환해야하는지 알아야합니다.

답변

12

현재 로그는 Lift's MongoDB-Record 시스템에서만 작동합니다.

전체 유형 안전을 제공하는 이유 중 하나는 Lift-Record에 강력하고 잘 정의 된 개체 구조를 사용한다는 점입니다. 당신은 그것을위한 완벽한 구조를 가지고 있기 때문에 "자유 형식"쿼리를 수행하는 능력이 훨씬 적습니다. 최근 스칼라 웨비나에 대한 Lift-Record + Rogue 데모를 통해 내가 의미하는 바가 있습니다. 어떤 것들은 Lift & Rogue에서 변경되었으므로 코드가 약간 오래된 것일 수 있지만 여전히 대표적입니다. 당신이 볼 수 있듯이, 당신은 ... 악성을 강력하게 형식화, 안전 쿼리의 혜택을 받기 위해 미리 당신의 MongoDB의 데이터 모델을 정의하는 데 필요한

object LiftRecordDemo extends Application { 
    // We'll use enums for Type and Subtype 
    object EventType extends Enumeration { 
    type EventType = Value 
    val Conference, Webinar = Value 
    } 

    object EventSubType extends Enumeration { 
    type EventSubType = Value 
    val FullDay = Value("Full Day") 
    val HalfDay = Value("Half Day") 
    } 



    class MongoEvent extends MongoRecord[MongoEvent] with MongoId[MongoEvent] { 
    def meta = MongoEvent 

    object name extends StringField(this, 255) 
    object eventType extends EnumField(this, EventType) 
    object eventSubType extends OptionalEnumField(this, EventSubType) 
    object location extends JsonObjectField[MongoEvent, EventLocation](this, EventLocation) { 
     def defaultValue = EventLocation(None, None, None, None, None, None, None) 
    } 

    object hashtag extends OptionalStringField(this, 32) 
    object language extends OptionalStringField(this, 32) 
    object date extends JsonObjectField[MongoEvent, EventDate](this, EventDate) { 
     def defaultValue = EventDate(new DateTime, None) 
    } 

    object url extends OptionalStringField(this, 255) 
    object presenter extends OptionalStringField(this, 255) 

    } 

    object MongoEvent extends MongoEvent with MongoMetaRecord[MongoEvent] { 
    override def collectionName = "mongoEvents" 
    override def formats = super.formats + new EnumSerializer(EventType) + new EnumSerializer(EventSubType) 
    } 


    case class EventLocation(val venueName: Option[String], val url: Option[String], val address: Option[String], val city: Option[String], val state: Option[String], val zip: Option[String], val country: Option[String]) extends JsonObject[EventLocation] { 
    def meta = EventLocation 
    } 

    object EventLocation extends JsonObjectMeta[EventLocation] 

    case class EventDate(start: DateTime, end: Option[DateTime]) extends JsonObject[EventDate] { 
    def meta = EventDate 
    } 

    object EventDate extends JsonObjectMeta[EventDate] 
} 

:이 MongoDB를위한 리프트-기록 모델 컴파일시에 대부분을 강제합니다. 다음은이 모델에 대한 일부 악성의 예는 다음과 같습니다

// Tell Lift about our DB 
    val mongoAddr = MongoAddress(MongoHost("127.0.0.1", 27017), "scalaWebinar") 

    MongoDB.defineDb(DefaultMongoIdentifier, mongoAddr) 

    // Rogue gives us a saner approach, although still hobbled by some 
    // of Lift-MongoDB-Record's limits on embedded docs 

    val q = MongoEvent where (_.eventType eqs EventType.Webinar) 

    println("Rogue created a Query '%s'\n\n".format(q)) 

    for (x <- MongoEvent where (_.eventType eqs EventType.Webinar)) { 
    println("Name: %s Presenter: %s\n".format(x.name, x.presenter)) 
    } 

    // Rogue can also do sorting for you, which is useful 

    println("\n\n\n") 

    for (x <- MongoEvent where (_.eventType eqs EventType.Conference) 
         orderAsc(_.language) andDesc(_.name)) { 
    println("Name: %s Language: %s\n".format(x.name, x.language)) 
    } 
    val start = new DateTime(2011, 2, 1, 0, 0, 0, 0) 
    val end = new DateTime(2011, 3, 1, 0, 0, 0, 0) 

    /** The following would be nice but unfortunately, 
     doesn't work because of lift's current embedded doc 
     implementation 
    */ 
    //val dateQ = MongoEvent where (_.date.start after start) 
          //and (_.date.end before end) 

공지 사항 나는 그들이 강력하게 정의 된 컴파일 시간 데이터 모델의 오프 작동 단지, 불량 및 리프트 기록이 굉장하지 말하고 있지 않다.

Casbah와 비슷한 컨텍스트를 사용하려는 경우 가능한 한 MongoDB의 inbuilt Query 모델을 모방하도록 설계된 기본 DSL이 있습니다. 그것은 임의의 기본 모델에 대해 작동하지만 가능한 경우 유형 안전 수준을 강화하기 위해 많은 작업을 수행합니다. 여기에 카스바의 쿼리의 예 (약간의 같은 프레젠테이션으로 일자)된다

// What about querying? Lets find all the non-US events 

    for (x <- mongo.find(MongoDBObject("location.country" -> 
         MongoDBObject("$ne" -> "USA")))) println(x) 

    /* There's a problem here: We got back the Webinars too because 
    They don't have a country at all, so they aren't "USA" 
    */ 
    println("\n\nTesting for existence of Location.Country:") 

    for (x <- mongo.find(MongoDBObject("location.country" -> MongoDBObject(
         "$ne" -> "USA", 
         "$exists" -> true 
        )))) println(x) 

    // This is getting a bit unwieldy. Thankfully, Casbah offers a DSL 
    val q = $or ("location.country" -> "USA", "location.country" -> "Japan") 

    println("\n Created a DBObject: %s".format(q)) 

    println("\n Querying using DSL Object...") 

    for (x <- mongo.find(q)) println(x) 

    // It's possible to construct more complex queries too. 

    // Lets find everything in February 

    println("\n February Events...") 
    val start = new DateTime(2011, 2, 1, 0, 0, 0, 0) 
    val end = new DateTime(2011, 3, 1, 0, 0, 0, 0) 
    val dateQ = "date.start" $gte start $lt end 

    println("\n Date Query: %s".format(dateQ)) 

    for (x <- mongo.find(dateQ, MongoDBObject("name" -> true, "date" -> true))) println(x) 

특히 우리는 자유 형식 모델에 대해 쿼리 대신 중첩 된 MongoDB를 정의의 DSL 연산자를 사용하고 있습니다. 클래스를 사용하여 유형을 테스트하기 위해 오른쪽 $ 유형 운영자에 이르기까지 운영자의 환상적인 매핑 많이, 컴파일 타임의 안전을 위해이 매니페스트 있습니다

"Casbah's $type operator" should { 
    "Accept raw Byte indicators (e.g. from org.bson.BSON)" in { 
    // Don't need to test every value here since it's just a byte 
    val typeOper = "foo" $type org.bson.BSON.NUMBER_LONG 
    typeOper must notBeNull 
    typeOper.toString must notBeNull 
    typeOper must haveSuperClass[DBObject] 
    typeOper must beEqualTo(nonDSL("foo", "$type", org.bson.BSON.NUMBER_LONG)) 
    } 

    "Accept manifested Type arguments" in { 
    "Doubles" in { 
     val typeOper = "foo".$type[Double] 
     typeOper must notBeNull 
     typeOper.toString must notBeNull 
     typeOper must haveSuperClass[DBObject] 
     typeOper must beEqualTo(nonDSL("foo", "$type", org.bson.BSON.NUMBER)) 
    } 
    "Strings" in { 
     val typeOper = "foo".$type[String] 
     typeOper must notBeNull 
     typeOper.toString must notBeNull 
     typeOper must haveSuperClass[DBObject] 
     typeOper must beEqualTo(nonDSL("foo", "$type", org.bson.BSON.STRING)) 
    } 
    "Object" in { 
     "via BSONObject" in { 
     val typeOper = "foo".$type[org.bson.BSONObject] 
     typeOper must notBeNull 
     typeOper.toString must notBeNull 
     typeOper must haveSuperClass[DBObject] 
     typeOper must beEqualTo(nonDSL("foo", "$type", org.bson.BSON.OBJECT)) 
     } 
     "via DBObject" in { 
     val typeOper = "foo".$type[DBObject] 
     typeOper must notBeNull 
     typeOper.toString must notBeNull 
     typeOper must haveSuperClass[DBObject] 
     typeOper must beEqualTo(nonDSL("foo", "$type", org.bson.BSON.OBJECT)) 
     } 
    } 
    "Array" in { 
     "via BasicDBList" in { 
     val typeOper = "foo".$type[BasicDBList] 
     typeOper must notBeNull 
     typeOper.toString must notBeNull 
     typeOper must haveSuperClass[DBObject] 
     typeOper must beEqualTo(nonDSL("foo", "$type", org.bson.BSON.ARRAY)) 
     } 
     "via BasicBSONList" in { 
     val typeOper = "foo".$type[org.bson.types.BasicBSONList] 
     typeOper must notBeNull 
     typeOper.toString must notBeNull 
     typeOper must haveSuperClass[DBObject] 
     typeOper must beEqualTo(nonDSL("foo", "$type", org.bson.BSON.ARRAY)) 
     } 
    } 
    "OID" in { 
     val typeOper = "foo".$type[ObjectId] 
     typeOper must notBeNull 
     typeOper.toString must notBeNull 
     typeOper must haveSuperClass[DBObject] 
     typeOper must beEqualTo(nonDSL("foo", "$type", org.bson.BSON.OID)) 
    } 
    "Boolean" in { 
     val typeOper = "foo".$type[Boolean] 
     typeOper must notBeNull 
     typeOper.toString must notBeNull 
     typeOper must haveSuperClass[DBObject] 
     typeOper must beEqualTo(nonDSL("foo", "$type", org.bson.BSON.BOOLEAN)) 
    } 
    "Date" in { 
     "via JDKDate" in { 
     val typeOper = "foo".$type[java.util.Date] 
     typeOper must notBeNull 
     typeOper.toString must notBeNull 
     typeOper must haveSuperClass[DBObject] 
     typeOper must beEqualTo(nonDSL("foo", "$type", org.bson.BSON.DATE)) 
     } 
     "via Joda DateTime" in { 
     val typeOper = "foo".$type[org.joda.time.DateTime] 
     typeOper must notBeNull 
     typeOper.toString must notBeNull 
     typeOper must haveSuperClass[DBObject] 
     typeOper must beEqualTo(nonDSL("foo", "$type", org.bson.BSON.DATE)) 
     } 
    } 
    "None (null)" in { 
     // For some reason you can't use NONE 
     val typeOper = "foo".$type[Option[Nothing]] 
     typeOper must notBeNull 
     typeOper.toString must notBeNull 
     typeOper must haveSuperClass[DBObject] 
     typeOper must beEqualTo(nonDSL("foo", "$type", org.bson.BSON.NULL)) 
    } 
    "Regex" in { 
     "Scala Regex" in { 
     val typeOper = "foo".$type[scala.util.matching.Regex] 
     typeOper must notBeNull 
     typeOper.toString must notBeNull 
     typeOper must haveSuperClass[DBObject] 
     typeOper must beEqualTo(nonDSL("foo", "$type", org.bson.BSON.REGEX)) 
     } 
    } 
    "Symbol" in { 
     val typeOper = "foo".$type[Symbol] 
     typeOper must notBeNull 
     typeOper.toString must notBeNull 
     typeOper must haveSuperClass[DBObject] 
     typeOper must beEqualTo(nonDSL("foo", "$type", org.bson.BSON.SYMBOL)) 
    } 
    "Number (integer)" in { 
     val typeOper = "foo".$type[Int] 
     typeOper must notBeNull 
     typeOper.toString must notBeNull 
     typeOper must haveSuperClass[DBObject] 
     typeOper must beEqualTo(nonDSL("foo", "$type", org.bson.BSON.NUMBER_INT)) 
    } 
    "Number (Long)" in { 
     val typeOper = "foo".$type[Long] 
     typeOper must notBeNull 
     typeOper.toString must notBeNull 
     typeOper must haveSuperClass[DBObject] 
     typeOper must beEqualTo(nonDSL("foo", "$type", org.bson.BSON.NUMBER_LONG)) 
    } 
    "Timestamp" in { 
     val typeOper = "foo".$type[java.sql.Timestamp] 
     typeOper must notBeNull 
     typeOper.toString must notBeNull 
     typeOper must haveSuperClass[DBObject] 
     typeOper must beEqualTo(nonDSL("foo", "$type", org.bson.BSON.TIMESTAMP)) 
    } 
    "Binary" in { 
     val typeOper = "foo".$type[Array[Byte]] 
     typeOper must notBeNull 
     typeOper.toString must notBeNull 
     typeOper must haveSuperClass[DBObject] 
     typeOper must beEqualTo(nonDSL("foo", "$type", org.bson.BSON.BINARY)) 
    } 

    } 

(주 : 카스바 쿼리 패키지와 관련을 가져 중 하나 필요 코드로 가져 오거나 사전 modulisation에서 기본 'casbah'가져 오기를 사용하십시오. Casbah의 스펙은 현재 모든 DSL 운영자를 대상으로합니다. 지금은 문서가 뒤떨어져 있지만 사양은 사용법을 잘 설명하는 역할을합니다. CasBah에는 두 개의 종류의 연산자가 있습니다. MongoDB와 같습니다. bareword는 운영자

가있는 명령문의 왼쪽 부분은 $ 운영자입니다. 예를 들면 $set, $rename 등이 있습니다.자세한 내용은 the Specs for Bareword Operators을 참조하십시오.

"핵심"연산자은 문의 오른쪽에있는 연산자로 $type과 같은 연산자입니다. 자세한 내용은 the Specs for Core Operators을 참조하십시오.

이 답변은 다소 상세한 답변으로 알고 있지만 두 가지 해결책의 한계와 옵션을 이해하고 싶습니다. Casbah는 MongoDB와 밀접하게 매핑되는 DSL을 제공하고 구문 충돌을 제거합니다 (Casbah는 DBObject에있는 getAs[T] 메서드를 제공하여 DBObject의 값을 특정 유형으로 요청합니다. 사람들은 종종 간과합니다) ; 많은 사람들은 DSL이 내장되어있는 것을하기 전에 DSL이 존재한다는 사실을 모르고있다. 그러나, Casbah의 Query DSL은 일부 사람들의 견해에서 조금은 "보기 흉한"것으로 보인다. 우아함은 MongoDB와 다른 DSL 모두를 쿼리하는 ** MONGODB * 구문 만 기억하면됩니다. 또한 자유 형식 쿼리를 기반으로하며 Rogue가 인식하는 동일한 구조화 된 컴파일 타임 형식의 안전한 & 인식 기능을 제공하지 않습니다.

대조적으로 Rogue는 모든 응용 프로그램에 맞지 않는 완전히 정의 된 레코드 모델도 요구합니다.

두 제품 중 하나가 적절하게 충족되지 않는 "중간 지점"이있는 경우 두 제품 모두 개선 될 수 있다는 점을 알고 싶습니다.

+1

"MongoDB와 다른 DSL 둘 다 아닌 쿼리를 위해 * MONGODB 구문을 기억하면되므로 간단하고 우아합니다." - 아마도 카스바에 머물러있게 될 것 같습니다. 필자는 컴파일 타임 형 안전성을 더 복잡하게하고 싶지 않습니다. – gofeddy

+1

그리고 나는 Rogue로 할 수있는 모든 일이 Casbah 자체로 구현하기가 너무 어렵지 않게 할 수 있다는 것을 확신하고 싶었습니다. 지금까지 Casbah는 나를 위해 꽤 잘 작동하고 다른 데이터 모델을 사용하고 싶지 않을 수 있습니다. – gofeddy

+1

개체를 Casbah에 매핑하고 Casbah의 쿼리를 지원하는 [Salat] [https://github.com/novus/salat/wiki/Quick-start]을 추가하는 것도 고려해 볼 수 있습니다. –