2011-05-10 38 views
3

我目前正在使用Casbah和MongoDB來實現Web服務。到目前爲止,我沒有任何問題。我也在使用Scala。針對MongoDB的Casbah&Rogue - 查詢功能

但是,我只是想知道是否有更好的事情比Casbah做了很多find/findOne類型的查詢。

我遇到了Rogue,這是一種類型安全的基於Scala的DSL,它將使查詢更容易,更易讀。

因此,我想知道是否應該轉向Rogue,以便隨着Web服務項目變得越來越大,越來越複雜,這可能有助於讓Rogue支持查詢?

只是想找出是否應該繼續或轉移到更好的東西。

回答

12

此刻,盜賊只能對付Lift's MongoDB-Record系統。

它提供完整類型安全性的部分原因是它爲Lift-Record使用了一個強大的,明確定義的對象結構。由於您擁有完整的結構,因此執行「自由格式」查詢的能力要低得多。這就是我通過一個最近的Scala網絡研討會的Lift-Record + 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類似的上下文,我們確實有一個內置的DSL,旨在儘可能地模仿MongoDB內置的查詢模型。它可以抵制任何潛在的基礎模型,但可以儘可能地執行類型安全級別。下面是一個(有點過時作爲來自同一演示文稿)卡斯巴的查詢的例子:

// 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)) 
    } 

    } 

(注意:您需要可以導入卡斯巴查詢包及其相關導入到您的代碼或使用從預先模塊化的默認'casbah'導入)。目前,Casbah的規格已全面覆蓋每個DSL運營商;該文檔目前落後,但規格作爲一個很好的介紹他們的用法。請注意,Casbah中有兩種種類的操作符,就像在MongoDB中一樣。

裸字運營商,該陳述的最左邊部分是$操作。這方面的例子有$set$rename等。有關更多信息,請參閱the Specs for Bareword Operators

「核心」運算符是存在於語句右側的運算符,例如$type。有關更多信息,請參閱the Specs for Core Operators

我知道這是一個相當詳細的答案,但我想確保你瞭解你的選擇,以及這兩種解決方案的侷限性。 Casbah會爲您提供一個與MongoDB緊密映射的DSL,並刪除一些句法錯誤(請記住,Casbah在DBObject上提供了getAs[T]方法來請求DBObject中的值作爲特定的類型,人們經常會忽略) ;許多用戶在他們去尋求某些東西來構建內容之前並不知道DSL存在。然而,,Casbah的查詢DSL在某些人的觀點中也看起來有點「蹩腳」...作爲它的作者,我更喜歡它的簡單性和優雅,因爲我只需要記住查詢的** MONGODB *語法,而不是MongoDB和另一個DSL。它也基於自由格式查詢,並且不提供與Rogue相同的結構化,編譯時安全類型的安全知識工具&。

相比之下,Rogue還需要一個完全定義的記錄模型,它不適合每個應用程序。

但是,如果您的需求存在一個「中間地帶」,哪種產品不能滿足要求,我都很想聽聽哪種產品可以改進。

+1

「我更喜歡它簡單和優雅,因爲我只需要記住查詢的* MONGODB語法,而不是MongoDB和另一個DSL。」 - 這可能會讓我暫時留在Casbah。我不想爲編譯時類型安全增加更多複雜性。 – gofeddy 2011-05-10 15:44:26

+1

我想我只是想確保所有可以用Rogue完成的事情都可以用Casbah完成,而不會讓它太難實現。到目前爲止,Casbah對我來說工作得很好,我可能不想使用不同的數據模型。 – gofeddy 2011-05-10 15:46:36

+1

您也可以考慮添加[Salat] [https://github.com/novus/salat/wiki/Quick-start],它將對象映射到Casbah,並支持Casbah的查詢。 – 2011-05-10 17:49:32