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 ================================================ ================================================ FILE: .idea/copyright/profiles_settings.xml ================================================ ================================================ FILE: .idea/encodings.xml ================================================ ================================================ FILE: .idea/gradle.xml ================================================ ================================================ FILE: .idea/libraries/Gradle__joda_time_joda_time_2_3.xml ================================================ ================================================ FILE: .idea/libraries/Gradle__junit_junit_4_11.xml ================================================ ================================================ FILE: .idea/libraries/Gradle__org_hamcrest_hamcrest_core_1_3.xml ================================================ ================================================ FILE: .idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_reflect_1_0_0.xml ================================================ ================================================ FILE: .idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_runtime_1_0_0.xml ================================================ ================================================ FILE: .idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_1_0_0.xml ================================================ ================================================ FILE: .idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_test_1_0_0.xml ================================================ ================================================ FILE: .idea/libraries/Gradle__org_jetbrains_spek_spek_0_1_195.xml ================================================ ================================================ FILE: .idea/libraries/Gradle__org_mongodb_mongo_java_driver_3_0_3.xml ================================================ ================================================ FILE: .idea/misc.xml ================================================ ================================================ FILE: .idea/modules.xml ================================================ ================================================ FILE: .idea/vcs.xml ================================================ ================================================ 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 org.jetbrains.kotlin kotlin-nosql-mongodb 0.1-SNAPSHOT kotlin-nosql http://repository.jetbrains.com/kotlin-nosql ``` 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("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("author", AuthorInfo::class) { val authorId = id("id", Authors) val name = string("name") } } class Comment(val discussionId: Id, val slug: String, val fullSlug: String, posted: DateTime, text: String, authorInfo: AuthorInfo) { val id: Id? = null } class AuthorInfo(val authorId: Id, 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(javaClass: Class, discriminator: String) : DocumentSchema("products", discriminator = Discriminator(string("type"), discriminator)) { val sku = string("sku") val title = string("title") val description = string("description") val asin = string("asin") val Shipping = ShippingColumn() val Pricing = PricingColumn() inner class ShippingColumn>() : Column("shipping", Shipping::class) { val weight = integer("weight") val dimensions = DimensionsColumn() } inner class DimensionsColumn>() : Column("dimensions", Dimensions::class) { val width = integer("width") val height = integer("height") val depth = integer("depth") } inner class PricingColumn>() : Column("pricing", Pricing::class) { val list = integer("list") val retail = integer("retail") val savings = integer("savings") val ptcSavings = integer("pct_savings") } } object Products : ProductSchema(Product::class, "") abstract class Product(val id: Id? = null, val sku: String, val title: String, val description: String, val asin: String, val shipping: Shipping, val pricing: Pricing) { val id: Id? = 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::class, discriminator = "Audio Album") { val details = DetailsColumn() class DetailsColumn() : Column("details", Details::class) { val title = string("title") val artistId = id("artistId", Artists) val genre = setOfString("genre") val tracks = TracksColumn() } class TracksColumn() : ListColumn("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, val genre: Set, val tracks: List) ``` #### 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 ================================================ ================================================ 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(name: String, valueClass: KClass, discriminator: Discriminator>? = null) : kotlinx.nosql.DocumentSchema(name, valueClass, string("_id"), discriminator) { fun ensureIndex(name: String = "", unique: Boolean = false, ascending: Array> = arrayOf>(), descending: Array> = arrayOf>(), text: Array> = arrayOf>()) { 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, initialization: SchemaGenerationAction = Validate()): MongoDB { val seeds: Array = 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 = arrayOf(ServerAddress()), val database: String = "test", val credentials: Array = arrayOf(), val options: MongoClientOptions = MongoClientOptions.Builder().build()!!, schemas: Array, action: SchemaGenerationAction = Validate()) : Database(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 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>, val descending: Array>, val text: Array>) : 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 incr(schema: KeyValueSchema, column: AbstractColumn, value: T): T { throw UnsupportedOperationException() } /*override fun incr(schema: AbstractSchema, column: AbstractColumn, 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.create() { db.createCollection(this.schemaName, BasicDBObject()) } override fun T.drop() { val collection = db.getCollection(this.schemaName)!! collection.drop() } override fun , P: Any, V: Any> T.insert(v: V): Id { 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(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? = 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 = 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, { 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)) { 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 , P: Any, C: Any> find(params: DocumentSchemaQueryParams): Iterator { if (params.query != null && !searchOperatorSupported && params.query!!.usesSearch()) return params.schema.runCommandText(params.query!!) else return object : Iterator { 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 , P: Any, C: Any> T.runCommandText(op: Query): Iterator { 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() for (doc in result.get("results") as BasicDBList) { objects.add(getObject((doc as DBObject).get("obj") as DBObject, schema)) } return objects.iterator() } override fun , P: Any, V: Any> find(params: TableSchemaProjectionQueryParams): Iterator { return object:Iterator { 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() 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 , 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) val valueFields = getAllFieldsMap(valueInstance.javaClass as Class) 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(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>(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) } 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) 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>(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>(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 insert(columns: Array, Any?>>) { throw UnsupportedOperationException() } override fun 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, *>>, 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 addAll(schema: AbstractSchema, column: AbstractColumn, *, *>, values: Collection, 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 removeAll(schema: AbstractSchema, column: AbstractColumn, *, *>, values: Collection, 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 removeAll(schema: AbstractSchema, column: AbstractColumn, *, *>, 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>(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>(it.toString()) }.toSet() } else if (column.columnType.id && column.columnType.list) { (columnObject as BasicDBList).map { Id>(it.toString()) } } else if (column.columnType.custom && column.columnType.list) { (columnObject as BasicDBList).map { getObject(it as DBObject, column as ListColumn) } } else if (column.columnType.custom && column.columnType.set) { (columnObject as BasicDBList).map { getObject(it as DBObject, column as ListColumn) }.toSet() } else if (column.columnType.custom) { getObject(columnObject as DBObject, column) } else { UnsupportedOperationException() } } private fun parse(doc: DBObject, path: Array, 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 , P: Any, C: Any> T.find(query: T.() -> Query): DocumentSchemaQueryWrapper { val params = DocumentSchemaQueryParams(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>(klass: KClass, discriminator: String) : DocumentSchema("products", klass, discriminator = Discriminator(string("type"), discriminator)) { val sku = string("sku") val title = string("title") val description = string("description") val asin = string("asin") val available = boolean("available") val createdAtDate = date("createdAtDate") val nullableBooleanNoValue = nullableBoolean("nullableBooleanNoValue") val nullableBooleanWithValue = nullableBoolean("nullableBooleanWithValue") val nullableDateNoValue = nullableDate("nullableDateNoValue") val nullableDateWithValue = nullableDate("nullableDateWithValue") val cost = double("cost") val nullableDoubleNoValue = nullableDouble("nullableDoubleNoValue") val nullableDoubleWithValue = nullableDouble("nullableDoubleWithValue") val setOfStrings = setOfString("setOfStrings") val shipping = ShippingColumn() val pricing = PricingColumn() inner class ShippingColumn>() : Column("shipping", Shipping::class) { val weight = integer("weight") val dimensions = DimensionsColumn() } inner class DimensionsColumn>() : Column("dimensions", Dimensions::class) { val width = integer("width") val height = integer("height") val depth = integer("depth") } inner class PricingColumn>() : Column("pricing", Pricing::class) { val list = integer("list") val retail = integer("retail") val savings = integer("savings") val pctSavings = integer("pct_savings") } init { ensureIndex(text = arrayOf(title, description)) ensureIndex(name = "asinIndex", unique = true, ascending = arrayOf(asin)) } } object Artists : DocumentSchema("artists", Artist::class) { val name = string("name") } object Products : ProductSchema(Product::class, "") { } object Albums : ProductSchema(Album::class, discriminator = "Audio Album") { val details = DetailsColumn() class DetailsColumn() : Column("details", Details::class) { val title: AbstractColumn = string("title") val artistId = id("artistId", Artists) val artistIds = setOfId("artistIds", Artists) val genre = setOfString("genre") val tracks = TracksColumn() } class TracksColumn() : ListColumn("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, val shipping: Shipping, val pricing: Pricing) { val id: Id? = 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, 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? = null } class Details(val title: String, val artistId: Id, val artistIds: Set>, val genre: Set, val tracks: List) { } class Track(val title: String, val duration: Int) { } init { given("a polymorhpic schema") { var artistId: Id? = null var artistId2: Id? = null var artistId3: Id? = null var albumId: Id? = null val db = MongoDB(schemas = arrayOf(Artists, Products, Albums), action = CreateDrop(onCreate = { val arId: Id = Artists.insert(Artist(name = "John Coltrane")) val arId2: Id = Artists.insert(Artist(name = "Andrey Cheptsov")) val arId3: Id = 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) { 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)) 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 = 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) { MongoDBSpek() } ================================================ FILE: kotlin-nosql.iml ================================================ ================================================ 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(val name: String, val valueClass: KClass, val columnType: ColumnType) : ColumnQueryWrapper(), Expression { 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 plus(c: AbstractColumn): ColumnPair { return ColumnPair(this, c) as ColumnPair } } /* fun AbstractColumn.get(): C { throw UnsupportedOperationException() } */ fun AbstractColumn.update(value: C): Int { val wrapper = TableSchemaProjectionQueryWrapper.get() return Session.current().update(wrapper.params.table, arrayOf(wrapper.params.projection.get(0) to value), wrapper.params.query!!) } fun > AbstractColumn.addAll(values: S): Int { val wrapper = TableSchemaProjectionQueryWrapper.get() return Session.current().addAll(wrapper.params.table, wrapper.params.projection.get(0) as AbstractColumn, out AbstractSchema, out Any>, values, wrapper.params.query!!) } fun > AbstractColumn.add(value: C): Int { val wrapper = TableSchemaProjectionQueryWrapper.get() val values: Collection = if (wrapper.params.projection.get(0).columnType.list) listOf(value) else setOf(value) return Session.current().addAll(wrapper.params.table, wrapper.params.projection.get(0) as AbstractColumn, out AbstractSchema, out Any>, values, wrapper.params.query!!) } fun > AbstractColumn.removeAll(values: S): Int { val wrapper = TableSchemaProjectionQueryWrapper.get() return Session.current().removeAll(wrapper.params.table, wrapper.params.projection.get(0) as AbstractColumn, out AbstractSchema, out Any>, values, wrapper.params.query!!) } fun AbstractColumn, *, *>.remove(value: C): Int { val wrapper = TableSchemaProjectionQueryWrapper.get() val values: Collection = if (wrapper.params.projection.get(0).columnType.list) listOf(value) else setOf(value) return Session.current().removeAll(wrapper.params.table, wrapper.params.projection.get(0) as AbstractColumn, out AbstractSchema, out Any>, values, wrapper.params.query!!) } fun , *, *>> C.remove(removeOp: C.() -> Query): Int { val wrapper = TableSchemaProjectionQueryWrapper.get() val removeOpValue = with (wrapper.params.projection.get(0)) { removeOp() } return Session.current().removeAll(wrapper.params.table, wrapper.params.projection.get(0) as AbstractColumn, out AbstractSchema, out Any>, removeOpValue, wrapper.params.query!!) } fun AbstractColumn.isNull(): Query { return IsNullQuery(this) } fun AbstractColumn.notNull(): Query { return IsNotNullQuery(this) } fun AbstractColumn.equal(other: C): Query { return EqualQuery(this, LiteralExpression(other)) } fun AbstractColumn.notEqual(other: C): Query { return NotEqualQuery(this, LiteralExpression(other)) } fun AbstractColumn.memberOf(other: Iterable): Query { return MemberOfQuery(this, LiteralExpression(other)) } fun AbstractColumn.memberOf(other: Array): Query { return MemberOfQuery(this, LiteralExpression(other)) } // TODO TODO TODO: Expression should be typed fun AbstractColumn.memberOf(other: Expression>): Query { return MemberOfQuery(this, LiteralExpression(other)) } fun AbstractColumn.notMemberOf(other: Iterable): Query { return NotMemberOfQuery(this, LiteralExpression(other)) } fun AbstractColumn.notMemberOf(other: Array): Query { return NotMemberOfQuery(this, LiteralExpression(other)) } fun AbstractColumn.notMemberOf(other: Expression>): Query { return NotMemberOfQuery(this, LiteralExpression(other)) } fun AbstractColumn.equal(other: Expression): Query { return EqualQuery(this, other) } fun AbstractColumn.notEqual(other: Expression): Query { return NotEqualQuery(this, other) } fun AbstractColumn.gt(other: Expression): Query { return GreaterQuery(this, other) } fun AbstractColumn.gt(other: Int): Query { return GreaterQuery(this, LiteralExpression(other)) } fun AbstractColumn.ge(other: Expression): Query { return GreaterEqualQuery(this, other) } fun AbstractColumn.ge(other: Int): Query { return GreaterEqualQuery(this, LiteralExpression(other)) } fun AbstractColumn.le(other: Expression): Query { return LessEqualQuery(this, other) } fun AbstractColumn.le(other: Int): Query { return LessEqualQuery(this, LiteralExpression(other)) } fun AbstractColumn.lt(other: Expression): Query { return LessQuery(this, other) } fun AbstractColumn.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() // TODO TODO TODO // val columns = ArrayList>() // TODO TODO TODO companion object { val threadLocale = ThreadLocal() fun 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 string(name: String): AbstractColumn = AbstractColumn(name, String::class, ColumnType.STRING) fun S.string(name: String): AbstractColumn = AbstractColumn(name, String::class, ColumnType.STRING) fun boolean(name: String): AbstractColumn = AbstractColumn(name, Boolean::class, ColumnType.BOOLEAN) fun S.boolean(name: String): AbstractColumn = AbstractColumn(name, Boolean::class, ColumnType.BOOLEAN) fun date(name: String): AbstractColumn = AbstractColumn(name, LocalDate::class, ColumnType.DATE) fun S.date(name: String): AbstractColumn = AbstractColumn(name, LocalDate::class, ColumnType.DATE) fun time(name: String): AbstractColumn = AbstractColumn(name, LocalTime::class, ColumnType.TIME) fun S.time(name: String): AbstractColumn = AbstractColumn(name, LocalTime::class, ColumnType.TIME) fun dateTime(name: String): AbstractColumn = AbstractColumn(name, DateTime::class, ColumnType.DATE_TIME) fun S.dateTime(name: String): AbstractColumn = AbstractColumn(name, DateTime::class, ColumnType.DATE_TIME) fun double(name: String): AbstractColumn = AbstractColumn(name, Double::class, ColumnType.DOUBLE) fun S.double(name: String): AbstractColumn = AbstractColumn(name, Double::class, ColumnType.DOUBLE) fun integer(name: String): AbstractColumn = AbstractColumn(name, Int::class, ColumnType.INTEGER) fun S.integer(name: String): AbstractColumn = AbstractColumn(name, Int::class, ColumnType.INTEGER) fun float(name: String): AbstractColumn = AbstractColumn(name, Float::class, ColumnType.FLOAT) fun S.float(name: String): AbstractColumn = AbstractColumn(name, Float::class, ColumnType.FLOAT) fun long(name: String): AbstractColumn = AbstractColumn(name, Long::class, ColumnType.LONG) fun S.long(name: String): AbstractColumn = AbstractColumn(name, Long::class, ColumnType.LONG) fun short(name: String): AbstractColumn = AbstractColumn(name, Short::class, ColumnType.SHORT) fun S.short(name: String): AbstractColumn = AbstractColumn(name, Short::class, ColumnType.SHORT) fun byte(name: String): AbstractColumn = AbstractColumn(name, Byte::class, ColumnType.BYTE) fun S.byte(name: String): AbstractColumn = AbstractColumn(name, Byte::class, ColumnType.BYTE) fun nullableString(name: String): NullableColumn = NullableColumn(name, String::class, ColumnType.STRING) fun S.nullableString(name: String): NullableColumn = NullableColumn(name, String::class, ColumnType.STRING) fun nullableInteger(name: String): NullableColumn = NullableColumn(name, Int::class, ColumnType.INTEGER) fun S.nullableInteger(name: String): NullableColumn = NullableColumn(name, Int::class, ColumnType.INTEGER) fun nullableBoolean(name: String): NullableColumn = NullableColumn(name, Boolean::class, ColumnType.BOOLEAN) fun S.nullableBoolean(name: String): NullableColumn = NullableColumn(name, Boolean::class, ColumnType.BOOLEAN) fun nullableDate(name: String): NullableColumn = NullableColumn(name, LocalDate::class, ColumnType.DATE) fun S.nullableDate(name: String): NullableColumn = NullableColumn(name, LocalDate::class, ColumnType.DATE) fun nullableTime(name: String): NullableColumn = NullableColumn(name, LocalTime::class, ColumnType.TIME) fun S.nullableTime(name: String): NullableColumn = NullableColumn(name, LocalTime::class, ColumnType.TIME) fun nullableDateTime(name: String): NullableColumn = NullableColumn(name, DateTime::class, ColumnType.DATE_TIME) fun S.nullableDateTime(name: String): NullableColumn = NullableColumn(name, DateTime::class, ColumnType.DATE_TIME) fun nullableDouble(name: String): NullableColumn = NullableColumn(name, Double::class, ColumnType.DOUBLE) fun S.nullableDouble(name: String): NullableColumn = NullableColumn(name, Double::class, ColumnType.DOUBLE) fun nullableFloat(name: String): NullableColumn = NullableColumn(name, Float::class, ColumnType.FLOAT) fun S.nullableFloat(name: String): NullableColumn = NullableColumn(name, Float::class, ColumnType.FLOAT) fun nullableLong(name: String): NullableColumn = NullableColumn(name, Long::class, ColumnType.LONG) fun S.nullableLong(name: String): NullableColumn = NullableColumn(name, Long::class, ColumnType.LONG) fun nullableShort(name: String): NullableColumn = NullableColumn(name, Short::class, ColumnType.SHORT) fun S.nullableShort(name: String): NullableColumn = NullableColumn(name, Short::class, ColumnType.SHORT) fun nullableByte(name: String): NullableColumn = NullableColumn(name, Byte::class, ColumnType.BYTE) fun S.nullableByte(name: String): NullableColumn = NullableColumn(name, Byte::class, ColumnType.BYTE) fun setOfString(name: String): AbstractColumn, S, String> = AbstractColumn, S, String>(name, String::class, ColumnType.STRING_SET) fun S.setOfString(name: String): AbstractColumn, S, String> = AbstractColumn, S, String>(name, String::class, ColumnType.STRING_SET) fun setOfInteger(name: String): AbstractColumn, S, Int> = AbstractColumn, S, Int>(name, Int::class, ColumnType.INTEGER_SET) fun S.setOfInteger(name: String): AbstractColumn, S, Int> = AbstractColumn, S, Int>(name, Int::class, ColumnType.INTEGER_SET) fun listOfString(name: String): AbstractColumn, S, String> = AbstractColumn, S, String>(name, String::class, ColumnType.STRING_LIST) fun S.listOfString(name: String): AbstractColumn, S, String> = AbstractColumn, S, String>(name, String::class, ColumnType.STRING_LIST) fun listOfInteger(name: String): AbstractColumn, S, Int> = AbstractColumn, S, Int>(name, Int::class, ColumnType.INTEGER_LIST) fun S.listOfInteger(name: String): AbstractColumn, S, Int> = AbstractColumn, 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(name: String, valueClass: KClass, columnType: ColumnType = ColumnType.CUSTOM_CLASS) : AbstractColumn(name, valueClass, columnType) { } ================================================ FILE: src/main/kotlin/kotlinx/nosql/ColumnDecuple.kt ================================================ package kotlinx.nosql class ColumnDecuple(val a: AbstractColumn, val b: AbstractColumn, val c: AbstractColumn, val d: AbstractColumn, val e: AbstractColumn, val f: AbstractColumn, val g: AbstractColumn, val h: AbstractColumn, val i: AbstractColumn, val j: AbstractColumn): ColumnQueryWrapper>() { } ================================================ FILE: src/main/kotlin/kotlinx/nosql/ColumnNonuple.kt ================================================ package kotlinx.nosql class ColumnNonuple(val a: AbstractColumn, val b: AbstractColumn, val c: AbstractColumn, val d: AbstractColumn, val e: AbstractColumn, val f: AbstractColumn, val g: AbstractColumn, val h: AbstractColumn, val j: AbstractColumn): ColumnQueryWrapper>() { operator fun plus(k: AbstractColumn): ColumnDecuple { 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(val a: AbstractColumn, val b: AbstractColumn, val c: AbstractColumn, val d: AbstractColumn, val e: AbstractColumn, val f: AbstractColumn, val g: AbstractColumn, val h: AbstractColumn): ColumnQueryWrapper>() { operator fun plus(j: AbstractColumn): ColumnNonuple { return ColumnNonuple(a, b, c, d, e, f, g, h, j) } } ================================================ FILE: src/main/kotlin/kotlinx/nosql/ColumnPair.kt ================================================ package kotlinx.nosql class ColumnPair(val a: AbstractColumn, val b: AbstractColumn): ColumnQueryWrapper>() { operator fun plus(c: AbstractColumn): ColumnTriple { return ColumnTriple(a, b, c) } } /* fun ColumnPair.get(): Pair { throw UnsupportedOperationException() }*/ ================================================ FILE: src/main/kotlin/kotlinx/nosql/ColumnQuadruple.kt ================================================ package kotlinx.nosql class ColumnQuadruple(val a: AbstractColumn, val b: AbstractColumn, val c: AbstractColumn, val d: AbstractColumn): ColumnQueryWrapper>() { operator fun plus(e: AbstractColumn): ColumnQuintuple { return ColumnQuintuple(a, b, c, d, e) } fun insert(statement: () -> Quadruple) { val tt = statement() Session.current().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 : Iterable { override fun iterator(): Iterator { val wrapper = TableSchemaProjectionQueryWrapper.get() return wrapper.iterator() as Iterator } } fun ColumnQueryWrapper>.update(a: A, b: B): Int { val wrapper = TableSchemaProjectionQueryWrapper.get() return Session.current().update(wrapper.params.table, arrayOf(wrapper.params.projection.get(0) to a, wrapper.params.projection.get(1) to b), wrapper.params.query!!) } fun ColumnQueryWrapper>.update(a: A, b: B, c: C): Int { val wrapper = TableSchemaProjectionQueryWrapper.get() return Session.current().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 ColumnQueryWrapper>.update(a: A, b: B, c: C, d: D): Int { val wrapper = TableSchemaProjectionQueryWrapper.get() return Session.current().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 ColumnQueryWrapper>.update(a: A, b: B, c: C, d: D, e: E): Int { val wrapper = TableSchemaProjectionQueryWrapper.get() return Session.current().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 ColumnQueryWrapper>.update(a: A, b: B, c: C, d: D, e: E, f: F): Int { val wrapper = TableSchemaProjectionQueryWrapper.get() return Session.current().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 ColumnQueryWrapper>.update(a: A, b: B, c: C, d: D, e: E, f: F, g: G): Int { val wrapper = TableSchemaProjectionQueryWrapper.get() return Session.current().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 ColumnQueryWrapper>.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().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 ColumnQueryWrapper>.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().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 ColumnQueryWrapper>.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().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(val a: AbstractColumn, val b: AbstractColumn, val c: AbstractColumn, val d: AbstractColumn, val e: AbstractColumn): ColumnQueryWrapper>() { operator fun plus(f: AbstractColumn): ColumnSextuple { return ColumnSextuple(a, b, c, d, e, f) } } ================================================ FILE: src/main/kotlin/kotlinx/nosql/ColumnSeptuple.kt ================================================ package kotlinx.nosql class ColumnSeptuple(val a: AbstractColumn, val b: AbstractColumn, val c: AbstractColumn, val d: AbstractColumn, val e: AbstractColumn, val f: AbstractColumn, val g: AbstractColumn): ColumnQueryWrapper>() { operator fun plus(h: AbstractColumn): ColumnOctuple { return ColumnOctuple(a, b, c, d, e, f, g, h) } } ================================================ FILE: src/main/kotlin/kotlinx/nosql/ColumnSextuple.kt ================================================ package kotlinx.nosql class ColumnSextuple(val a: AbstractColumn, val b: AbstractColumn, val c: AbstractColumn, val d: AbstractColumn, val e: AbstractColumn, val f: AbstractColumn): ColumnQueryWrapper>() { operator fun plus(g: AbstractColumn): ColumnSeptuple { return ColumnSeptuple(a, b, c, d, e, f, g) } } ================================================ FILE: src/main/kotlin/kotlinx/nosql/ColumnTriple.kt ================================================ package kotlinx.nosql class ColumnTriple(val a: AbstractColumn, val b: AbstractColumn, val c: AbstractColumn): ColumnQueryWrapper>() { operator fun plus(d: AbstractColumn): ColumnQuadruple { 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(val schemas: Array, val action: SchemaGenerationAction) { abstract fun 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, String>() } } val AbstractColumn<*, *, *>.fullName: String get() { return Database.fullColumnNames.get(this)!! } ================================================ FILE: src/main/kotlin/kotlinx/nosql/Decuple.kt ================================================ package kotlinx.nosql class Decuple(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>(val column: AbstractColumn, 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(name: String, val valueClass: KClass, primaryKey: AbstractColumn, I>, val discriminator: Discriminator>? = null) : TableSchema(name, primaryKey) { init { if (discriminator != null) { val emptyDiscriminators = CopyOnWriteArrayList>() 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>>() val discriminatorClasses = ConcurrentHashMap, KClass<*>>() val discriminatorSchemaClasses = ConcurrentHashMap, Class<*>>() val discriminatorSchemas = ConcurrentHashMap, AbstractSchema>() } } ================================================ FILE: src/main/kotlin/kotlinx/nosql/DocumentSchemaIdQueryWrapper.kt ================================================ package kotlinx.nosql import java.util.ArrayList import kotlin.collections.single class DocumentSchemaIdQueryWrapper, P: Any, C: Any>(val schema: T, val id: Id): DocumentSchemaQueryWrapper(DocumentSchemaQueryParams(schema, schema.id.equal(id)) ) { fun get(): C { return single() } override fun iterator(): Iterator { val list = ArrayList() val value = with (Session.current()) { 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 , P: Any, V: Any> T.insert(v: V): Id fun , P: Any, C: Any> T.find(query: T.() -> Query = { NoQuery }): DocumentSchemaQueryWrapper // TODO: Implement find(id) -> Wrapper /*fun , P, C> T.find(id: Id): C? { val w = find { this.id.equal(id) } return if (w.count() > 0) w.single() else null }*/ fun , P: Any, C: Any> find(params: DocumentSchemaQueryParams): Iterator } ================================================ FILE: src/main/kotlin/kotlinx/nosql/DocumentSchemaQueryParams.kt ================================================ package kotlinx.nosql class DocumentSchemaQueryParams, 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, P: Any, C: Any>(val params: kotlinx.nosql.DocumentSchemaQueryParams): Iterable { override fun iterator(): Iterator { return Session.current().find(params) } fun skip(num: Int): kotlinx.nosql.DocumentSchemaQueryWrapper { params.skip = num return this } fun take(num: Int): DocumentSchemaQueryWrapper { params.take = num return this } fun remove(): Int { return Session.current().delete(params.schema, params.query!!) } fun projection(x: T.() -> X): X { val xx = params.schema.x() val projectionParams = kotlinx.nosql.TableSchemaProjectionQueryParams, Any, Any>(params.schema as TableSchema, when (xx) { is AbstractColumn<*, *, *> -> listOf(xx) as List> 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 { } ================================================ FILE: src/main/kotlin/kotlinx/nosql/Id.kt ================================================ package kotlinx.nosql class Id>(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, R: TableSchema

, P: Any> (name: String, val refSchema: R) : AbstractColumn>, S, Id>(name, Id::class as KClass>, ColumnType.ID_LIST) { } ================================================ FILE: src/main/kotlin/kotlinx/nosql/IdSetColumn.kt ================================================ package kotlinx.nosql import kotlin.reflect.KClass open class IdSetColumn, R: TableSchema

, P: Any> (name: String, val refSchema: R) : AbstractColumn>, S, Id>(name, Id::class as KClass>, 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 , P: Any, V: Any> T.insert(v: V): Id operator fun , P: Any, C: Any> T.get(id: Id): C? fun , P: Any, C: Any> T.find(id: Id): DocumentSchemaIdQueryWrapper { return DocumentSchemaIdQueryWrapper(this, id) } } ================================================ FILE: src/main/kotlin/kotlinx/nosql/KeyValueSchema.kt ================================================ package kotlinx.nosql abstract class KeyValueSchema(name: String): AbstractSchema(name) { } fun T.projection(x: T.() -> X): X { return x() } ================================================ FILE: src/main/kotlin/kotlinx/nosql/KeyValueSchemaOperations.kt ================================================ package kotlinx.nosql interface KeyValueSchemaOperations { operator fun T.get(c: T.() -> AbstractColumn): C? operator fun T.set(c: () -> AbstractColumn, v: C) fun AbstractColumn.incr(value: S = 1 as S): X { val value = Session.current().incr(schema, this, value) if (columnType.id) { return Id>(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 (name: String, valueClass: KClass) : AbstractColumn, 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 { } ================================================ FILE: src/main/kotlin/kotlinx/nosql/Nonuple.kt ================================================ package kotlinx.nosql class Nonuple(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 (name: String, valueClass: KClass, columnType: ColumnType) : AbstractColumn(name, valueClass, columnType), AbstractNullableColumn { } ================================================ FILE: src/main/kotlin/kotlinx/nosql/NullableIdColumn.kt ================================================ package kotlinx.nosql import kotlin.reflect.KClass open class NullableIdColumn, R: TableSchema> (name: String, valueClass: KClass, columnType: ColumnType) : AbstractColumn?, S, I>(name, valueClass, columnType), AbstractNullableColumn { } ================================================ FILE: src/main/kotlin/kotlinx/nosql/Octuple.kt ================================================ package kotlinx.nosql class Octuple(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(val name: String, val javaClass: Class, val columnType: ColumnType) { companion object { fun string(name: String) = PrimaryKey(name, String::class.java, ColumnType.STRING) fun integer(name: String) = PrimaryKey(name, Int::class.java, ColumnType.INTEGER) } } ================================================ FILE: src/main/kotlin/kotlinx/nosql/Quadruple.kt ================================================ package kotlinx.nosql class Quadruple(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(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() { } class Create(val onCreate: S.() -> Unit = { }) : SchemaGenerationAction() class CreateDrop(val onCreate: S.() -> Unit = { }, onDrop: S.() -> Unit = { }) : SchemaGenerationAction() class Update() : SchemaGenerationAction() class Validate() : SchemaGenerationAction() ================================================ FILE: src/main/kotlin/kotlinx/nosql/Septuple.kt ================================================ package kotlinx.nosql class Septuple(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.create() fun T.drop() // TODO: Refactor fun insert(columns: Array, *>>) // TODO: Refactor fun delete(table: T, op: Query): Int fun update(schema: AbstractSchema, columnValues: Array, *>>, op: Query): Int fun addAll(schema: AbstractSchema, column: AbstractColumn, *, *>, values: Collection, op: Query): Int fun incr(schema: KeyValueSchema, column: AbstractColumn<*, *, T>, value: T): T //internal fun incr(schema: AbstractSchema, column: AbstractColumn<*, *, T>, value: T, op: Query): T fun removeAll(schema: AbstractSchema, column: AbstractColumn, *, *>, values: Collection, op: Query): Int fun removeAll(schema: AbstractSchema, column: AbstractColumn, *, *>, removeOp: Query, op: Query): Int companion object { val threadLocale = ThreadLocal() fun 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 (name: String, valueClass: KClass) : AbstractColumn, S, C>(name, valueClass, ColumnType.CUSTOM_CLASS_SET) { } ================================================ FILE: src/main/kotlin/kotlinx/nosql/Sextuple.kt ================================================ package kotlinx.nosql class Sextuple(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(tableName: String, primaryKey: AbstractColumn, I>): AbstractTableSchema(tableName) { val pk = AbstractColumn>, TableSchema, I>(primaryKey.name, primaryKey.valueClass, ColumnType.PRIMARY_ID) } // Extension functions val > T.id: AbstractColumn, T, C> get () { return pk as AbstractColumn, T, C> } fun , P: Any> id(name: String, refSchema: R): AbstractColumn, S, P> = AbstractColumn(name, refSchema.id.valueClass, ColumnType.FOREIGN_ID) fun , P: Any> S.id(name: String, refSchema: R): AbstractColumn, S, P> = AbstractColumn(name, refSchema.id.valueClass, ColumnType.FOREIGN_ID) fun , R: TableSchema

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

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

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

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

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

, P: Any> S.nullableId(name: String, refSchema: R): NullableIdColumn = NullableIdColumn(name, refSchema.id.valueClass, ColumnType.FOREIGN_ID) ================================================ FILE: src/main/kotlin/kotlinx/nosql/TableSchemaOperations.kt ================================================ package kotlinx.nosql interface TableSchemaOperations { fun , P: Any, V: Any> find(params: TableSchemaProjectionQueryParams): Iterator } ================================================ FILE: src/main/kotlin/kotlinx/nosql/TableSchemaProjectionQueryParams.kt ================================================ package kotlinx.nosql class TableSchemaProjectionQueryParams, P: Any, V>(val table: T, val projection: List>, 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, P: Any, V: Any>(val params: TableSchemaProjectionQueryParams): Iterable { override fun iterator(): Iterator { return Session.current().find(params) } fun skip(num: Int): TableSchemaProjectionQueryWrapper { params.skip = num return this } fun take(num: Int): TableSchemaProjectionQueryWrapper { params.take = num return this } companion object { val threadLocal = ThreadLocal, *, *>>() fun get(): TableSchemaProjectionQueryWrapper, *, *> { return threadLocal.get()!! as TableSchemaProjectionQueryWrapper, *, *> } fun set(value: TableSchemaProjectionQueryWrapper, *, *>) { 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, condition: (Field) -> Boolean = { f -> true }, fields: MutableList = ArrayList()): MutableList { 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, condition: (Field) -> Boolean = { f -> true }, fields: MutableMap = HashMap()): MutableMap { 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): Any { val constructor = clazz.constructors!![0] val constructorParamTypes = constructor.parameterTypes!! val constructorParamValues = Array(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() "java.util.Set" -> setOf() else -> newInstance(constructorParamTypes[index]) } }) return constructor.newInstance(*constructorParamValues)!! }