Giter VIP home page Giter VIP logo

mongoquery's Introduction

MongoQuery

Build Status codecov Maven Central

MongoQuery is a macro based MongoDB query builder for Scala.

Currently the MongoDB queries API requires to construct DBObjects explicitly. This makes even simple queries bulky. Even though you are using Casbah DSL it often still requires to create MongoDBObjects, and also requires to study new syntax, instead of using MongoDB queries. The purpose of this project is to provide a simple API for creating queries from strings. The goal is to make compile time queries syntax checking (as much as possible).

How to use

Installation

Since version 0.7 MongoQuery supports only Scala 2.11 and 2.12. If you are still using Scala 2.10 you can use MongoQuery 0.6 (or any version before).

MongoQuery is published to Sonatype maven repository. Add following dependency to libraryDependencies in your SBT build file:

"com.github.limansky" %% "mongoquery-casbah" % "0.7"       // for Casbah users
"com.github.limansky" %% "mongoquery-reactive" % "0.7"     // for ReactiveMongo users
"com.github.limansky" %% "mongoquery-scala-driver" % "0.7" // for Mongo Scala driver

If you want use latest development version:

"com.github.limansky" %% "mongoquery-casbah" % "0.8-SNAPSHOT"       // Casbah users
"com.github.limansky" %% "mongoquery-reactive" % "0.8-SNAPSHOT"     // ReactiveMongo users
"com.github.limansky" %% "mongoquery-scala-driver" % "0.8-SNAPSHOT" // for Mongo Scala driver

mq interpolator

The mq string interpolator converts string to the BSON objects. If you use Casbah it creates DBObjects:

import com.github.limansky.mongoquery.casbah._

def findByName(name: String) = {
  myCollection.find(mq"{ name : $name}")
}

For ReactiveMongo it creates BSONDocuments:

import com.github.limansky.mongoquery.reactive._

collection.
  find(mq"""{ firstName : "Jack" }""", mq"{ lastName : 1, _id : 1 }").
  cursor[BSONDocument].
  enumerate().apply(Iteratee.foreach { doc =>
  println("found document: " + BSONDocument.pretty(doc))
})

For Mongo Scala Driver it creates BsonDocuments:

import com.github.limansky.mongoquery.scaladriver._

collection.insertOne(mq"""{ name: "John", lastName: "Doe", age : 42 }""")

Since the query is defined inside of the string interpolator, the words started with $ are handled as variable references. To type MongoDB operator use $$, e.g:

def makeOlder(age: Int) = {
  people.update(mq"""{ age : { $$lt : $age } }""",
                mq"""{ $$inc : { age : 1 }}""",
                multi = 1)
}

Since the interpolator is implemented using macro it can perform compile time checks of provided queries. The code will not compile if the query is malformed. Also MongoQuery checks if all MongoDB operators are known.

[error] Test.scala:44: Unknown operator '$kte'. Possible you mean '$lte'
[error]     val query = mq"{start : {$$kte : $start}}"
[error]                              ^

[error] Test.scala:49: '{' expected, but Variable found
[error]     val q = mq"{ color : {$$in : $colors}"
[error]                                ^

Unfortunately, some errors messages does not reflect the error itself. I'm working on it, but it seems like the issue in the Scala Parser Combinators library.

Built-in types

MongoQuery supports several MongoDB specific literal types.

  • ObjectIds. mq"""{ clientId : ObjectId("01234567890abcdef1234") }"""
  • Booleans. mq"{ expired : false }"
  • Regular expressions (since 0.5). mq"{ name : /^joe/i }"

mqt interpolator

mqt is another one interpolator adding type checking feature. If you have a model classes, you can check if the query contains only fields available in the class. E.g.:

case class Phone(kind: String, number: String)
case class Person(name: String, age: Int, phones: List[Phone])

// OK
persons.update(mq"{}", mqt"{$$inc : { age : 1 }}"[Person])

// Failed, person doesn't contain field 'nme'
persons.update(mq"{}", mqt"""{$$set : { nme : "Joe" }}"""[Person])

//Failed, name is not indexed field
persons.find(mqt"{ name.1 : 'Joe' }"[Person])

// OK
persons.find(mqt"{ phone.number : '223322' }"[Person])

// Failed, Phone doesn't contain field num
persons.find(mqt"{ phone.num : '223322' }"[Person])

Runtime parsing

MongoQuery also provides runtime parsers for both backends. It might be useful for testing purposes, or if you generate queries on runtime, or for converting JSON to BSON. For example:

import com.github.limansky.mongoquery.casbah.BSONParser

persons.find(BSONParser.parse("""{ age : { $lt : 42 }}"""))

Feedback

Any feedback is very welcome! You can ask any questions in MongoQuery mailing list.

mongoquery's People

Contributors

kell18 avatar limansky avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

mongoquery's Issues

Type-check fields inside operators

For example, this would compile:

case class Foo(s: String)
mqt"{ $$set : { nonExistent : 'aaa'}}"[Foo]

But it shouldn't. Do you have any ideas on how we can implement this if it's possible?

Love it!

I have written similar library for constructing queries based on string interpolator but yours have many interesting features. I think I'll use mix of the two with following features, I hope they will be useful to develop your own library:

  • Scala 2.12
  • outer {} are optional: coll.findOne(m"name:$name")
  • typechecking of field values
  • hand-written recursive descent parser. It is not that difficult to create: https://gist.github.com/scf37/17caf87eacb82d097a06e292a0161f52
  • mongodb scala drivers are not necessary - modern mongo java driver is good enough for Scala

Unquoting keys

Is this supposed to work?

    val a = "a"
    mq"{ $a: 1 }"

I get ``}' expected, but Variable found`

Regex cannot to be parsed

When using regular expressions in the mq interpolator a variable expected error is thrown. It seems that regex is not supported

import com.github.limansky.mongoquery.casbah._

scala> val term = "anything"
term: String = anything

scala> mq"""{naam: /^$term/i}"""
<console>:12: error: variable expected
       mq"""{naam: /^$term/i}"""

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.