[Groovy] Groovy是一种很酷的语言,可以轻松处理MongoDB -基本的增删改查操作-

这是2015年G* Advent日历的第7天文章。
昨天是yukungs先生的“用Groovy度过寒冬的故事#gadvent”。
像这样能处理硬件的人真的很酷!

首先

尽管这是 G* 的圣诞节日历,但我们考虑到可能会有对 MongoDB 感兴趣的人从 Google 等地方找到的情况,所以在这里写下了以下的文字。

对于 G* 用户来说,可能会觉得有些不自然,请谅解。

本次内容分为两个部分,今天我们将使用 Groovy 从 Java 的 mongo-java-driver 开始基础地处理 MongoDB,明天我们将进一步展示如何使用 Groovy 的库和语法更加简易方便地处理 MongoDB。

什么是Groovy?

正式名称是Apache Groovy。
这是一种在JVM上运行的动态类型语言,可以进一步简化Java语言。
在运行Groovy之前,需要安装Java 8。换句话说,只要有Java 8,它就可以运行!
如果已经安装了Java 8,请安装SDKMAN。
SDKMAN是一个可以轻松安装和管理各种与Groovy相关的产品以及其他各种Java平台产品的工具。
安装SDKMAN本身非常简单。
安装了SDKMAN之后,只需运行sdk install groovy,即可安装最新版本的Groovy。
如果您是Windows用户,请参考posh-gvm。
如果不太明白,也可以使用Groovy自身的安装程序进行安装,同时参考以下文章。
安装和运行(使用Groovy入门编程)

时髦的Groovy和MongoDB

我已经提到过,Groovy是一个让Java更方便的语言。简而言之,Groovy就是Java。
换句话说,Java的MongoDB库mongo-java-driver也可以直接从Groovy中使用。
而且,最近的mongo-java-driver比以前的版本简单得多,可以更轻松地处理MongoDB。
另外,使用Groovy还可以减少冗长的代码。
对于那些已经在Java中使用过MongoDB但还没尝试过最新的MongoDB驱动的人,
或者已经接触过MongoDB但从未从编程语言中操作过它的人,这些内容也有参考价值。
此外,Groovy还有针对MongoDB的第三方库GMongo,但最新的mongo-java-driver本身也能提供相当简洁的代码编写方式。对于那些除了GMongo外没有使用过其他Groovy库的用户来说,这也是一个有参考价值的内容。

前提

这次,我实际运行代码的环境是

NameValueOSLinux Mint 17MongogDB3.0.7Java1.8.0_60

在中文中重新表达以上内容:请确保在顶部提到的情况下事先安装最新版的Groovy。此外,为了流畅地执行本次示例代码,建议使用Groovy控制台。请参考这里的使用方法。

在这里不谈论Groovy语法的问题。但是,我认为您仍然可以了解到Groovy可以非常轻松地操作MongoDB。

开始编码

好了!那么我们就开始吧。

准备好

首先,我们要编写以下代码。

@Grab(group='org.mongodb', module='mongo-java-driver', version='3.1.1')

import com.mongodb.MongoClient
import com.mongodb.client.MongoDatabase
import com.mongodb.client.MongoCollection
import org.bson.Document
import static com.mongodb.client.model.Filters.*
import static com.mongodb.client.model.Sorts.*
import static com.mongodb.client.model.Updates.*
import com.mongodb.client.result.UpdateResult

只需根据这个描述,即可完成mongo-java-driver的下载、路径设置以及本次示例中所需的类的导入。

连接到MongoDB

请在上述代码的底部添加以下内容。
请注意,今后请不断将所有代码添加到现有代码的下方。

// MongoDB本体に接続
MongoClient mongoClient = new MongoClient("localhost", 27017)
// 利用するデータベースに接続(データベース名はgroovy)
MongoDatabase db = mongoClient.getDatabase("groovy")
// 利用するコレクションに接続(コレクション名はtest)
MongoCollection<Document> collection = db.getCollection("test")

这就是全部了!作为MongoDB的特点,特别是在没有事先存在数据库或集合的情况下也可以使用。
此外,Groovy在声明变量时不需要指定类型,因此可以使用def关键字更简洁地编写如下。

def mongoClient = new MongoClient("localhost", 27017)
def db = mongoClient.getDatabase("groovy")
def collection = db.getCollection("test")

此时,groovy数据库和test集合都还没有在MongoDB上创建。
在保存值到集合阶段,集合将会自动创建。

删除文件

突然ですが、今后多次执行示例代码,数据将不断积累在MongoDB中会变得麻烦。
因此,我们写一个代码来每次删除数据。

// DELETE ALL EXISTING DOCUMENTS
if (collection.count() > 0) {
    collection.deleteMany(new Document())
}

如果test集合中有文档,那么每次都会被删除。
虽然可能还不太理解上述内容,但只要继续往下进行,就会逐渐明白,所以请毫不犹豫地继续前进吧。

保存文档

那么,现在让我们开始保存文件吧!

// 1件登録する
Document doc = new Document("name", "MongoDB")
                .append("type", "database")
                .append("count", 1)
                .append("info", new Document("x", 203).append("y", 102))
collection.insertOne(doc)

// 複数件一気に登録する
List<Document> documents = (1..10).collect { new Document("i", it).append("name", it) }
collection.insertMany(documents)

不仅可以用于注册文档,还可以用于更新和删除文档的方法中,有一个用于处理单个文档的方法,还有一个用于同时处理多个文档的方法。
从上面的代码中可以一目了然地看出,insertOne()方法用于单个文档的注册,而insertMany()方法用于同时注册多个文档。
为了生成要注册到MongoDB的文档,我们需要创建一个名为Document的类的实例。
使用方法非常简单,将键名作为第一个参数传递,将值作为第二个参数传递。即使在将值传递给实例生成时的构造函数时,也可以通过将值传递给后续返回的实例的append方法来生成MongoDB文档。
如果文档是嵌套的,可以通过给值指定一个新的Document对象,就像上面示例中的info部分一样,来实现。上述doc的内容是

{
    "name" : "MongoDB", 
    "type" : "database", 
    "count" : 1,
    "info" : {
        "x" : 203,
        "y" : 102 
    }
}

就像这样的JSON。
你可以将这个JSON(Document对象)传递给insertOne,或者将它存入List并将该列表传递给insertMany,这样就可以将文档保存到MongoDB中。
此时,groovy数据库和test集合都已经保存到MongoDB中。

获取文件

获取文档,相当于SQL中的SELECT语句。
基本上只需要运行Collection对象的find方法即可。
运行find方法后,将返回一个包含存储的文档的列表。
进一步运行first方法,可以从该列表中获取第一个文档对象。

// 全件取得
assert 11 == collection.find().size()

// 最初のレコードを取得
Document myDoc = collection.find().first()
assert "MongoDB" == myDoc.name

很简单。
那么,当我们在搜索时要指定条件,应该怎么做呢?
也很简单,只需在find中直接写下条件即可。
就像直接从MongoDB的mongo shell中操作一样方便流畅!

myDoc = collection.find(eq('i', 5)).first()
assert 5 == myDoc.i
assert 45 == collection.find(gte('i',5)).sum{it.i} // シンプルな取得条件
assert 30 == collection.find( and(gt('i',5),lt('i',10)) ).sum{it.i} // ANDで複数の条件を指定

有一个记述.sum{it.i},这是Groovy的方法。
它表示从存储在列表中的每个元素中获取i的总和的操作。
你会发现,自然地将表达式从MongoDB流向Groovy是可行的。
使用Groovy,甚至可以如此简单地计算总和!
有关可用的比较运算符(如gt和lt)请参考Filters。

下面是一个示例,将根据指定的值进行排序,并获取第一个文档的i的值。

// iというフィールドを持つドキュメントを、降順で取得する。そして、最初の値を取得。
// なお、sortとdescendingは、com.mongodb.client.model.Sortsクラスにある。
assert 10 == collection.find( exists('i') ).sort(descending('i')).first().i

由于MongoDB是面向文档的数据库,与关系型数据库不同,文档可以含有或不含有特定的字段。
因此,在上述示例中,我们使用exists首先检索出具有字段”i”的文档,然后按照字段”i”降序排列,并从中获取第一个文档的”i”的值。
有关sort的详细信息,请参考Sorts。

文档的更新

接下来是文件的更新。相当于SQL中的UPDATE操作。

// UpdateOneの第1引数に条件式、第2引数にUPDATEで実行する式を与える。
// 存在しないフィールドを与えれば該当するドキュメントにそのフィールド追加される。以下はiフィールドが追加される
// フィールドが存在する場合は、単純にそのフィールドの値を上書きする。
collection.updateOne( eq('name', 'MongoDB'), set('i', 1) )

// UPDATEで複数のフィールドをアップデートしたい場合は、combineの中に記述する。
// nameフィールドがMongoDBというドキュメントを1件更新する。
// iフィールドと、countフィールドがそれぞれ1インクリメントされる。
collection.updateOne( eq('name', 'MongoDB'), combine( inc('i',1), inc('count',1) ) )

// 複数のドキュメントを一気にUPDATE
// iフィールドの値が8より大きいドキュメントのiフィールドの値を10インクリメントする。
UpdateResult ur = collection.updateMany(gt('i',8), inc('i',10)) // このサンプルではiが9と10のものは、iの値に10を加算する。

// 更新されたか確認してみる。なお、collectとsumはGroovyの便利なメソッド!
assert 39 == collection.find(gt('i',8)).sum{it.i}
assert 2 == collection.find(eq('name', 'MongoDB')).first().i
assert 2 == ur.matchedCount

如果您看看上面的源代码和注释,大概就能明白了。
如果您只想更新一个文档,可以使用updateOne,如果想批量更新多个文档,可以使用updateMany。
每个方法都需要传入的第一个参数是要更新的文档条件,第二个参数是要更新的内容。
您可以参考Updates中提供的更新方法列表,如inc或set等。
与SQL中的UPDATE TABLE_NAME SET i=1 WHERE i > 8的语句相比,更新内容首先出现,然后通过WHERE指定条件,所以顺序正好相反。

删除文件

你已经在程序的顶部写下了删除所有记录的代码了吗?
以下是只删除检索到的一条记录的方法。

// 1件削除
collection.deleteOne(gt('i', 8))

如果只需要删除一条记录,与创建或更新文档一样,可以使用deleteOne;如果需要删除所有相关的文档,可以使用deleteMany。

其他

在更新和删除文档时指定条件是可以的,但如果适用于所有文件会怎样呢?这个问题是,就像最开始的deleteMany一样,将new Document()作为第一个参数条件传递,所有文档都将成为处理的对象。

总结

你觉得怎么样呢?从G*开发者和Java开发者那里,使用最新的JavaDriver可以让处理MongoDB的代码相当简单。从MongoDB用户那里,使用Groovy可以很容易地通过程序来处理MongoDB。但是Groovy的便利性不仅仅限于此!明天我会发布更多使用Groovy功能的示例!

最后,我会在下面附上当前代码的全部内容。

@Grab(group='org.mongodb', module='mongo-java-driver', version='3.1.1')

import com.mongodb.MongoClient
import com.mongodb.client.MongoDatabase
import com.mongodb.client.MongoCollection
import org.bson.Document
import static com.mongodb.client.model.Filters.*
import static com.mongodb.client.model.Sorts.*
import static com.mongodb.client.model.Updates.*
import com.mongodb.client.result.UpdateResult

// MongoDB本体に接続
MongoClient mongoClient = new MongoClient("localhost", 27017)
// 利用するデータベースに接続(データベース名はgroovy)
MongoDatabase db = mongoClient.getDatabase("groovy")
// 利用するコレクションに接続(コレクション名はtest)
MongoCollection<Document> collection = db.getCollection("test")

// DELETE ALL EXISTING DOCUMENTS
if (collection.count() > 0) {
    collection.deleteMany(new Document())
}

// 1件登録する
Document doc = new Document("name", "MongoDB")
                .append("type", "database")
                .append("count", 1)
                .append("info", new Document("x", 203).append("y", 102))
collection.insertOne(doc)

// 複数件一気に登録する
List<Document> documents = (1..10).collect { new Document("i", it).append("name", it) }
collection.insertMany(documents)

assert 11 == collection.find().size()

// 最初のレコードを取得
Document myDoc = collection.find().first()
assert "MongoDB" == myDoc.name

myDoc = collection.find(eq('i', 5)).first()
assert 5 == myDoc.i
assert 45 == collection.find(gte('i',5)).sum{it.i} // シンプルな取得条件
assert 30 == collection.find( and(gt('i',5),lt('i',10)) ).sum{it.i} // ANDで複数の条件を指定

// iというフィールドを持つドキュメントを、降順で取得する。そして、最初の値を取得。
// なお、sortとdescendingは、com.mongodb.client.model.Sortsクラスにある。
assert 10 == collection.find( exists('i') ).sort(descending('i')).first().i

// UpdateOneの第1引数に条件式、第2引数にUPDATEで実行する式を与える。
// 存在しないフィールドを与えれば該当するドキュメントにそのフィールド追加される。以下はiフィールドが追加される
// フィールドが存在する場合は、単純にそのフィールドの値を上書きする。
collection.updateOne( eq('name', 'MongoDB'), set('i', 1) )

// UPDATEで複数のフィールドをアップデートしたい場合は、combineの中に記述する。
// nameフィールドがMongoDBというドキュメントを1件更新する。
// iフィールドと、countフィールドがそれぞれ1インクリメントされる。
collection.updateOne( eq('name', 'MongoDB'), combine( inc('i',1), inc('count',1) ) )

// 複数のドキュメントを一気にUPDATE
// iフィールドの値が8より大きいドキュメントのiフィールドの値を10インクリメントする。
UpdateResult ur = collection.updateMany(gt('i',8), inc('i',10)) // このサンプルではiが9と10のものは、iの値に10を加算する。

// 更新されたか確認してみる。なお、collectとsumはGroovyの便利なメソッド!
assert 39 == collection.find(gt('i',8)).sum{it.i}
assert 2 == collection.find(eq('name', 'MongoDB')).first().i
assert 2 == ur.matchedCount

// 1件削除
collection.deleteOne(gt('i', 8))

请进行参考

开始使用MongoDB(Java版)
MongoDB Java驱动程序
Apache Groovy
SDKMAN
过滤器
排序
更新

广告
将在 10 秒后关闭
bannerAds