Full Code of cheptsov/kotlin-nosql for AI

master 58ec5e5834f7 cached
100 files
173.5 KB
44.7k tokens
1 requests
Download .txt
Repository: cheptsov/kotlin-nosql
Branch: master
Commit: 58ec5e5834f7
Files: 100
Total size: 173.5 KB

Directory structure:
gitextract_0ev3bdve/

├── .idea/
│   ├── .name
│   ├── compiler.xml
│   ├── copyright/
│   │   └── profiles_settings.xml
│   ├── encodings.xml
│   ├── gradle.xml
│   ├── libraries/
│   │   ├── Gradle__joda_time_joda_time_2_3.xml
│   │   ├── Gradle__junit_junit_4_11.xml
│   │   ├── Gradle__org_hamcrest_hamcrest_core_1_3.xml
│   │   ├── Gradle__org_jetbrains_kotlin_kotlin_reflect_1_0_0.xml
│   │   ├── Gradle__org_jetbrains_kotlin_kotlin_runtime_1_0_0.xml
│   │   ├── Gradle__org_jetbrains_kotlin_kotlin_stdlib_1_0_0.xml
│   │   ├── Gradle__org_jetbrains_kotlin_kotlin_test_1_0_0.xml
│   │   ├── Gradle__org_jetbrains_spek_spek_0_1_195.xml
│   │   └── Gradle__org_mongodb_mongo_java_driver_3_0_3.xml
│   ├── misc.xml
│   ├── modules.xml
│   └── vcs.xml
├── README.md
├── build.gradle
├── gradle/
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── kotlin-nosql-mongodb/
│   ├── build.gradle
│   ├── kotlin-nosql-mongodb.iml
│   └── src/
│       ├── main/
│       │   └── kotlin/
│       │       └── kotlinx/
│       │           └── nosql/
│       │               └── mongodb/
│       │                   ├── DocumentSchema.kt
│       │                   ├── MongoDB.kt
│       │                   ├── MongoDBIndex.kt
│       │                   └── MongoDBSession.kt
│       └── test/
│           └── kotlin/
│               └── kotlinx/
│                   └── nosql/
│                       └── mongodb/
│                           └── test/
│                               └── MongoDBSpek.kt
├── kotlin-nosql.iml
├── settings.gradle
└── src/
    └── main/
        └── kotlin/
            └── kotlinx/
                └── nosql/
                    ├── AbstractColumn.kt
                    ├── AbstractIndex.kt
                    ├── AbstractNullableColumn.kt
                    ├── AbstractSchema.kt
                    ├── AbstractTableSchema.kt
                    ├── AndQuery.kt
                    ├── Column.kt
                    ├── ColumnDecuple.kt
                    ├── ColumnNonuple.kt
                    ├── ColumnOctuple.kt
                    ├── ColumnPair.kt
                    ├── ColumnQuadruple.kt
                    ├── ColumnQueryWrapper.kt
                    ├── ColumnQuintuple.kt
                    ├── ColumnSeptuple.kt
                    ├── ColumnSextuple.kt
                    ├── ColumnTriple.kt
                    ├── ColumnType.kt
                    ├── Database.kt
                    ├── Decuple.kt
                    ├── Discriminator.kt
                    ├── DocumentSchema.kt
                    ├── DocumentSchemaIdQueryWrapper.kt
                    ├── DocumentSchemaOperations.kt
                    ├── DocumentSchemaQueryParams.kt
                    ├── DocumentSchemaQueryWrapper.kt
                    ├── Expression.kt
                    ├── Id.kt
                    ├── IdListColumn.kt
                    ├── IdSetColumn.kt
                    ├── IndexOperations.kt
                    ├── KeyValueDocumentSchemaOperations.kt
                    ├── KeyValueSchema.kt
                    ├── KeyValueSchemaOperations.kt
                    ├── ListColumn.kt
                    ├── LiteralExpression.kt
                    ├── Nonuple.kt
                    ├── NotEqualQuery.kt
                    ├── NotMemberOfQuery.kt
                    ├── NullableColumn.kt
                    ├── NullableIdColumn.kt
                    ├── Octuple.kt
                    ├── PrimaryKey.kt
                    ├── Quadruple.kt
                    ├── Query.kt
                    ├── Quintuple.kt
                    ├── SchemaGenerationAction.kt
                    ├── Septuple.kt
                    ├── Session.kt
                    ├── SetColumn.kt
                    ├── Sextuple.kt
                    ├── TableSchema.kt
                    ├── TableSchemaOperations.kt
                    ├── TableSchemaProjectionQueryParams.kt
                    ├── TableSchemaProjectionQueryWrapper.kt
                    ├── query/
                    │   ├── EqualQuery.kt
                    │   ├── GreateQuery.kt
                    │   ├── GreaterEqualQuery.kt
                    │   ├── IsNotNullQuery.kt
                    │   ├── IsNullQuery.kt
                    │   ├── LessEqualQuery.kt
                    │   ├── LessQuery.kt
                    │   ├── MatchesQuery.kt
                    │   ├── MemberOfQuery.kt
                    │   ├── NoQuery.kt
                    │   ├── OrQuery.kt
                    │   └── TextQuery.kt
                    └── util/
                        └── SchemaUtils.kt

================================================
FILE CONTENTS
================================================

================================================
FILE: .idea/.name
================================================
kotlin-nosql

================================================
FILE: .idea/compiler.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="CompilerConfiguration">
    <resourceExtensions />
    <wildcardResourcePatterns>
      <entry name="!?*.java" />
      <entry name="!?*.form" />
      <entry name="!?*.class" />
      <entry name="!?*.groovy" />
      <entry name="!?*.scala" />
      <entry name="!?*.flex" />
      <entry name="!?*.kt" />
      <entry name="!?*.clj" />
      <entry name="!?*.aj" />
    </wildcardResourcePatterns>
    <annotationProcessing>
      <profile default="true" name="Default" enabled="false">
        <processorPath useClasspath="true" />
      </profile>
    </annotationProcessing>
    <bytecodeTargetLevel>
      <module name="kotlin_nosql_main" target="1.8" />
      <module name="kotlin_nosql_mongodb_main" target="1.8" />
      <module name="kotlin_nosql_mongodb_test" target="1.8" />
      <module name="kotlin_nosql_test" target="1.8" />
    </bytecodeTargetLevel>
  </component>
</project>

================================================
FILE: .idea/copyright/profiles_settings.xml
================================================
<component name="CopyrightManager">
  <settings default="" />
</component>

================================================
FILE: .idea/encodings.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="Encoding">
    <file url="PROJECT" charset="UTF-8" />
  </component>
</project>

================================================
FILE: .idea/gradle.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="GradleSettings">
    <option name="linkedExternalProjectsSettings">
      <GradleProjectSettings>
        <option name="disableWrapperSourceDistributionNotification" value="true" />
        <option name="distributionType" value="DEFAULT_WRAPPED" />
        <option name="externalProjectPath" value="$PROJECT_DIR$" />
        <option name="gradleHome" value="/usr/local/Cellar/gradle/2.2.1/bin" />
        <option name="gradleJvm" value="1.8" />
        <option name="modules">
          <set>
            <option value="$PROJECT_DIR$" />
            <option value="$PROJECT_DIR$/kotlin-nosql-mongodb" />
          </set>
        </option>
        <option name="useAutoImport" value="true" />
        <option name="myModules">
          <set>
            <option value="$PROJECT_DIR$" />
            <option value="$PROJECT_DIR$/kotlin-nosql-mongodb" />
          </set>
        </option>
      </GradleProjectSettings>
    </option>
  </component>
</project>

================================================
FILE: .idea/libraries/Gradle__joda_time_joda_time_2_3.xml
================================================
<component name="libraryTable">
  <library name="Gradle: joda-time:joda-time:2.3">
    <CLASSES>
      <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/joda-time/joda-time/2.3/56498efd17752898cfcc3868c1b6211a07b12b8f/joda-time-2.3.jar!/" />
    </CLASSES>
    <JAVADOC />
    <SOURCES>
      <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/joda-time/joda-time/2.3/ecd8588d8ce0963eb443de31d5fea29e3205f160/joda-time-2.3-sources.jar!/" />
    </SOURCES>
  </library>
</component>

================================================
FILE: .idea/libraries/Gradle__junit_junit_4_11.xml
================================================
<component name="libraryTable">
  <library name="Gradle: junit:junit:4.11">
    <CLASSES>
      <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/junit/junit/4.11/4e031bb61df09069aeb2bffb4019e7a5034a4ee0/junit-4.11.jar!/" />
    </CLASSES>
    <JAVADOC />
    <SOURCES>
      <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/junit/junit/4.11/28e0ad201304e4a4abf999ca0570b7cffc352c3c/junit-4.11-sources.jar!/" />
    </SOURCES>
  </library>
</component>

================================================
FILE: .idea/libraries/Gradle__org_hamcrest_hamcrest_core_1_3.xml
================================================
<component name="libraryTable">
  <library name="Gradle: org.hamcrest:hamcrest-core:1.3">
    <CLASSES>
      <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.hamcrest/hamcrest-core/1.3/42a25dc3219429f0e5d060061f71acb49bf010a0/hamcrest-core-1.3.jar!/" />
    </CLASSES>
    <JAVADOC />
    <SOURCES>
      <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.hamcrest/hamcrest-core/1.3/1dc37250fbc78e23a65a67fbbaf71d2e9cbc3c0b/hamcrest-core-1.3-sources.jar!/" />
    </SOURCES>
  </library>
</component>

================================================
FILE: .idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_reflect_1_0_0.xml
================================================
<component name="libraryTable">
  <library name="Gradle: org.jetbrains.kotlin:kotlin-reflect:1.0.0">
    <CLASSES>
      <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-reflect/1.0.0/a082d34e973f906f839fd01381f6cd1d177354af/kotlin-reflect-1.0.0.jar!/" />
    </CLASSES>
    <JAVADOC />
    <SOURCES>
      <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-reflect/1.0.0/98cb9e87c2b88492443968480e3d6b3a817c4465/kotlin-reflect-1.0.0-sources.jar!/" />
    </SOURCES>
  </library>
</component>

================================================
FILE: .idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_runtime_1_0_0.xml
================================================
<component name="libraryTable">
  <library name="Gradle: org.jetbrains.kotlin:kotlin-runtime:1.0.0">
    <CLASSES>
      <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-runtime/1.0.0/8739b3cd3bf98584cf2d1cabf59b55dbd5f7349d/kotlin-runtime-1.0.0.jar!/" />
    </CLASSES>
    <JAVADOC />
    <SOURCES>
      <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-runtime/1.0.0/d0c738e20f1658e1705ce166caa1768267bf4262/kotlin-runtime-1.0.0-sources.jar!/" />
    </SOURCES>
  </library>
</component>

================================================
FILE: .idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_1_0_0.xml
================================================
<component name="libraryTable">
  <library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.0.0">
    <CLASSES>
      <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.0.0/2db5ac6eb2746708e77e4c439625f9331c25e766/kotlin-stdlib-1.0.0.jar!/" />
    </CLASSES>
    <JAVADOC />
    <SOURCES>
      <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.0.0/3a8a73d14b5fdda2425b40b440bb0ce71cb627fc/kotlin-stdlib-1.0.0-sources.jar!/" />
    </SOURCES>
  </library>
</component>

================================================
FILE: .idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_test_1_0_0.xml
================================================
<component name="libraryTable">
  <library name="Gradle: org.jetbrains.kotlin:kotlin-test:1.0.0">
    <CLASSES>
      <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-test/1.0.0/1ec4ba71422eb4cb7133a6611792b471183b5a11/kotlin-test-1.0.0.jar!/" />
    </CLASSES>
    <JAVADOC />
    <SOURCES>
      <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-test/1.0.0/1fa859d6fbc04d8b1a71c1f09f362760498cec65/kotlin-test-1.0.0-sources.jar!/" />
    </SOURCES>
  </library>
</component>

================================================
FILE: .idea/libraries/Gradle__org_jetbrains_spek_spek_0_1_195.xml
================================================
<component name="libraryTable">
  <library name="Gradle: org.jetbrains.spek:spek:0.1.195">
    <CLASSES>
      <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.spek/spek/0.1.195/94e5aa0dd854b86b5b43102f5eb89d9ac2821a58/spek-0.1.195.jar!/" />
    </CLASSES>
    <JAVADOC />
    <SOURCES>
      <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.spek/spek/0.1.195/2781b9a0edfddeb18e4680743795b0495cdefa6d/spek-0.1.195-sources.jar!/" />
    </SOURCES>
  </library>
</component>

================================================
FILE: .idea/libraries/Gradle__org_mongodb_mongo_java_driver_3_0_3.xml
================================================
<component name="libraryTable">
  <library name="Gradle: org.mongodb:mongo-java-driver:3.0.3">
    <CLASSES>
      <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.mongodb/mongo-java-driver/3.0.3/2861ec441c95b6cff99ad6761c01776cf708817a/mongo-java-driver-3.0.3.jar!/" />
    </CLASSES>
    <JAVADOC />
    <SOURCES>
      <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.mongodb/mongo-java-driver/3.0.3/9101a823cdd4d76b8121f500d7ca2545223749cf/mongo-java-driver-3.0.3-sources.jar!/" />
    </SOURCES>
  </library>
</component>

================================================
FILE: .idea/misc.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="EntryPointsManager">
    <entry_points version="2.0" />
  </component>
  <component name="ProjectLevelVcsManager" settingsEditedManually="false">
    <OptionsSetting value="true" id="Add" />
    <OptionsSetting value="true" id="Remove" />
    <OptionsSetting value="true" id="Checkout" />
    <OptionsSetting value="true" id="Update" />
    <OptionsSetting value="true" id="Status" />
    <OptionsSetting value="true" id="Edit" />
    <ConfirmationsSetting value="0" id="Add" />
    <ConfirmationsSetting value="0" id="Remove" />
  </component>
  <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" assert-keyword="true" jdk-15="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
    <output url="file://$PROJECT_DIR$/build/classes" />
  </component>
</project>

================================================
FILE: .idea/modules.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="ProjectModuleManager">
    <modules>
      <module fileurl="file://$PROJECT_DIR$/kotlin-nosql.iml" filepath="$PROJECT_DIR$/kotlin-nosql.iml" />
      <module fileurl="file://$PROJECT_DIR$/.idea/modules/kotlin-nosql.iml" filepath="$PROJECT_DIR$/.idea/modules/kotlin-nosql.iml" />
      <module fileurl="file://$PROJECT_DIR$/kotlin-nosql-mongodb/kotlin-nosql-mongodb.iml" filepath="$PROJECT_DIR$/kotlin-nosql-mongodb/kotlin-nosql-mongodb.iml" />
      <module fileurl="file://$PROJECT_DIR$/.idea/modules/kotlin-nosql-mongodb/kotlin-nosql-mongodb.iml" filepath="$PROJECT_DIR$/.idea/modules/kotlin-nosql-mongodb/kotlin-nosql-mongodb.iml" group="kotlin-nosql-mongodb" />
      <module fileurl="file://$PROJECT_DIR$/.idea/modules/kotlin_nosql_main.iml" filepath="$PROJECT_DIR$/.idea/modules/kotlin_nosql_main.iml" group="kotlin-nosql" />
      <module fileurl="file://$PROJECT_DIR$/.idea/modules/kotlin-nosql-mongodb/kotlin_nosql_mongodb_main.iml" filepath="$PROJECT_DIR$/.idea/modules/kotlin-nosql-mongodb/kotlin_nosql_mongodb_main.iml" group="kotlin-nosql-mongodb" />
      <module fileurl="file://$PROJECT_DIR$/.idea/modules/kotlin-nosql-mongodb/kotlin_nosql_mongodb_test.iml" filepath="$PROJECT_DIR$/.idea/modules/kotlin-nosql-mongodb/kotlin_nosql_mongodb_test.iml" group="kotlin-nosql-mongodb" />
      <module fileurl="file://$PROJECT_DIR$/.idea/modules/kotlin_nosql_test.iml" filepath="$PROJECT_DIR$/.idea/modules/kotlin_nosql_test.iml" group="kotlin-nosql" />
    </modules>
  </component>
</project>

================================================
FILE: .idea/vcs.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="VcsDirectoryMappings">
    <mapping directory="$PROJECT_DIR$" vcs="Git" />
  </component>
</project>

================================================
FILE: README.md
================================================
# 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:

- [MongoDB](https://www.mongodb.org/)

Feedback is welcome.

## Download

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

```xml
<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:

```groovy
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

```kotlin
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

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

db.withSession {
    // ...
}
```

#### Insert a document

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

#### Get a document by id

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

#### Get a list of documents by a filter expression

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

#### Get selected fields by document id

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

#### Get selected fields by a filter expression

```kotlin
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

```kotlin
Comments.find { id.equal(commentId) }.projection { posted }.update(newDate)
```

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

### Inheritance

#### Define a base schema

```kotlin
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

```kotlin
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

```kotlin
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

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

#### Access documents via an inherited schema

```kotlin

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

## Examples

- [PetClinic Sample Application](http://kotlin-nosql-mongodb-petclinic.herokuapp.com) ([GitHub](https://github.com/cheptsov/kotlin-nosql-mongodb-petclinic))


================================================
FILE: build.gradle
================================================
configurations.all {
    // check for updates every build
    resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
}

buildscript {
    repositories {
        mavenCentral()
        maven {
            url "http://oss.sonatype.org/content/repositories/snapshots"
        }
    }
    dependencies {
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.0.0"
    }
}

allprojects {
    apply plugin: "kotlin"

    group = "org.jetbrains.kotlin"
    version = "0.1-SNAPSHOT"

    repositories {
        mavenCentral()
        maven {
            url "http://repository.jetbrains.com/spek"
        }
        maven {
            url "http://oss.sonatype.org/content/repositories/snapshots"
        }
    }

    apply plugin: "maven"

    dependencies {
        compile 'joda-time:joda-time:2.3'
        compile group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib', version: '1.0.0', changing: true
        testCompile group: 'org.jetbrains.spek', name: 'spek', version: '0.1.195', changing: true
    }

    task sourcesJar(type: Jar, dependsOn: classes) {
        classifier 'sources'
        from sourceSets.main.allSource
    }

    artifacts {
        archives sourcesJar
    }

/*
    uploadArchives {
        repositories {
            mavenDeployer {
                repository(url: "http://repository.jetbrains.com/kotlin-nosql") {
                    authentication(userName: deploy_username, password: deploy_password)
                }
                pom.groupId = project.group
                pom.artifactId = project.name
                pom.version = project.version
            }
        }
    }
*/
}

project(':kotlin-nosql-mongodb') {
    dependencies {
        compile project(':')
        compile 'org.mongodb:mongo-java-driver:3.0.3'
    }
}


================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
#Tue Sep 30 10:02:29 PDT 2014
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.1-bin.zip


================================================
FILE: gradlew
================================================
#!/usr/bin/env bash

##############################################################################
##
##  Gradle start up script for UN*X
##
##############################################################################

# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""

APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`

# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"

warn ( ) {
    echo "$*"
}

die ( ) {
    echo
    echo "$*"
    echo
    exit 1
}

# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
  CYGWIN* )
    cygwin=true
    ;;
  Darwin* )
    darwin=true
    ;;
  MINGW* )
    msys=true
    ;;
esac

# For Cygwin, ensure paths are in UNIX format before anything is touched.
if $cygwin ; then
    [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
fi

# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
    ls=`ls -ld "$PRG"`
    link=`expr "$ls" : '.*-> \(.*\)$'`
    if expr "$link" : '/.*' > /dev/null; then
        PRG="$link"
    else
        PRG=`dirname "$PRG"`"/$link"
    fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >&-
APP_HOME="`pwd -P`"
cd "$SAVED" >&-

CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar

# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
        # IBM's JDK on AIX uses strange locations for the executables
        JAVACMD="$JAVA_HOME/jre/sh/java"
    else
        JAVACMD="$JAVA_HOME/bin/java"
    fi
    if [ ! -x "$JAVACMD" ] ; then
        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME

Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
    fi
else
    JAVACMD="java"
    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.

Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi

# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
    MAX_FD_LIMIT=`ulimit -H -n`
    if [ $? -eq 0 ] ; then
        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
            MAX_FD="$MAX_FD_LIMIT"
        fi
        ulimit -n $MAX_FD
        if [ $? -ne 0 ] ; then
            warn "Could not set maximum file descriptor limit: $MAX_FD"
        fi
    else
        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
    fi
fi

# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi

# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`

    # We build the pattern for arguments to be converted via cygpath
    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
    SEP=""
    for dir in $ROOTDIRSRAW ; do
        ROOTDIRS="$ROOTDIRS$SEP$dir"
        SEP="|"
    done
    OURCYGPATTERN="(^($ROOTDIRS))"
    # Add a user-defined pattern to the cygpath arguments
    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
    fi
    # Now convert the arguments - kludge to limit ourselves to /bin/sh
    i=0
    for arg in "$@" ; do
        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option

        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
        else
            eval `echo args$i`="\"$arg\""
        fi
        i=$((i+1))
    done
    case $i in
        (0) set -- ;;
        (1) set -- "$args0" ;;
        (2) set -- "$args0" "$args1" ;;
        (3) set -- "$args0" "$args1" "$args2" ;;
        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
    esac
fi

# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
    JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"

exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"


================================================
FILE: gradlew.bat
================================================
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem  Gradle startup script for Windows
@rem
@rem ##########################################################################

@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal

@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=

set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%

@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome

set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init

echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.

goto fail

:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe

if exist "%JAVA_EXE%" goto init

echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.

goto fail

:init
@rem Get command-line arguments, handling Windowz variants

if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args

:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2

:win9xME_args_slurp
if "x%~1" == "x" goto execute

set CMD_LINE_ARGS=%*
goto execute

:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$

:execute
@rem Setup the command line

set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar

@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%

:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd

:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1

:mainEnd
if "%OS%"=="Windows_NT" endlocal

:omega


================================================
FILE: kotlin-nosql-mongodb/build.gradle
================================================


================================================
FILE: kotlin-nosql-mongodb/kotlin-nosql-mongodb.iml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id=":kotlin-nosql-mongodb" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" external.system.module.group="org.jetbrains.kotlin" external.system.module.version="0.1-SNAPSHOT" type="JAVA_MODULE" version="4">
  <component name="NewModuleRootManager" inherit-compiler-output="false">
    <output url="file://$MODULE_DIR$/build/classes/main" />
    <output-test url="file://$MODULE_DIR$/build/classes/test" />
    <exclude-output />
    <content url="file://$MODULE_DIR$">
      <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
      <sourceFolder url="file://$MODULE_DIR$/src/main/kotlin" isTestSource="false" />
      <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
      <sourceFolder url="file://$MODULE_DIR$/src/test/kotlin" isTestSource="true" />
      <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
      <sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
      <excludeFolder url="file://$MODULE_DIR$/.gradle" />
      <excludeFolder url="file://$MODULE_DIR$/build" />
    </content>
    <orderEntry type="inheritedJdk" />
    <orderEntry type="sourceFolder" forTests="false" />
    <orderEntry type="module" module-name="kotlin-nosql" exported="" />
    <orderEntry type="library" exported="" name="Gradle: joda-time:joda-time:2.3" level="project" />
    <orderEntry type="library" exported="" name="Gradle: org.mongodb:mongo-java-driver:3.0.3" level="project" />
    <orderEntry type="library" scope="TEST" name="Gradle: junit:junit:4.11" level="project" />
    <orderEntry type="library" scope="TEST" name="Gradle: org.hamcrest:hamcrest-core:1.3" level="project" />
    <orderEntry type="library" exported="" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.0.0" level="project" />
    <orderEntry type="library" exported="" name="Gradle: org.jetbrains.kotlin:kotlin-runtime:1.0.0" level="project" />
    <orderEntry type="library" scope="TEST" name="Gradle: org.jetbrains.spek:spek:0.1.195" level="project" />
    <orderEntry type="library" scope="TEST" name="Gradle: org.jetbrains.kotlin:kotlin-reflect:1.0.0" level="project" />
    <orderEntry type="library" scope="TEST" name="Gradle: org.jetbrains.kotlin:kotlin-test:1.0.0" level="project" />
  </component>
</module>

================================================
FILE: kotlin-nosql-mongodb/src/main/kotlin/kotlinx/nosql/mongodb/DocumentSchema.kt
================================================
package kotlinx.nosql.mongodb

import kotlinx.nosql.AbstractColumn
import kotlinx.nosql.Discriminator
import kotlinx.nosql.string
import kotlin.reflect.KClass

abstract class DocumentSchema<D: Any>(name: String, valueClass: KClass<D>, discriminator: Discriminator<out Any, out kotlinx.nosql.DocumentSchema<String, D>>? = null) : kotlinx.nosql.DocumentSchema<String, D>(name, valueClass, string("_id"), discriminator) {
    fun ensureIndex(name: String = "", unique: Boolean = false,
                             ascending: Array<out AbstractColumn<*, *, *>> = arrayOf<AbstractColumn<*, *, *>>(),
                             descending: Array<out AbstractColumn<*, *, *>> = arrayOf<AbstractColumn<*, *, *>>(),
                             text: Array<out AbstractColumn<*, *, String>> = arrayOf<AbstractColumn<*, *, String>>()) {
        indices.add(MongoDBIndex(name, unique, ascending, descending, text))
    }
}

================================================
FILE: kotlin-nosql-mongodb/src/main/kotlin/kotlinx/nosql/mongodb/MongoDB.kt
================================================
package kotlinx.nosql.mongodb

import kotlinx.nosql.Database
import kotlinx.nosql.Session
import com.mongodb.MongoClient
import kotlinx.nosql.AbstractColumn
import kotlinx.nosql.util.*
import java.util.concurrent.ConcurrentHashMap
import com.mongodb.ServerAddress
import com.mongodb.MongoClientOptions
import com.mongodb.MongoClientURI
import kotlinx.nosql.SchemaGenerationAction
import kotlinx.nosql.Create
import kotlinx.nosql.CreateDrop
import kotlinx.nosql.Validate
import kotlinx.nosql.Update
import kotlinx.nosql.AbstractSchema
import com.mongodb.MongoCredential

fun MongoDB(uri: MongoClientURI, schemas: Array<out AbstractSchema>, initialization: SchemaGenerationAction<MongoDBSession> = Validate()): MongoDB {
    val seeds: Array<ServerAddress> = uri.getHosts()!!.map { host ->
        if (host.indexOf(':') > 0) {
            val tokens = host.split(':')
            ServerAddress(tokens[0], tokens[1].toInt())
        } else
            ServerAddress(host)
    }.toTypedArray()
    val database: String = if (uri.getDatabase() != null) uri.getDatabase()!! else "test"
    val options: MongoClientOptions = uri.getOptions()!!
    val credentials = if (uri.getUsername() != null)
      arrayOf(MongoCredential.createMongoCRCredential(uri.getUsername(), database, uri.getPassword())!!)
    else arrayOf()
  return MongoDB(seeds, database, credentials, options, schemas, initialization)
}

// TODO: Allow use more than one database
class MongoDB(seeds: Array<ServerAddress> = arrayOf(ServerAddress()), val database: String = "test",
              val credentials: Array<MongoCredential> = arrayOf(), val options: MongoClientOptions = MongoClientOptions.Builder().build()!!,
              schemas: Array<out AbstractSchema>, action: SchemaGenerationAction<MongoDBSession> = Validate()) : Database<MongoDBSession>(schemas, action) {
    val seeds = seeds
    val db = MongoClient(seeds.toList(), credentials.toList(), options).getDB(database)!!
    var session = MongoDBSession(db)

    init {
        initialize()
    }

    // TODO: Use session pool
    override fun <R> withSession(statement: MongoDBSession.() -> R): R {
        Session.threadLocale.set(session)
        val r = session.statement()
        Session.threadLocale.set(null)
        return r
    }
}


================================================
FILE: kotlin-nosql-mongodb/src/main/kotlin/kotlinx/nosql/mongodb/MongoDBIndex.kt
================================================
package kotlinx.nosql.mongodb

import kotlinx.nosql.AbstractColumn
import kotlinx.nosql.AbstractIndex

class MongoDBIndex(name: String = "", unique: Boolean = false, val ascending: Array<out AbstractColumn<*, *, *>>,
                   val descending: Array<out AbstractColumn<*, *, *>>,
                   val text: Array<out AbstractColumn<*, *, String>>) : AbstractIndex(name)

================================================
FILE: kotlin-nosql-mongodb/src/main/kotlin/kotlinx/nosql/mongodb/MongoDBSession.kt
================================================
package kotlinx.nosql.mongodb

import com.mongodb.DB
import com.mongodb.BasicDBObject
import java.lang.reflect.Field
import java.util.ArrayList
import java.util.HashMap
import com.mongodb.DBObject
import java.util.Arrays
import org.bson.types.ObjectId
import com.mongodb.BasicDBList
import kotlinx.nosql.*
import kotlinx.nosql.util.*
import java.util.regex.Pattern
import java.util.Date
import java.util.concurrent.atomic.AtomicReference
import org.joda.time.DateTime
import org.joda.time.format.ISODateTimeFormat
import org.joda.time.LocalDate
import org.joda.time.LocalTime
import com.mongodb.DBCollection
import com.mongodb.DBCursor
import kotlinx.nosql.query.*
import kotlin.collections.*
import kotlin.text.*

class MongoDBSession(val db: DB) : Session, DocumentSchemaOperations, TableSchemaOperations, IndexOperations {
    override fun <T : Number> incr(schema: KeyValueSchema, column: AbstractColumn<out Any?, out AbstractSchema, T>, value: T): T {
        throw UnsupportedOperationException()
    }
    /*override fun <T : Number> incr(schema: AbstractSchema, column: AbstractColumn<out Any?, out AbstractSchema, T>, value: T, op: Query): T {
        throw UnsupportedOperationException()
    }*/
    val dbVersion : String
    val searchOperatorSupported: Boolean

    init {
        val results = db.command("buildInfo")
        dbVersion = results!!.get("version")!!.toString()
        val versions = dbVersion.split('.')
        searchOperatorSupported = versions[0].toInt() > 2 || (versions[0].toInt() == 2 && versions[1].toInt() >= 6)
    }

    override fun createIndex(schema: AbstractSchema, index: AbstractIndex) {
        val collection = db.getCollection(schema.schemaName)!!
        val dbObject = BasicDBObject()
        val i = (index as MongoDBIndex)
        for (column in i.ascending) {
            dbObject.append(column.name, 1)
        }
        for (column in i.descending) {
            dbObject.append(column.name, -1)
        }
        for (column in i.text) {
            dbObject.append(column.name, "text")
        }
        if (i.name.isNotEmpty())
            collection.createIndex(dbObject, i.name)
        else
            collection.createIndex(dbObject)
    }

    override fun <T : AbstractSchema> T.create() {
        db.createCollection(this.schemaName, BasicDBObject())
    }

    override fun <T : AbstractSchema> T.drop() {
        val collection = db.getCollection(this.schemaName)!!
        collection.drop()
    }

    override fun <T : kotlinx.nosql.DocumentSchema<P, V>, P: Any, V: Any> T.insert(v: V): Id<P, T> {
        val collection = db.getCollection(this.schemaName)!!
        val doc = getDBObject(v, this)
        if (discriminator != null) {
            var dominatorValue: Any? = null
            for (entry in kotlinx.nosql.DocumentSchema.discriminatorClasses.entries) {
                if (entry.value.java.equals(v.javaClass)) {
                    dominatorValue = entry.key.value
                }
            }
            doc.set(this.discriminator!!.column.name, dominatorValue!!)
        }
        collection.insert(doc)
        return Id<P, T>(doc.get("_id").toString() as P)
    }

    private fun getDBObject(o: Any, schema: Any): BasicDBObject {
        val doc = BasicDBObject()
        val javaClass = o.javaClass
        val fields = getAllFields(javaClass)
        var sc: Class<out Any?>? = null
        var s: AbstractSchema? = null
        if (schema is kotlinx.nosql.DocumentSchema<*, *> && schema.discriminator != null) {
            for (entry in kotlinx.nosql.DocumentSchema.discriminatorClasses.entries) {
                if (entry.value.java.equals(o.javaClass)) {
                    sc = kotlinx.nosql.DocumentSchema.discriminatorSchemaClasses.get(entry.key)!!
                    s = kotlinx.nosql.DocumentSchema.discriminatorSchemas.get(entry.key)!!
                }
            }
        }
        val schemaClass: Class<out Any?> = if (schema is kotlinx.nosql.DocumentSchema<*, *> && schema.discriminator != null) sc!! else schema.javaClass
        val objectSchema: Any = if (schema is kotlinx.nosql.DocumentSchema<*, *> && schema.discriminator != null) s!! else schema
        val schemaFields = getAllFieldsMap(schemaClass as Class<in Any>, { f -> f.isColumn })
        for (field in fields) {
            val schemaField = schemaFields.get(field.getName()!!.toLowerCase())
            if (schemaField != null && schemaField.isColumn) {
                field.setAccessible(true)
                schemaField.setAccessible(true)
                val column = schemaField.asColumn(objectSchema)
                val value = field.get(o)
                if (value != null) {
                    if (column.columnType.primitive) {
                        doc.append(column.name, when (value) {
                            is DateTime, is LocalDate, is LocalTime -> value.toString()
                            is Id<*, *> -> ObjectId(value.value.toString())
                            else -> value
                        })
                    } else if (column.columnType.iterable) {
                        val list = BasicDBList()
                        for (v in (value as Iterable<Any>)) {
                            list.add(if (column.columnType.custom) getDBObject(v, column) else
                                (if (v is Id<*, *>) ObjectId(v.toString()) else v))
                        }
                        doc.append(column.name, list)
                    } else doc.append(column.name, getDBObject(value, column))
                }
            }
        }
        return doc
    }

    override fun <T : kotlinx.nosql.DocumentSchema<P, C>, P: Any, C: Any> find(params: DocumentSchemaQueryParams<T, P, C>): Iterator<C> {
        if (params.query != null && !searchOperatorSupported && params.query!!.usesSearch())
          return params.schema.runCommandText(params.query!!)
        else
          return object : Iterator<C> {
            var cursor: DBCursor? = null
            var pos = 0
            override fun next(): C {
              if (cursor == null) {
                val collection = db.getCollection(params.schema.schemaName)
                val query = if (params.query != null) getQuery(params.query!!) else BasicDBObject()
                cursor = collection!!.find(query)!!
                if (params.skip != null) {
                  cursor!!.skip(params.skip!!)
                }
              }
              val value = getObject(cursor!!.next(), params.schema) as C
              pos++
              if (!cursor!!.hasNext() || (params.take != null && pos == params.take!!)) {
                cursor!!.close()
                pos = -1
              }
              return value
            }
            override fun hasNext(): Boolean {
              if (cursor == null) {
                val collection = db.getCollection(params.schema.schemaName)
                val query = if (params.query != null) getQuery(params.query!!) else BasicDBObject()
                cursor = collection!!.find(query)!!
                if (params.skip != null) {
                  cursor!!.skip(params.skip!!)
                }
              }
              return pos != -1 && cursor!!.hasNext() && (params.take == null || pos < params.take!!)
            }
          }
    }

    private fun <T : kotlinx.nosql.DocumentSchema<P, C>, P: Any, C: Any> T.runCommandText(op: Query): Iterator<C> {
      val searchCmd = BasicDBObject()
      searchCmd.append("text", this.schemaName)
      // TODO: Only supports text(...) and other condition
      searchCmd.append("search", when (op) {
        is TextQuery -> op.search
        is AndQuery -> if (op.expr1 is TextQuery) (op.expr1 as TextQuery).search else throw UnsupportedOperationException()
        else -> throw UnsupportedOperationException()
      })
      val schema = this
      if (op is AndQuery) {
        searchCmd.append("filter", getQuery(op.expr2))
      }
      val result = db.command(searchCmd)!!

      val objects = ArrayList<C>()
      for (doc in result.get("results") as BasicDBList) {
        objects.add(getObject((doc as DBObject).get("obj") as DBObject, schema))
      }

      return objects.iterator()
    }

    override fun <T : TableSchema<P>, P: Any, V: Any> find(params: TableSchemaProjectionQueryParams<T, P, V>): Iterator<V> {
        return object:Iterator<V> {
            var cursor: DBCursor? = null
            var pos = 0
            override fun next(): V {
                if (cursor == null) {
                    val collection = db.getCollection(params.table.schemaName)
                    val fields = BasicDBObject()
                    params.projection.forEach {
                        fields.append(it.fullName, "1")
                    }
                    val query = if (params.query != null) getQuery(params.query!!) else BasicDBObject()
                    cursor = collection!!.find(query, fields)!!
                    if (params.skip != null) {
                        cursor!!.skip(params.skip!!)
                    }
                }
                val doc = cursor!!.next()
                val values = ArrayList<Any?>()
                params.projection.forEach {
                    values.add(getColumnObject(doc, it))
                }
                val value = when (values.size) {
                    1 -> values[0] as V
                    2 -> Pair(values[0], values[1]) as V
                    3 -> Triple(values[0], values[1], values[2]) as V
                    4 -> Quadruple(values[0], values[1], values[2], values[3]) as V
                    5 -> Quintuple(values[0], values[1], values[2], values[3], values[4]) as V
                    6 -> Sextuple(values[0], values[1], values[2], values[3], values[4], values[5]) as V
                    7 -> Septuple(values[0], values[1], values[2], values[3], values[4], values[5], values[6]) as V
                    8 -> Octuple(values[0], values[1], values[2], values[3], values[4], values[5], values[6], values[7]) as V
                    9 -> Nonuple(values[0], values[1], values[2], values[3], values[4], values[5], values[6], values[7], values[8]) as V
                    10 -> Decuple(values[0], values[1], values[2], values[3], values[4], values[5], values[6], values[7], values[8], values[9]) as V
                    else -> throw UnsupportedOperationException()
                }
                pos++
                if (!cursor!!.hasNext() || (params.take != null && pos == params.take!!)) {
                    cursor!!.close()
                    pos = -1
                }
                return value
            }
            override fun hasNext(): Boolean {
                if (cursor == null) {
                    val collection = db.getCollection(params.table.schemaName)
                    val fields = BasicDBObject()
                    params.projection.forEach {
                        fields.append(it.fullName, "1")
                    }
                    val query = if (params.query != null) getQuery(params.query!!) else BasicDBObject()
                    cursor = collection!!.find(query, fields)!!
                    if (params.skip != null) {
                        cursor!!.skip(params.skip!!)
                    }
                }
                return pos != -1 && cursor!!.hasNext() && (params.take == null || pos < params.take!!)
            }
        }
    }

    private fun Query.usesSearch(): Boolean {
        return when (this) {
            is TextQuery -> true
            is OrQuery -> this.expr1.usesSearch() || this.expr2.usesSearch()
            is AndQuery -> this.expr1.usesSearch() || this.expr2.usesSearch()
            else -> false
        }
    }

    protected fun getQuery(op: Query, removePrefix: String = ""): BasicDBObject {
        val query = BasicDBObject()
        when (op) {
            is EqualQuery -> {
                if (op.expr1 is AbstractColumn<*, *, *>) {
                    if (op.expr2 is LiteralExpression) {
                        if ((op.expr1 as AbstractColumn<*, *, *>).columnType.primitive) {
                            if ((op.expr1 as AbstractColumn<*, *, *>).columnType.id) {
                                query.append((op.expr1 as AbstractColumn<*, *, *>).fullName, ObjectId((op.expr2 as LiteralExpression).value.toString()))
                            } else {
                                var columnName = (op.expr1 as AbstractColumn<*, *, *>).fullName
                                if (removePrefix.isNotEmpty() && columnName.startsWith(removePrefix)) {
                                    columnName = columnName.substring(removePrefix.length + 1)
                                }
                                query.append( columnName, (op.expr2 as LiteralExpression).value)
                            }
                        } else {
                            throw UnsupportedOperationException()
                        }
                    } else if (op.expr2 is AbstractColumn<*, *, *>) {
                        query.append("\$where", "this.${(op.expr1 as AbstractColumn<*, *, *>).fullName} == this.${(op.expr2 as AbstractColumn<*, *, *>).fullName}")
                    } else {
                        throw UnsupportedOperationException()
                    }
                } else {
                    throw UnsupportedOperationException()
                }
            }
            is MatchesQuery -> {
                if (op.expr1 is AbstractColumn<*, *, *>) {
                    if (op.expr2 is LiteralExpression) {
                        if ((op.expr2 as LiteralExpression).value is Pattern) {
                            query.append((op.expr1 as AbstractColumn<*, *, *>).fullName, BasicDBObject().append("\$regex", (op.expr2 as LiteralExpression).value))
                        } else {
                            throw UnsupportedOperationException()
                        }
                    } else {
                        throw UnsupportedOperationException()
                    }
                } else {
                    throw UnsupportedOperationException()
                }
            }
            is NotEqualQuery -> {
                if (op.expr1 is AbstractColumn<*, *, *>) {
                    if (op.expr2 is LiteralExpression) {
                        if ((op.expr2 as LiteralExpression).value is String || (op.expr2 as LiteralExpression).value is Int) {
                            if ((op.expr1 as AbstractColumn<*, *, *>).columnType.id) {
                                query.append((op.expr1 as AbstractColumn<*, *, *>).fullName, BasicDBObject().append("\$ne", ObjectId((op.expr2 as LiteralExpression).value.toString())))
                            } else {
                                query.append((op.expr1 as AbstractColumn<*, *, *>).fullName, BasicDBObject().append("\$ne", (op.expr2 as LiteralExpression).value))
                            }
                        } else {
                            throw UnsupportedOperationException()
                        }
                    } else if (op.expr2 is AbstractColumn<*, *, *>) {
                        query.append("\$where", "this.${(op.expr1 as AbstractColumn<*, *, *>).fullName} != this.${(op.expr2 as AbstractColumn<*, *, *>).fullName}")
                    } else {
                        throw UnsupportedOperationException()
                    }
                } else {
                    throw UnsupportedOperationException()
                }
            }
            is GreaterQuery -> {
                if (op.expr1 is AbstractColumn<*, *, *>) {
                    if (op.expr2 is LiteralExpression) {
                        if ((op.expr2 as LiteralExpression).value is String || (op.expr2 as LiteralExpression).value is Int) {
                            query.append((op.expr1 as AbstractColumn<*, *, *>).fullName, BasicDBObject().append("\$gt", (op.expr2 as LiteralExpression).value))
                        } else {
                            throw UnsupportedOperationException()
                        }
                    } else if (op.expr2 is AbstractColumn<*, *, *>) {
                        query.append("\$where", "this.${(op.expr1 as AbstractColumn<*, *, *>).fullName} > this.${(op.expr2 as AbstractColumn<*, *, *>).fullName}")
                    } else {
                        throw UnsupportedOperationException()
                    }
                } else {
                    throw UnsupportedOperationException()
                }
            }
            is LessQuery -> {
                if (op.expr1 is AbstractColumn<*, *, *>) {
                    if (op.expr2 is LiteralExpression) {
                        if ((op.expr2 as LiteralExpression).value is String || (op.expr2 as LiteralExpression).value is Int) {
                            query.append((op.expr1 as AbstractColumn<*, *, *>).fullName, BasicDBObject().append("\$lt", (op.expr2 as LiteralExpression).value))
                        } else {
                            throw UnsupportedOperationException()
                        }
                    } else if (op.expr2 is AbstractColumn<*, *, *>) {
                        query.append("\$where", "this.${(op.expr1 as AbstractColumn<*, *, *>).fullName} < this.${(op.expr2 as AbstractColumn<*, *, *>).fullName}")
                    } else {
                        throw UnsupportedOperationException()
                    }
                } else {
                    throw UnsupportedOperationException()
                }
            }
            is GreaterEqualQuery -> {
                if (op.expr1 is AbstractColumn<*, *, *>) {
                    if (op.expr2 is LiteralExpression) {
                        if ((op.expr2 as LiteralExpression).value is String || (op.expr2 as LiteralExpression).value is Int) {
                            query.append((op.expr1 as AbstractColumn<*, *, *>).fullName, BasicDBObject().append("\$gte", (op.expr2 as LiteralExpression).value))
                        } else {
                            throw UnsupportedOperationException()
                        }
                    } else if (op.expr2 is AbstractColumn<*, *, *>) {
                        query.append("\$where", "this.${(op.expr1 as AbstractColumn<*, *, *>).fullName} >= this.${(op.expr2 as AbstractColumn<*, *, *>).fullName}")
                    } else {
                        throw UnsupportedOperationException()
                    }
                } else {
                    throw UnsupportedOperationException()
                }
            }
            is LessEqualQuery -> {
                if (op.expr1 is AbstractColumn<*, *, *>) {
                    if (op.expr2 is LiteralExpression) {
                        if ((op.expr2 as LiteralExpression).value is String || (op.expr2 as LiteralExpression).value is Int) {
                            query.append((op.expr1 as AbstractColumn<*, *, *>).fullName, BasicDBObject().append("\$lte", (op.expr2 as LiteralExpression).value))
                        } else {
                            throw UnsupportedOperationException()
                        }
                    } else if (op.expr2 is AbstractColumn<*, *, *>) {
                        query.append("\$where", "this.${(op.expr1 as AbstractColumn<*, *, *>).fullName} <= this.${(op.expr2 as AbstractColumn<*, *, *>).fullName}")
                    } else {
                        throw UnsupportedOperationException()
                    }
                } else {
                    throw UnsupportedOperationException()
                }
            }
            is MemberOfQuery -> {
                if (op.expr1 is AbstractColumn<*, *, *>) {
                    if (op.expr2 is LiteralExpression) {
                        if ((op.expr2 as LiteralExpression).value is List<*> || (op.expr2 as LiteralExpression).value is Array<*>) {
                            query.append((op.expr1 as AbstractColumn<*, *, *>).fullName, BasicDBObject().append("\$in", (op.expr2 as LiteralExpression).value))
                        } else {
                            throw UnsupportedOperationException()
                        }
                    } else {
                        throw UnsupportedOperationException()
                    }
                } else {
                    throw UnsupportedOperationException()
                }
            }
            is NotMemberOfQuery -> {
                if (op.expr1 is AbstractColumn<*, *, *>) {
                    if (op.expr2 is LiteralExpression) {
                        if ((op.expr2 as LiteralExpression).value is List<*> || (op.expr2 as LiteralExpression).value is Array<*>) {
                            query.append((op.expr1 as AbstractColumn<*, *, *>).fullName, BasicDBObject().append("\$nin", (op.expr2 as LiteralExpression).value))
                        } else {
                            throw UnsupportedOperationException()
                        }
                    } else {
                        throw UnsupportedOperationException()
                    }
                } else {
                    throw UnsupportedOperationException()
                }
            }
            // TODO TODO TODO eq expression and eq expression
            is AndQuery -> {
                val query1 = getQuery(op.expr1)
                val query2 = getQuery(op.expr2)
                for (entry in query1.entries) {
                    query.append(entry.key, entry.value)
                }
                for (entry in query2.entries) {
                    query.append(entry.key, entry.value)
                }
                return query
            }
            is OrQuery -> {
                query.append("\$or", Arrays.asList(getQuery(op.expr1), getQuery(op.expr2)))
            }
            is TextQuery -> {
                query.append("\$text", BasicDBObject().append("\$search", op.search))
            }
            is NoQuery -> {
                // Do nothing
            }
            else -> {
                throw UnsupportedOperationException()
            }
        }
        return query
    }

    private fun <T: kotlinx.nosql.DocumentSchema<P, V>, P: Any, V : Any /* not sure */> getObject(doc: DBObject, schema: T): V {
        var s: AbstractSchema? = null
        val valueInstance: Any = if (schema is kotlinx.nosql.DocumentSchema<*, *> && schema.discriminator != null) {
            var instance: Any? = null
            val discriminatorValue = doc.get(schema.discriminator!!.column.name)
            for (discriminator in kotlinx.nosql.DocumentSchema.tableDiscriminators.get(schema.schemaName)!!) {
                if (discriminator.value!!.equals(discriminatorValue)) {
                    instance = newInstance(kotlinx.nosql.DocumentSchema.discriminatorClasses.get(discriminator)!!.java)
                    s = kotlinx.nosql.DocumentSchema.discriminatorSchemas.get(discriminator)!!
                    break
                }
            }
            instance!!
        } else {
            s = schema
            newInstance(schema.valueClass.java)
        }
        val schemaClass = s!!.javaClass
        val schemaFields = getAllFields(schemaClass as Class<in Any?>)
        val valueFields = getAllFieldsMap(valueInstance.javaClass as Class<in Any?>)
        for (schemaField in schemaFields) {
            if (AbstractColumn::class.java.isAssignableFrom(schemaField.getType()!!)) {
                val valueField = valueFields.get(if (schemaField.getName()!!.equals("pk")) "id" else schemaField.getName()!!.toLowerCase())
                if (valueField != null) {
                    schemaField.setAccessible(true)
                    valueField.setAccessible(true)
                    val column = schemaField.asColumn(s!!)
                    val value = doc.get(column.name)
                    val columnValue: Any? = if (value == null) {
                        null
                    } else if (column.columnType.id && !column.columnType.iterable)
                        Id<P, T>(value.toString() as P)
                    else if (column.columnType.primitive) {
                        when (column.columnType) {
                            ColumnType.DATE -> LocalDate(value.toString())
                            ColumnType.TIME -> LocalTime(value.toString())
                            ColumnType.DATE_TIME -> DateTime(value.toString())
                            else -> doc.get(column.name)
                        }
                    } else if (column.columnType.list && !column.columnType.custom) {
                        (doc.get(column.name) as BasicDBList).toList()
                    } else if (column.columnType.set && !column.columnType.custom && !column.columnType.id) {
                        (doc.get(column.name) as BasicDBList).toSet()
                    } else if (column.columnType.id && column.columnType.set) {
                        val list = doc.get(column.name) as BasicDBList
                        list.map { Id<String, TableSchema<String>>(it.toString()) }.toSet()
                    } else if (column.columnType.custom && column.columnType.set) {
                        val list = doc.get(column.name) as BasicDBList
                        list.map { getObject(it as DBObject, column as ListColumn<*, out AbstractSchema>) }.toSet()
                    } else if (column.columnType.custom && column.columnType.list) {
                        val list = doc.get(column.name) as BasicDBList
                        list.map { getObject(it as DBObject, column as ListColumn<*, out AbstractSchema>) }.toList()
                    } else {
                        getObject(doc.get(column.name) as DBObject, column as Column<Any, T>)
                    }
                    if (columnValue != null || column is AbstractNullableColumn) {
                        valueField.set(valueInstance, columnValue)
                    } else {
                        throw NullPointerException()
                    }
                }
            }
        }
        return valueInstance as V
    }

    private fun getObject(doc: DBObject, column: AbstractColumn<*, *, *>): Any? {
        val valueInstance = newInstance(column.valueClass.java)
        val schemaClass = column.javaClass
        val columnFields = schemaClass.getDeclaredFields()!!
        val valueFields = getAllFieldsMap(valueInstance.javaClass as Class<in Any?>)
        for (columnField in columnFields) {
            if (columnField.isColumn) {
                val valueField = valueFields.get(columnField.getName()!!.toLowerCase())
                if (valueField != null) {
                    columnField.setAccessible(true)
                    valueField.setAccessible(true)
                    val column = columnField.asColumn(column)
                    val columnValue: Any? = if (column.columnType.id && !column.columnType.iterable) Id<String, TableSchema<String>>(doc.get(column.name).toString())
                    else if (column.columnType.primitive) doc.get(column.name)
                    else if (column.columnType.list && !column.columnType.custom) (doc.get(column.name) as BasicDBList).toList()
                    else if (column.columnType.set && !column.columnType.custom && !column.columnType.id) (doc.get(column.name) as BasicDBList).toSet()
                    else if (column.columnType.custom && column.columnType.list) {
                        val list = doc.get(column.name) as BasicDBList
                        list.map { getObject(it as DBObject, column as ListColumn<*, out AbstractSchema>) }.toList()
                    } else if (column.columnType.id && column.columnType.set) {
                        val list = doc.get(column.name) as BasicDBList
                        list.map { Id<String, TableSchema<String>>(it.toString()) }.toSet()
                    } else if (column.columnType.custom && column.columnType.set) {
                        val list = doc.get(column.name) as BasicDBList
                        list.map { getObject(it as DBObject, column as ListColumn<*, out AbstractSchema>) }.toSet()
                    } else {
                        getObject(doc.get(column.name) as DBObject, column as Column<*, out AbstractSchema>)
                    }
                    if (columnValue != null || column is AbstractNullableColumn) {
                        valueField.set(valueInstance, columnValue)
                    } else {
                        throw NullPointerException()
                    }
                }
            }
        }
        return valueInstance
    }

    override fun <T : AbstractSchema> insert(columns: Array<Pair<AbstractColumn<out Any?, T, out Any>, Any?>>) {
        throw UnsupportedOperationException()
    }

    override fun <T : AbstractSchema> delete(table: T, op: Query): Int {
        val collection = db.getCollection(table.schemaName)!!
        val query = getQuery(op)
        return collection.remove(query)!!.getN()
    }

    override fun update(schema: AbstractSchema, columnValues: Array<Pair<AbstractColumn<*, *, *>, *>>, op: Query): Int {
        val collection = db.getCollection(schema.schemaName)!!
        val statement = BasicDBObject()
        val doc = BasicDBObject().append("\$set", statement)
        for ((column, value) in columnValues) {
            statement.append(column.fullName, getDBValue(value, column))
        }
        return collection.update(getQuery(op), doc)!!.getN()
    }

    override fun <T> addAll(schema: AbstractSchema, column: AbstractColumn<Collection<T>, *, *>, values: Collection<T>, op: Query): Int {
        val collection = db.getCollection(schema.schemaName)!!
        val statement = BasicDBObject()
        val doc = BasicDBObject().append("\$pushAll", statement)
        statement.append(column.fullName, getDBValue(values, column))
        return collection.update(getQuery(op), doc)!!.getN()
    }

    override fun <T> removeAll(schema: AbstractSchema, column: AbstractColumn<Collection<T>, *, *>, values: Collection<T>, op: Query): Int {
        val collection = db.getCollection(schema.schemaName)!!
        val statement = BasicDBObject()
        val doc = BasicDBObject().append("\$pullAll", statement)
        statement.append(column.fullName, getDBValue(values, column))
        return collection.update(getQuery(op), doc)!!.getN()
    }

    override fun <T> removeAll(schema: AbstractSchema, column: AbstractColumn<Collection<T>, *, *>, removeOp: Query, op: Query): Int {
        val collection = db.getCollection(schema.schemaName)!!
        val statement = BasicDBObject()
        val doc = BasicDBObject().append("\$pull", statement)
        statement.append(column.fullName, getQuery(removeOp, column.fullName))
        return collection.update(getQuery(op), doc)!!.getN()
    }

    private fun getDBValue(value: Any?, column: AbstractColumn<*, *, *>, withinIterable: Boolean = false): Any? {
        return if (!column.columnType.custom && (!column.columnType.iterable || withinIterable )) {
            when (value) {
                is DateTime, is LocalDate, is LocalTime -> value.toString()
                is Id<*, *> -> ObjectId(value.value.toString())
                else -> value
            }
        } else if (column.columnType.custom && !column.columnType.iterable)
            if (value != null) getDBObject(value, column) else null
        else if (column.columnType.list && column.columnType.custom)
            (value as List<*>).map { getDBObject(it!!, column) }
        else if (column.columnType.set && column.columnType.custom)
            (value as Set<*>).map { getDBObject(it!!, column) }.toSet()
        else if (column.columnType.list && !column.columnType.custom)
            (value as List<*>).map { getDBValue(it!!, column, true) }
        else if (column.columnType.set && !column.columnType.custom)
            (value as Set<*>).map { getDBValue(it!!, column, true) }.toSet()
        else throw UnsupportedOperationException()
    }

    private fun getColumnObject(doc: DBObject, column: AbstractColumn<*, *, *>): Any? {
        val columnObject = parse(doc, column.fullName.split(".").toTypedArray())
        return if (column.columnType.id && !column.columnType.iterable) {
            Id<String, TableSchema<String>>(columnObject.toString())
        } else if (column.columnType.primitive && !column.columnType.iterable) when (column.columnType) {
            ColumnType.DATE -> LocalDate.parse(columnObject.toString())
            ColumnType.TIME -> LocalTime.parse(columnObject.toString())
            ColumnType.DATE_TIME -> DateTime.parse(columnObject.toString())
            else -> columnObject
        } else if (column.columnType == ColumnType.STRING_SET) {
            (columnObject as BasicDBList).toSet()
        } else if (column.columnType == ColumnType.STRING_LIST) {
            (columnObject as BasicDBList).toList()
        } else if (column.columnType.id && column.columnType.set) {
            (columnObject as BasicDBList).map { Id<String, TableSchema<String>>(it.toString()) }.toSet()
        } else if (column.columnType.id && column.columnType.list) {
            (columnObject as BasicDBList).map { Id<String, TableSchema<String>>(it.toString()) }
        } else if (column.columnType.custom && column.columnType.list) {
            (columnObject as BasicDBList).map { getObject(it as DBObject, column as ListColumn<Any, out AbstractSchema>) }
        } else if (column.columnType.custom && column.columnType.set) {
            (columnObject as BasicDBList).map { getObject(it as DBObject, column as ListColumn<Any, out AbstractSchema>) }.toSet()
        } else if (column.columnType.custom) {
            getObject(columnObject as DBObject, column)
        } else {
            UnsupportedOperationException()
        }
    }

    private fun parse(doc: DBObject, path: Array<String>, position: Int = 0): Any? {
        val value = doc.get(path[position])
        if (position < path.size - 1) {
            return parse(value as DBObject, path, position + 1)
        } else {
            return value
        }
    }

    override fun <T: kotlinx.nosql.DocumentSchema<P, C>, P: Any, C: Any> T.find(query: T.() -> Query): DocumentSchemaQueryWrapper<T, P, C> {
        val params = DocumentSchemaQueryParams<T, P, C>(this, query())
        return DocumentSchemaQueryWrapper(params)
    }
}


================================================
FILE: kotlin-nosql-mongodb/src/test/kotlin/kotlinx/nosql/mongodb/test/MongoDBSpek.kt
================================================
package kotlinx.nosql.mongodb.test

import kotlin.test.assertEquals
import kotlinx.nosql.*
import kotlinx.nosql.mongodb.*
import kotlinx.nosql.mongodb.DocumentSchema
import org.joda.time.LocalDate
import org.jetbrains.spek.api.Spek
import java.util.regex.Pattern
import kotlin.reflect.KClass
import kotlin.test.assertTrue

class MongoDBSpek : Spek() {
    open class ProductSchema<D: Any, S : DocumentSchema<D>>(klass: KClass<D>, discriminator: String) : DocumentSchema<D>("products",
            klass, 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 available = boolean<S>("available")
        val createdAtDate = date<S>("createdAtDate")
        val nullableBooleanNoValue = nullableBoolean<S>("nullableBooleanNoValue")
        val nullableBooleanWithValue = nullableBoolean<S>("nullableBooleanWithValue")
        val nullableDateNoValue = nullableDate<S>("nullableDateNoValue")
        val nullableDateWithValue = nullableDate<S>("nullableDateWithValue")
        val cost = double<S>("cost")
        val nullableDoubleNoValue = nullableDouble<S>("nullableDoubleNoValue")
        val nullableDoubleWithValue = nullableDouble<S>("nullableDoubleWithValue")
        val setOfStrings = setOfString<S>("setOfStrings")

        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<T : DocumentSchema<D>>() : Column<Pricing, T>("pricing", Pricing::class) {
            val list = integer<T>("list")
            val retail = integer<T>("retail")
            val savings = integer<T>("savings")
            val pctSavings = integer<T>("pct_savings")
        }

        init {
            ensureIndex(text = arrayOf(title, description))
            ensureIndex(name = "asinIndex", unique = true, ascending = arrayOf(asin))
        }
    }

    object Artists : DocumentSchema<Artist>("artists", Artist::class) {
        val name = string("name")
    }

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

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

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

            val tracks = TracksColumn()
        }

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

    abstract class Product(val sku: String, val title: String, val description: String,
                           val asin: String, val available: Boolean, val cost: Double,
                           val createdAtDate: LocalDate, val nullableBooleanNoValue: Boolean?,
                           val nullableBooleanWithValue: Boolean?,
                           val nullableDateNoValue: org.joda.time.LocalDate?, val nullableDateWithValue: LocalDate?,
                           val nullableDoubleNoValue: Double?, val nullableDoubleWithValue: Double?,
                           val setOfStrings: Set<String>, val shipping: Shipping, val pricing: Pricing) {
        val id: Id<String, Products>? = null // How to define id for implementation classes?
    }

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

    class Album(sku: String, title: String, description: String, asin: String, available: Boolean,
                cost: Double, createdAtDate: org.joda.time.LocalDate,
                nullableBooleanNoValue: Boolean?, nullableBooleanWithValue: Boolean?,
                nullableDateNoValue: LocalDate?, nullableDateWithValue: LocalDate?,
                nullableDoubleNoValue: Double?, nullableDoubleWithValue: Double?,
                setOfStrings: Set<String>, shipping: Shipping, pricing: Pricing,
                val details: Details) : Product(sku, title, description, asin, available, cost, createdAtDate,
            nullableBooleanNoValue, nullableBooleanWithValue, nullableDateNoValue, nullableDateWithValue,
            nullableDoubleNoValue, nullableDoubleWithValue, setOfStrings, shipping, pricing) {
    }

    class Artist(val name: String) {
        val id: Id<String, Artists>? = null
    }

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

    class Track(val title: String, val duration: Int) {
    }

    init {
        given("a polymorhpic schema") {
            var artistId: Id<String, Artists>? = null
            var artistId2: Id<String, Artists>? = null
            var artistId3: Id<String, Artists>? = null
            var albumId: Id<String, Albums>? = null

            val db = MongoDB(schemas = arrayOf(Artists, Products, Albums), action = CreateDrop(onCreate = {
                val arId: Id<String, Artists> = Artists.insert(Artist(name = "John Coltrane"))
                val arId2: Id<String, Artists> = Artists.insert(Artist(name = "Andrey Cheptsov"))
                val arId3: Id<String, Artists> = Artists.insert(Artist(name = "Daft Punk"))
                assert(arId.value.length > 0)
                val aId = Albums.insert(Album(sku = "00e8da9b", title = "A Love Supreme", description = "by John Coltrane",
                        asin = "B0000A118M", available = true, cost = 1.23, createdAtDate = LocalDate(2014, 3, 8), nullableBooleanNoValue = null,
                        nullableBooleanWithValue = false, nullableDateNoValue = null, nullableDateWithValue = LocalDate(2014, 3, 7),
                        nullableDoubleNoValue = null, nullableDoubleWithValue = 1.24,
                        setOfStrings = setOf("Something"), 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 = arId, artistIds = setOf(arId, arId2),
                                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)))))
                assert(aId.value.length > 0)
                albumId = aId
                artistId = arId
                artistId2 = arId2
                artistId3 = arId3
            }))

            on("filtering a non-inherited schema") {
                val a = db.withSession {
                    val artists = Artists.find { name.equal("John Coltrane") }.toList()
                    it("should return a generated id for artist") {
                        assert(artists.size == 1)
                    }
                    "a"
                }
                kotlin.test.assertEquals("a", a)
            }

            fun validate(results: List<Product>) {
                assert(results.size == 1)
                assert(results[0] is Album)
                val album = results[0] as Album
                assertEquals("00e8da9b", results[0].sku)
                assertEquals(true, results[0].available)
                assertEquals(1.23, results[0].cost)
                kotlin.test.assertEquals(LocalDate(2014, 3, 8), results[0].createdAtDate)
                assert(results[0].nullableDateNoValue == null)
                assertEquals(LocalDate(2014, 3, 7), results[0].nullableDateWithValue)
                assert(results[0].nullableDoubleNoValue == null)
                assertEquals(1.24, results[0].nullableDoubleWithValue)
                assert(results[0].nullableBooleanNoValue == null)
                assertEquals(false, results[0].nullableBooleanWithValue)
                kotlin.test.assertEquals("A Love Supreme", results[0].title)
                assertEquals("by John Coltrane", results[0].description)
                assertEquals("B0000A118M", results[0].asin)
                assert(album.setOfStrings.contains("Something"))
                kotlin.test.assertEquals(6, results[0].shipping.weight)
                kotlin.test.assertEquals(10, results[0].shipping.dimensions.width)
                kotlin.test.assertEquals(10, results[0].shipping.dimensions.height)
                kotlin.test.assertEquals(1, results[0].shipping.dimensions.depth)
                assertEquals(1200, results[0].pricing.list)
                kotlin.test.assertEquals(1100, results[0].pricing.retail)
                assertEquals(100, results[0].pricing.savings)
                kotlin.test.assertEquals(8, results[0].pricing.pctSavings)
                kotlin.test.assertEquals("A Love Supreme [Original Recording Reissued]", album.details.title)
                assertEquals(artistId!!, album.details.artistId)
                assert(album.details.artistIds.size == 2)
                assert(album.details.artistIds.contains(artistId as Id<String, Artists>))
                assert(album.details.artistIds.contains(artistId2))
                assert(album.details.genre.size == 2)
                assert(album.details.genre.contains("Jazz"))
                assert(album.details.genre.contains("General"))
                assert(album.details.tracks.size == 3)
                assertEquals(album.details.tracks[0].title, "A Love Supreme Part I: Acknowledgement")
                assertEquals(album.details.tracks[0].duration, 100)
                assertEquals(album.details.tracks[1].title, "A Love Supreme Part II - Resolution")
                assertEquals(album.details.tracks[1].duration, 200)
                assertEquals(album.details.tracks[2].title, "A Love Supreme, Part III: Pursuance")
                kotlin.test.assertEquals(album.details.tracks[2].duration, 300)
            }

            /**
             * Remove Rxjava
             * Replace Observable with Iterable
             */

            on("filtering an abstract schema") {
                db.withSession {
                    val results = Products.find { (sku.equal("00e8da9b")).or(shipping.weight.equal(6)) }.toList()
                    it("should return a correct object") {
                        validate(results)
                    }
                }
            }

            on("filtering a non-abstract schema") {
                db.withSession {
                    val results: List<Album> = Albums.find { details.artistId.equal(artistId!!) }.toList()
                    it("should return a correct object") {
                        validate(results)
                    }
                }
            }

            on("filtering a non-abstract schema drop take") {
                db.withSession {
                    val results = Products.find { (sku.equal("00e8da9b")).or(shipping.weight.equal(6)) }.skip(1).take(1).toList()
                    it("should return nothing") {
                        assert(results.isEmpty())
                    }
                }
            }

            on("getting all elements from a non-abstract schema") {
                db.withSession {
                    val results = Products.find().toList()
                    it("should return a correct object") {
                        validate(results)
                    }
                }
            }

            on("getting a document by id") {
                db.withSession {
                    val album = Albums.find { id.equal(albumId!!) }.single()
                    it("should return a correct object") {
                        validate(listOf(album))
                    }
                }
            }

            on("getting one column by id") {
                db.withSession {
                    val title = Albums.find { id.equal(albumId!!) }.projection { details.title }.single()
                    it("returns correct values") {
                        assertEquals("A Love Supreme [Original Recording Reissued]", title)
                    }
                }
            }

            on("getting two columns by id") {
                db.withSession {
                    val (title, pricing) = Albums.find { id.equal(albumId!!) }.projection { details.title + pricing }.single()
                    it("returns correct values") {
                        assertEquals("A Love Supreme [Original Recording Reissued]", title)
                        assertEquals(1200, pricing.list)
                        assertEquals(1100, pricing.retail)
                        assertEquals(100, pricing.savings)
                        assertEquals(8, pricing.pctSavings)
                    }
                }
            }

            on("getting three columns by id") {
                db.withSession {
                    val (sku, title, pricing) = Albums.find { id.equal(albumId!!) }.projection { sku + details.title + pricing }.single()
                    it("returns correct values") {
                        assertEquals("00e8da9b", sku)
                        assertEquals("A Love Supreme [Original Recording Reissued]", title)
                        assertEquals(1200, pricing.list)
                        assertEquals(1100, pricing.retail)
                        assertEquals(100, pricing.savings)
                        assertEquals(8, pricing.pctSavings)
                    }
                }
            }

            on("getting four columns by id") {
                db.withSession {
                    val (sku, title, description, pricing) = Albums.find{ id.equal(albumId!!) }.projection { sku + title + description + pricing }.single()
                    it("returns correct values") {
                        assertEquals("00e8da9b", sku)
                        assertEquals("A Love Supreme", title)
                        assertEquals("by John Coltrane", description)
                        assertEquals(1200, pricing.list)
                        assertEquals(1100, pricing.retail)
                        assertEquals(100, pricing.savings)
                        assertEquals(8, pricing.pctSavings)
                    }
                }
            }

            on("getting five columns by id") {
                db.withSession {
                    val (sku, title, description, asin, pricing) = Albums.find{ id.equal(albumId!!) }.projection { sku + title + description + asin + pricing }.single()
                    it("returns correct values") {
                        assertEquals("00e8da9b", sku)
                        assertEquals("A Love Supreme", title)
                        assertEquals("by John Coltrane", description)
                        assertEquals("B0000A118M", asin)
                        assertEquals(1200, pricing.list)
                        assertEquals(1100, pricing.retail)
                        assertEquals(100, pricing.savings)
                        assertEquals(8, pricing.pctSavings)
                    }
                }
            }

            on("getting six columns by id") {
                db.withSession {
                    val (sku, title, description, asin, list, retail) = Albums.find { id.equal(albumId!!) }. projection { sku + title + description + asin + pricing.list + pricing.retail }.single()
                    it("returns correct values") {
                        assertEquals("00e8da9b", sku)
                        assertEquals("A Love Supreme", title)
                        assertEquals("by John Coltrane", description)
                        assertEquals("B0000A118M", asin)
                        assertEquals(1200, list)
                        assertEquals(1100, retail)
                    }
                }
            }

            on("getting seven columns by id") {
                db.withSession {
                    val (sku, title, description, asin, list, retail, savings) = Albums.find { id.equal(albumId!!) }.projection { sku + title + description + asin + pricing.list + pricing.retail + pricing.savings}.single()
                    it("returns correct values") {
                        assertEquals("00e8da9b", sku)
                        assertEquals("A Love Supreme", title)
                        assertEquals("by John Coltrane", description)
                        assertEquals("B0000A118M", asin)
                        assertEquals(1200, list)
                        assertEquals(1100, retail)
                        assertEquals(100, savings)
                    }
                }
            }

            on("getting eight columns by id") {
                db.withSession {
                    val (sku, title, description, asin, list, retail, savings, pctSavings) = Albums.find { id.equal(albumId!!) }.projection { sku + title + description + asin + pricing.list + pricing.retail + pricing.savings + pricing.pctSavings }.single()
                    it("returns correct values") {
                        assertEquals("00e8da9b", sku)
                        assertEquals("A Love Supreme", title)
                        assertEquals("by John Coltrane", description)
                        assertEquals("B0000A118M", asin)
                        assertEquals(1200, list)
                        assertEquals(1100, retail)
                        assertEquals(100, savings)
                        assertEquals(8, pctSavings)
                    }
                }
            }

            on("getting nine columns by id") {
                db.withSession {
                    val (sku, title, description, asin, list, retail, savings, pctSavings, shipping) = Albums.find { id.equal(albumId!!) }.projection { sku + title + description + asin + pricing.list + pricing.retail + pricing.savings + pricing.pctSavings + shipping }.single()
                    it("returns correct values") {
                        assertEquals("00e8da9b", sku)
                        assertEquals("A Love Supreme", title)
                        assertEquals("by John Coltrane", description)
                        assertEquals("B0000A118M", asin)
                        assertEquals(1200, list)
                        assertEquals(1100, retail)
                        assertEquals(100, savings)
                        assertEquals(8, pctSavings)
                        assertEquals(6, shipping.weight)
                        assertEquals(10, shipping.dimensions.width)
                        assertEquals(10, shipping.dimensions.height)
                        assertEquals(1, shipping.dimensions.depth)
                    }
                }
            }

            on("getting ten columns by id") {
                db.withSession {
                    val a = with (Albums) { sku + title + description + asin + pricing.list + pricing.retail + pricing.savings + pricing.pctSavings + shipping.weight + shipping.dimensions }
                    val (sku, title, description, asin, list, retail, savings, pctSavings, weight, dimensions) = Albums.find { id.equal(albumId!!) }.projection { a }.single()
                    it("returns correct values") {
                        assertEquals("00e8da9b", sku)
                        assertEquals("A Love Supreme", title)
                        assertEquals("by John Coltrane", description)
                        assertEquals("B0000A118M", asin)
                        assertEquals(1200, list)
                        assertEquals(1100, retail)
                        assertEquals(100, savings)
                        assertEquals(8, pctSavings)
                        assertEquals(6, weight)
                        assertEquals(10, dimensions.width)
                        assertEquals(10, dimensions.height)
                        assertEquals(1, dimensions.depth)
                    }
                }
            }

            on("getting one id-column by another id") {
                db.withSession {
                    val aId = Albums.find { id.equal(albumId!!) }.projection { details.artistId }.single()
                    it("returns correct values") {
                        assertEquals(artistId, aId)
                    }
                }
            }

            on("getting one column by filter expression") {
                db.withSession {
                    val title = Albums.find { sku.equal("00e8da9b") }.projection { details.title }.single()
                    it("returns correct values") {
                        assertEquals("A Love Supreme [Original Recording Reissued]", title)
                    }
                }
            }

            on("getting two columns by a filter expression") {
                db.withSession {
                    val (title, pricing) = Albums.find { sku.equal("00e8da9b") }.projection { details.title + pricing }.single()
                    it("returns correct values") {
                        assertEquals("A Love Supreme [Original Recording Reissued]", title)
                        assertEquals(1200, pricing.list)
                        assertEquals(1100, pricing.retail)
                        assertEquals(100, pricing.savings)
                        assertEquals(8, pricing.pctSavings)
                    }
                }
            }

            on("getting three columns by a filter expression") {
                db.withSession {
                    val (sku, title, pricing) = Albums.find { sku.equal("00e8da9b") }.projection { sku + details.title + pricing }.single()
                    it("returns correct values") {
                        assertEquals("00e8da9b", sku)
                        assertEquals("A Love Supreme [Original Recording Reissued]", title)
                        assertEquals(1200, pricing.list)
                        assertEquals(1100, pricing.retail)
                        assertEquals(100, pricing.savings)
                        assertEquals(8, pricing.pctSavings)
                    }
                }
            }

            on("getting four columns by a filter expression") {
                db.withSession {
                    val (sku, title, description, pricing) = Products.find { sku.equal("00e8da9b") }.projection { sku + title + description + pricing }.single()
                    it("returns correct values") {
                        assertEquals("00e8da9b", sku)
                        assertEquals("A Love Supreme", title)
                        assertEquals("by John Coltrane", description)
                        assertEquals(1200, pricing.list)
                        assertEquals(1100, pricing.retail)
                        assertEquals(100, pricing.savings)
                        assertEquals(8, pricing.pctSavings)
                    }
                }
            }

            on("getting five columns by a filter expression") {
                db.withSession {
                    val (sku, title, description, asin, pricing) = Products.find { sku.equal("00e8da9b") }.projection { sku + title + description + asin + pricing }.single()
                    it("returns correct values") {
                        assertEquals("00e8da9b", sku)
                        assertEquals("A Love Supreme", title)
                        assertEquals("by John Coltrane", description)
                        assertEquals("B0000A118M", asin)
                        assertEquals(1200, pricing.list)
                        assertEquals(1100, pricing.retail)
                        assertEquals(100, pricing.savings)
                        assertEquals(8, pricing.pctSavings)
                    }
                }
            }

            on("getting six columns by a filter expression") {
                db.withSession {
                    val (sku, title, description, asin, list, retail) = Products.find { sku.equal("00e8da9b") }.projection { sku + title + description + asin + pricing.list + pricing.retail }.single()
                    it("returns correct values") {
                        assertEquals("00e8da9b", sku)
                        assertEquals("A Love Supreme", title)
                        assertEquals("by John Coltrane", description)
                        assertEquals("B0000A118M", asin)
                        assertEquals(1200, list)
                        assertEquals(1100, retail)
                    }
                }
            }

            on("getting seven columns by a filter expression") {
                db.withSession {
                    val (sku, title, description, asin, list, retail, savings) = Products.find { sku.equal("00e8da9b") }.projection { sku + title + description + asin + pricing.list + pricing.retail + pricing.savings }.single()
                    it("returns correct values") {
                        assertEquals("00e8da9b", sku)
                        assertEquals("A Love Supreme", title)
                        assertEquals("by John Coltrane", description)
                        assertEquals("B0000A118M", asin)
                        assertEquals(1200, list)
                        assertEquals(1100, retail)
                        assertEquals(100, savings)
                    }
                }
            }

            on("getting eight columns by a filter expression") {
                db.withSession {
                    val (sku, title, description, asin, list, retail, savings, pctSavings) = Products.find { sku.equal("00e8da9b") }.projection { sku + title + description + asin + pricing.list + pricing.retail + pricing.savings + pricing.pctSavings }.single()
                    it("returns correct values") {
                        assertEquals("00e8da9b", sku)
                        assertEquals("A Love Supreme", title)
                        assertEquals("by John Coltrane", description)
                        assertEquals("B0000A118M", asin)
                        assertEquals(1200, list)
                        assertEquals(1100, retail)
                        assertEquals(100, savings)
                        assertEquals(8, pctSavings)
                    }
                }
            }

            on("getting nine columns by a filter expression") {
                db.withSession {
                    val (sku, title, description, asin, list, retail, savings, pctSavings, shipping) = Products.find { sku.equal("00e8da9b") }.projection { sku + title + description + asin + pricing.list + pricing.retail + pricing.savings + pricing.pctSavings + shipping}.single()
                    it("returns correct values") {
                        assertEquals("00e8da9b", sku)
                        assertEquals("A Love Supreme", title)
                        assertEquals("by John Coltrane", description)
                        assertEquals("B0000A118M", asin)
                        assertEquals(1200, list)
                        assertEquals(1100, retail)
                        assertEquals(100, savings)
                        assertEquals(8, pctSavings)
                        assertEquals(6, shipping.weight)
                        assertEquals(10, shipping.dimensions.width)
                        assertEquals(10, shipping.dimensions.height)
                        assertEquals(1, shipping.dimensions.depth)
                    }
                }
            }

            on("getting ten columns by a filter expression") {
                db.withSession {
                    val (sku, title, description, asin, list, retail, savings, pctSavings, weight, dimensions) = Products.find { sku.equal("00e8da9b") }.projection { sku + title + description + asin + pricing.list + pricing.retail + pricing.savings + pricing.pctSavings + shipping.weight + shipping.dimensions }.single()
                    it("returns correct values") {
                        assertEquals("00e8da9b", sku)
                        assertEquals("A Love Supreme", title)
                        assertEquals("by John Coltrane", description)
                        assertEquals("B0000A118M", asin)
                        assertEquals(1200, list)
                        assertEquals(1100, retail)
                        assertEquals(100, savings)
                        assertEquals(8, pctSavings)
                        assertEquals(6, weight)
                        assertEquals(10, dimensions.width)
                        assertEquals(10, dimensions.height)
                        assertEquals(1, dimensions.depth)
                    }
                }
            }

            on("filtering an abstract schema by search expression") {
                db.withSession {
                    val results = Products.find { text("Love") }.toList()
                    it("should return a correct object") {
                        validate(results)
                    }
                }
            }

            on("filtering an abstract schema by search expression") {
                db.withSession {
                    val results = Products.find { text("Love") and shipping.weight.equal(16) }.toList()
                    it("should return nothing") {
                        assert(results.isEmpty())
                    }
                }
            }

            on("filtering an abstract schema by search expression (returns nothing)") {
                db.withSession {
                    val results = Products.find { text("Love1") }.toList()
                    it("should return nothing") {
                        assert(results.isEmpty())
                    }
                }
            }

            on("filtering an abstract schema by equal expression") {
                db.withSession {
                    val results = Products.find { shipping.weight.equal(6) }.toList()
                    it("should return a correct object") {
                        validate(results)
                    }
                }
            }

            on("filtering an abstract schema by equal expression") {
                db.withSession {
                    val results = Products.find { shipping.weight.equal(7) }.toList()
                    it("should return nothing") {
                        assert(results.isEmpty())
                    }
                }
            }

            on("filtering an abstract schema by notEqual expression") {
                db.withSession {
                    val results = Products.find { shipping.weight.notEqual(7) }.toList()
                    it("should return a correct object") {
                        validate(results)
                    }
                }
            }

            on("filtering an abstract schema by notEqual expression") {
                db.withSession {
                    val results = Products.find { shipping.weight.notEqual(6) }.toList()
                    it("should return nothing") {
                        assert(results.isEmpty())
                    }
                }
            }

            on("filtering an abstract schema by gt expression") {
                db.withSession {
                    val results = Products.find { shipping.weight.gt(5) }.toList()
                    it("should return a correct object") {
                        validate(results)
                    }
                }
            }

            on("filtering an abstract schema by gt expression") {
                db.withSession {
                    val results = Products.find { shipping.weight.gt(6) }.toList()
                    it("should return nothing") {
                        assert(results.isEmpty())
                    }
                }
            }

            on("filtering an abstract schema by lt expression") {
                db.withSession {
                    val results = Products.find { shipping.weight.lt(7) }.toList()
                    it("should return a correct object") {
                        validate(results)
                    }
                }
            }

            on("filtering an abstract schema by lt expression") {
                db.withSession {
                    val results = Products.find { shipping.weight.lt(6) }.toList()
                    it("should return nothing") {
                        assert(results.isEmpty())
                    }
                }
            }

            on("filtering an abstract schema by ge expression") {
                db.withSession {
                    val results = Products.find { shipping.weight.ge(6) }.toList()
                    it("should return a correct object") {
                        validate(results)
                    }
                }
            }

            on("filtering an abstract schema by ge expression") {
                db.withSession {
                    val results = Products.find { shipping.weight.ge(5) }.toList()
                    it("should return a correct object") {
                        validate(results)
                    }
                }
            }

            on("filtering an abstract schema by ge expression") {
                db.withSession {
                    val results = Products.find { shipping.weight.ge(7) }.toList()
                    it("should return nothing") {
                        assert(results.isEmpty())
                    }
                }
            }

            on("filtering an abstract schema by le expression") {
                db.withSession {
                    val results = Products.find { shipping.weight.le(6) }.toList()
                    it("should return a correct object") {
                        validate(results)
                    }
                }
            }

            on("filtering an abstract schema by le expression") {
                db.withSession {
                    val results = Products.find { shipping.weight.le(7) }.toList()
                    it("should return a correct object") {
                        validate(results)
                    }
                }
            }

            on("filtering an abstract schema by le expression") {
                db.withSession {
                    val results = Products.find { shipping.weight.le(5) }.toList()
                    it("should return nothing") {
                        assert(results.isEmpty())
                    }
                }
            }

            on("filtering an abstract schema by mb expression") {
                db.withSession {
                    val results = Products.find { shipping.weight.memberOf( arrayOf(5, 6)) }.toList()
                    it("should return a correct object") {
                        validate(results)
                    }
                }
            }

            on("filtering an abstract schema by mb expression") {
                db.withSession {
                    val results = Products.find { shipping.weight.memberOf( arrayOf(5, 7)) }.toList()
                    it("should return nothing") {
                        assert(results.isEmpty())
                    }
                }
            }

            on("filtering an abstract schema by nm expression") {
                db.withSession {
                    val results = Products.find { shipping.weight.notMemberOf( arrayOf(5, 7)) }.toList()
                    it("should return a correct object") {
                        validate(results)
                    }
                }
            }

            on("filtering an abstract schema by nm expression") {
                db.withSession {
                    val results = Products.find { shipping.weight.notMemberOf( arrayOf(5, 6)) }.toList()
                    it("should return nothing") {
                        assert(results.isEmpty())
                    }
                }
            }

            on("filtering an abstract schema by equal expression - compare to a column") {
                db.withSession {
                    val results = Products.find { with (shipping.dimensions) { width.equal(height) } }.toList()
                    it("should return a correct object") {
                        validate(results)
                    }
                }
            }

            on("filtering an abstract schema by equal expression - compare to a column") {
                db.withSession {
                    val results = Products.find { with (shipping.dimensions) { width.equal(depth) } }.toList()
                    it("should return nothing") {
                        assert(results.isEmpty())
                    }
                }
            }

            on("filtering an abstract schema by equal expression - compare to a column") {
                db.withSession {
                    val results = Products.find { with (shipping.dimensions) { width.notEqual(depth) } }.toList()
                    it("should return a correct object") {
                        validate(results)
                    }
                }
            }

            on("filtering an abstract schema by equal expression - compare to a column") {
                db.withSession {
                    val results = Products.find { with (shipping.dimensions) { width.notEqual(height) } }.toList()
                    it("should return nothing") {
                        assert(results.isEmpty())
                    }
                }
            }

            //

            on("filtering an abstract schema by gt expression - compare to a column") {
                db.withSession {
                    val results = Products.find { shipping.dimensions.width.gt(shipping.dimensions.depth) }.toList()
                    it("should return a correct object") {
                        validate(results)
                    }
                }
            }

            on("filtering an abstract schema by gt expression - compare to a column") {
                db.withSession {
                    val results = Products.find { shipping.dimensions.depth.gt(shipping.dimensions.width) }.toList()
                    it("should return nothing") {
                        assert(results.isEmpty())
                    }
                }
            }

            on("filtering an abstract schema by lt expression - compare to a column") {
                db.withSession {
                    val results = Products.find { shipping.dimensions.depth.lt(shipping.dimensions.width) }.toList()
                    it("should return a correct object") {
                        validate(results)
                    }
                }
            }

            on("filtering an abstract schema by lt expression - compare to a column") {
                db.withSession {
                    val results = Products.find { shipping.dimensions.width.lt(shipping.dimensions.depth) }.toList()
                    it("should return nothing") {
                        assert(results.isEmpty())
                    }
                }
            }

            on("filtering an abstract schema by ge expression - compare to a column") {
                db.withSession {
                    val results = Products.find { shipping.dimensions.width.ge(shipping.dimensions.height) }.toList()
                    it("should return a correct object") {
                        validate(results)
                    }
                }
            }

            on("filtering an abstract schema by ge expression - compare to a column") {
                db.withSession {
                    val results = Products.find { shipping.dimensions.width.ge(shipping.dimensions.depth) }.toList()
                    it("should return a correct object") {
                        validate(results)
                    }
                }
            }

            on("filtering an abstract schema by ge expression - compare to a column") {
                db.withSession {
                    val results = Products.find { shipping.dimensions.depth.ge(shipping.dimensions.width) }.toList()
                    it("should return nothing") {
                        assert(results.isEmpty())
                    }
                }
            }

            on("filtering an abstract schema by le expression - compare to a column") {
                db.withSession {
                    val results = Products.find { shipping.dimensions.depth.le(shipping.dimensions.width) }.toList()
                    it("should return a correct object") {
                        validate(results)
                    }
                }
            }

            on("filtering an abstract schema by le expression - compare to a column") {
                db.withSession {
                    val results = Products.find { shipping.dimensions.width.le(shipping.dimensions.height) }.toList()
                    it("should return a correct object") {
                        validate(results)
                    }
                }
            }

            on("filtering an abstract schema by le expression - compare to a column") {
                db.withSession {
                    val results = Products.find { shipping.dimensions.width.le(shipping.dimensions.depth) }.toList()
                    it("should return nothing") {
                        assert(results.isEmpty())
                    }
                }
            }

            on("getting one column by regex filter expression") {
                db.withSession {
                    val results = Albums.find { details.title.matches(Pattern.compile("Love Supreme")) }.toList()
                    it("returns correct values") {
                        validate(results)
                    }
                }
            }

            on("getting one column by regex filter expression") {
                db.withSession {
                    val results = Albums.find { details.title.matches(Pattern.compile("Love Supremex")) }.toList()
                    it("should return nothing") {
                        assert(results.isEmpty())
                    }
                }
            }

            on("setting a new value to a string column on a non-abstract schema by id") {
                db.withSession {
                    Albums.find { id.equal(albumId!!) }.projection { details.title }.update("A Love Supreme. Original Recording Reissued")
                    val title = Albums.find { id.equal(albumId!!) }.projection { details.title }.single()
                    it("takes effect") {
                        assertEquals("A Love Supreme. Original Recording Reissued", title)
                    }
                }
            }

            on("setting a new value for a string column on a non-abstract schema by id") {
                db.withSession {
                    Albums.find { id.equal(albumId!!) }.projection { details.title }.update("A Love Supreme. Original Recording Reissued")
                    val title = Albums.find { id.equal(albumId!!) }.projection { details.title }.single()
                    it("takes effect") {
                        assertEquals("A Love Supreme. Original Recording Reissued", title)
                    }
                }
            }

            on("setting values for two integer columns on an abstract schema by a filter expression") {
                db.withSession {
                    Products.find { sku.equal("00e8da9b") }.projection { pricing.retail + pricing.savings }.update(1150, 50)
                    val (retail, savings) = Albums.find { id.equal(albumId!!) }.projection { pricing.retail + pricing.savings }.single()
                    it("takes effect") {
                        assertEquals(1150, retail)
                        assertEquals(50, savings)
                    }
                }
            }

            on("setting values for three columns on an abstract schema by a filter expression") {
                db.withSession {
                    Products.find { sku.equal("00e8da9b") }.projection { pricing.retail + pricing.savings + pricing.list }.update(1150, 50, 1250)
                    val (retail, savings, list) = Albums.find { id.equal(albumId!!) }.projection { pricing.retail + pricing.savings + pricing.list }.single()
                    it("takes effect") {
                        assertEquals(1150, retail)
                        assertEquals(50, savings)
                        assertEquals(1250, list)
                    }
                }
            }

            on("setting values for four columns on an abstract schema by a filter expression") {
                db.withSession {
                    Products.find { sku.equal("00e8da9b") }.projection { pricing.retail + pricing.savings + pricing.list + shipping.dimensions.width }.update(1150, 50, 1250, 11)
                    val (retail, savings, list, width) = Albums.find { id.equal(albumId!!) }.projection { pricing.retail + pricing.savings + pricing.list + shipping.dimensions.width }.single()
                    it("takes effect") {
                        assertEquals(1150, retail)
                        assertEquals(50, savings)
                        assertEquals(1250, list)
                        assertEquals(11, width)
                    }
                }
            }

            on("setting values for five columns on an abstract schema by a filter expression") {
                db.withSession {
                    Products.find { sku.equal("00e8da9b") }.projection { pricing.retail + pricing.savings + pricing.list + shipping.dimensions.width + shipping.dimensions.height }.update(1150, 50, 1250, 11, 13)
                    val (retail, savings, list, width, height) = Albums.find { id.equal(albumId!!) }.projection { pricing.retail + pricing.savings + pricing.list + shipping.dimensions.width + shipping.dimensions.height }.single()
                    it("takes effect") {
                        assertEquals(1150, retail)
                        assertEquals(50, savings)
                        assertEquals(1250, list)
                        assertEquals(11, width)
                        assertEquals(13, height)
                    }
                }
            }

            on("setting values for six columns on an abstract schema by a filter expression") {
                db.withSession {
                    Products.find { sku.equal("00e8da9b") }.projection { pricing.retail + pricing.savings + pricing.list + shipping.dimensions.width + shipping.dimensions.height + shipping.dimensions.depth }.update(1150, 50, 1250, 11, 13, 2)
                    val (retail, savings, list, width, height, depth) = Albums.find { id.equal(albumId!!) }.projection { pricing.retail + pricing.savings + pricing.list + shipping.dimensions.width + shipping.dimensions.height + shipping.dimensions.depth }.single()
                    it("takes effect") {
                        assertEquals(1150, retail)
                        assertEquals(50, savings)
                        assertEquals(1250, list)
                        assertEquals(11, width)
                        assertEquals(13, height)
                        assertEquals(2, depth)
                    }
                }
            }

            on("setting values for seven columns on an abstract schema by a filter expression") {
                db.withSession {
                    Products.find { sku.equal("00e8da9b") }.projection { pricing.retail + pricing.savings + pricing.list + shipping.dimensions.width + shipping.dimensions.height + shipping.dimensions.depth + shipping.weight }.update(1150, 50, 1250, 11, 13, 2, 7)
                    val (retail, savings, list, width, height, depth, weight) = Albums.find { id.equal(albumId!!) }.projection { pricing.retail + pricing.savings + pricing.list + shipping.dimensions.width + shipping.dimensions.height + shipping.dimensions.depth + shipping.weight }.single()
                    it("takes effect") {
                        assertEquals(1150, retail)
                        assertEquals(50, savings)
                        assertEquals(1250, list)
                        assertEquals(11, width)
                        assertEquals(13, height)
                        assertEquals(2, depth)
                        assertEquals(7, weight)
                    }
                }
            }

            on("setting values for eight columns on an abstract schema by a filter expression") {
                db.withSession {
                    Products.find { sku.equal("00e8da9b") }.projection { pricing.retail + pricing.savings + pricing.list + shipping.dimensions.width + shipping.dimensions.height + shipping.dimensions.depth + shipping.weight + cost }.update(1150, 50, 1250, 11, 13, 2, 7, 1.25)
                    val (retail, savings, list, width, height, depth, weight, cost) = Albums.find { id.equal(albumId!!) }.projection { pricing.retail + pricing.savings + pricing.list + shipping.dimensions.width + shipping.dimensions.height + shipping.dimensions.depth + shipping.weight + cost }.single()
                    it("takes effect") {
                        assertEquals(1150, retail)
                        assertEquals(50, savings)
                        assertEquals(1250, list)
                        assertEquals(11, width)
                        assertEquals(13, height)
                        assertEquals(2, depth)
                        assertEquals(7, weight)
                        assertEquals(1.25, cost)
                    }
                }
            }

            on("setting values for nine columns on an abstract schema by a filter expression") {
                db.withSession {
                    Products.find { sku.equal("00e8da9b") }.projection { pricing.retail + pricing.savings + pricing.list + shipping.dimensions.width + shipping.dimensions.height + shipping.dimensions.depth + shipping.weight + cost + available }.update(1150, 50, 1250, 11, 13, 2, 7, 1.25, false)
                    val (retail, savings, list, width, height, depth, weight, cost, available) = Albums.find { id.equal(albumId!!) }.projection { pricing.retail + pricing.savings + pricing.list + shipping.dimensions.width + shipping.dimensions.height + shipping.dimensions.depth + shipping.weight + cost + available }.single()
                    it("takes effect") {
                        assertEquals(1150, retail)
                        assertEquals(50, savings)
                        assertEquals(1250, list)
                        assertEquals(11, width)
                        assertEquals(13, height)
                        assertEquals(2, depth)
                        assertEquals(7, weight)
                        assertEquals(1.25, cost)
                        assertEquals(false, available)
                    }
                }
            }

            on("setting values for ten columns on an abstract schema by a filter expression") {
                db.withSession {
                    Products.find { sku.equal("00e8da9b") }.projection { pricing.retail + pricing.savings + pricing.list + shipping.dimensions.width + shipping.dimensions.height + shipping.dimensions.depth + shipping.weight + cost + available + nullableDoubleWithValue }.update(1150, 50, 1250, 11, 13, 2, 7, 1.25, false, 10.1)
                    val (retail, savings, list, width, height, depth, weight, cost, available, nullableDoubleWithValue) = Albums.find { id.equal(albumId!!) }.projection { pricing.retail + pricing.savings + pricing.list + shipping.dimensions.width + shipping.dimensions.height + shipping.dimensions.depth + shipping.weight + cost + available + nullableDoubleWithValue }.single()
                    it("takes effect") {
                        assertEquals(1150, retail)
                        assertEquals(50, savings)
                        assertEquals(1250, list)
                        assertEquals(11, width)
                        assertEquals(13, height)
                        assertEquals(2, depth)
                        assertEquals(7, weight)
                        assertEquals(1.25, cost)
                        assertEquals(false, available)
                        assertEquals(10.1, nullableDoubleWithValue)
                    }
                }
            }

            on("setting a new value to a date column on a non-abstract schema by id") {
                db.withSession {
                    Albums.find { id.equal(albumId!!) }.projection { nullableDateNoValue }.update(LocalDate(2014, 3, 20))
                    // TODO: single is nullable here
                    val nullableDateNoValue = Albums.find { id.equal(albumId!!) }.projection { nullableDateNoValue }.single()
                    it("takes effect") {
                        assertEquals(LocalDate(2014, 3, 20), nullableDateNoValue!!)
                    }
                }
            }

            on("adding a new element to a list column on a non-abstract schema by id") {
                db.withSession {
                    Albums.find { id.equal(albumId!!) }.projection { details.tracks }.add(Track("A Love Supreme, Part IV-Psalm", 400))
                    val tracks = Albums.find { id.equal(albumId!!) }.projection { Albums.details.tracks }.single()!!
                    it("takes effect") {
                        assertEquals(4, tracks.size)
                        assertEquals("A Love Supreme, Part IV-Psalm", tracks[3].title)
                        assertEquals(400, tracks[3].duration)
                    }
                }
            }

            on("adding a new id to an id set column on a non-abstract schema by id") {
                db.withSession {
                    Albums.find { id.equal(albumId!!) }.projection { details.artistIds }.add(artistId3)
                    val artistIds = Albums.find { id.equal(albumId!!) }.projection { details.artistIds }.single()
                    it("takes effect") {
                        assertEquals(3, artistIds.size)
                        assertTrue(artistIds.contains(artistId3))
                    }
                }
            }

            on("removing sn element from a collection column on a non-abstract schema by id") {
                db.withSession {
                    Albums.find { id.equal(albumId!!) }.projection { details.tracks }.remove { duration.equal(100) }
                    val tracks = Albums.find { id.equal(albumId!!) }.projection { details.tracks }.single()
                    it("takes effect") {
                        assertEquals(3, tracks.size)
                    }
                }
            }

            on("removing an element from a collection column on a non-abstract schema by a filter expression") {
                db.withSession {
                    Albums.find { sku.equal("00e8da9b") }.projection { details.tracks }.remove { duration.equal(200) }
                    val tracks = Albums.find { id.equal(albumId!!) }.projection { Albums.details.tracks }.single()
                    it("takes effect") {
                        assertEquals(2, tracks.size)
                    }
                }
            }

            on("removing an element from a set column on a non-abstract schema by id") {
                db.withSession {
                    Albums.find { id.equal(albumId!!) }.projection { details.genre }.remove("General")
                    val genre = Albums.find { id.equal(albumId!!) }.projection { Albums.details.genre }.single()
                    it("takes effect") {
                        assertEquals(1, genre.size)
                    }
                }
            }

            on("deleting a document") {
                db.withSession {
                    Albums.find { id.equal(albumId!!) }.remove()
                    val results =  Albums.find { id.equal(albumId!!) }.toList()
                    it("deletes the document from database") {
                        assert(results.isEmpty())
                    }
                }
            }
        }
    }
}

fun main(args: Array<String>) {
    MongoDBSpek()
}

================================================
FILE: kotlin-nosql.iml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id="kotlin-nosql" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="org.jetbrains.kotlin" external.system.module.version="0.1-SNAPSHOT" type="JAVA_MODULE" version="4">
  <component name="NewModuleRootManager" inherit-compiler-output="false">
    <output url="file://$MODULE_DIR$/build/classes/main" />
    <output-test url="file://$MODULE_DIR$/build/classes/test" />
    <exclude-output />
    <content url="file://$MODULE_DIR$">
      <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
      <sourceFolder url="file://$MODULE_DIR$/src/main/kotlin" isTestSource="false" />
      <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
      <sourceFolder url="file://$MODULE_DIR$/src/test/kotlin" isTestSource="true" />
      <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
      <sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
      <excludeFolder url="file://$MODULE_DIR$/.gradle" />
      <excludeFolder url="file://$MODULE_DIR$/build" />
    </content>
    <orderEntry type="inheritedJdk" />
    <orderEntry type="sourceFolder" forTests="false" />
    <orderEntry type="library" exported="" name="Gradle: joda-time:joda-time:2.3" level="project" />
    <orderEntry type="library" scope="TEST" name="Gradle: junit:junit:4.11" level="project" />
    <orderEntry type="library" scope="TEST" name="Gradle: org.hamcrest:hamcrest-core:1.3" level="project" />
    <orderEntry type="library" exported="" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.0.0" level="project" />
    <orderEntry type="library" exported="" name="Gradle: org.jetbrains.kotlin:kotlin-runtime:1.0.0" level="project" />
    <orderEntry type="library" scope="TEST" name="Gradle: org.jetbrains.spek:spek:0.1.195" level="project" />
    <orderEntry type="library" scope="TEST" name="Gradle: org.jetbrains.kotlin:kotlin-reflect:1.0.0" level="project" />
    <orderEntry type="library" scope="TEST" name="Gradle: org.jetbrains.kotlin:kotlin-test:1.0.0" level="project" />
  </component>
</module>

================================================
FILE: settings.gradle
================================================
include 'kotlin-nosql-mongodb'


================================================
FILE: src/main/kotlin/kotlinx/nosql/AbstractColumn.kt
================================================
package kotlinx.nosql

import java.util.ArrayList
import java.util.regex.Pattern
import kotlinx.nosql.query.*
import kotlin.collections.listOf
import kotlin.collections.setOf
import kotlin.reflect.KClass

open class AbstractColumn<C, T : AbstractSchema, S: Any>(val name: String, val valueClass: KClass<S>, val columnType: ColumnType) : ColumnQueryWrapper<C>(), Expression<C> {
    internal var _schema: AbstractSchema? = null

    val schema: T
        get() {
            return _schema!! as T
        }

    fun matches(other: Pattern): Query {
        return MatchesQuery(this, LiteralExpression(other))
    }

    override fun toString(): String {
        return "$name"
    }

    operator fun <C2> plus(c: AbstractColumn<C2, T, *>): ColumnPair<T, C, C2> {
        return ColumnPair(this, c) as ColumnPair<T, C, C2>
    }
}

/*
fun<C, T: AbstractSchema> AbstractColumn<C, T, *>.get(): C {
    throw UnsupportedOperationException()
}
*/

fun <C, T: AbstractSchema> AbstractColumn<C, T, *>.update(value: C): Int {
    val wrapper = TableSchemaProjectionQueryWrapper.get()
    return Session.current<Session>().update(wrapper.params.table, arrayOf(wrapper.params.projection.get(0) to value),
            wrapper.params.query!!)
}

fun <C, S: Collection<C>> AbstractColumn<S, *, *>.addAll(values: S): Int {
    val wrapper = TableSchemaProjectionQueryWrapper.get()
    return Session.current<Session>().addAll(wrapper.params.table,
            wrapper.params.projection.get(0)
                    as AbstractColumn<Collection<C>, out AbstractSchema, out Any>, values,
            wrapper.params.query!!)
}

fun <C, S: Collection<C>> AbstractColumn<S, *, *>.add(value: C): Int {
    val wrapper = TableSchemaProjectionQueryWrapper.get()
    val values: Collection<C> = if (wrapper.params.projection.get(0).columnType.list) listOf(value) else setOf(value)
    return Session.current<Session>().addAll(wrapper.params.table,
                    wrapper.params.projection.get(0)
                            as AbstractColumn<Collection<C>, out AbstractSchema, out Any>, values,
                    wrapper.params.query!!)
}

fun <C, S: Collection<C>> AbstractColumn<S, *, *>.removeAll(values: S): Int {
    val wrapper = TableSchemaProjectionQueryWrapper.get()
    return Session.current<Session>().removeAll(wrapper.params.table,
            wrapper.params.projection.get(0)
                    as AbstractColumn<Collection<C>, out AbstractSchema, out Any>, values,
            wrapper.params.query!!)
}

fun <C> AbstractColumn<out Collection<C>, *, *>.remove(value: C): Int {
    val wrapper = TableSchemaProjectionQueryWrapper.get()
    val values: Collection<C> = if (wrapper.params.projection.get(0).columnType.list) listOf(value) else setOf(value)
    return Session.current<Session>().removeAll(wrapper.params.table,
            wrapper.params.projection.get(0)
                    as AbstractColumn<Collection<C>, out AbstractSchema, out Any>, values,
            wrapper.params.query!!)
}

fun <C: AbstractColumn<out Collection<*>, *, *>> C.remove(removeOp: C.() -> Query): Int {
    val wrapper = TableSchemaProjectionQueryWrapper.get()
        val removeOpValue = with (wrapper.params.projection.get(0)) {
            removeOp()
        }
    return Session.current<Session>().removeAll(wrapper.params.table,
            wrapper.params.projection.get(0)
                    as AbstractColumn<Collection<C>, out AbstractSchema, out Any>, removeOpValue,
            wrapper.params.query!!)
}

fun <T : AbstractSchema, C: Any> AbstractColumn<C?, T, C>.isNull(): Query {
    return IsNullQuery(this)
}

fun <T : AbstractSchema, C: Any> AbstractColumn<C?, T, C>.notNull(): Query {
    return IsNotNullQuery(this)
}

fun <T : AbstractSchema, C> AbstractColumn<out C?, T, *>.equal(other: C): Query {
    return EqualQuery(this, LiteralExpression(other))
}

fun <T : AbstractSchema, C> AbstractColumn<out C?, T, *>.notEqual(other: C): Query {
    return NotEqualQuery(this, LiteralExpression(other))
}

fun <T : AbstractSchema, C> AbstractColumn<out C?, T, *>.memberOf(other: Iterable<C>): Query {
    return MemberOfQuery(this, LiteralExpression(other))
}

fun <T : AbstractSchema, C> AbstractColumn<out C?, T, *>.memberOf(other: Array<C>): Query {
    return MemberOfQuery(this, LiteralExpression(other))
}

// TODO TODO TODO: Expression should be typed
fun <T : AbstractSchema, C> AbstractColumn<out C?, T, *>.memberOf(other: Expression<out Iterable<C>>): Query {
    return MemberOfQuery(this, LiteralExpression(other))
}

fun <T : AbstractSchema, C> AbstractColumn<out C?, T, *>.notMemberOf(other: Iterable<C>): Query {
    return NotMemberOfQuery(this, LiteralExpression(other))
}

fun <T : AbstractSchema, C> AbstractColumn<out C?, T, *>.notMemberOf(other: Array<C>): Query {
    return NotMemberOfQuery(this, LiteralExpression(other))
}

fun <T : AbstractSchema, C> AbstractColumn<out C?, T, *>.notMemberOf(other: Expression<out Iterable<C>>): Query {
    return NotMemberOfQuery(this, LiteralExpression(other))
}

fun <T : AbstractSchema, C: Any> AbstractColumn<out C?, T, C>.equal(other: Expression<out C?>): Query {
    return EqualQuery(this, other)
}

fun <T : AbstractSchema, C: Any> AbstractColumn<out C?, T, C>.notEqual(other: Expression<out C?>): Query {
    return NotEqualQuery(this, other)
}

fun <T : AbstractSchema> AbstractColumn<out Int?, T, Int>.gt(other: Expression<out Int?>): Query {
    return GreaterQuery(this, other)
}

fun <T : AbstractSchema> AbstractColumn<out Int?, T, Int>.gt(other: Int): Query {
    return GreaterQuery(this, LiteralExpression(other))
}

fun <T : AbstractSchema> AbstractColumn<out Int?, T, Int>.ge(other: Expression<out Int?>): Query {
    return GreaterEqualQuery(this, other)
}

fun <T : AbstractSchema> AbstractColumn<out Int?, T, Int>.ge(other: Int): Query {
    return GreaterEqualQuery(this, LiteralExpression(other))
}

fun <T : AbstractSchema> AbstractColumn<out Int?, T, Int>.le(other: Expression<out Int?>): Query {
    return LessEqualQuery(this, other)
}

fun <T : AbstractSchema> AbstractColumn<out Int?, T, Int>.le(other: Int): Query {
    return LessEqualQuery(this, LiteralExpression(other))
}

fun <T : AbstractSchema> AbstractColumn<out Int?, T, Int>.lt(other: Expression<out Int?>): Query {
    return LessQuery(this, other)
}

fun <T : AbstractSchema> AbstractColumn<out Int?, T, Int>.lt(other: Int): Query {
    return LessQuery(this, LiteralExpression(other))
}

================================================
FILE: src/main/kotlin/kotlinx/nosql/AbstractIndex.kt
================================================
package kotlinx.nosql

abstract class AbstractIndex(val name: String)

================================================
FILE: src/main/kotlin/kotlinx/nosql/AbstractNullableColumn.kt
================================================
package kotlinx.nosql

interface AbstractNullableColumn {
}

================================================
FILE: src/main/kotlin/kotlinx/nosql/AbstractSchema.kt
================================================
package kotlinx.nosql

import java.util.ArrayList
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.CopyOnWriteArrayList
import org.joda.time.LocalDate
import org.joda.time.LocalTime
import org.joda.time.DateTime
import kotlinx.nosql.query.TextQuery

abstract class AbstractSchema(val schemaName: String) {
    // TODO TODO TODO
    val indices = ArrayList<AbstractIndex>()

    // TODO TODO TODO
    // val columns = ArrayList<AbstractColumn<*, *, *>>()

    // TODO TODO TODO
    companion object {
        val threadLocale = ThreadLocal<AbstractSchema>()

        fun <T: AbstractSchema> current(): T {
            return threadLocale.get() as T
        }

        fun set(schema: AbstractSchema) {
            return threadLocale.set(schema)
        }
    }
}

// TODO
fun text(search: String): Query {
    return TextQuery(search)
}

// Extension functions

fun <S : AbstractSchema> string(name: String): AbstractColumn<String, S, String> = AbstractColumn(name, String::class, ColumnType.STRING)
fun <S : AbstractSchema> S.string(name: String): AbstractColumn<String, S, String> = AbstractColumn(name, String::class, ColumnType.STRING)

fun <S : AbstractSchema> boolean(name: String): AbstractColumn<Boolean, S, Boolean> = AbstractColumn(name, Boolean::class, ColumnType.BOOLEAN)
fun <S : AbstractSchema> S.boolean(name: String): AbstractColumn<Boolean, S, Boolean> = AbstractColumn(name, Boolean::class, ColumnType.BOOLEAN)

fun <S : AbstractSchema> date(name: String): AbstractColumn<LocalDate, S, LocalDate> = AbstractColumn(name, LocalDate::class, ColumnType.DATE)
fun <S : AbstractSchema> S.date(name: String): AbstractColumn<LocalDate, S, LocalDate> = AbstractColumn(name, LocalDate::class, ColumnType.DATE)

fun <S : AbstractSchema> time(name: String): AbstractColumn<LocalTime, S, LocalTime> = AbstractColumn(name, LocalTime::class, ColumnType.TIME)
fun <S : AbstractSchema> S.time(name: String): AbstractColumn<LocalTime, S, LocalTime> = AbstractColumn(name, LocalTime::class, ColumnType.TIME)

fun <S : AbstractSchema> dateTime(name: String): AbstractColumn<DateTime, S, DateTime> = AbstractColumn(name, DateTime::class, ColumnType.DATE_TIME)
fun <S : AbstractSchema> S.dateTime(name: String): AbstractColumn<DateTime, S, DateTime> = AbstractColumn(name, DateTime::class, ColumnType.DATE_TIME)

fun <S : AbstractSchema> double(name: String): AbstractColumn<Double, S, Double> = AbstractColumn(name, Double::class, ColumnType.DOUBLE)
fun <S : AbstractSchema> S.double(name: String): AbstractColumn<Double, S, Double> = AbstractColumn(name, Double::class, ColumnType.DOUBLE)

fun <S : AbstractSchema> integer(name: String): AbstractColumn<Int, S, Int> = AbstractColumn(name, Int::class, ColumnType.INTEGER)
fun <S : AbstractSchema> S.integer(name: String): AbstractColumn<Int, S, Int> = AbstractColumn(name, Int::class, ColumnType.INTEGER)

fun <S : AbstractSchema> float(name: String): AbstractColumn<Float, S, Float> = AbstractColumn(name, Float::class, ColumnType.FLOAT)
fun <S : AbstractSchema> S.float(name: String): AbstractColumn<Float, S, Float> = AbstractColumn(name, Float::class, ColumnType.FLOAT)

fun <S : AbstractSchema> long(name: String): AbstractColumn<Long, S, Long> = AbstractColumn(name, Long::class, ColumnType.LONG)
fun <S : AbstractSchema> S.long(name: String): AbstractColumn<Long, S, Long> = AbstractColumn(name, Long::class, ColumnType.LONG)

fun <S : AbstractSchema> short(name: String): AbstractColumn<Short, S, Short> = AbstractColumn(name, Short::class, ColumnType.SHORT)
fun <S : AbstractSchema> S.short(name: String): AbstractColumn<Short, S, Short> = AbstractColumn(name, Short::class, ColumnType.SHORT)

fun <S : AbstractSchema> byte(name: String): AbstractColumn<Byte, S, Byte> = AbstractColumn(name, Byte::class, ColumnType.BYTE)
fun <S : AbstractSchema> S.byte(name: String): AbstractColumn<Byte, S, Byte> = AbstractColumn(name, Byte::class, ColumnType.BYTE)

fun <S : AbstractSchema> nullableString(name: String): NullableColumn<String, S> = NullableColumn(name, String::class, ColumnType.STRING)
fun <S : AbstractSchema> S.nullableString(name: String): NullableColumn<String, S> = NullableColumn(name, String::class, ColumnType.STRING)

fun <S : AbstractSchema> nullableInteger(name: String): NullableColumn<Int, S> = NullableColumn(name, Int::class, ColumnType.INTEGER)
fun <S : AbstractSchema> S.nullableInteger(name: String): NullableColumn<Int, S> = NullableColumn(name, Int::class, ColumnType.INTEGER)

fun <S : AbstractSchema> nullableBoolean(name: String): NullableColumn<Boolean, S> = NullableColumn(name, Boolean::class, ColumnType.BOOLEAN)
fun <S : AbstractSchema> S.nullableBoolean(name: String): NullableColumn<Boolean, S> = NullableColumn(name, Boolean::class, ColumnType.BOOLEAN)

fun <S : AbstractSchema> nullableDate(name: String): NullableColumn<LocalDate, S> = NullableColumn(name, LocalDate::class, ColumnType.DATE)
fun <S : AbstractSchema> S.nullableDate(name: String): NullableColumn<LocalDate, S> = NullableColumn(name, LocalDate::class, ColumnType.DATE)

fun <S : AbstractSchema> nullableTime(name: String): NullableColumn<LocalTime, S> = NullableColumn(name, LocalTime::class, ColumnType.TIME)
fun <S : AbstractSchema> S.nullableTime(name: String): NullableColumn<LocalTime, S> = NullableColumn(name, LocalTime::class, ColumnType.TIME)

fun <S : AbstractSchema> nullableDateTime(name: String): NullableColumn<DateTime, S> = NullableColumn(name, DateTime::class, ColumnType.DATE_TIME)
fun <S : AbstractSchema> S.nullableDateTime(name: String): NullableColumn<DateTime, S> = NullableColumn(name, DateTime::class, ColumnType.DATE_TIME)

fun <S : AbstractSchema> nullableDouble(name: String): NullableColumn<Double, S> = NullableColumn(name, Double::class, ColumnType.DOUBLE)
fun <S : AbstractSchema> S.nullableDouble(name: String): NullableColumn<Double, S> = NullableColumn(name, Double::class, ColumnType.DOUBLE)

fun <S : AbstractSchema> nullableFloat(name: String): NullableColumn<Float, S> = NullableColumn(name, Float::class, ColumnType.FLOAT)
fun <S : AbstractSchema> S.nullableFloat(name: String): NullableColumn<Float, S> = NullableColumn(name, Float::class, ColumnType.FLOAT)

fun <S : AbstractSchema> nullableLong(name: String): NullableColumn<Long, S> = NullableColumn(name, Long::class, ColumnType.LONG)
fun <S : AbstractSchema> S.nullableLong(name: String): NullableColumn<Long, S> = NullableColumn(name, Long::class, ColumnType.LONG)

fun <S : AbstractSchema> nullableShort(name: String): NullableColumn<Short, S> = NullableColumn(name, Short::class, ColumnType.SHORT)
fun <S : AbstractSchema> S.nullableShort(name: String): NullableColumn<Short, S> = NullableColumn(name, Short::class, ColumnType.SHORT)

fun <S : AbstractSchema> nullableByte(name: String): NullableColumn<Byte, S> = NullableColumn(name, Byte::class, ColumnType.BYTE)
fun <S : AbstractSchema> S.nullableByte(name: String): NullableColumn<Byte, S> = NullableColumn(name, Byte::class, ColumnType.BYTE)

fun <S : AbstractSchema> setOfString(name: String): AbstractColumn<Set<String>, S, String> = AbstractColumn<Set<String>, S, String>(name, String::class, ColumnType.STRING_SET)
fun <S : AbstractSchema> S.setOfString(name: String): AbstractColumn<Set<String>, S, String> = AbstractColumn<Set<String>, S, String>(name, String::class, ColumnType.STRING_SET)

fun <S : AbstractSchema> setOfInteger(name: String): AbstractColumn<Set<Int>, S, Int> = AbstractColumn<Set<Int>, S, Int>(name, Int::class, ColumnType.INTEGER_SET)
fun <S : AbstractSchema> S.setOfInteger(name: String): AbstractColumn<Set<Int>, S, Int> = AbstractColumn<Set<Int>, S, Int>(name, Int::class, ColumnType.INTEGER_SET)

fun <S : AbstractSchema> listOfString(name: String): AbstractColumn<List<String>, S, String> = AbstractColumn<List<String>, S, String>(name, String::class, ColumnType.STRING_LIST)
fun <S : AbstractSchema> S.listOfString(name: String): AbstractColumn<List<String>, S, String> = AbstractColumn<List<String>, S, String>(name, String::class, ColumnType.STRING_LIST)

fun <S : AbstractSchema> listOfInteger(name: String): AbstractColumn<List<Int>, S, Int> = AbstractColumn<List<Int>, S, Int>(name, Int::class, ColumnType.INTEGER_LIST)
fun <S : AbstractSchema> S.listOfInteger(name: String): AbstractColumn<List<Int>, S, Int> = AbstractColumn<List<Int>, S, Int>(name, Int::class, ColumnType.INTEGER_LIST)


================================================
FILE: src/main/kotlin/kotlinx/nosql/AbstractTableSchema.kt
================================================
package kotlinx.nosql

abstract class AbstractTableSchema(name: String): AbstractSchema(name) {
}

================================================
FILE: src/main/kotlin/kotlinx/nosql/AndQuery.kt
================================================
package kotlinx.nosql

class AndQuery(val expr1: kotlinx.nosql.Query, val expr2: kotlinx.nosql.Query): Query() {
}

================================================
FILE: src/main/kotlin/kotlinx/nosql/Column.kt
================================================
package kotlinx.nosql

import kotlin.reflect.KClass

abstract class Column<C: Any, S : AbstractSchema>(name: String, valueClass: KClass<C>, columnType: ColumnType = ColumnType.CUSTOM_CLASS) : AbstractColumn<C, S, C>(name, valueClass, columnType) {
}

================================================
FILE: src/main/kotlin/kotlinx/nosql/ColumnDecuple.kt
================================================
package kotlinx.nosql

class ColumnDecuple<S : AbstractSchema, A, B, C, D, E, F, G, H, J, K>(val a: AbstractColumn<A, S, *>, val b: AbstractColumn<B, S, *>,
                                                                   val c: AbstractColumn<C, S, *>, val d: AbstractColumn<D, S, *>,
                                                                   val e: AbstractColumn<E, S, *>, val f: AbstractColumn<F, S, *>,
                                                                   val g: AbstractColumn<G, S, *>, val h: AbstractColumn<H, S, *>,
                                                                   val i: AbstractColumn<J, S, *>, val j: AbstractColumn<K, S, *>): ColumnQueryWrapper<Decuple<A, B, C, D, E, F, G, H, J, K>>() {
}

================================================
FILE: src/main/kotlin/kotlinx/nosql/ColumnNonuple.kt
================================================
package kotlinx.nosql

class ColumnNonuple<S : AbstractSchema, A, B, C, D, E, F, G, H, J>(val a: AbstractColumn<A, S, *>, val b: AbstractColumn<B, S, *>,
                                                               val c: AbstractColumn<C, S, *>, val d: AbstractColumn<D, S, *>,
                                                               val e: AbstractColumn<E, S, *>, val f: AbstractColumn<F, S, *>,
                                                               val g: AbstractColumn<G, S, *>, val h: AbstractColumn<H, S, *>,
                                                               val j: AbstractColumn<J, S, *>): ColumnQueryWrapper<Nonuple<A, B, C, D, E, F, G, H, J>>() {
    operator fun <K> plus(k: AbstractColumn<K, S, *>): ColumnDecuple<S, A, B, C, D, E, F, G, H, J, K> {
        return ColumnDecuple(a, b, c, d, e, f, g, h, j, k)
    }
}

================================================
FILE: src/main/kotlin/kotlinx/nosql/ColumnOctuple.kt
================================================
package kotlinx.nosql

class ColumnOctuple<S : AbstractSchema, A, B, C, D, E, F, G, H>(val a: AbstractColumn<A, S, *>, val b: AbstractColumn<B, S, *>,
                                                            val c: AbstractColumn<C, S, *>, val d: AbstractColumn<D, S, *>,
                                                            val e: AbstractColumn<E, S, *>, val f: AbstractColumn<F, S, *>,
                                                            val g: AbstractColumn<G, S, *>, val h: AbstractColumn<H, S, *>): ColumnQueryWrapper<Octuple<A, B, C, D, E, F, G, H>>() {
    operator fun <J> plus(j: AbstractColumn<J, S, *>): ColumnNonuple<S, A, B, C, D, E, F, G, H, J> {
        return ColumnNonuple(a, b, c, d, e, f, g, h, j)
    }
}

================================================
FILE: src/main/kotlin/kotlinx/nosql/ColumnPair.kt
================================================
package kotlinx.nosql

class ColumnPair<S : AbstractSchema, A, B>(val a: AbstractColumn<A, S, *>, val b: AbstractColumn<B, S, *>): ColumnQueryWrapper<Pair<A, B>>() {
    operator fun <C> plus(c: AbstractColumn<C, S, *>): ColumnTriple<S, A, B, C> {
        return ColumnTriple(a, b, c)
    }
}

/*
fun <S : KeyValueSchema, A, B> ColumnPair<S, A, B>.get(): Pair<A, B> {
    throw UnsupportedOperationException()
}*/


================================================
FILE: src/main/kotlin/kotlinx/nosql/ColumnQuadruple.kt
================================================
package kotlinx.nosql

class ColumnQuadruple<S : AbstractSchema, A, B, C, D>(val a: AbstractColumn<A, S, *>, val b: AbstractColumn<B, S, *>, val c: AbstractColumn<C, S, *>, val d: AbstractColumn<D, S, *>): ColumnQueryWrapper<Quadruple<A, B, C, D>>() {
    operator fun <E> plus(e: AbstractColumn<E, S, *>): ColumnQuintuple<S, A, B, C, D, E> {
        return ColumnQuintuple(a, b, c, d, e)
    }

    fun insert(statement: () -> Quadruple<A, B, C, D>) {
        val tt = statement()
        Session.current<Session>().insert(arrayOf(Pair(a, tt.component1()), Pair(b, tt.component2()), Pair(c, tt.component3()), Pair(d, tt.component4())))
    }
}

================================================
FILE: src/main/kotlin/kotlinx/nosql/ColumnQueryWrapper.kt
================================================
package kotlinx.nosql

abstract class ColumnQueryWrapper<C> : Iterable<C> {
    override fun iterator(): Iterator<C> {
        val wrapper = TableSchemaProjectionQueryWrapper.get()
        return wrapper.iterator() as Iterator<C>
    }
}

fun <A, B> ColumnQueryWrapper<Pair<A, B>>.update(a: A, b: B): Int {
    val wrapper = TableSchemaProjectionQueryWrapper.get()
    return Session.current<Session>().update(wrapper.params.table, arrayOf(wrapper.params.projection.get(0) to a,
            wrapper.params.projection.get(1) to b),
            wrapper.params.query!!)
}

fun <A, B, C> ColumnQueryWrapper<Triple<A, B, C>>.update(a: A, b: B, c: C): Int {
    val wrapper = TableSchemaProjectionQueryWrapper.get()
    return Session.current<Session>().update(wrapper.params.table, arrayOf(wrapper.params.projection.get(0) to a,
            wrapper.params.projection.get(1) to b,
            wrapper.params.projection.get(2) to c),
            wrapper.params.query!!)
}

fun <A, B, C, D> ColumnQueryWrapper<Quadruple<A, B, C, D>>.update(a: A, b: B, c: C, d: D): Int {
    val wrapper = TableSchemaProjectionQueryWrapper.get()
    return Session.current<Session>().update(wrapper.params.table, arrayOf(wrapper.params.projection.get(0) to a,
            wrapper.params.projection.get(1) to b,
            wrapper.params.projection.get(2) to c,
            wrapper.params.projection.get(3) to d),
            wrapper.params.query!!)
}

fun <A, B, C, D, E> ColumnQueryWrapper<Quintuple<A, B, C, D, E>>.update(a: A, b: B, c: C, d: D, e: E): Int {
    val wrapper = TableSchemaProjectionQueryWrapper.get()
    return Session.current<Session>().update(wrapper.params.table, arrayOf(wrapper.params.projection.get(0) to a,
            wrapper.params.projection.get(1) to b,
            wrapper.params.projection.get(2) to c,
            wrapper.params.projection.get(3) to d,
            wrapper.params.projection.get(4) to e),
            wrapper.params.query!!)
}

fun <A, B, C, D, E, F> ColumnQueryWrapper<Sextuple<A, B, C, D, E, F>>.update(a: A, b: B, c: C, d: D, e: E, f: F): Int {
    val wrapper = TableSchemaProjectionQueryWrapper.get()
    return Session.current<Session>().update(wrapper.params.table, arrayOf(wrapper.params.projection.get(0) to a,
            wrapper.params.projection.get(1) to b,
            wrapper.params.projection.get(2) to c,
            wrapper.params.projection.get(3) to d,
            wrapper.params.projection.get(4) to e,
            wrapper.params.projection.get(5) to f),
            wrapper.params.query!!)
}

fun <A, B, C, D, E, F, G> ColumnQueryWrapper<Septuple<A, B, C, D, E, F, G>>.update(a: A, b: B, c: C, d: D, e: E, f: F, g: G): Int {
    val wrapper = TableSchemaProjectionQueryWrapper.get()
    return Session.current<Session>().update(wrapper.params.table, arrayOf(wrapper.params.projection.get(0) to a,
            wrapper.params.projection.get(1) to b,
            wrapper.params.projection.get(2) to c,
            wrapper.params.projection.get(3) to d,
            wrapper.params.projection.get(4) to e,
            wrapper.params.projection.get(5) to f,
            wrapper.params.projection.get(6) to g),
            wrapper.params.query!!)
}

fun <A, B, C, D, E, F, G, H> ColumnQueryWrapper<Octuple<A, B, C, D, E, F, G, H>>.update(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H): Int {
    val wrapper = TableSchemaProjectionQueryWrapper.get()
    return Session.current<Session>().update(wrapper.params.table, arrayOf(wrapper.params.projection.get(0) to a,
            wrapper.params.projection.get(1) to b,
            wrapper.params.projection.get(2) to c,
            wrapper.params.projection.get(3) to d,
            wrapper.params.projection.get(4) to e,
            wrapper.params.projection.get(5) to f,
            wrapper.params.projection.get(6) to g,
            wrapper.params.projection.get(7) to h),
            wrapper.params.query!!)
}

fun <A, B, C, D, E, F, G, H, J> ColumnQueryWrapper<Nonuple<A, B, C, D, E, F, G, H, J>>.update(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, j: J): Int {
    val wrapper = TableSchemaProjectionQueryWrapper.get()
    return Session.current<Session>().update(wrapper.params.table, arrayOf(wrapper.params.projection.get(0) to a,
            wrapper.params.projection.get(1) to b,
            wrapper.params.projection.get(2) to c,
            wrapper.params.projection.get(3) to d,
            wrapper.params.projection.get(4) to e,
            wrapper.params.projection.get(5) to f,
            wrapper.params.projection.get(6) to g,
            wrapper.params.projection.get(7) to h,
            wrapper.params.projection.get(8) to j),
            wrapper.params.query!!)
}

fun <A, B, C, D, E, F, G, H, J, K> ColumnQueryWrapper<Decuple<A, B, C, D, E, F, G, H, J, K>>.update(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, j: J, k: K): Int {
    val wrapper = TableSchemaProjectionQueryWrapper.get()
    return Session.current<Session>().update(wrapper.params.table, arrayOf(wrapper.params.projection.get(0) to a,
            wrapper.params.projection.get(1) to b,
            wrapper.params.projection.get(2) to c,
            wrapper.params.projection.get(3) to d,
            wrapper.params.projection.get(4) to e,
            wrapper.params.projection.get(5) to f,
            wrapper.params.projection.get(6) to g,
            wrapper.params.projection.get(7) to h,
            wrapper.params.projection.get(8) to j,
            wrapper.params.projection.get(9) to k),
            wrapper.params.query!!)
}

================================================
FILE: src/main/kotlin/kotlinx/nosql/ColumnQuintuple.kt
================================================
package kotlinx.nosql

class ColumnQuintuple<S : AbstractSchema, A, B, C, D, E>(val a: AbstractColumn<A, S, *>, val b: AbstractColumn<B, S, *>,
                                                   val c: AbstractColumn<C, S, *>, val d: AbstractColumn<D, S, *>,
                                                   val e: AbstractColumn<E, S, *>): ColumnQueryWrapper<Quintuple<A, B, C, D, E>>() {
    operator fun <F> plus(f: AbstractColumn<F, S, *>): ColumnSextuple<S, A, B, C, D, E, F> {
        return ColumnSextuple(a, b, c, d, e, f)
    }
}

================================================
FILE: src/main/kotlin/kotlinx/nosql/ColumnSeptuple.kt
================================================
package kotlinx.nosql

class ColumnSeptuple<S : AbstractSchema, A, B, C, D, E, F, G>(val a: AbstractColumn<A, S, *>, val b: AbstractColumn<B, S, *>,
                                                         val c: AbstractColumn<C, S, *>, val d: AbstractColumn<D, S, *>,
                                                         val e: AbstractColumn<E, S, *>, val f: AbstractColumn<F, S, *>,
                                                         val g: AbstractColumn<G, S, *>): ColumnQueryWrapper<Septuple<A, B, C, D, E, F, G>>() {
    operator fun <H> plus(h: AbstractColumn<H, S, *>): ColumnOctuple<S, A, B, C, D, E, F, G, H> {
        return ColumnOctuple(a, b, c, d, e, f, g, h)
    }
}

================================================
FILE: src/main/kotlin/kotlinx/nosql/ColumnSextuple.kt
================================================
package kotlinx.nosql

class ColumnSextuple<S : AbstractSchema, A, B, C, D, E, F>(val a: AbstractColumn<A, S, *>, val b: AbstractColumn<B, S, *>,
                                                      val c: AbstractColumn<C, S, *>, val d: AbstractColumn<D, S, *>,
                                                      val e: AbstractColumn<E, S, *>, val f: AbstractColumn<F, S, *>): ColumnQueryWrapper<Sextuple<A, B, C, D, E, F>>() {
    operator fun <G> plus(g: AbstractColumn<G, S, *>): ColumnSeptuple<S, A, B, C, D, E, F, G> {
        return ColumnSeptuple(a, b, c, d, e, f, g)
    }
}

================================================
FILE: src/main/kotlin/kotlinx/nosql/ColumnTriple.kt
================================================
package kotlinx.nosql

class ColumnTriple<S : AbstractSchema, A, B, C>(val a: AbstractColumn<A, S, *>, val b: AbstractColumn<B, S, *>, val c: AbstractColumn<C, S, *>): ColumnQueryWrapper<Triple<A, B, C>>() {
    operator fun <D> plus(d: AbstractColumn<D, S, *>): ColumnQuadruple<S, A, B, C, D> {
        return ColumnQuadruple(a, b, c, d)
    }
}

================================================
FILE: src/main/kotlin/kotlinx/nosql/ColumnType.kt
================================================
package kotlinx.nosql

enum class ColumnType(val primitive: Boolean = false,
                      val iterable: Boolean = false,
                      val list: Boolean = false,
                      val set: Boolean = false,
                      val custom: Boolean = false,
                      val id: Boolean = false) {
    INTEGER(primitive = true),
    PRIMARY_ID(primitive = true, id = true),
    FOREIGN_ID(primitive = true, id = true),
    STRING(primitive = true),
    BOOLEAN(primitive = true),
    DATE(primitive = true),
    TIME(primitive = true),
    DATE_TIME(primitive = true),
    DOUBLE(primitive = true),
    FLOAT(primitive = true),
    LONG(primitive = true),
    SHORT(primitive = true),
    BYTE(primitive = true),
    INTEGER_SET(iterable = true, set = true),
    ID_SET(iterable = true, set = true, id = true),
    ID_LIST(iterable = true, list = true, id = true),
    STRING_SET(iterable = true, set = true),
    INTEGER_LIST(iterable = true, list = true),
    STRING_LIST(iterable = true, list = true),
    CUSTOM_CLASS(custom = true),
    CUSTOM_CLASS_LIST(iterable = true, custom = true, list = true),
    CUSTOM_CLASS_SET(iterable = true, custom = true, set = true)
}


================================================
FILE: src/main/kotlin/kotlinx/nosql/Database.kt
================================================
package kotlinx.nosql

import java.util.concurrent.ConcurrentHashMap
import kotlinx.nosql.util.getAllFields
import kotlinx.nosql.util.isColumn
import kotlinx.nosql.util.asColumn
import kotlin.text.isNotEmpty

abstract class Database<S: Session>(val schemas: Array<out AbstractSchema>, val action: SchemaGenerationAction<S>) {
    abstract fun <R> withSession(statement: S.() -> R): R

    fun initialize() {
        for (schema in schemas) {
            buildFullColumnNames(schema)
            when (action) {
            // TODO: implement validation
                is Create, is CreateDrop -> {
                    withSession {
                        schema.drop()
                        schema.create()
                        if (this is IndexOperations)
                            for (index in schema.indices)
                                createIndex(schema, index)
                    }
                }
                is Update -> {
                    withSession {
                        if (this is IndexOperations)
                            for (index in schema.indices)
                                createIndex(schema, index)
                    }
                }
            // TODO: implement drop after exit
            }
        }
        withSession {
            if (action is Create) {
                action.onCreate(this)
            } else if (action is CreateDrop) {
                action.onCreate(this)
            }
        }
    }

    private fun buildFullColumnNames(root: AbstractSchema, path: String = "",  schema: Any = root) {
        val fields = getAllFields(schema.javaClass)
        for (field in fields) {
            if (field.isColumn) {
                val column = field.asColumn(schema)
                column._schema = root
                val columnFullName = path + (if (path.isNotEmpty()) "." else "") + column.name
                fullColumnNames.put(column, columnFullName)
                buildFullColumnNames(root, columnFullName, column)
            }
        }
    }

    companion object {
        val fullColumnNames = ConcurrentHashMap<AbstractColumn<*, *, *>, String>()
    }
}

val AbstractColumn<*, *, *>.fullName: String
    get() {
        return Database.fullColumnNames.get(this)!!
    }


================================================
FILE: src/main/kotlin/kotlinx/nosql/Decuple.kt
================================================
package kotlinx.nosql

class Decuple<A1, A2, A3, A4, A5, A6, A7, A8, A9, A10>(val a1: A1, val a2: A2, val a3: A3, val a4: A4, val a5: A5, val a6: A6, val a7: A7, val a8: A8, val a9: A9, val a10: A10) {
    operator public fun component1(): A1 = a1
    operator public fun component2(): A2 = a2
    operator public fun component3(): A3 = a3
    operator public fun component4(): A4 = a4
    operator public fun component5(): A5 = a5
    operator public fun component6(): A6 = a6
    operator public fun component7(): A7 = a7
    operator public fun component8(): A8 = a8
    operator public fun component9(): A9 = a9
    operator public fun component10(): A10 = a10
}

================================================
FILE: src/main/kotlin/kotlinx/nosql/Discriminator.kt
================================================
package kotlinx.nosql

class Discriminator<D: Any, S : DocumentSchema<out Any, out Any>>(val column: AbstractColumn<D, S, D>, val value: D) {
}

================================================
FILE: src/main/kotlin/kotlinx/nosql/DocumentSchema.kt
================================================
package kotlinx.nosql

import java.util.concurrent.CopyOnWriteArrayList
import java.util.concurrent.ConcurrentHashMap
import kotlin.reflect.KClass

abstract class DocumentSchema<I: Any, D: Any>(name: String, val valueClass: KClass<D>, primaryKey: AbstractColumn<I,
        out DocumentSchema<I, D>, I>, val discriminator: Discriminator<out Any, out DocumentSchema<out Any, out Any>>? = null) : TableSchema<I>(name, primaryKey) {
    init {
        if (discriminator != null) {
            val emptyDiscriminators = CopyOnWriteArrayList<Discriminator<*, *>>()
            val discriminators = tableDiscriminators.putIfAbsent(name, emptyDiscriminators)
            if (discriminators != null)
                discriminators.add(discriminator)
            else
                emptyDiscriminators.add(discriminator)
            // TODO TODO TODO
            discriminatorClasses.put(discriminator, this.valueClass)
            discriminatorSchemaClasses.put(discriminator, this.javaClass)
            discriminatorSchemas.put(discriminator, this)
        }
    }

    companion object {
        val tableDiscriminators = ConcurrentHashMap<String, MutableList<Discriminator<*, *>>>()
        val discriminatorClasses = ConcurrentHashMap<Discriminator<*, *>, KClass<*>>()
        val discriminatorSchemaClasses = ConcurrentHashMap<Discriminator<*, *>, Class<*>>()
        val discriminatorSchemas = ConcurrentHashMap<Discriminator<*, *>, AbstractSchema>()
    }
}

================================================
FILE: src/main/kotlin/kotlinx/nosql/DocumentSchemaIdQueryWrapper.kt
================================================
package kotlinx.nosql

import java.util.ArrayList
import kotlin.collections.single

class DocumentSchemaIdQueryWrapper<T : DocumentSchema<P, C>, P: Any, C: Any>(val schema: T, val id: Id<P, T>): DocumentSchemaQueryWrapper<T, P, C>(DocumentSchemaQueryParams(schema,
        schema.id.equal(id))
) {
    fun get(): C {
        return single()
    }

    override fun iterator(): Iterator<C> {
        val list = ArrayList<C>()
        val value = with (Session.current<KeyValueDocumentSchemaOperations>()) { schema.get(id) }
        if (value != null) {
            list.add(value)
        }
        return list.iterator()
    }
}

================================================
FILE: src/main/kotlin/kotlinx/nosql/DocumentSchemaOperations.kt
================================================
package kotlinx.nosql

import kotlinx.nosql.query.NoQuery

interface DocumentSchemaOperations {
    fun <T : DocumentSchema<P, V>, P: Any, V: Any> T.insert(v: V): Id<P, T>
    fun <T: DocumentSchema<P, C>, P: Any, C: Any> T.find(query: T.() -> Query = { NoQuery }): DocumentSchemaQueryWrapper<T, P, C>
    // TODO: Implement find(id) -> Wrapper
    /*fun <T: DocumentSchema<P, C>, P, C> T.find(id: Id<P, T>): C? {
        val w = find { this.id.equal(id) }
        return if (w.count() > 0) w.single() else null
    }*/

    fun <T : DocumentSchema<P, C>, P: Any, C: Any> find(params: DocumentSchemaQueryParams<T, P, C>): Iterator<C>
}


================================================
FILE: src/main/kotlin/kotlinx/nosql/DocumentSchemaQueryParams.kt
================================================
package kotlinx.nosql

class DocumentSchemaQueryParams<T : DocumentSchema<P, C>, P: Any, C: Any>(val schema: T, val query: Query? = null,
                                                                var skip: Int? = null, var take: Int? = null, var subscribed: Boolean = false)


================================================
FILE: src/main/kotlin/kotlinx/nosql/DocumentSchemaQueryWrapper.kt
================================================
package kotlinx.nosql

import kotlin.collections.listOf

open class DocumentSchemaQueryWrapper<T : DocumentSchema<P, C>, P: Any, C: Any>(val params: kotlinx.nosql.DocumentSchemaQueryParams<T, P, C>): Iterable<C> {
    override fun iterator(): Iterator<C> {
        return Session.current<DocumentSchemaOperations>().find(params)
    }

    fun skip(num: Int): kotlinx.nosql.DocumentSchemaQueryWrapper<T, P, C> {
        params.skip = num
        return this
    }

    fun take(num: Int): DocumentSchemaQueryWrapper<T, P, C> {
        params.take = num
        return this
    }

    fun remove(): Int {
        return Session.current<Session>().delete(params.schema, params.query!!)
    }

    fun <X> projection(x: T.() -> X): X {
        val xx = params.schema.x()
        val projectionParams = kotlinx.nosql.TableSchemaProjectionQueryParams<TableSchema<Any>, Any, Any>(params.schema as TableSchema<Any>,
                when (xx) {
                    is AbstractColumn<*, *, *> -> listOf(xx) as List<AbstractColumn<*, *, *>>
                    is ColumnPair<*, *, *> -> listOf(xx.a, xx.b)
                    is ColumnTriple<*, *, *, *> -> listOf(xx.a, xx.b, xx.c)
                    is kotlinx.nosql.ColumnQuadruple<*, *, *, *, *> -> listOf(xx.a, xx.b, xx.c, xx.d)
                    is ColumnQuintuple<*, *, *, *, *, *> -> listOf(xx.a, xx.b, xx.c, xx.d, xx.e)
                    is ColumnSextuple<*, *, *, *, *, *, *> -> listOf(xx.a, xx.b, xx.c, xx.d, xx.e, xx.f)
                    is ColumnSeptuple<*, *, *, *, *, *, *, *> -> listOf(xx.a, xx.b, xx.c, xx.d, xx.e, xx.f, xx.g)
                    is ColumnOctuple<*, *, *, *, *, *, *, *, *> -> listOf(xx.a, xx.b, xx.c, xx.d, xx.e, xx.f, xx.g, xx.h)
                    is ColumnNonuple<*, *, *, *, *, *, *, *, *, *> -> listOf(xx.a, xx.b, xx.c, xx.d, xx.e, xx.f, xx.g, xx.h, xx.j)
                    is kotlinx.nosql.ColumnDecuple<*, *, *, *, *, *, *, *, *, *, *> -> listOf(xx.a, xx.b, xx.c, xx.d, xx.e, xx.f, xx.g, xx.h, xx.i, xx.j)
                    else -> throw UnsupportedOperationException()
                }, params.query)
        TableSchemaProjectionQueryWrapper.set(TableSchemaProjectionQueryWrapper(projectionParams))
        return params.schema.x()
    }
}

================================================
FILE: src/main/kotlin/kotlinx/nosql/Expression.kt
================================================
package kotlinx.nosql

interface Expression<T> {
}

================================================
FILE: src/main/kotlin/kotlinx/nosql/Id.kt
================================================
package kotlinx.nosql

class Id<I: Any, R: TableSchema<I>>(val value: I) {
    override fun toString() = value.toString()

    override fun equals(other: Any?): Boolean {
        return if (other is Id<*, *>) value.equals(other.value) else false
    }
    override fun hashCode(): Int {
        return value.hashCode()
    }
}

================================================
FILE: src/main/kotlin/kotlinx/nosql/IdListColumn.kt
================================================
package kotlinx.nosql

import kotlin.reflect.KClass

open class IdListColumn<S : TableSchema<P>, R: TableSchema<P>, P: Any>  (name: String, val refSchema: R) : AbstractColumn<List<Id<P, R>>, S, Id<P, R>>(name, Id::class as KClass<Id<P, R>>, ColumnType.ID_LIST) {
}


================================================
FILE: src/main/kotlin/kotlinx/nosql/IdSetColumn.kt
================================================
package kotlinx.nosql

import kotlin.reflect.KClass

open class IdSetColumn<S : TableSchema<P>, R: TableSchema<P>, P: Any>  (name: String, val refSchema: R) : AbstractColumn<Set<Id<P, R>>, S, Id<P, R>>(name, Id::class as KClass<Id<P, R>>, ColumnType.ID_SET) {
}


================================================
FILE: src/main/kotlin/kotlinx/nosql/IndexOperations.kt
================================================
package kotlinx.nosql

interface IndexOperations {
    fun createIndex(schema: AbstractSchema, index: AbstractIndex)
}

================================================
FILE: src/main/kotlin/kotlinx/nosql/KeyValueDocumentSchemaOperations.kt
================================================
package kotlinx.nosql

import kotlinx.nosql.query.NoQuery

interface KeyValueDocumentSchemaOperations {
  fun <T : DocumentSchema<P, V>, P: Any, V: Any> T.insert(v: V): Id<P, T>
  operator fun <T: DocumentSchema<P, C>, P: Any, C: Any> T.get(id: Id<P, T>): C?
  fun <T: DocumentSchema<P, C>, P: Any, C: Any> T.find(id: Id<P, T>): DocumentSchemaIdQueryWrapper<T, P, C> {
      return DocumentSchemaIdQueryWrapper(this, id)
  }
}


================================================
FILE: src/main/kotlin/kotlinx/nosql/KeyValueSchema.kt
================================================
package kotlinx.nosql

abstract class KeyValueSchema(name: String): AbstractSchema(name) {

}

fun <T: KeyValueSchema, X> T.projection(x: T.() -> X): X {
    return x()
}

================================================
FILE: src/main/kotlin/kotlinx/nosql/KeyValueSchemaOperations.kt
================================================
package kotlinx.nosql

interface KeyValueSchemaOperations {
    operator fun <T : KeyValueSchema, C: Any> T.get(c: T.() -> AbstractColumn<C, T, *>): C?
    operator fun <T : KeyValueSchema, C> T.set(c: () -> AbstractColumn<C, T, *>, v: C)

    fun <X, S: Number, T: KeyValueSchema> AbstractColumn<X, T, S>.incr(value: S = 1 as S): X {
        val value = Session.current<Session>().incr(schema, this, value)
        if (columnType.id) {
            return Id<Any, TableSchema<Any>>(value) as X
        } else {
            return value as X
        }
    }
}


================================================
FILE: src/main/kotlin/kotlinx/nosql/ListColumn.kt
================================================
package kotlinx.nosql

import kotlin.reflect.KClass

open class ListColumn<C: Any, S : AbstractSchema> (name: String, valueClass: KClass<C>) : AbstractColumn<List<C>, S, C>(name, valueClass, ColumnType.CUSTOM_CLASS_LIST) {
}

================================================
FILE: src/main/kotlin/kotlinx/nosql/LiteralExpression.kt
================================================
package kotlinx.nosql

class LiteralExpression(val value: Any?): Expression<Any?> {
}

================================================
FILE: src/main/kotlin/kotlinx/nosql/Nonuple.kt
================================================
package kotlinx.nosql

class Nonuple<A1, A2, A3, A4, A5, A6, A7, A8, A9>(val a1: A1, val a2: A2, val a3: A3, val a4: A4, val a5: A5, val a6: A6, val a7: A7, val a8: A8, val a9: A9) {
    operator public fun component1(): A1 = a1
    operator public fun component2(): A2 = a2
    operator public fun component3(): A3 = a3
    operator public fun component4(): A4 = a4
    operator public fun component5(): A5 = a5
    operator public fun component6(): A6 = a6
    operator public fun component7(): A7 = a7
    operator public fun component8(): A8 = a8
    operator public fun component9(): A9 = a9
}

================================================
FILE: src/main/kotlin/kotlinx/nosql/NotEqualQuery.kt
================================================
package kotlinx.nosql

class NotEqualQuery(val expr1: Expression<*>, val expr2: Expression<*>): Query() {
}

================================================
FILE: src/main/kotlin/kotlinx/nosql/NotMemberOfQuery.kt
================================================
package kotlinx.nosql

class NotMemberOfQuery(val expr1: Expression<*>, val expr2: Expression<*>): Query() {
}

================================================
FILE: src/main/kotlin/kotlinx/nosql/NullableColumn.kt
================================================
package kotlinx.nosql

import kotlin.reflect.KClass

open class NullableColumn<C: Any, S : AbstractSchema> (name: String, valueClass: KClass<C>,
                                                  columnType: ColumnType) : AbstractColumn<C?, S, C>(name, valueClass, columnType), AbstractNullableColumn {
}

================================================
FILE: src/main/kotlin/kotlinx/nosql/NullableIdColumn.kt
================================================
package kotlinx.nosql

import kotlin.reflect.KClass

open class NullableIdColumn<I: Any, S : TableSchema<I>, R: TableSchema<I>> (name: String, valueClass: KClass<I>,
                                                                            columnType: ColumnType) : AbstractColumn<Id<I, R>?, S, I>(name, valueClass, columnType), AbstractNullableColumn {
}

================================================
FILE: src/main/kotlin/kotlinx/nosql/Octuple.kt
================================================
package kotlinx.nosql

class Octuple<A1, A2, A3, A4, A5, A6, A7, A8>(val a1: A1, val a2: A2, val a3: A3, val a4: A4, val a5: A5, val a6: A6, val a7: A7, val a8: A8) {
    operator public fun component1(): A1 = a1
    operator public fun component2(): A2 = a2
    operator public fun component3(): A3 = a3
    operator public fun component4(): A4 = a4
    operator public fun component5(): A5 = a5
    operator public fun component6(): A6 = a6
    operator public fun component7(): A7 = a7
    operator public fun component8(): A8 = a8
}

================================================
FILE: src/main/kotlin/kotlinx/nosql/PrimaryKey.kt
================================================
package kotlinx.nosql

open class PrimaryKey<I>(val name: String, val javaClass: Class<I>, val columnType: ColumnType) {
    companion object {
        fun string(name: String) = PrimaryKey<String>(name, String::class.java, ColumnType.STRING)
        fun integer(name: String) = PrimaryKey<Int>(name, Int::class.java, ColumnType.INTEGER)
    }
}

================================================
FILE: src/main/kotlin/kotlinx/nosql/Quadruple.kt
================================================
package kotlinx.nosql

class Quadruple<A1, A2, A3, A4>(val a1: A1, val a2: A2, val a3: A3, val a4: A4) {
    operator public fun component1(): A1 = a1
    operator public fun component2(): A2 = a2
    operator public fun component3(): A3 = a3
    operator public fun component4(): A4 = a4
}

================================================
FILE: src/main/kotlin/kotlinx/nosql/Query.kt
================================================
package kotlinx.nosql

import kotlinx.nosql.query.OrQuery

abstract class Query() {
    infix fun and(op: Query): Query {
        return AndQuery(this, op)
    }

    infix fun or(op: Query): Query {
        return OrQuery(this, op)
    }
}

================================================
FILE: src/main/kotlin/kotlinx/nosql/Quintuple.kt
================================================
package kotlinx.nosql

class Quintuple<A1, A2, A3, A4, A5>(val a1: A1, val a2: A2, val a3: A3, val a4: A4, val a5: A5) {
    operator public fun component1(): A1 = a1
    operator public fun component2(): A2 = a2
    operator public fun component3(): A3 = a3
    operator public fun component4(): A4 = a4
    operator public fun component5(): A5 = a5
}

================================================
FILE: src/main/kotlin/kotlinx/nosql/SchemaGenerationAction.kt
================================================
package kotlinx.nosql

abstract class SchemaGenerationAction<S : Session>() {
}

class Create<S: Session>(val onCreate: S.() -> Unit = { }) : SchemaGenerationAction<S>()

class CreateDrop<S: Session>(val onCreate: S.() -> Unit = { },
             onDrop: S.() -> Unit = { }) : SchemaGenerationAction<S>()

class Update<S: Session>() : SchemaGenerationAction<S>()

class Validate<S: Session>() : SchemaGenerationAction<S>()


================================================
FILE: src/main/kotlin/kotlinx/nosql/Septuple.kt
================================================
package kotlinx.nosql

class Septuple<A1, A2, A3, A4, A5, A6, A7>(val a1: A1, val a2: A2, val a3: A3, val a4: A4, val a5: A5, val a6: A6, val a7: A7) {
    operator public fun component1(): A1 = a1
    operator public fun component2(): A2 = a2
    operator public fun component3(): A3 = a3
    operator public fun component4(): A4 = a4
    operator public fun component5(): A5 = a5
    operator public fun component6(): A6 = a6
    operator public fun component7(): A7 = a7
}

================================================
FILE: src/main/kotlin/kotlinx/nosql/Session.kt
================================================
package kotlinx.nosql

import java.util.ArrayList

interface Session {
    fun <T : AbstractSchema>T.create()

    fun <T : AbstractSchema>T.drop()

    // TODO: Refactor
    fun <T : AbstractSchema> insert(columns: Array<Pair<AbstractColumn<*, T, *>, *>>)

    // TODO: Refactor
    fun <T : AbstractSchema> delete(table: T, op: Query): Int

    fun update(schema: AbstractSchema, columnValues: Array<Pair<AbstractColumn<*, *, *>, *>>, op: Query): Int

    fun <T> addAll(schema: AbstractSchema, column: AbstractColumn<Collection<T>, *, *>, values: Collection<T>, op: Query): Int
    fun <T: Number> incr(schema: KeyValueSchema, column: AbstractColumn<*, *, T>, value: T): T
    //internal fun <T: Number> incr(schema: AbstractSchema, column: AbstractColumn<*, *, T>, value: T, op: Query): T

    fun <T> removeAll(schema: AbstractSchema, column: AbstractColumn<Collection<T>, *, *>, values: Collection<T>, op: Query): Int

    fun <T> removeAll(schema: AbstractSchema, column: AbstractColumn<Collection<T>, *, *>, removeOp: Query, op: Query): Int

    companion object {
        val threadLocale = ThreadLocal<Session>()

        fun <T> current(): T {
            return threadLocale.get()!! as T
        }
    }
}


================================================
FILE: src/main/kotlin/kotlinx/nosql/SetColumn.kt
================================================
package kotlinx.nosql

import kotlin.reflect.KClass

open class SetColumn<C: Any, S : AbstractSchema> (name: String, valueClass: KClass<C>) : AbstractColumn<Set<C>, S, C>(name, valueClass, ColumnType.CUSTOM_CLASS_SET) {
}

================================================
FILE: src/main/kotlin/kotlinx/nosql/Sextuple.kt
================================================
package kotlinx.nosql

class Sextuple<A1, A2, A3, A4, A5, A6>(val a1: A1, val a2: A2, val a3: A3, val a4: A4, val a5: A5, val a6: A6) {
    operator public fun component1(): A1 = a1
    operator public fun component2(): A2 = a2
    operator public fun component3(): A3 = a3
    operator public fun component4(): A4 = a4
    operator public fun component5(): A5 = a5
    operator public fun component6(): A6 = a6
}

================================================
FILE: src/main/kotlin/kotlinx/nosql/TableSchema.kt
================================================
package kotlinx.nosql

abstract class TableSchema<I: Any>(tableName: String, primaryKey: AbstractColumn<I, out TableSchema<I>, I>): AbstractTableSchema(tableName) {
    val pk = AbstractColumn<Id<I, TableSchema<I>>, TableSchema<I>, I>(primaryKey.name, primaryKey.valueClass, ColumnType.PRIMARY_ID)
}

// Extension functions

val <C: Any, T : TableSchema<C>> T.id: AbstractColumn<Id<C, T>, T, C>
    get () {
        return pk as AbstractColumn<Id<C, T>, T, C>
    }

fun <S : AbstractSchema, R: TableSchema<P>, P: Any> id(name: String, refSchema: R): AbstractColumn<Id<P, R>, S, P> = AbstractColumn(name, refSchema.id.valueClass, ColumnType.FOREIGN_ID)
fun <S : AbstractSchema, R: TableSchema<P>, P: Any> S.id(name: String, refSchema: R): AbstractColumn<Id<P, R>, S, P> = AbstractColumn(name, refSchema.id.valueClass, ColumnType.FOREIGN_ID)

fun <S : TableSchema<P>, R: TableSchema<P>, P: Any>  listOfId(name: String, refSchema: R): IdListColumn<S, R, P> = IdListColumn(name, refSchema)
fun <S : TableSchema<P>, R: TableSchema<P>, P: Any> S.listOfId(name: String, refSchema: R): IdListColumn<S, R, P> = IdListColumn(name, refSchema)

fun <S : TableSchema<P>, R: TableSchema<P>, P: Any>  setOfId(name: String, refSchema: R): IdSetColumn<S, R, P> = IdSetColumn(name, refSchema)
fun <S : TableSchema<P>, R: TableSchema<P>, P: Any> S.setOfId(name: String, refSchema: R): IdSetColumn<S, R, P> = IdSetColumn(name, refSchema)

fun <S : TableSchema<P>, R: TableSchema<P>, P: Any> nullableId(name: String, refSchema: R): NullableIdColumn<P, S, R> = NullableIdColumn(name, refSchema.id.valueClass, ColumnType.FOREIGN_ID)
fun <S : TableSchema<P>, R: TableSchema<P>, P: Any> S.nullableId(name: String, refSchema: R): NullableIdColumn<P, S, R> = NullableIdColumn(name, refSchema.id.valueClass, ColumnType.FOREIGN_ID)


================================================
FILE: src/main/kotlin/kotlinx/nosql/TableSchemaOperations.kt
================================================
package kotlinx.nosql

interface TableSchemaOperations {
    fun <T : TableSchema<P>, P: Any, V: Any> find(params: TableSchemaProjectionQueryParams<T, P, V>): Iterator<V>
}

================================================
FILE: src/main/kotlin/kotlinx/nosql/TableSchemaProjectionQueryParams.kt
================================================
package kotlinx.nosql

class TableSchemaProjectionQueryParams<T : TableSchema<P>, P: Any, V>(val table: T, val projection: List<AbstractColumn<*, *, *>>, val query: Query? = null,
                                                                 var skip: Int? = null, var take: Int? = null) {
}

================================================
FILE: src/main/kotlin/kotlinx/nosql/TableSchemaProjectionQueryWrapper.kt
================================================
package kotlinx.nosql

class TableSchemaProjectionQueryWrapper<T : TableSchema<P>, P: Any, V: Any>(val params: TableSchemaProjectionQueryParams<T, P, V>): Iterable<V> {
    override fun iterator(): Iterator<V> {
        return Session.current<TableSchemaOperations>().find(params)
    }

    fun skip(num: Int): TableSchemaProjectionQueryWrapper<T, P, V> {
        params.skip = num
        return this
    }

    fun take(num: Int): TableSchemaProjectionQueryWrapper<T, P, V> {
        params.take = num
        return this
    }

    companion object {
        val threadLocal = ThreadLocal<TableSchemaProjectionQueryWrapper<out TableSchema<*>, *, *>>()

        fun get(): TableSchemaProjectionQueryWrapper<out TableSchema<*>, *, *> {
            return threadLocal.get()!! as TableSchemaProjectionQueryWrapper<out TableSchema<*>, *, *>
        }

        fun set(value: TableSchemaProjectionQueryWrapper<out TableSchema<*>, *, *>) {
            threadLocal.set(value)
        }
    }
}

================================================
FILE: src/main/kotlin/kotlinx/nosql/query/EqualQuery.kt
================================================
package kotlinx.nosql.query

import kotlinx.nosql.Expression
import kotlinx.nosql.Query

class EqualQuery(val expr1: Expression<*>, val expr2: Expression<*>): Query() {
}

================================================
FILE: src/main/kotlin/kotlinx/nosql/query/GreateQuery.kt
================================================
package kotlinx.nosql.query

import kotlinx.nosql.Query
import kotlinx.nosql.Expression

class GreaterQuery(val expr1: Expression<*>, val expr2: Expression<*>): Query() {
}

================================================
FILE: src/main/kotlin/kotlinx/nosql/query/GreaterEqualQuery.kt
================================================
package kotlinx.nosql.query

import kotlinx.nosql.Expression
import kotlinx.nosql.Query

class GreaterEqualQuery(val expr1: Expression<*>, val expr2: Expression<*>): Query() {
}

================================================
FILE: src/main/kotlin/kotlinx/nosql/query/IsNotNullQuery.kt
================================================
package kotlinx.nosql.query

import kotlinx.nosql.AbstractColumn
import kotlinx.nosql.Query

class IsNotNullQuery(val column: AbstractColumn<*, *, *>): Query() {
}

================================================
FILE: src/main/kotlin/kotlinx/nosql/query/IsNullQuery.kt
================================================
package kotlinx.nosql.query

import kotlinx.nosql.AbstractColumn
import kotlinx.nosql.Query

class IsNullQuery(val column: AbstractColumn<*, *, *>): Query() {
}

================================================
FILE: src/main/kotlin/kotlinx/nosql/query/LessEqualQuery.kt
================================================
package kotlinx.nosql.query

import kotlinx.nosql.Expression
import kotlinx.nosql.Query

class LessEqualQuery(val expr1: Expression<*>, val expr2: Expression<*>): Query() {
}


================================================
FILE: src/main/kotlin/kotlinx/nosql/query/LessQuery.kt
================================================
package kotlinx.nosql.query

import kotlinx.nosql.Expression
import kotlinx.nosql.Query

class LessQuery(val expr1: Expression<*>, val expr2: Expression<*>): Query() {
}

================================================
FILE: src/main/kotlin/kotlinx/nosql/query/MatchesQuery.kt
================================================
package kotlinx.nosql.query

import kotlinx.nosql.Expression
import kotlinx.nosql.Query

class MatchesQuery(val expr1: Expression<*>, val expr2: Expression<*>): Query() {
}


================================================
FILE: src/main/kotlin/kotlinx/nosql/query/MemberOfQuery.kt
================================================
package kotlinx.nosql.query

import kotlinx.nosql.Query
import kotlinx.nosql.Expression

class MemberOfQuery(val expr1: Expression<*>, val expr2: Expression<*>): Query() {
}


================================================
FILE: src/main/kotlin/kotlinx/nosql/query/NoQuery.kt
================================================
package kotlinx.nosql.query

import kotlinx.nosql.Query

object NoQuery : Query() {
}

================================================
FILE: src/main/kotlin/kotlinx/nosql/query/OrQuery.kt
================================================
package kotlinx.nosql.query

import kotlinx.nosql.Query

class OrQuery(val expr1: Query, val expr2: Query): Query() {
}

================================================
FILE: src/main/kotlin/kotlinx/nosql/query/TextQuery.kt
================================================
package kotlinx.nosql.query

import kotlinx.nosql.Query

class TextQuery(val search: String): Query() {
}

================================================
FILE: src/main/kotlin/kotlinx/nosql/util/SchemaUtils.kt
================================================
package kotlinx.nosql.util

import java.lang.reflect.Field
import java.util.ArrayList
import java.util.HashMap
import kotlinx.nosql.AbstractColumn
import kotlinx.nosql.AbstractSchema
import org.joda.time.LocalDate
import org.joda.time.LocalTime
import org.joda.time.DateTime
import kotlin.collections.listOf
import kotlin.collections.setOf
import kotlin.text.toLowerCase

fun getAllFields(_type: Class<in Any>, condition: (Field) -> Boolean = { f -> true },
                 fields: MutableList<Field> = ArrayList()): MutableList<Field> {
    for (field in _type.declaredFields!!) {
        if (condition(field)) fields.add(field)
    }
    if (_type.superclass != null) {
        getAllFields(_type.superclass!!, condition, fields)
    }
    return fields
}

fun getAllFieldsMap(_type: Class<in Any>, condition: (Field) -> Boolean = { f -> true },
                    fields: MutableMap<String, Field> = HashMap()): MutableMap<String, Field> {
    for (field in _type.declaredFields!!) {
        if (condition(field)) fields.put(field.name!!.toLowerCase(), field)
    }
    if (_type.superclass != null) {
        getAllFieldsMap(_type.superclass, condition, fields)
    }
    return fields
}

val Field.isColumn: Boolean
    get() {
        return AbstractColumn::class.java.isAssignableFrom(this.type!!)
    }

fun Field.asColumn(schema: Any): AbstractColumn<*, *, *> {
    this.isAccessible = true
    return this.get(schema) as AbstractColumn<*, *, *>
}

fun newInstance(clazz: Class<out Any?>): Any {
  val constructor = clazz.constructors!![0]
  val constructorParamTypes = constructor.parameterTypes!!
  val constructorParamValues = Array<Any?>(constructor.parameterTypes!!.size, { index: Int ->
    when (constructorParamTypes[index].name) {
      "int" -> 0
      "java.lang.String" -> ""
      "org.joda.time.LocalDate" -> LocalDate()
      "org.joda.time.LocalTime" -> LocalTime()
      "org.joda.time.DateTime" -> DateTime()
      "double" -> 0.toDouble()
      "float" -> 0.toFloat()
      "long" -> 0.toLong()
      "short" -> 0.toShort()
      "byte" -> 0.toByte()
      "boolean" -> false
      "java.util.List" -> listOf<Any>()
      "java.util.Set" -> setOf<Any>()
      else -> newInstance(constructorParamTypes[index])
    }
  })
  return constructor.newInstance(*constructorParamValues)!!
}
Download .txt
gitextract_0ev3bdve/

├── .idea/
│   ├── .name
│   ├── compiler.xml
│   ├── copyright/
│   │   └── profiles_settings.xml
│   ├── encodings.xml
│   ├── gradle.xml
│   ├── libraries/
│   │   ├── Gradle__joda_time_joda_time_2_3.xml
│   │   ├── Gradle__junit_junit_4_11.xml
│   │   ├── Gradle__org_hamcrest_hamcrest_core_1_3.xml
│   │   ├── Gradle__org_jetbrains_kotlin_kotlin_reflect_1_0_0.xml
│   │   ├── Gradle__org_jetbrains_kotlin_kotlin_runtime_1_0_0.xml
│   │   ├── Gradle__org_jetbrains_kotlin_kotlin_stdlib_1_0_0.xml
│   │   ├── Gradle__org_jetbrains_kotlin_kotlin_test_1_0_0.xml
│   │   ├── Gradle__org_jetbrains_spek_spek_0_1_195.xml
│   │   └── Gradle__org_mongodb_mongo_java_driver_3_0_3.xml
│   ├── misc.xml
│   ├── modules.xml
│   └── vcs.xml
├── README.md
├── build.gradle
├── gradle/
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── kotlin-nosql-mongodb/
│   ├── build.gradle
│   ├── kotlin-nosql-mongodb.iml
│   └── src/
│       ├── main/
│       │   └── kotlin/
│       │       └── kotlinx/
│       │           └── nosql/
│       │               └── mongodb/
│       │                   ├── DocumentSchema.kt
│       │                   ├── MongoDB.kt
│       │                   ├── MongoDBIndex.kt
│       │                   └── MongoDBSession.kt
│       └── test/
│           └── kotlin/
│               └── kotlinx/
│                   └── nosql/
│                       └── mongodb/
│                           └── test/
│                               └── MongoDBSpek.kt
├── kotlin-nosql.iml
├── settings.gradle
└── src/
    └── main/
        └── kotlin/
            └── kotlinx/
                └── nosql/
                    ├── AbstractColumn.kt
                    ├── AbstractIndex.kt
                    ├── AbstractNullableColumn.kt
                    ├── AbstractSchema.kt
                    ├── AbstractTableSchema.kt
                    ├── AndQuery.kt
                    ├── Column.kt
                    ├── ColumnDecuple.kt
                    ├── ColumnNonuple.kt
                    ├── ColumnOctuple.kt
                    ├── ColumnPair.kt
                    ├── ColumnQuadruple.kt
                    ├── ColumnQueryWrapper.kt
                    ├── ColumnQuintuple.kt
                    ├── ColumnSeptuple.kt
                    ├── ColumnSextuple.kt
                    ├── ColumnTriple.kt
                    ├── ColumnType.kt
                    ├── Database.kt
                    ├── Decuple.kt
                    ├── Discriminator.kt
                    ├── DocumentSchema.kt
                    ├── DocumentSchemaIdQueryWrapper.kt
                    ├── DocumentSchemaOperations.kt
                    ├── DocumentSchemaQueryParams.kt
                    ├── DocumentSchemaQueryWrapper.kt
                    ├── Expression.kt
                    ├── Id.kt
                    ├── IdListColumn.kt
                    ├── IdSetColumn.kt
                    ├── IndexOperations.kt
                    ├── KeyValueDocumentSchemaOperations.kt
                    ├── KeyValueSchema.kt
                    ├── KeyValueSchemaOperations.kt
                    ├── ListColumn.kt
                    ├── LiteralExpression.kt
                    ├── Nonuple.kt
                    ├── NotEqualQuery.kt
                    ├── NotMemberOfQuery.kt
                    ├── NullableColumn.kt
                    ├── NullableIdColumn.kt
                    ├── Octuple.kt
                    ├── PrimaryKey.kt
                    ├── Quadruple.kt
                    ├── Query.kt
                    ├── Quintuple.kt
                    ├── SchemaGenerationAction.kt
                    ├── Septuple.kt
                    ├── Session.kt
                    ├── SetColumn.kt
                    ├── Sextuple.kt
                    ├── TableSchema.kt
                    ├── TableSchemaOperations.kt
                    ├── TableSchemaProjectionQueryParams.kt
                    ├── TableSchemaProjectionQueryWrapper.kt
                    ├── query/
                    │   ├── EqualQuery.kt
                    │   ├── GreateQuery.kt
                    │   ├── GreaterEqualQuery.kt
                    │   ├── IsNotNullQuery.kt
                    │   ├── IsNullQuery.kt
                    │   ├── LessEqualQuery.kt
                    │   ├── LessQuery.kt
                    │   ├── MatchesQuery.kt
                    │   ├── MemberOfQuery.kt
                    │   ├── NoQuery.kt
                    │   ├── OrQuery.kt
                    │   └── TextQuery.kt
                    └── util/
                        └── SchemaUtils.kt
Condensed preview — 100 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (191K chars).
[
  {
    "path": ".idea/.name",
    "chars": 12,
    "preview": "kotlin-nosql"
  },
  {
    "path": ".idea/compiler.xml",
    "chars": 975,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"CompilerConfiguration\">\n    <resourceExt"
  },
  {
    "path": ".idea/copyright/profiles_settings.xml",
    "chars": 74,
    "preview": "<component name=\"CopyrightManager\">\n  <settings default=\"\" />\n</component>"
  },
  {
    "path": ".idea/encodings.xml",
    "chars": 159,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"Encoding\">\n    <file url=\"PROJECT\" chars"
  },
  {
    "path": ".idea/gradle.xml",
    "chars": 1038,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"GradleSettings\">\n    <option name=\"linke"
  },
  {
    "path": ".idea/libraries/Gradle__joda_time_joda_time_2_3.xml",
    "chars": 508,
    "preview": "<component name=\"libraryTable\">\n  <library name=\"Gradle: joda-time:joda-time:2.3\">\n    <CLASSES>\n      <root url=\"jar://"
  },
  {
    "path": ".idea/libraries/Gradle__junit_junit_4_11.xml",
    "chars": 481,
    "preview": "<component name=\"libraryTable\">\n  <library name=\"Gradle: junit:junit:4.11\">\n    <CLASSES>\n      <root url=\"jar://$USER_H"
  },
  {
    "path": ".idea/libraries/Gradle__org_hamcrest_hamcrest_core_1_3.xml",
    "chars": 537,
    "preview": "<component name=\"libraryTable\">\n  <library name=\"Gradle: org.hamcrest:hamcrest-core:1.3\">\n    <CLASSES>\n      <root url="
  },
  {
    "path": ".idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_reflect_1_0_0.xml",
    "chars": 576,
    "preview": "<component name=\"libraryTable\">\n  <library name=\"Gradle: org.jetbrains.kotlin:kotlin-reflect:1.0.0\">\n    <CLASSES>\n     "
  },
  {
    "path": ".idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_runtime_1_0_0.xml",
    "chars": 576,
    "preview": "<component name=\"libraryTable\">\n  <library name=\"Gradle: org.jetbrains.kotlin:kotlin-runtime:1.0.0\">\n    <CLASSES>\n     "
  },
  {
    "path": ".idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_1_0_0.xml",
    "chars": 571,
    "preview": "<component name=\"libraryTable\">\n  <library name=\"Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.0.0\">\n    <CLASSES>\n      "
  },
  {
    "path": ".idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_test_1_0_0.xml",
    "chars": 561,
    "preview": "<component name=\"libraryTable\">\n  <library name=\"Gradle: org.jetbrains.kotlin:kotlin-test:1.0.0\">\n    <CLASSES>\n      <r"
  },
  {
    "path": ".idea/libraries/Gradle__org_jetbrains_spek_spek_0_1_195.xml",
    "chars": 530,
    "preview": "<component name=\"libraryTable\">\n  <library name=\"Gradle: org.jetbrains.spek:spek:0.1.195\">\n    <CLASSES>\n      <root url"
  },
  {
    "path": ".idea/libraries/Gradle__org_mongodb_mongo_java_driver_3_0_3.xml",
    "chars": 564,
    "preview": "<component name=\"libraryTable\">\n  <library name=\"Gradle: org.mongodb:mongo-java-driver:3.0.3\">\n    <CLASSES>\n      <root"
  },
  {
    "path": ".idea/misc.xml",
    "chars": 883,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"EntryPointsManager\">\n    <entry_points v"
  },
  {
    "path": ".idea/modules.xml",
    "chars": 1582,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"ProjectModuleManager\">\n    <modules>\n   "
  },
  {
    "path": ".idea/vcs.xml",
    "chars": 180,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"VcsDirectoryMappings\">\n    <mapping dire"
  },
  {
    "path": "README.md",
    "chars": 6867,
    "preview": "# Kotlin NoSQL\n\nKotlin NoSQL is a reactive and type-safe DSL for working with NoSQL databases.\n\n## Status\n\nUnder develop"
  },
  {
    "path": "build.gradle",
    "chars": 1770,
    "preview": "configurations.all {\n    // check for updates every build\n    resolutionStrategy.cacheChangingModulesFor 0, 'seconds'\n}\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "chars": 230,
    "preview": "#Tue Sep 30 10:02:29 PDT 2014\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_"
  },
  {
    "path": "gradlew",
    "chars": 5080,
    "preview": "#!/usr/bin/env bash\n\n##############################################################################\n##\n##  Gradle start "
  },
  {
    "path": "gradlew.bat",
    "chars": 2314,
    "preview": "@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem "
  },
  {
    "path": "kotlin-nosql-mongodb/build.gradle",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "kotlin-nosql-mongodb/kotlin-nosql-mongodb.iml",
    "chars": 2450,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<module external.linked.project.id=\":kotlin-nosql-mongodb\" external.linked.projec"
  },
  {
    "path": "kotlin-nosql-mongodb/src/main/kotlin/kotlinx/nosql/mongodb/DocumentSchema.kt",
    "chars": 914,
    "preview": "package kotlinx.nosql.mongodb\n\nimport kotlinx.nosql.AbstractColumn\nimport kotlinx.nosql.Discriminator\nimport kotlinx.nos"
  },
  {
    "path": "kotlin-nosql-mongodb/src/main/kotlin/kotlinx/nosql/mongodb/MongoDB.kt",
    "chars": 2273,
    "preview": "package kotlinx.nosql.mongodb\n\nimport kotlinx.nosql.Database\nimport kotlinx.nosql.Session\nimport com.mongodb.MongoClient"
  },
  {
    "path": "kotlin-nosql-mongodb/src/main/kotlin/kotlinx/nosql/mongodb/MongoDBIndex.kt",
    "chars": 379,
    "preview": "package kotlinx.nosql.mongodb\n\nimport kotlinx.nosql.AbstractColumn\nimport kotlinx.nosql.AbstractIndex\n\nclass MongoDBInde"
  },
  {
    "path": "kotlin-nosql-mongodb/src/main/kotlin/kotlinx/nosql/mongodb/MongoDBSession.kt",
    "chars": 34427,
    "preview": "package kotlinx.nosql.mongodb\n\nimport com.mongodb.DB\nimport com.mongodb.BasicDBObject\nimport java.lang.reflect.Field\nimp"
  },
  {
    "path": "kotlin-nosql-mongodb/src/test/kotlin/kotlinx/nosql/mongodb/test/MongoDBSpek.kt",
    "chars": 57029,
    "preview": "package kotlinx.nosql.mongodb.test\n\nimport kotlin.test.assertEquals\nimport kotlinx.nosql.*\nimport kotlinx.nosql.mongodb."
  },
  {
    "path": "kotlin-nosql.iml",
    "chars": 2253,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<module external.linked.project.id=\"kotlin-nosql\" external.linked.project.path=\"$"
  },
  {
    "path": "settings.gradle",
    "chars": 31,
    "preview": "include 'kotlin-nosql-mongodb'\n"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/AbstractColumn.kt",
    "chars": 6439,
    "preview": "package kotlinx.nosql\n\nimport java.util.ArrayList\nimport java.util.regex.Pattern\nimport kotlinx.nosql.query.*\nimport kot"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/AbstractIndex.kt",
    "chars": 69,
    "preview": "package kotlinx.nosql\n\nabstract class AbstractIndex(val name: String)"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/AbstractNullableColumn.kt",
    "chars": 59,
    "preview": "package kotlinx.nosql\n\ninterface AbstractNullableColumn {\n}"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/AbstractSchema.kt",
    "chars": 8364,
    "preview": "package kotlinx.nosql\n\nimport java.util.ArrayList\nimport java.util.concurrent.ConcurrentHashMap\nimport java.util.concurr"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/AbstractTableSchema.kt",
    "chars": 97,
    "preview": "package kotlinx.nosql\n\nabstract class AbstractTableSchema(name: String): AbstractSchema(name) {\n}"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/AndQuery.kt",
    "chars": 114,
    "preview": "package kotlinx.nosql\n\nclass AndQuery(val expr1: kotlinx.nosql.Query, val expr2: kotlinx.nosql.Query): Query() {\n}"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/Column.kt",
    "chars": 249,
    "preview": "package kotlinx.nosql\n\nimport kotlin.reflect.KClass\n\nabstract class Column<C: Any, S : AbstractSchema>(name: String, val"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/ColumnDecuple.kt",
    "chars": 745,
    "preview": "package kotlinx.nosql\n\nclass ColumnDecuple<S : AbstractSchema, A, B, C, D, E, F, G, H, J, K>(val a: AbstractColumn<A, S,"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/ColumnNonuple.kt",
    "chars": 860,
    "preview": "package kotlinx.nosql\n\nclass ColumnNonuple<S : AbstractSchema, A, B, C, D, E, F, G, H, J>(val a: AbstractColumn<A, S, *>"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/ColumnOctuple.kt",
    "chars": 744,
    "preview": "package kotlinx.nosql\n\nclass ColumnOctuple<S : AbstractSchema, A, B, C, D, E, F, G, H>(val a: AbstractColumn<A, S, *>, v"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/ColumnPair.kt",
    "chars": 414,
    "preview": "package kotlinx.nosql\n\nclass ColumnPair<S : AbstractSchema, A, B>(val a: AbstractColumn<A, S, *>, val b: AbstractColumn<"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/ColumnQuadruple.kt",
    "chars": 644,
    "preview": "package kotlinx.nosql\n\nclass ColumnQuadruple<S : AbstractSchema, A, B, C, D>(val a: AbstractColumn<A, S, *>, val b: Abst"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/ColumnQueryWrapper.kt",
    "chars": 5513,
    "preview": "package kotlinx.nosql\n\nabstract class ColumnQueryWrapper<C> : Iterable<C> {\n    override fun iterator(): Iterator<C> {\n "
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/ColumnQuintuple.kt",
    "chars": 540,
    "preview": "package kotlinx.nosql\n\nclass ColumnQuintuple<S : AbstractSchema, A, B, C, D, E>(val a: AbstractColumn<A, S, *>, val b: A"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/ColumnSeptuple.kt",
    "chars": 693,
    "preview": "package kotlinx.nosql\n\nclass ColumnSeptuple<S : AbstractSchema, A, B, C, D, E, F, G>(val a: AbstractColumn<A, S, *>, val"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/ColumnSextuple.kt",
    "chars": 588,
    "preview": "package kotlinx.nosql\n\nclass ColumnSextuple<S : AbstractSchema, A, B, C, D, E, F>(val a: AbstractColumn<A, S, *>, val b:"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/ColumnTriple.kt",
    "chars": 346,
    "preview": "package kotlinx.nosql\n\nclass ColumnTriple<S : AbstractSchema, A, B, C>(val a: AbstractColumn<A, S, *>, val b: AbstractCo"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/ColumnType.kt",
    "chars": 1202,
    "preview": "package kotlinx.nosql\n\nenum class ColumnType(val primitive: Boolean = false,\n                      val iterable: Boolean"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/Database.kt",
    "chars": 2271,
    "preview": "package kotlinx.nosql\n\nimport java.util.concurrent.ConcurrentHashMap\nimport kotlinx.nosql.util.getAllFields\nimport kotli"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/Decuple.kt",
    "chars": 666,
    "preview": "package kotlinx.nosql\n\nclass Decuple<A1, A2, A3, A4, A5, A6, A7, A8, A9, A10>(val a1: A1, val a2: A2, val a3: A3, val a4"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/Discriminator.kt",
    "chars": 143,
    "preview": "package kotlinx.nosql\n\nclass Discriminator<D: Any, S : DocumentSchema<out Any, out Any>>(val column: AbstractColumn<D, S"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/DocumentSchema.kt",
    "chars": 1458,
    "preview": "package kotlinx.nosql\n\nimport java.util.concurrent.CopyOnWriteArrayList\nimport java.util.concurrent.ConcurrentHashMap\nim"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/DocumentSchemaIdQueryWrapper.kt",
    "chars": 628,
    "preview": "package kotlinx.nosql\n\nimport java.util.ArrayList\nimport kotlin.collections.single\n\nclass DocumentSchemaIdQueryWrapper<T"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/DocumentSchemaOperations.kt",
    "chars": 636,
    "preview": "package kotlinx.nosql\n\nimport kotlinx.nosql.query.NoQuery\n\ninterface DocumentSchemaOperations {\n    fun <T : DocumentSch"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/DocumentSchemaQueryParams.kt",
    "chars": 281,
    "preview": "package kotlinx.nosql\n\nclass DocumentSchemaQueryParams<T : DocumentSchema<P, C>, P: Any, C: Any>(val schema: T, val quer"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/DocumentSchemaQueryWrapper.kt",
    "chars": 2235,
    "preview": "package kotlinx.nosql\n\nimport kotlin.collections.listOf\n\nopen class DocumentSchemaQueryWrapper<T : DocumentSchema<P, C>,"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/Expression.kt",
    "chars": 50,
    "preview": "package kotlinx.nosql\n\ninterface Expression<T> {\n}"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/Id.kt",
    "chars": 326,
    "preview": "package kotlinx.nosql\n\nclass Id<I: Any, R: TableSchema<I>>(val value: I) {\n    override fun toString() = value.toString("
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/IdListColumn.kt",
    "chars": 265,
    "preview": "package kotlinx.nosql\n\nimport kotlin.reflect.KClass\n\nopen class IdListColumn<S : TableSchema<P>, R: TableSchema<P>, P: A"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/IdSetColumn.kt",
    "chars": 262,
    "preview": "package kotlinx.nosql\n\nimport kotlin.reflect.KClass\n\nopen class IdSetColumn<S : TableSchema<P>, R: TableSchema<P>, P: An"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/IndexOperations.kt",
    "chars": 118,
    "preview": "package kotlinx.nosql\n\ninterface IndexOperations {\n    fun createIndex(schema: AbstractSchema, index: AbstractIndex)\n}"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/KeyValueDocumentSchemaOperations.kt",
    "chars": 427,
    "preview": "package kotlinx.nosql\n\nimport kotlinx.nosql.query.NoQuery\n\ninterface KeyValueDocumentSchemaOperations {\n  fun <T : Docum"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/KeyValueSchema.kt",
    "chars": 170,
    "preview": "package kotlinx.nosql\n\nabstract class KeyValueSchema(name: String): AbstractSchema(name) {\n\n}\n\nfun <T: KeyValueSchema, X"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/KeyValueSchemaOperations.kt",
    "chars": 559,
    "preview": "package kotlinx.nosql\n\ninterface KeyValueSchemaOperations {\n    operator fun <T : KeyValueSchema, C: Any> T.get(c: T.() "
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/ListColumn.kt",
    "chars": 224,
    "preview": "package kotlinx.nosql\n\nimport kotlin.reflect.KClass\n\nopen class ListColumn<C: Any, S : AbstractSchema> (name: String, va"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/LiteralExpression.kt",
    "chars": 85,
    "preview": "package kotlinx.nosql\n\nclass LiteralExpression(val value: Any?): Expression<Any?> {\n}"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/Nonuple.kt",
    "chars": 598,
    "preview": "package kotlinx.nosql\n\nclass Nonuple<A1, A2, A3, A4, A5, A6, A7, A8, A9>(val a1: A1, val a2: A2, val a3: A3, val a4: A4,"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/NotEqualQuery.kt",
    "chars": 107,
    "preview": "package kotlinx.nosql\n\nclass NotEqualQuery(val expr1: Expression<*>, val expr2: Expression<*>): Query() {\n}"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/NotMemberOfQuery.kt",
    "chars": 110,
    "preview": "package kotlinx.nosql\n\nclass NotMemberOfQuery(val expr1: Expression<*>, val expr2: Expression<*>): Query() {\n}"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/NullableColumn.kt",
    "chars": 303,
    "preview": "package kotlinx.nosql\n\nimport kotlin.reflect.KClass\n\nopen class NullableColumn<C: Any, S : AbstractSchema> (name: String"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/NullableIdColumn.kt",
    "chars": 357,
    "preview": "package kotlinx.nosql\n\nimport kotlin.reflect.KClass\n\nopen class NullableIdColumn<I: Any, S : TableSchema<I>, R: TableSch"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/Octuple.kt",
    "chars": 536,
    "preview": "package kotlinx.nosql\n\nclass Octuple<A1, A2, A3, A4, A5, A6, A7, A8>(val a1: A1, val a2: A2, val a3: A3, val a4: A4, val"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/PrimaryKey.kt",
    "chars": 345,
    "preview": "package kotlinx.nosql\n\nopen class PrimaryKey<I>(val name: String, val javaClass: Class<I>, val columnType: ColumnType) {"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/Quadruple.kt",
    "chars": 290,
    "preview": "package kotlinx.nosql\n\nclass Quadruple<A1, A2, A3, A4>(val a1: A1, val a2: A2, val a3: A3, val a4: A4) {\n    operator pu"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/Query.kt",
    "chars": 240,
    "preview": "package kotlinx.nosql\n\nimport kotlinx.nosql.query.OrQuery\n\nabstract class Query() {\n    infix fun and(op: Query): Query "
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/Quintuple.kt",
    "chars": 352,
    "preview": "package kotlinx.nosql\n\nclass Quintuple<A1, A2, A3, A4, A5>(val a1: A1, val a2: A2, val a3: A3, val a4: A4, val a5: A5) {"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/SchemaGenerationAction.kt",
    "chars": 423,
    "preview": "package kotlinx.nosql\n\nabstract class SchemaGenerationAction<S : Session>() {\n}\n\nclass Create<S: Session>(val onCreate: "
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/Septuple.kt",
    "chars": 475,
    "preview": "package kotlinx.nosql\n\nclass Septuple<A1, A2, A3, A4, A5, A6, A7>(val a1: A1, val a2: A2, val a3: A3, val a4: A4, val a5"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/Session.kt",
    "chars": 1218,
    "preview": "package kotlinx.nosql\n\nimport java.util.ArrayList\n\ninterface Session {\n    fun <T : AbstractSchema>T.create()\n\n    fun <"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/SetColumn.kt",
    "chars": 221,
    "preview": "package kotlinx.nosql\n\nimport kotlin.reflect.KClass\n\nopen class SetColumn<C: Any, S : AbstractSchema> (name: String, val"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/Sextuple.kt",
    "chars": 413,
    "preview": "package kotlinx.nosql\n\nclass Sextuple<A1, A2, A3, A4, A5, A6>(val a1: A1, val a2: A2, val a3: A3, val a4: A4, val a5: A5"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/TableSchema.kt",
    "chars": 1804,
    "preview": "package kotlinx.nosql\n\nabstract class TableSchema<I: Any>(tableName: String, primaryKey: AbstractColumn<I, out TableSche"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/TableSchemaOperations.kt",
    "chars": 172,
    "preview": "package kotlinx.nosql\n\ninterface TableSchemaOperations {\n    fun <T : TableSchema<P>, P: Any, V: Any> find(params: Table"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/TableSchemaProjectionQueryParams.kt",
    "chars": 294,
    "preview": "package kotlinx.nosql\n\nclass TableSchemaProjectionQueryParams<T : TableSchema<P>, P: Any, V>(val table: T, val projectio"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/TableSchemaProjectionQueryWrapper.kt",
    "chars": 989,
    "preview": "package kotlinx.nosql\n\nclass TableSchemaProjectionQueryWrapper<T : TableSchema<P>, P: Any, V: Any>(val params: TableSche"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/query/EqualQuery.kt",
    "chars": 170,
    "preview": "package kotlinx.nosql.query\n\nimport kotlinx.nosql.Expression\nimport kotlinx.nosql.Query\n\nclass EqualQuery(val expr1: Exp"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/query/GreateQuery.kt",
    "chars": 172,
    "preview": "package kotlinx.nosql.query\n\nimport kotlinx.nosql.Query\nimport kotlinx.nosql.Expression\n\nclass GreaterQuery(val expr1: E"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/query/GreaterEqualQuery.kt",
    "chars": 177,
    "preview": "package kotlinx.nosql.query\n\nimport kotlinx.nosql.Expression\nimport kotlinx.nosql.Query\n\nclass GreaterEqualQuery(val exp"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/query/IsNotNullQuery.kt",
    "chars": 163,
    "preview": "package kotlinx.nosql.query\n\nimport kotlinx.nosql.AbstractColumn\nimport kotlinx.nosql.Query\n\nclass IsNotNullQuery(val co"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/query/IsNullQuery.kt",
    "chars": 160,
    "preview": "package kotlinx.nosql.query\n\nimport kotlinx.nosql.AbstractColumn\nimport kotlinx.nosql.Query\n\nclass IsNullQuery(val colum"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/query/LessEqualQuery.kt",
    "chars": 175,
    "preview": "package kotlinx.nosql.query\n\nimport kotlinx.nosql.Expression\nimport kotlinx.nosql.Query\n\nclass LessEqualQuery(val expr1:"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/query/LessQuery.kt",
    "chars": 169,
    "preview": "package kotlinx.nosql.query\n\nimport kotlinx.nosql.Expression\nimport kotlinx.nosql.Query\n\nclass LessQuery(val expr1: Expr"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/query/MatchesQuery.kt",
    "chars": 173,
    "preview": "package kotlinx.nosql.query\n\nimport kotlinx.nosql.Expression\nimport kotlinx.nosql.Query\n\nclass MatchesQuery(val expr1: E"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/query/MemberOfQuery.kt",
    "chars": 174,
    "preview": "package kotlinx.nosql.query\n\nimport kotlinx.nosql.Query\nimport kotlinx.nosql.Expression\n\nclass MemberOfQuery(val expr1: "
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/query/NoQuery.kt",
    "chars": 85,
    "preview": "package kotlinx.nosql.query\n\nimport kotlinx.nosql.Query\n\nobject NoQuery : Query() {\n}"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/query/OrQuery.kt",
    "chars": 119,
    "preview": "package kotlinx.nosql.query\n\nimport kotlinx.nosql.Query\n\nclass OrQuery(val expr1: Query, val expr2: Query): Query() {\n}"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/query/TextQuery.kt",
    "chars": 105,
    "preview": "package kotlinx.nosql.query\n\nimport kotlinx.nosql.Query\n\nclass TextQuery(val search: String): Query() {\n}"
  },
  {
    "path": "src/main/kotlin/kotlinx/nosql/util/SchemaUtils.kt",
    "chars": 2313,
    "preview": "package kotlinx.nosql.util\n\nimport java.lang.reflect.Field\nimport java.util.ArrayList\nimport java.util.HashMap\nimport ko"
  }
]

// ... and 1 more files (download for full content)

About this extraction

This page contains the full source code of the cheptsov/kotlin-nosql GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 100 files (173.5 KB), approximately 44.7k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!