현재 로그는 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는 모든 응용 프로그램에 맞지 않는 완전히 정의 된 레코드 모델도 요구합니다.
두 제품 중 하나가 적절하게 충족되지 않는 "중간 지점"이있는 경우 두 제품 모두 개선 될 수 있다는 점을 알고 싶습니다.
"MongoDB와 다른 DSL 둘 다 아닌 쿼리를 위해 * MONGODB 구문을 기억하면되므로 간단하고 우아합니다." - 아마도 카스바에 머물러있게 될 것 같습니다. 필자는 컴파일 타임 형 안전성을 더 복잡하게하고 싶지 않습니다. – gofeddy
그리고 나는 Rogue로 할 수있는 모든 일이 Casbah 자체로 구현하기가 너무 어렵지 않게 할 수 있다는 것을 확신하고 싶었습니다. 지금까지 Casbah는 나를 위해 꽤 잘 작동하고 다른 데이터 모델을 사용하고 싶지 않을 수 있습니다. – gofeddy
개체를 Casbah에 매핑하고 Casbah의 쿼리를 지원하는 [Salat] [https://github.com/novus/salat/wiki/Quick-start]을 추가하는 것도 고려해 볼 수 있습니다. –