Skip to content

Commit 0926b1d

Browse files
author
QuadStingray
committed
feat: Compact Methods for Database or DAO
1 parent cc13a06 commit 0926b1d

File tree

10 files changed

+121
-49
lines changed

10 files changed

+121
-49
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,8 +154,14 @@ class RestaurantDemoSpec extends Specification with RestaurantDemoDatabaseFuncti
154154
}
155155

156156
}
157+
```
157158

159+
## Run Tests
160+
```shell
161+
docker run --publish 27017:27017 mongocamp/mongodb:latest;
162+
sbt test;
158163
```
164+
159165
## Supporters
160166

161167
JetBrains is supporting this open source project with:

src/main/scala/dev/mongocamp/driver/mongodb/MongoDAO.scala

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,19 @@
11
package dev.mongocamp.driver.mongodb
22

3-
import java.nio.charset.Charset
4-
53
import better.files.File
6-
import dev.mongocamp.driver.mongodb.bson.{ BsonConverter, DocumentHelper }
7-
import dev.mongocamp.driver.mongodb.database.{ ChangeObserver, CollectionStatus, DatabaseProvider }
4+
import dev.mongocamp.driver.mongodb.bson.{BsonConverter, DocumentHelper}
5+
import dev.mongocamp.driver.mongodb.database.{ChangeObserver, CollectionStatus, CompactResult, DatabaseProvider}
86
import dev.mongocamp.driver.mongodb.operation.Crud
97
import org.bson.json.JsonParseException
10-
import org.mongodb.scala.model.Aggregates.project
8+
import org.mongodb.scala.model.Accumulators._
9+
import org.mongodb.scala.model.Aggregates._
10+
import org.mongodb.scala.model.Filters._
1111
import org.mongodb.scala.model.Projections
12-
import org.mongodb.scala.{ BulkWriteResult, Document, MongoCollection, Observable, SingleObservable }
12+
import org.mongodb.scala.{BulkWriteResult, Document, MongoCollection, Observable, SingleObservable}
1313

14+
import java.nio.charset.Charset
1415
import scala.collection.mutable.ArrayBuffer
1516
import scala.reflect.ClassTag
16-
import org.mongodb.scala.model.Filters._
17-
import org.mongodb.scala.model.Aggregates._
18-
import org.mongodb.scala.model.Accumulators._
1917

2018
/** Created by tom on 20.01.17.
2119
*/
@@ -32,8 +30,13 @@ abstract class MongoDAO[A](provider: DatabaseProvider, collectionName: String)(i
3230
observer
3331
}
3432

35-
def collectionStatus: Observable[CollectionStatus] =
33+
def collectionStatus: Observable[CollectionStatus] = {
3634
provider.runCommand(Map("collStats" -> collectionName)).map(document => CollectionStatus(document))
35+
}
36+
37+
def compact: Observable[Option[CompactResult]] = {
38+
provider.runCommand(Map("compact" -> collectionName)).map(document => CompactResult(document))
39+
}
3740

3841
/** @param sampleSize
3942
* use sample size greater 0 for better performance on big collections
@@ -45,10 +48,12 @@ abstract class MongoDAO[A](provider: DatabaseProvider, collectionName: String)(i
4548
val unwindStage = unwind("$tempArray")
4649
val groupStage = group("_id", addToSet("keySet", "$tempArray.k"))
4750
val pipeline = {
48-
if (sampleSize > 0)
51+
if (sampleSize > 0) {
4952
List(sample(sampleSize), projectStage, unwindStage, groupStage)
50-
else
53+
}
54+
else {
5155
List(projectStage, unwindStage, groupStage)
56+
}
5257
}
5358

5459
val aggregationResult: Document = Raw.findAggregated(pipeline).result()
@@ -62,9 +67,11 @@ abstract class MongoDAO[A](provider: DatabaseProvider, collectionName: String)(i
6267

6368
def importJsonFile(file: File): SingleObservable[BulkWriteResult] = {
6469
val docs = new ArrayBuffer[Document]()
65-
try if (file.exists) {
66-
val iterator = file.lineIterator(Charset.forName("UTF-8"))
67-
iterator.foreach(line => docs.+=(DocumentHelper.documentFromJsonString(line).get))
70+
try {
71+
if (file.exists) {
72+
val iterator = file.lineIterator(Charset.forName("UTF-8"))
73+
iterator.foreach(line => docs.+=(DocumentHelper.documentFromJsonString(line).get))
74+
}
6875
}
6976
catch {
7077
case e: JsonParseException =>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package dev.mongocamp.driver.mongodb.database
2+
import dev.mongocamp.driver.mongodb._
3+
import org.mongodb.scala.bson.Document
4+
5+
case class CompactResult(bytesFreed: Long)
6+
7+
object CompactResult {
8+
def apply(document: Document): Option[CompactResult] = {
9+
if (document.getLongValue("ok") == 1) {
10+
Some(
11+
CompactResult(
12+
document.getLongValue("bytesFreed")
13+
)
14+
)
15+
}
16+
else {
17+
None
18+
}
19+
}
20+
}

src/main/scala/dev/mongocamp/driver/mongodb/database/DatabaseProvider.scala

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package dev.mongocamp.driver.mongodb.database
22

33
import dev.mongocamp.driver.mongodb._
44
import dev.mongocamp.driver.mongodb.bson.codecs.CustomCodecProvider
5-
import org.bson.codecs.configuration.CodecRegistries.{ fromProviders, fromRegistries }
5+
import org.bson.codecs.configuration.CodecRegistries.{fromProviders, fromRegistries}
66
import org.bson.codecs.configuration.CodecRegistry
77
import org.mongodb.scala.MongoClient.DEFAULT_CODEC_REGISTRY
88
import org.mongodb.scala._
@@ -42,6 +42,16 @@ class DatabaseProvider(val config: MongoConfig, val registry: CodecRegistry) ext
4242

4343
def dropDatabase(databaseName: String = DefaultDatabaseName): SingleObservable[Void] = database(databaseName).drop()
4444

45+
def compact(databaseName: String = DefaultDatabaseName, maxWaitPerCollection: Int = DefaultMaxWait): CompactResult = {
46+
CompactResult(
47+
collectionNames(databaseName)
48+
.map(
49+
collectionName => dao(collectionName).compact.result(maxWaitPerCollection).getOrElse(CompactResult(0)))
50+
.map(_.bytesFreed)
51+
.sum
52+
)
53+
}
54+
4555
def database(databaseName: String = DefaultDatabaseName): MongoDatabase = {
4656
if (!cachedDatabaseMap.contains(databaseName)) {
4757
cachedDatabaseMap.put(databaseName, client.getDatabase(databaseName).withCodecRegistry(registry))

src/main/scala/dev/mongocamp/driver/mongodb/operation/Base.scala

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
package dev.mongocamp.driver.mongodb.operation
22

3-
import dev.mongocamp.driver.mongodb.database.{ ConfigHelper, MongoIndex }
43
import com.typesafe.scalalogging.LazyLogging
4+
import dev.mongocamp.driver.mongodb.database.MongoIndex
55
import org.mongodb.scala.bson.conversions.Bson
66
import org.mongodb.scala.model.Sorts._
7-
import org.mongodb.scala.model.{ CountOptions, DropIndexOptions, IndexOptions, Indexes }
8-
import org.mongodb.scala.{ Document, ListIndexesObservable, MongoCollection, Observable, SingleObservable }
7+
import org.mongodb.scala.model.{CountOptions, DropIndexOptions, IndexOptions, Indexes}
8+
import org.mongodb.scala.{Document, ListIndexesObservable, MongoCollection, Observable, SingleObservable}
99

1010
import scala.concurrent.duration.Duration
1111
import scala.reflect.ClassTag
@@ -23,11 +23,14 @@ abstract class Base[A]()(implicit ct: ClassTag[A]) extends LazyLogging {
2323
fieldName: String,
2424
sortAscending: Boolean = true,
2525
options: IndexOptions = IndexOptions()
26-
): SingleObservable[String] =
27-
if (sortAscending)
26+
): SingleObservable[String] = {
27+
if (sortAscending) {
2828
createIndex(ascending(fieldName), options)
29-
else
29+
}
30+
else {
3031
createIndex(descending(fieldName), options)
32+
}
33+
}
3134

3235
def createIndexForFieldWithName(
3336
fieldName: String,
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package dev.mongocamp.driver.mongodb
2+
3+
import better.files.{File, Resource}
4+
import dev.mongocamp.driver.mongodb.database.CompactResult
5+
import dev.mongocamp.driver.mongodb.test.TestDatabase
6+
import dev.mongocamp.driver.mongodb.test.TestDatabase.BookDAO
7+
import org.specs2.mutable.Specification
8+
import org.specs2.specification.BeforeAll
9+
10+
import java.text.SimpleDateFormat
11+
import java.util.Date
12+
13+
class CompactSpec extends Specification with BeforeAll {
14+
val DateFormat = new SimpleDateFormat("yyyy-MM-dd")
15+
val From: Date = DateFormat.parse("2000-01-01")
16+
17+
override def beforeAll(): Unit = {
18+
BookDAO.drop().result()
19+
BookDAO.importJsonFile(File(Resource.getUrl("json/books.json"))).result()
20+
val stats = BookDAO.collectionStatus.result()
21+
stats.count mustEqual 431
22+
}
23+
24+
"CompactSpec" should {
25+
"compact single collection" in {
26+
val count: Option[CompactResult] = BookDAO.compact.result()
27+
count must beSome()
28+
count.get.bytesFreed must beGreaterThanOrEqualTo(0L)
29+
}
30+
"compact complete database" in {
31+
val count: CompactResult = TestDatabase.provider.compact()
32+
count.bytesFreed must beGreaterThanOrEqualTo(0L)
33+
}
34+
}
35+
36+
}

src/test/scala/dev/mongocamp/driver/mongodb/dao/BookDAOSpec.scala

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,20 @@
11
package dev.mongocamp.driver.mongodb.dao
22

3-
import java.text.SimpleDateFormat
4-
import java.util.Date
5-
63
import better.files.{File, Resource}
74
import dev.mongocamp.driver.mongodb.Filter._
85
import dev.mongocamp.driver.mongodb._
96
import dev.mongocamp.driver.mongodb.database.DatabaseProvider
107
import dev.mongocamp.driver.mongodb.test.TestDatabase.BookDAO
118
import org.mongodb.scala.bson.conversions.Bson
12-
import org.mongodb.scala.model.Aggregates.group
13-
import org.mongodb.scala.model.Aggregates.filter
14-
import org.mongodb.scala.model.Aggregates.project
9+
import org.mongodb.scala.model.Aggregates.{filter, group, project}
1510
import org.mongodb.scala.model.Filters.and
1611
import org.mongodb.scala.model.Projections
12+
import org.mongodb.scala.model.Updates._
1713
import org.specs2.mutable.Specification
1814
import org.specs2.specification.BeforeAll
19-
import org.mongodb.scala.model.Updates._
15+
16+
import java.text.SimpleDateFormat
17+
import java.util.Date
2018

2119
class BookDAOSpec extends Specification with BeforeAll {
2220
val DateFormat = new SimpleDateFormat("yyyy-MM-dd")

src/test/scala/dev/mongocamp/driver/mongodb/dao/PersonDAOSpec.scala

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
package dev.mongocamp.driver.mongodb.dao
22

3-
import java.util.concurrent.TimeUnit
4-
53
import dev.mongocamp.driver.MongoImplicits
6-
import dev.mongocamp.driver.mongodb.test.TestDatabase.PersonDAO
74
import dev.mongocamp.driver.mongodb.model.Person
5+
import dev.mongocamp.driver.mongodb.test.TestDatabase.PersonDAO
86

7+
import java.util.concurrent.TimeUnit
98
import scala.concurrent.ExecutionContext.Implicits.global
109
import scala.concurrent.duration.Duration
1110
import scala.concurrent.{Await, Future}

src/test/scala/dev/mongocamp/driver/mongodb/test/TestDatabase.scala

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ package dev.mongocamp.driver.mongodb.test
22

33
import better.files.File
44
import com.mongodb.client.model.changestream.OperationType
5+
import com.typesafe.scalalogging.LazyLogging
56
import dev.mongocamp.driver.mongodb.database.DatabaseProvider
67
import dev.mongocamp.driver.mongodb.model._
78
import dev.mongocamp.driver.mongodb.{GridFSDAO, MongoDAO}
8-
import com.typesafe.scalalogging.LazyLogging
99
import org.bson.codecs.configuration.CodecRegistries.{fromProviders, fromRegistries}
1010
import org.mongodb.scala.Document
1111
import org.mongodb.scala.bson.codecs.Macros._
@@ -19,20 +19,19 @@ object TestDatabase extends LazyLogging {
1919

2020
private val registry = fromProviders(classOf[Person], classOf[Friend], classOf[CodecTest], classOf[Book])
2121

22-
val provider =
23-
DatabaseProvider.fromPath(configPath = "unit.test.mongo", registry = fromRegistries(registry))
24-
25-
// provider.addChangeObserver(ChangeObserver(consumeDatabaseChanges))
22+
val provider = DatabaseProvider.fromPath(configPath = "unit.test.mongo", registry = fromRegistries(registry))
2623

27-
def consumeDatabaseChanges(changeStreamDocument: ChangeStreamDocument[Document]): Unit =
28-
if (changeStreamDocument.getOperationType != OperationType.INSERT)
24+
def consumeDatabaseChanges(changeStreamDocument: ChangeStreamDocument[Document]): Unit = {
25+
if (changeStreamDocument.getOperationType != OperationType.INSERT) {
2926
logger.info(
3027
"changed %s:%s with ID: %s".format(
3128
changeStreamDocument.getNamespace,
3229
changeStreamDocument.getOperationType,
3330
changeStreamDocument.getDocumentKey
3431
)
3532
)
33+
}
34+
}
3635

3736
object PersonDAO extends MongoDAO[Person](provider, "people")
3837

version.sbt

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,5 @@
1-
import scala.io.Source
2-
import scala.tools.nsc.io.File
1+
import dev.quadstingray.sbt.json.JsonFile
32

4-
ThisBuild / version := {
5-
val packageJsonFile = File("package.json")
6-
val source = Source.fromFile(packageJsonFile.toURI)
7-
val versionPattern = "\"version\":(.*?)\"(.*?)\"".r
8-
val versionPartString = versionPattern.findFirstIn(source.mkString).get
9-
val replacedVersion = versionPartString.replace("\"version\":", "").replace("\"", "").replace("\",", "").trim.trim.trim
10-
replacedVersion.toLowerCase.replace(".snapshot", "-SNAPSHOT")
11-
}
3+
val json = JsonFile(file("package.json"))
4+
5+
ThisBuild / version := json.stringValue("version")

0 commit comments

Comments
 (0)