/kotlin-nosql

NoSQL database query and access library for Kotlin

Primary LanguageKotlin

Kotlin NoSQL

Kotlin NoSQL is a reactive and type-safe DSL for working with NoSQL databases.

Status

Under development (POC). The following NoSQL databases are supported now:

Feedback is welcome.

Download

To use it with Maven insert the following code in your pom.xml file:

<dependency>
    <groupId>org.jetbrains.kotlin</groupId>
    <artifactId>kotlin-nosql-mongodb</artifactId>
    <version>0.1-SNAPSHOT</version>
 </dependency>

 <repositories>
     <repository>
       <id>kotlin-nosql</id>
       <url>http://repository.jetbrains.com/kotlin-nosql</url>
     </repository>
</repositories>

To use it with Gradle insert the following code in your build.gradle:

repositories {
    maven {
        url "http://repository.jetbrains.com/kotlin-nosql"
    }
}

dependencies {
    compile 'org.jetbrains.kotlin:kotlin-nosql-mongodb:0.1-SNAPSHOT'
}

Getting Started

Demo: http://www.youtube.com/watch?v=80xgl3KThvM

Basics

Define a schema

object Comments: DocumentSchema<Comment>("comments", Comment::class) {
    val discussionId = id("discussion_id", Discussions)
    val slug = string("slug")
    val fullSlug = string("full_slug")
    val posted = dateTime("posted")
    val text = string("text")

    val AuthorInfo = AuthorInfoColumn()

    class AuthorInfoColumn() : Column<AuthorInfo, Comments>("author", AuthorInfo::class) {
        val authorId = id("id", Authors)
        val name = string("name")
    }
}

class Comment(val discussionId: Id<String, Discussions>, val slug: String,
              val fullSlug: String, posted: DateTime, text: String, authorInfo: AuthorInfo) {
    val id: Id<String, Comments>? = null
}

class AuthorInfo(val authorId: Id<String, Authors>, val name: String)

Define a database

val db = MongoDB(database = "test", schemas = arrayOf(Comments), action = CreateDrop(onCreate = {
    // ...
}))

db.withSession {
    // ...
}

Insert a document

Comments.insert(Comment(DiscussionId, slug, fullSlug, posted, text, AuthorInfo(author.id, author.name)))

Get a document by id

val comment = Comments.find { id.equal(commentId) }.single()

Get a list of documents by a filter expression

val comments = Comments.find { authorInfo.id.equal(authorId) }.sortBy { posted }.skip(10).take(5).toList()

Get selected fields by document id

val authorInfo = Comments.find { id.equal(commentId) }.projection { authorInfo }.single()

Get selected fields by a filter expression

Comments.find { discussionId.equal(id) }).projection { slug + fullSlug + posted + text + authorInfo }.forEach {
    val (slug, fullSlug, posted, text, authorInfo) = it
}

Update selected fields by document id

Comments.find { id.equal(commentId) }.projection { posted }.update(newDate)
Comments.find { id.equal(commentId) }.projection { posted + text }.update(newDate, newText)

Inheritance

Define a base schema

open class ProductSchema<D, S : DocumentSchema<D>(javaClass: Class<V>, discriminator: String) : DocumentSchema<V>("products",
            discriminator = Discriminator(string("type"), discriminator)) {
    val sku = string<S>("sku")
    val title = string<S>("title")
    val description = string<S>("description")
    val asin = string<S>("asin")

    val Shipping = ShippingColumn<S>()
    val Pricing = PricingColumn<S>()

    inner class ShippingColumn<S : DocumentSchema<D>>() : Column<Shipping, S>("shipping", Shipping::class) {
        val weight = integer<S>("weight")
        val dimensions = DimensionsColumn<S>()
    }

    inner class DimensionsColumn<S : DocumentSchema<D>>() : Column<Dimensions, S>("dimensions", Dimensions::class) {
        val width = integer<S>("width")
        val height = integer<S>("height")
        val depth = integer<S>("depth")
    }

    inner class PricingColumn<S : DocumentSchema<D>>() : Column<Pricing, S>("pricing", Pricing::class) {
        val list = integer<S>("list")
        val retail = integer<S>("retail")
        val savings = integer<S>("savings")
        val ptcSavings = integer<S>("pct_savings")
    }
}

object Products : ProductSchema<Product, Products>(Product::class, "")

abstract class Product(val id: Id<String, Products>? = null, val sku: String, val title: String, val description: String,
                       val asin: String, val shipping: Shipping, val pricing: Pricing) {
    val id: Id<String, Products>? = null
}

class Shipping(val weight: Int, val dimensions: Dimensions)

class Dimensions(val width: Int, val height: Int, val depth: Int)

class Pricing(val list: Int, val retail: Int, val savings: Int, val pctSavings: Int)

Define an inherited schema

object Albums : ProductSchema<Album, Albums>(Album::class, discriminator = "Audio Album") {
    val details = DetailsColumn()

    class DetailsColumn() : Column<Details, Albums>("details", Details::class) {
        val title = string("title")
        val artistId = id("artistId", Artists)
        val genre = setOfString("genre")

        val tracks = TracksColumn()
    }

    class TracksColumn() : ListColumn<Track, Albums>("tracks", Track::class) {
        val title = string("title")
        val duration = integer("duration")
    }
}

class Album(sku: String, title: String, description: String, asin: String, shipping: Shipping,
    pricing: Pricing, val details: Details) : Product(sku, title, description, asin, shipping, pricing)

class Details(val title: String, val artistId: Id<String, Artists>, val genre: Set<String>, val tracks: List<Track>)

Insert a document

val productId = Products.insert(Album(sku = "00e8da9b", title = "A Love Supreme", description = "by John Coltrane",
    asin = "B0000A118M", shipping = Shipping(weight = 6, dimensions = Dimensions(10, 10, 1)),
    pricing = Pricing(list = 1200, retail = 1100, savings = 100, pctSavings = 8),
    details = Details(title = "A Love Supreme [Original Recording Reissued]",
            artistId = artistId, genre = setOf("Jazz", "General"),
            tracks = listOf(Track("A Love Supreme Part I: Acknowledgement", 100),
                    Track("A Love Supreme Part II: Resolution", 200),
                    Track("A Love Supreme, Part III: Pursuance", 300)))))
}

Access documents via an abstract schema

for (product in Products.find { id.equal(productId) }) {
    if (product is Album) {
    }
}

Access documents via an inherited schema

val album = Albums.find { details.artistId.equal(artistId) }.single()

Examples