Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import me.sgrouples.rogue.map.MapKeyFormat
import me.sgrouples.rogue.{BaseBsonFormat, BsonFormat, SupportedLocales}
import org.bson._
import org.bson.types.{Decimal128, ObjectId}

import java.math.{BigDecimal => BigDec}
import scala.collection.Factory
import scala.annotation.implicitNotFound
import scala.reflect.ClassTag
Expand Down Expand Up @@ -120,6 +120,29 @@ final class BigDecimalMacroBsonFormat()
}
}

final class BigIntMacroBsonFormat()
extends MacroBaseBsonFormat[BigInt] {
override def read(b: BsonValue): BigInt = if (b.isDecimal128) {
BigInt(b.asDecimal128().decimal128Value().bigDecimalValue().toBigInteger)
} else {
defaultValue
}

override def write(v: BigInt): BsonValue = new BsonDecimal128(
new Decimal128(new BigDec(v.bigInteger))
)

override def defaultValue: BigInt = BigInt(0)

override def append(writer: BsonWriter, k: String, v: BigInt): Unit = {
writer.writeDecimal128(k, new Decimal128(new BigDec(v.bigInteger)))
}

override def append(writer: BsonWriter, v: BigInt): Unit = {
writer.writeDecimal128(new Decimal128(new BigDec(v.bigInteger)))
}
}

final class BooleanMacroBsonFormat(default: Boolean = false)
extends MacroBaseBsonFormat[Boolean] {
override def read(b: BsonValue): Boolean = if (b.isBoolean) {
Expand Down Expand Up @@ -538,6 +561,7 @@ object MacroBsonFormat extends MacroBsonFormatDeriving:
given MacroBsonFormat[Long] = LongMacroBsonFormat(0L)
given MacroBsonFormat[Double] = DoubleMacroBsonFormat(0d)
given MacroBsonFormat[BigDecimal] = BigDecimalMacroBsonFormat()
given MacroBsonFormat[BigInt] = BigIntMacroBsonFormat()
given MacroBsonFormat[Boolean] = BooleanMacroBsonFormat(false)
given MacroBsonFormat[String] = StringMacroBsonFormat("")
given MacroBsonFormat[ObjectId] = ObjectIdMacroBsonFormat[ObjectId]()
Expand Down
7 changes: 7 additions & 0 deletions cc/src/main/scala/me/sgrouples/rogue/Fields.scala
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ class BigDecimalField[O](name: String, o: O)
override def defaultValue = BigDecimal(0)
}

class BigIntField[O](name: String, o: O)
extends MCField[BigInt, O](name, o) {
override def defaultValue = BigInt(0)
}

class LongSubtypeField[T <: Long, O](name: String, o: O)
extends MCField[T, O](name, o) {
override def defaultValue: T = 0L.asInstanceOf[T]
Expand Down Expand Up @@ -221,6 +226,8 @@ class OptLongSubtypeField[T <: Long, O](name: String, o: O)
class OptBigDecimalField[O](name: String, o: O)
extends OCField[BigDecimal, O](name, o)

class OptBigIntField[O](name: String, o: O) extends OCField[BigInt, O](name, o)

class OptStringField[O](name: String, o: O) extends OCField[String, O](name, o)
class OptStringSubtypeField[T <: String, O](name: String, o: O)
extends OCField[T, O](name, o)
Expand Down
18 changes: 17 additions & 1 deletion cc/src/main/scala/me/sgrouples/rogue/SelectFields.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import io.fsq.rogue._
import me.sgrouples.rogue.cc.CcMeta
import org.bson.types.Decimal128
import org.bson._

import java.math.{BigDecimal => BigDec}
/** Trait representing a field and all the operations on it.
*
* @tparam F
Expand Down Expand Up @@ -196,6 +196,22 @@ class BigDecimalModifyField[M](field: Field[BigDecimal, M])
)
}

class BigIntQueryField[M](field: Field[BigInt, M])
extends AbstractQueryField[BigInt, BigInt, BsonDecimal128, M](
field
) {
override def valueToDB(v: BigInt): BsonDecimal128 = new BsonDecimal128(
new Decimal128(new BigDec(v.bigInteger))
)
}

class BigIntModifyField[M](field: Field[BigInt, M])
extends AbstractModifyField[BigInt, BsonDecimal128, M](field) {
override def valueToDB(v: BigInt): BsonDecimal128 = new BsonDecimal128(
new Decimal128(new BigDec(v.bigInteger))
)
}

class CurrencyQueryField[M](field: Field[Currency, M])
extends AbstractQueryField[Currency, Currency, BsonString, M](field) {
override def valueToDB(v: Currency): BsonString = new BsonString(
Expand Down
10 changes: 10 additions & 0 deletions cc/src/main/scala/me/sgrouples/rogue/cc/CcRogue.scala
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,11 @@ trait CcRogue {
): BigDecimalQueryField[O] =
new BigDecimalQueryField[O](f)

implicit def bigIntFieldToCurrencyQueryField[O <: CcMeta[_]](
f: RField[BigInt, O]
): BigIntQueryField[O] =
new BigIntQueryField[O](f)

implicit def caseClassFieldToQueryField[C, M <: CcMeta[C], O](
f: CClassField[C, M, O]
): CClassQueryField[C, M, O] =
Expand Down Expand Up @@ -292,6 +297,11 @@ trait CcRogue {
): BigDecimalModifyField[O] =
new BigDecimalModifyField[O](f)

implicit def bigIntFieldToCurrencyModifyField[O <: CcMeta[_]](
f: RField[BigInt, O]
): BigIntModifyField[O] =
new BigIntModifyField[O](f)

implicit def localeFieldToLocaleModifyField[O <: CcMeta[_]](
f: RField[Locale, O]
): LocaleModifyField[O] =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,14 @@ trait QueryFieldHelpers[Meta] extends NamesResolver {
): OptBigDecimalField[Meta] =
named(name)(new OptBigDecimalField[Meta](_, this))

protected def BigIntField(name: String): BigIntField[Meta] =
named(name)(new BigIntField[Meta](_, this))

protected def OptBigIntField(
name: String
): OptBigIntField[Meta] =
named(name)(new OptBigIntField[Meta](_, this))

protected def LongSubtypeField[T <: Long](
name: String
): LongSubtypeField[T, Meta] =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ import scala.language.implicitConversions
import java.time.LocalDateTime
import java.util.{Currency, Locale}
import java.util.regex.Pattern
import org.mongodb.scala._
import org.mongodb.scala.*
import me.sgrouples.rogue.cc.CcRogue.*
import me.sgrouples.rogue.cc.*
import org.bson.types.ObjectId
import munit.FunSuite
import org.mongodb.scala.result.DeleteResult

import scala.concurrent.duration._
import scala.concurrent.duration.*
import com.softwaremill.tagging.*

import scala.concurrent.ExecutionContext.Implicits.global
Expand Down Expand Up @@ -696,6 +696,71 @@ class EndToEndBsonAsyncSpec extends FunSuite {
} yield ()
}

test("BigInt fields") {
val bigValue = BigInt("100000000000000000000000")
val data1 = BigIntData(bigValue, Some(bigValue))
val data2 = BigIntData(BigInt(111111111), None)
val data3 = BigIntData(BigInt(999999999), Some(BigInt(123)))
val data4 = BigIntData(BigInt(500000000), Some(BigInt(500000000)))

val allData = Seq(data1, data2, data3, data4)

for {
_ <- Future.traverse(allData)(data => BigIntDatas.insertOneAsync(data))
_ <- BigIntDatas
.where(_.value eqs bigValue)
.fetchAsync()
.map(assertEquals(_, Seq(data1)))
// Query by optional BigInt field
_ <- BigIntDatas
.where(_.optValue eqs bigValue)
.fetchAsync()
.map(assertEquals(_, Seq(data1)))
// Query with comparison operators
_ <- BigIntDatas
.where(_.value > BigInt(120000000))
.fetchAsync()
.map(result => assertEquals(result.toSet, Set(data1, data3, data4)))
_ <- BigIntDatas
.where(_.value < BigInt(200000000))
.fetchAsync()
.map(result => assertEquals(result.toSet, Set(data2)))
// Modify BigInt field
_ <- BigIntDatas
.where(_.value eqs BigInt(111111111))
.modify(_.value setTo BigInt(222222222))
.updateOneAsync()
_ <- BigIntDatas
.where(_.value eqs BigInt(222222222))
.fetchAsync()
.map(result => {
assertEquals(result.size, 1)
assertEquals(result.head.value, BigInt(222222222))
assertEquals(result.head.optValue, None)
})
// Modify optional BigInt field
_ <- BigIntDatas
.where(_.value eqs BigInt(999999999))
.modify(_.optValue setTo Some(BigInt(456)))
.updateOneAsync()
_ <- BigIntDatas
.where(_.value eqs BigInt(999999999))
.fetchAsync()
.map(result => {
assertEquals(result.size, 1)
assertEquals(result.head.optValue, Some(BigInt(456)))
})
// Query records where optional field is None
_ <- BigIntDatas
.where(_.value eqs BigInt(222222222))
.fetchAsync()
.map(result => {
assertEquals(result.size, 1)
assertEquals(result.head.optValue, None)
})
} yield ()
}

test("Map[K, V] field") {

val counts = Map(ObjectId.get -> 100L)
Expand Down
9 changes: 9 additions & 0 deletions cc/src/test/scala/me/sgrouples/rogue/cc/TestModels.scala
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,15 @@ object Metas {

val Invoices = new InvoiceMeta

case class BigIntData(value: BigInt, optValue: Option[BigInt])

class BigIntDataMeta extends MCcMeta[BigIntData, BigIntDataMeta] {
val value = BigIntField("value")
val optValue = OptBigIntField("optValue")
}

val BigIntDatas = new BigIntDataMeta

case class Counter(
_id: ObjectId = ObjectId.get(),
counts: Map[ObjectId, Long]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import org.mongodb.scala.model.Filters
import scala.concurrent.{Await, Future}
import scala.concurrent.duration.*
import com.softwaremill.tagging.*

import java.time.temporal.ChronoUnit
import scala.concurrent.ExecutionContext.Implicits.global

Expand Down Expand Up @@ -705,6 +704,72 @@ class MacroEndToEndSpec extends FunSuite {

}

test("BigInt fields should just work") {
val bigValue = BigInt("100000000000000000000000")
val data1 = BigIntData(bigValue, Some(bigValue))
val data2 = BigIntData(BigInt(111111111), None)
val data3 = BigIntData(BigInt(999999999), Some(BigInt(123)))
val data4 = BigIntData(BigInt(500000000), Some(BigInt(500000000)))

val allData = Seq(data1, data2, data3, data4)

for {
_ <- Future.traverse(allData)(data => BigIntDatas.insertOneAsync(data))
// Query by required BigInt field
_ <- BigIntDatas
.where(_.value eqs bigValue)
.fetchAsync()
.map(assertEquals(_, Seq(data1)))
// Query by optional BigInt field
_ <- BigIntDatas
.where(_.optValue eqs bigValue)
.fetchAsync()
.map(assertEquals(_, Seq(data1)))
// Query with comparison operators
_ <- BigIntDatas
.where(_.value > BigInt(120000000))
.fetchAsync()
.map(result => assertEquals(result.toSet, Set(data1, data3, data4)))
_ <- BigIntDatas
.where(_.value < BigInt(115000000))
.fetchAsync()
.map(assertEquals(_, Seq(data2)))
// Modify BigInt field
_ <- BigIntDatas
.where(_.value eqs BigInt(111111111))
.modify(_.value setTo BigInt(222222222))
.updateOneAsync()
_ <- BigIntDatas
.where(_.value eqs BigInt(222222222))
.fetchAsync()
.map(result => {
assertEquals(result.size, 1)
assertEquals(result.head.value, BigInt(222222222))
assertEquals(result.head.optValue, None)
})
// Modify optional BigInt field
_ <- BigIntDatas
.where(_.value eqs BigInt(999999999))
.modify(_.optValue setTo Some(BigInt(456)))
.updateOneAsync()
_ <- BigIntDatas
.where(_.value eqs BigInt(999999999))
.fetchAsync()
.map(result => {
assertEquals(result.size, 1)
assertEquals(result.head.optValue, Some(BigInt(456)))
})
// Query records where optional field is None
_ <- BigIntDatas
.where(_.value eqs BigInt(222222222))
.fetchAsync()
.map(result => {
assertEquals(result.size, 1)
assertEquals(result.head.optValue, None)
})
} yield ()
}

test("Map[K, V] field should just work") {

val counts = Map(ObjectId.get -> 100L)
Expand Down
9 changes: 9 additions & 0 deletions cc/src/test/scala/me/sgrouples/rogue/cc/macros/Metas.scala
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,15 @@ object Metas {

val Invoices = new InvoiceMeta

case class BigIntData(value: BigInt, optValue: Option[BigInt])

class BigIntDataMeta extends MCcMeta[BigIntData, BigIntDataMeta] {
val value = BigIntField("value")
val optValue = OptBigIntField("optValue")
}

val BigIntDatas = new BigIntDataMeta

case class MCounter(
_id: ObjectId = ObjectId.get(),
counts: Map[ObjectId, Long]
Expand Down