, P: Any, V: Any> T.insert(v: V): Id {
73 | val collection = db.getCollection(this.schemaName)!!
74 | val doc = getDBObject(v, this)
75 | if (discriminator != null) {
76 | var dominatorValue: Any? = null
77 | for (entry in kotlinx.nosql.DocumentSchema.discriminatorClasses.entries) {
78 | if (entry.value.java.equals(v.javaClass)) {
79 | dominatorValue = entry.key.value
80 | }
81 | }
82 | doc.set(this.discriminator!!.column.name, dominatorValue!!)
83 | }
84 | collection.insert(doc)
85 | return Id
(doc.get("_id").toString() as P)
86 | }
87 |
88 | private fun getDBObject(o: Any, schema: Any): BasicDBObject {
89 | val doc = BasicDBObject()
90 | val javaClass = o.javaClass
91 | val fields = getAllFields(javaClass)
92 | var sc: Class? = null
93 | var s: AbstractSchema? = null
94 | if (schema is kotlinx.nosql.DocumentSchema<*, *> && schema.discriminator != null) {
95 | for (entry in kotlinx.nosql.DocumentSchema.discriminatorClasses.entries) {
96 | if (entry.value.java.equals(o.javaClass)) {
97 | sc = kotlinx.nosql.DocumentSchema.discriminatorSchemaClasses.get(entry.key)!!
98 | s = kotlinx.nosql.DocumentSchema.discriminatorSchemas.get(entry.key)!!
99 | }
100 | }
101 | }
102 | val schemaClass: Class = if (schema is kotlinx.nosql.DocumentSchema<*, *> && schema.discriminator != null) sc!! else schema.javaClass
103 | val objectSchema: Any = if (schema is kotlinx.nosql.DocumentSchema<*, *> && schema.discriminator != null) s!! else schema
104 | val schemaFields = getAllFieldsMap(schemaClass as Class, { f -> f.isColumn })
105 | for (field in fields) {
106 | val schemaField = schemaFields.get(field.getName()!!.toLowerCase())
107 | if (schemaField != null && schemaField.isColumn) {
108 | field.setAccessible(true)
109 | schemaField.setAccessible(true)
110 | val column = schemaField.asColumn(objectSchema)
111 | val value = field.get(o)
112 | if (value != null) {
113 | if (column.columnType.primitive) {
114 | doc.append(column.name, when (value) {
115 | is DateTime, is LocalDate, is LocalTime -> value.toString()
116 | is Id<*, *> -> ObjectId(value.value.toString())
117 | else -> value
118 | })
119 | } else if (column.columnType.iterable) {
120 | val list = BasicDBList()
121 | for (v in (value as Iterable)) {
122 | list.add(if (column.columnType.custom) getDBObject(v, column) else
123 | (if (v is Id<*, *>) ObjectId(v.toString()) else v))
124 | }
125 | doc.append(column.name, list)
126 | } else doc.append(column.name, getDBObject(value, column))
127 | }
128 | }
129 | }
130 | return doc
131 | }
132 |
133 | override fun , P: Any, C: Any> find(params: DocumentSchemaQueryParams): Iterator {
134 | if (params.query != null && !searchOperatorSupported && params.query!!.usesSearch())
135 | return params.schema.runCommandText(params.query!!)
136 | else
137 | return object : Iterator {
138 | var cursor: DBCursor? = null
139 | var pos = 0
140 | override fun next(): C {
141 | if (cursor == null) {
142 | val collection = db.getCollection(params.schema.schemaName)
143 | val query = if (params.query != null) getQuery(params.query!!) else BasicDBObject()
144 | cursor = collection!!.find(query)!!
145 | if (params.skip != null) {
146 | cursor!!.skip(params.skip!!)
147 | }
148 | }
149 | val value = getObject(cursor!!.next(), params.schema) as C
150 | pos++
151 | if (!cursor!!.hasNext() || (params.take != null && pos == params.take!!)) {
152 | cursor!!.close()
153 | pos = -1
154 | }
155 | return value
156 | }
157 | override fun hasNext(): Boolean {
158 | if (cursor == null) {
159 | val collection = db.getCollection(params.schema.schemaName)
160 | val query = if (params.query != null) getQuery(params.query!!) else BasicDBObject()
161 | cursor = collection!!.find(query)!!
162 | if (params.skip != null) {
163 | cursor!!.skip(params.skip!!)
164 | }
165 | }
166 | return pos != -1 && cursor!!.hasNext() && (params.take == null || pos < params.take!!)
167 | }
168 | }
169 | }
170 |
171 | private fun , P: Any, C: Any> T.runCommandText(op: Query): Iterator {
172 | val searchCmd = BasicDBObject()
173 | searchCmd.append("text", this.schemaName)
174 | // TODO: Only supports text(...) and other condition
175 | searchCmd.append("search", when (op) {
176 | is TextQuery -> op.search
177 | is AndQuery -> if (op.expr1 is TextQuery) (op.expr1 as TextQuery).search else throw UnsupportedOperationException()
178 | else -> throw UnsupportedOperationException()
179 | })
180 | val schema = this
181 | if (op is AndQuery) {
182 | searchCmd.append("filter", getQuery(op.expr2))
183 | }
184 | val result = db.command(searchCmd)!!
185 |
186 | val objects = ArrayList()
187 | for (doc in result.get("results") as BasicDBList) {
188 | objects.add(getObject((doc as DBObject).get("obj") as DBObject, schema))
189 | }
190 |
191 | return objects.iterator()
192 | }
193 |
194 | override fun , P: Any, V: Any> find(params: TableSchemaProjectionQueryParams): Iterator {
195 | return object:Iterator {
196 | var cursor: DBCursor? = null
197 | var pos = 0
198 | override fun next(): V {
199 | if (cursor == null) {
200 | val collection = db.getCollection(params.table.schemaName)
201 | val fields = BasicDBObject()
202 | params.projection.forEach {
203 | fields.append(it.fullName, "1")
204 | }
205 | val query = if (params.query != null) getQuery(params.query!!) else BasicDBObject()
206 | cursor = collection!!.find(query, fields)!!
207 | if (params.skip != null) {
208 | cursor!!.skip(params.skip!!)
209 | }
210 | }
211 | val doc = cursor!!.next()
212 | val values = ArrayList()
213 | params.projection.forEach {
214 | values.add(getColumnObject(doc, it))
215 | }
216 | val value = when (values.size) {
217 | 1 -> values[0] as V
218 | 2 -> Pair(values[0], values[1]) as V
219 | 3 -> Triple(values[0], values[1], values[2]) as V
220 | 4 -> Quadruple(values[0], values[1], values[2], values[3]) as V
221 | 5 -> Quintuple(values[0], values[1], values[2], values[3], values[4]) as V
222 | 6 -> Sextuple(values[0], values[1], values[2], values[3], values[4], values[5]) as V
223 | 7 -> Septuple(values[0], values[1], values[2], values[3], values[4], values[5], values[6]) as V
224 | 8 -> Octuple(values[0], values[1], values[2], values[3], values[4], values[5], values[6], values[7]) as V
225 | 9 -> Nonuple(values[0], values[1], values[2], values[3], values[4], values[5], values[6], values[7], values[8]) as V
226 | 10 -> Decuple(values[0], values[1], values[2], values[3], values[4], values[5], values[6], values[7], values[8], values[9]) as V
227 | else -> throw UnsupportedOperationException()
228 | }
229 | pos++
230 | if (!cursor!!.hasNext() || (params.take != null && pos == params.take!!)) {
231 | cursor!!.close()
232 | pos = -1
233 | }
234 | return value
235 | }
236 | override fun hasNext(): Boolean {
237 | if (cursor == null) {
238 | val collection = db.getCollection(params.table.schemaName)
239 | val fields = BasicDBObject()
240 | params.projection.forEach {
241 | fields.append(it.fullName, "1")
242 | }
243 | val query = if (params.query != null) getQuery(params.query!!) else BasicDBObject()
244 | cursor = collection!!.find(query, fields)!!
245 | if (params.skip != null) {
246 | cursor!!.skip(params.skip!!)
247 | }
248 | }
249 | return pos != -1 && cursor!!.hasNext() && (params.take == null || pos < params.take!!)
250 | }
251 | }
252 | }
253 |
254 | private fun Query.usesSearch(): Boolean {
255 | return when (this) {
256 | is TextQuery -> true
257 | is OrQuery -> this.expr1.usesSearch() || this.expr2.usesSearch()
258 | is AndQuery -> this.expr1.usesSearch() || this.expr2.usesSearch()
259 | else -> false
260 | }
261 | }
262 |
263 | protected fun getQuery(op: Query, removePrefix: String = ""): BasicDBObject {
264 | val query = BasicDBObject()
265 | when (op) {
266 | is EqualQuery -> {
267 | if (op.expr1 is AbstractColumn<*, *, *>) {
268 | if (op.expr2 is LiteralExpression) {
269 | if ((op.expr1 as AbstractColumn<*, *, *>).columnType.primitive) {
270 | if ((op.expr1 as AbstractColumn<*, *, *>).columnType.id) {
271 | query.append((op.expr1 as AbstractColumn<*, *, *>).fullName, ObjectId((op.expr2 as LiteralExpression).value.toString()))
272 | } else {
273 | var columnName = (op.expr1 as AbstractColumn<*, *, *>).fullName
274 | if (removePrefix.isNotEmpty() && columnName.startsWith(removePrefix)) {
275 | columnName = columnName.substring(removePrefix.length + 1)
276 | }
277 | query.append( columnName, (op.expr2 as LiteralExpression).value)
278 | }
279 | } else {
280 | throw UnsupportedOperationException()
281 | }
282 | } else if (op.expr2 is AbstractColumn<*, *, *>) {
283 | query.append("\$where", "this.${(op.expr1 as AbstractColumn<*, *, *>).fullName} == this.${(op.expr2 as AbstractColumn<*, *, *>).fullName}")
284 | } else {
285 | throw UnsupportedOperationException()
286 | }
287 | } else {
288 | throw UnsupportedOperationException()
289 | }
290 | }
291 | is MatchesQuery -> {
292 | if (op.expr1 is AbstractColumn<*, *, *>) {
293 | if (op.expr2 is LiteralExpression) {
294 | if ((op.expr2 as LiteralExpression).value is Pattern) {
295 | query.append((op.expr1 as AbstractColumn<*, *, *>).fullName, BasicDBObject().append("\$regex", (op.expr2 as LiteralExpression).value))
296 | } else {
297 | throw UnsupportedOperationException()
298 | }
299 | } else {
300 | throw UnsupportedOperationException()
301 | }
302 | } else {
303 | throw UnsupportedOperationException()
304 | }
305 | }
306 | is NotEqualQuery -> {
307 | if (op.expr1 is AbstractColumn<*, *, *>) {
308 | if (op.expr2 is LiteralExpression) {
309 | if ((op.expr2 as LiteralExpression).value is String || (op.expr2 as LiteralExpression).value is Int) {
310 | if ((op.expr1 as AbstractColumn<*, *, *>).columnType.id) {
311 | query.append((op.expr1 as AbstractColumn<*, *, *>).fullName, BasicDBObject().append("\$ne", ObjectId((op.expr2 as LiteralExpression).value.toString())))
312 | } else {
313 | query.append((op.expr1 as AbstractColumn<*, *, *>).fullName, BasicDBObject().append("\$ne", (op.expr2 as LiteralExpression).value))
314 | }
315 | } else {
316 | throw UnsupportedOperationException()
317 | }
318 | } else if (op.expr2 is AbstractColumn<*, *, *>) {
319 | query.append("\$where", "this.${(op.expr1 as AbstractColumn<*, *, *>).fullName} != this.${(op.expr2 as AbstractColumn<*, *, *>).fullName}")
320 | } else {
321 | throw UnsupportedOperationException()
322 | }
323 | } else {
324 | throw UnsupportedOperationException()
325 | }
326 | }
327 | is GreaterQuery -> {
328 | if (op.expr1 is AbstractColumn<*, *, *>) {
329 | if (op.expr2 is LiteralExpression) {
330 | if ((op.expr2 as LiteralExpression).value is String || (op.expr2 as LiteralExpression).value is Int) {
331 | query.append((op.expr1 as AbstractColumn<*, *, *>).fullName, BasicDBObject().append("\$gt", (op.expr2 as LiteralExpression).value))
332 | } else {
333 | throw UnsupportedOperationException()
334 | }
335 | } else if (op.expr2 is AbstractColumn<*, *, *>) {
336 | query.append("\$where", "this.${(op.expr1 as AbstractColumn<*, *, *>).fullName} > this.${(op.expr2 as AbstractColumn<*, *, *>).fullName}")
337 | } else {
338 | throw UnsupportedOperationException()
339 | }
340 | } else {
341 | throw UnsupportedOperationException()
342 | }
343 | }
344 | is LessQuery -> {
345 | if (op.expr1 is AbstractColumn<*, *, *>) {
346 | if (op.expr2 is LiteralExpression) {
347 | if ((op.expr2 as LiteralExpression).value is String || (op.expr2 as LiteralExpression).value is Int) {
348 | query.append((op.expr1 as AbstractColumn<*, *, *>).fullName, BasicDBObject().append("\$lt", (op.expr2 as LiteralExpression).value))
349 | } else {
350 | throw UnsupportedOperationException()
351 | }
352 | } else if (op.expr2 is AbstractColumn<*, *, *>) {
353 | query.append("\$where", "this.${(op.expr1 as AbstractColumn<*, *, *>).fullName} < this.${(op.expr2 as AbstractColumn<*, *, *>).fullName}")
354 | } else {
355 | throw UnsupportedOperationException()
356 | }
357 | } else {
358 | throw UnsupportedOperationException()
359 | }
360 | }
361 | is GreaterEqualQuery -> {
362 | if (op.expr1 is AbstractColumn<*, *, *>) {
363 | if (op.expr2 is LiteralExpression) {
364 | if ((op.expr2 as LiteralExpression).value is String || (op.expr2 as LiteralExpression).value is Int) {
365 | query.append((op.expr1 as AbstractColumn<*, *, *>).fullName, BasicDBObject().append("\$gte", (op.expr2 as LiteralExpression).value))
366 | } else {
367 | throw UnsupportedOperationException()
368 | }
369 | } else if (op.expr2 is AbstractColumn<*, *, *>) {
370 | query.append("\$where", "this.${(op.expr1 as AbstractColumn<*, *, *>).fullName} >= this.${(op.expr2 as AbstractColumn<*, *, *>).fullName}")
371 | } else {
372 | throw UnsupportedOperationException()
373 | }
374 | } else {
375 | throw UnsupportedOperationException()
376 | }
377 | }
378 | is LessEqualQuery -> {
379 | if (op.expr1 is AbstractColumn<*, *, *>) {
380 | if (op.expr2 is LiteralExpression) {
381 | if ((op.expr2 as LiteralExpression).value is String || (op.expr2 as LiteralExpression).value is Int) {
382 | query.append((op.expr1 as AbstractColumn<*, *, *>).fullName, BasicDBObject().append("\$lte", (op.expr2 as LiteralExpression).value))
383 | } else {
384 | throw UnsupportedOperationException()
385 | }
386 | } else if (op.expr2 is AbstractColumn<*, *, *>) {
387 | query.append("\$where", "this.${(op.expr1 as AbstractColumn<*, *, *>).fullName} <= this.${(op.expr2 as AbstractColumn<*, *, *>).fullName}")
388 | } else {
389 | throw UnsupportedOperationException()
390 | }
391 | } else {
392 | throw UnsupportedOperationException()
393 | }
394 | }
395 | is MemberOfQuery -> {
396 | if (op.expr1 is AbstractColumn<*, *, *>) {
397 | if (op.expr2 is LiteralExpression) {
398 | if ((op.expr2 as LiteralExpression).value is List<*> || (op.expr2 as LiteralExpression).value is Array<*>) {
399 | query.append((op.expr1 as AbstractColumn<*, *, *>).fullName, BasicDBObject().append("\$in", (op.expr2 as LiteralExpression).value))
400 | } else {
401 | throw UnsupportedOperationException()
402 | }
403 | } else {
404 | throw UnsupportedOperationException()
405 | }
406 | } else {
407 | throw UnsupportedOperationException()
408 | }
409 | }
410 | is NotMemberOfQuery -> {
411 | if (op.expr1 is AbstractColumn<*, *, *>) {
412 | if (op.expr2 is LiteralExpression) {
413 | if ((op.expr2 as LiteralExpression).value is List<*> || (op.expr2 as LiteralExpression).value is Array<*>) {
414 | query.append((op.expr1 as AbstractColumn<*, *, *>).fullName, BasicDBObject().append("\$nin", (op.expr2 as LiteralExpression).value))
415 | } else {
416 | throw UnsupportedOperationException()
417 | }
418 | } else {
419 | throw UnsupportedOperationException()
420 | }
421 | } else {
422 | throw UnsupportedOperationException()
423 | }
424 | }
425 | // TODO TODO TODO eq expression and eq expression
426 | is AndQuery -> {
427 | val query1 = getQuery(op.expr1)
428 | val query2 = getQuery(op.expr2)
429 | for (entry in query1.entries) {
430 | query.append(entry.key, entry.value)
431 | }
432 | for (entry in query2.entries) {
433 | query.append(entry.key, entry.value)
434 | }
435 | return query
436 | }
437 | is OrQuery -> {
438 | query.append("\$or", Arrays.asList(getQuery(op.expr1), getQuery(op.expr2)))
439 | }
440 | is TextQuery -> {
441 | query.append("\$text", BasicDBObject().append("\$search", op.search))
442 | }
443 | is NoQuery -> {
444 | // Do nothing
445 | }
446 | else -> {
447 | throw UnsupportedOperationException()
448 | }
449 | }
450 | return query
451 | }
452 |
453 | private fun , P: Any, V : Any /* not sure */> getObject(doc: DBObject, schema: T): V {
454 | var s: AbstractSchema? = null
455 | val valueInstance: Any = if (schema is kotlinx.nosql.DocumentSchema<*, *> && schema.discriminator != null) {
456 | var instance: Any? = null
457 | val discriminatorValue = doc.get(schema.discriminator!!.column.name)
458 | for (discriminator in kotlinx.nosql.DocumentSchema.tableDiscriminators.get(schema.schemaName)!!) {
459 | if (discriminator.value!!.equals(discriminatorValue)) {
460 | instance = newInstance(kotlinx.nosql.DocumentSchema.discriminatorClasses.get(discriminator)!!.java)
461 | s = kotlinx.nosql.DocumentSchema.discriminatorSchemas.get(discriminator)!!
462 | break
463 | }
464 | }
465 | instance!!
466 | } else {
467 | s = schema
468 | newInstance(schema.valueClass.java)
469 | }
470 | val schemaClass = s!!.javaClass
471 | val schemaFields = getAllFields(schemaClass as Class)
472 | val valueFields = getAllFieldsMap(valueInstance.javaClass as Class)
473 | for (schemaField in schemaFields) {
474 | if (AbstractColumn::class.java.isAssignableFrom(schemaField.getType()!!)) {
475 | val valueField = valueFields.get(if (schemaField.getName()!!.equals("pk")) "id" else schemaField.getName()!!.toLowerCase())
476 | if (valueField != null) {
477 | schemaField.setAccessible(true)
478 | valueField.setAccessible(true)
479 | val column = schemaField.asColumn(s!!)
480 | val value = doc.get(column.name)
481 | val columnValue: Any? = if (value == null) {
482 | null
483 | } else if (column.columnType.id && !column.columnType.iterable)
484 | Id(value.toString() as P)
485 | else if (column.columnType.primitive) {
486 | when (column.columnType) {
487 | ColumnType.DATE -> LocalDate(value.toString())
488 | ColumnType.TIME -> LocalTime(value.toString())
489 | ColumnType.DATE_TIME -> DateTime(value.toString())
490 | else -> doc.get(column.name)
491 | }
492 | } else if (column.columnType.list && !column.columnType.custom) {
493 | (doc.get(column.name) as BasicDBList).toList()
494 | } else if (column.columnType.set && !column.columnType.custom && !column.columnType.id) {
495 | (doc.get(column.name) as BasicDBList).toSet()
496 | } else if (column.columnType.id && column.columnType.set) {
497 | val list = doc.get(column.name) as BasicDBList
498 | list.map { Id>(it.toString()) }.toSet()
499 | } else if (column.columnType.custom && column.columnType.set) {
500 | val list = doc.get(column.name) as BasicDBList
501 | list.map { getObject(it as DBObject, column as ListColumn<*, out AbstractSchema>) }.toSet()
502 | } else if (column.columnType.custom && column.columnType.list) {
503 | val list = doc.get(column.name) as BasicDBList
504 | list.map { getObject(it as DBObject, column as ListColumn<*, out AbstractSchema>) }.toList()
505 | } else {
506 | getObject(doc.get(column.name) as DBObject, column as Column)
507 | }
508 | if (columnValue != null || column is AbstractNullableColumn) {
509 | valueField.set(valueInstance, columnValue)
510 | } else {
511 | throw NullPointerException()
512 | }
513 | }
514 | }
515 | }
516 | return valueInstance as V
517 | }
518 |
519 | private fun getObject(doc: DBObject, column: AbstractColumn<*, *, *>): Any? {
520 | val valueInstance = newInstance(column.valueClass.java)
521 | val schemaClass = column.javaClass
522 | val columnFields = schemaClass.getDeclaredFields()!!
523 | val valueFields = getAllFieldsMap(valueInstance.javaClass as Class)
524 | for (columnField in columnFields) {
525 | if (columnField.isColumn) {
526 | val valueField = valueFields.get(columnField.getName()!!.toLowerCase())
527 | if (valueField != null) {
528 | columnField.setAccessible(true)
529 | valueField.setAccessible(true)
530 | val column = columnField.asColumn(column)
531 | val columnValue: Any? = if (column.columnType.id && !column.columnType.iterable) Id>(doc.get(column.name).toString())
532 | else if (column.columnType.primitive) doc.get(column.name)
533 | else if (column.columnType.list && !column.columnType.custom) (doc.get(column.name) as BasicDBList).toList()
534 | else if (column.columnType.set && !column.columnType.custom && !column.columnType.id) (doc.get(column.name) as BasicDBList).toSet()
535 | else if (column.columnType.custom && column.columnType.list) {
536 | val list = doc.get(column.name) as BasicDBList
537 | list.map { getObject(it as DBObject, column as ListColumn<*, out AbstractSchema>) }.toList()
538 | } else if (column.columnType.id && column.columnType.set) {
539 | val list = doc.get(column.name) as BasicDBList
540 | list.map { Id>(it.toString()) }.toSet()
541 | } else if (column.columnType.custom && column.columnType.set) {
542 | val list = doc.get(column.name) as BasicDBList
543 | list.map { getObject(it as DBObject, column as ListColumn<*, out AbstractSchema>) }.toSet()
544 | } else {
545 | getObject(doc.get(column.name) as DBObject, column as Column<*, out AbstractSchema>)
546 | }
547 | if (columnValue != null || column is AbstractNullableColumn) {
548 | valueField.set(valueInstance, columnValue)
549 | } else {
550 | throw NullPointerException()
551 | }
552 | }
553 | }
554 | }
555 | return valueInstance
556 | }
557 |
558 | override fun insert(columns: Array, Any?>>) {
559 | throw UnsupportedOperationException()
560 | }
561 |
562 | override fun delete(table: T, op: Query): Int {
563 | val collection = db.getCollection(table.schemaName)!!
564 | val query = getQuery(op)
565 | return collection.remove(query)!!.getN()
566 | }
567 |
568 | override fun update(schema: AbstractSchema, columnValues: Array, *>>, op: Query): Int {
569 | val collection = db.getCollection(schema.schemaName)!!
570 | val statement = BasicDBObject()
571 | val doc = BasicDBObject().append("\$set", statement)
572 | for ((column, value) in columnValues) {
573 | statement.append(column.fullName, getDBValue(value, column))
574 | }
575 | return collection.update(getQuery(op), doc)!!.getN()
576 | }
577 |
578 | override fun addAll(schema: AbstractSchema, column: AbstractColumn, *, *>, values: Collection, op: Query): Int {
579 | val collection = db.getCollection(schema.schemaName)!!
580 | val statement = BasicDBObject()
581 | val doc = BasicDBObject().append("\$pushAll", statement)
582 | statement.append(column.fullName, getDBValue(values, column))
583 | return collection.update(getQuery(op), doc)!!.getN()
584 | }
585 |
586 | override fun removeAll(schema: AbstractSchema, column: AbstractColumn, *, *>, values: Collection, op: Query): Int {
587 | val collection = db.getCollection(schema.schemaName)!!
588 | val statement = BasicDBObject()
589 | val doc = BasicDBObject().append("\$pullAll", statement)
590 | statement.append(column.fullName, getDBValue(values, column))
591 | return collection.update(getQuery(op), doc)!!.getN()
592 | }
593 |
594 | override fun removeAll(schema: AbstractSchema, column: AbstractColumn, *, *>, removeOp: Query, op: Query): Int {
595 | val collection = db.getCollection(schema.schemaName)!!
596 | val statement = BasicDBObject()
597 | val doc = BasicDBObject().append("\$pull", statement)
598 | statement.append(column.fullName, getQuery(removeOp, column.fullName))
599 | return collection.update(getQuery(op), doc)!!.getN()
600 | }
601 |
602 | private fun getDBValue(value: Any?, column: AbstractColumn<*, *, *>, withinIterable: Boolean = false): Any? {
603 | return if (!column.columnType.custom && (!column.columnType.iterable || withinIterable )) {
604 | when (value) {
605 | is DateTime, is LocalDate, is LocalTime -> value.toString()
606 | is Id<*, *> -> ObjectId(value.value.toString())
607 | else -> value
608 | }
609 | } else if (column.columnType.custom && !column.columnType.iterable)
610 | if (value != null) getDBObject(value, column) else null
611 | else if (column.columnType.list && column.columnType.custom)
612 | (value as List<*>).map { getDBObject(it!!, column) }
613 | else if (column.columnType.set && column.columnType.custom)
614 | (value as Set<*>).map { getDBObject(it!!, column) }.toSet()
615 | else if (column.columnType.list && !column.columnType.custom)
616 | (value as List<*>).map { getDBValue(it!!, column, true) }
617 | else if (column.columnType.set && !column.columnType.custom)
618 | (value as Set<*>).map { getDBValue(it!!, column, true) }.toSet()
619 | else throw UnsupportedOperationException()
620 | }
621 |
622 | private fun getColumnObject(doc: DBObject, column: AbstractColumn<*, *, *>): Any? {
623 | val columnObject = parse(doc, column.fullName.split(".").toTypedArray())
624 | return if (column.columnType.id && !column.columnType.iterable) {
625 | Id>(columnObject.toString())
626 | } else if (column.columnType.primitive && !column.columnType.iterable) when (column.columnType) {
627 | ColumnType.DATE -> LocalDate.parse(columnObject.toString())
628 | ColumnType.TIME -> LocalTime.parse(columnObject.toString())
629 | ColumnType.DATE_TIME -> DateTime.parse(columnObject.toString())
630 | else -> columnObject
631 | } else if (column.columnType == ColumnType.STRING_SET) {
632 | (columnObject as BasicDBList).toSet()
633 | } else if (column.columnType == ColumnType.STRING_LIST) {
634 | (columnObject as BasicDBList).toList()
635 | } else if (column.columnType.id && column.columnType.set) {
636 | (columnObject as BasicDBList).map { Id>(it.toString()) }.toSet()
637 | } else if (column.columnType.id && column.columnType.list) {
638 | (columnObject as BasicDBList).map { Id>(it.toString()) }
639 | } else if (column.columnType.custom && column.columnType.list) {
640 | (columnObject as BasicDBList).map { getObject(it as DBObject, column as ListColumn) }
641 | } else if (column.columnType.custom && column.columnType.set) {
642 | (columnObject as BasicDBList).map { getObject(it as DBObject, column as ListColumn) }.toSet()
643 | } else if (column.columnType.custom) {
644 | getObject(columnObject as DBObject, column)
645 | } else {
646 | UnsupportedOperationException()
647 | }
648 | }
649 |
650 | private fun parse(doc: DBObject, path: Array, position: Int = 0): Any? {
651 | val value = doc.get(path[position])
652 | if (position < path.size - 1) {
653 | return parse(value as DBObject, path, position + 1)
654 | } else {
655 | return value
656 | }
657 | }
658 |
659 | override fun , P: Any, C: Any> T.find(query: T.() -> Query): DocumentSchemaQueryWrapper {
660 | val params = DocumentSchemaQueryParams(this, query())
661 | return DocumentSchemaQueryWrapper(params)
662 | }
663 | }
664 |
--------------------------------------------------------------------------------
/kotlin-nosql-mongodb/src/test/kotlin/kotlinx/nosql/mongodb/test/MongoDBSpek.kt:
--------------------------------------------------------------------------------
1 | package kotlinx.nosql.mongodb.test
2 |
3 | import kotlin.test.assertEquals
4 | import kotlinx.nosql.*
5 | import kotlinx.nosql.mongodb.*
6 | import kotlinx.nosql.mongodb.DocumentSchema
7 | import org.joda.time.LocalDate
8 | import org.jetbrains.spek.api.Spek
9 | import java.util.regex.Pattern
10 | import kotlin.reflect.KClass
11 | import kotlin.test.assertTrue
12 |
13 | class MongoDBSpek : Spek() {
14 | open class ProductSchema>(klass: KClass, discriminator: String) : DocumentSchema("products",
15 | klass, discriminator = Discriminator(string("type"), discriminator)) {
16 | val sku = string("sku")
17 | val title = string("title")
18 | val description = string("description")
19 | val asin = string("asin")
20 | val available = boolean("available")
21 | val createdAtDate = date("createdAtDate")
22 | val nullableBooleanNoValue = nullableBoolean("nullableBooleanNoValue")
23 | val nullableBooleanWithValue = nullableBoolean("nullableBooleanWithValue")
24 | val nullableDateNoValue = nullableDate("nullableDateNoValue")
25 | val nullableDateWithValue = nullableDate("nullableDateWithValue")
26 | val cost = double("cost")
27 | val nullableDoubleNoValue = nullableDouble("nullableDoubleNoValue")
28 | val nullableDoubleWithValue = nullableDouble("nullableDoubleWithValue")
29 | val setOfStrings = setOfString("setOfStrings")
30 |
31 | val shipping = ShippingColumn()
32 | val pricing = PricingColumn()
33 |
34 | inner class ShippingColumn>() : Column("shipping", Shipping::class) {
35 | val weight = integer("weight")
36 | val dimensions = DimensionsColumn()
37 | }
38 |
39 | inner class DimensionsColumn>() : Column("dimensions", Dimensions::class) {
40 | val width = integer("width")
41 | val height = integer("height")
42 | val depth = integer("depth")
43 | }
44 |
45 | inner class PricingColumn>() : Column("pricing", Pricing::class) {
46 | val list = integer("list")
47 | val retail = integer("retail")
48 | val savings = integer("savings")
49 | val pctSavings = integer("pct_savings")
50 | }
51 |
52 | init {
53 | ensureIndex(text = arrayOf(title, description))
54 | ensureIndex(name = "asinIndex", unique = true, ascending = arrayOf(asin))
55 | }
56 | }
57 |
58 | object Artists : DocumentSchema("artists", Artist::class) {
59 | val name = string("name")
60 | }
61 |
62 | object Products : ProductSchema(Product::class, "") {
63 | }
64 |
65 | object Albums : ProductSchema(Album::class, discriminator = "Audio Album") {
66 | val details = DetailsColumn()
67 |
68 | class DetailsColumn() : Column("details", Details::class) {
69 | val title: AbstractColumn = string("title")
70 | val artistId = id("artistId", Artists)
71 | val artistIds = setOfId("artistIds", Artists)
72 | val genre = setOfString("genre")
73 |
74 | val tracks = TracksColumn()
75 | }
76 |
77 | class TracksColumn() : ListColumn