所以最近,我需要存储从Web服务返回的非结构化JSON数据。 该网络服务正在返回来自世界各地的各种足球队。 在大多数足球队包含的数据中,有一部分足球运动员属于该队。 有些球队有12名球员,有些则有20名,有些甚至超过20名。球员拥有自己的属性,有些很容易预测,有些则不可能。 对于整个数据结构,我知道肯定会回来的唯一属性是团队的团队名称。 之后,它取决于每个团队。
{
"teams": [{
"teamname":"Kung fu pirates",
"founded":1962,
"players": [
{"name": "Robbie Fowler", "age": 56},
{"name": "Larry David", "age": 55}
...
]},
{
"teamname":"Climate change observers",
"founded":1942,
"players": [
{"name": "Jim Carrey", "age": 26},
{"name": "Carl Craig", "age": 35}
...
]},
...
]
}
有几种不同的方法来存储此数据。 我决定去MongoDB。 主要原因:
- 我想以与从Web服务获取的JSON响应尽可能接近的格式存储数据。 这意味着更少的代码,更少的错误,更少的麻烦。
- 我想要的东西学习曲线很低,有很好的文档和良好的行业支持(stackoverflow线程,博客文章等)
- 包含已记录的grails插件,出现脚步声并看起来像在维护的东西
- 诸如文本词干等功能很不错。 一些支持本来会很好,但是并不需要缩短年龄。
- 将支持良好的JSON搜索工具,索引等。
MongoDB勾选了所有框。 这就是我如何使其全部正常工作的方式。 按照Mongo的说明和MongoDB Grails插件安装MongoDB之后,就该编写一些代码了。 现在这是整洁的部分,几乎没有任何代码。 我为团队创建了一个域对象。
class Team implements Serializable {
static mapWith = "mongo"
static constraints = {
}
static mapping = {
teamname index: true
}
String teamname
List players
static embedded = ['players']
}
关于团队域对象:
- 关于Team域对象的第一点是,我什至不需要创建它。 我之所以使用这种方法,是因为我愿意的话可以使用GORM风格的api,例如Team.find() 。
- 玩家只是对象列表。 我没有费心创建一个Player对象。 我喜欢始终确保团队中的球员始终处于List数据结构中的想法,但是我认为不需要进一步键入任何内容。
- 播放器被标记为嵌入式 。 这意味着团队和参与者都存储在单个非规范化数据结构中。 除其他事项外,这使您能够在单个数据库操作中检索和操作团队数据。
- 我将团队名称标记为索引。
- 我将域对象标记为:
static mapWith = "mongo"
这意味着,如果我还在我的GORM上使用了另一个持久性解决方案(postgres,MySQL等),我告诉GORM该Team域类仅适用于Mongo,请避免使用关系。 参见这里获取信息。
注意:这很好地提醒了GORM是比休眠更高的抽象级别。 可能有一个不使用休眠模式但转到NoSQL存储区并且没有进入休眠模式的GORM对象。
您会注意到,在JSON中,存在尚未在Team类中显式声明的team属性(例如founded) 。 这就是Groovy和NoSQL彼此真正发挥作用的地方。 我们可以使用Groovy的某些Meta编程功能将属性动态添加到Team域对象。
private List importTeams(int page) {
def rs = restClient.get("teams") // invoke web service
List teams = rs.responseData.teams.collect {
teamResponse ->
Team team = new Team(teamname: teamResponse.teamname)
team.save(); // Save is needed to dynamically add the attribute
teamname.each {key, value ->
team["$key"] = value
}
teamname.save(); // We need the second save to ensure the variants get saved.
return teamname
}
log.info("importTeams(),teams=teams);
teams
}
好的,所以我们的importTeams()方法的要点是:
- 得到JSON响应后,我们在teams数组上运行了一个collect函数。 这将创建团队域对象。
- 我们使用一些元编程将JSON团队结构中返回的任何属性动态添加到Team对象。 注意:我们必须先调用save()才能将Team域对象中声明的属性动态添加到Team域对象中。 我们还必须再次调用save(),以确保在Team域对象中声明的属性可以确保被保存。 这可能会在将来的MongoDB插件版本中发生变化,但这是我必须要做的工作(我使用的是MongoDB插件3.0.1版)
下一个是什么? 编写一些查询。 好的,这里有两个选择。 首先,得益于MongoDB插件,您可以将动态查找器和条件查询与GORM一起使用。 但是,我没有这样做。 为什么? 我想写的查询尽可能接近应该如何在Mongo中编写。 造成这种情况的原因有很多:
- 在这里泄漏抽象是不可避免的。 迟早您将不得不编写一个查询,指出GORM不能很好地执行。 最好还是朝这个方向前进。
- 我希望能够首先在Mongo控制台中运行查询,检查解释计划(如果需要),然后在代码中使用相同的查询。 如果我直接编写查询而不必担心GORM会做什么,则这样做更容易。
查询的一般格式为:
teams = Team.collection.find(queryMap) // where queryMap is a map of fields and the various values you are searching for.
好的,一些查询示例...
Team.collection.find(["teamname":"hicks"]) // Find a team name hicks
Team.collection.find(["teamname":"hicks", "players.name": "Robbie Fowler"] // As above but also has Robbie Fowler
Team.collection.find(["players.name": "Robbie Fowler"] // Any teams that has a Robbie Fowler
Team.collection.find(["teamname":"hicks", "players.name": "Robbie Fowler", {"players.$":1}] // Returns matching player only
Team.collection.find(["teamname":"/ick/"]) // Match on the regular expression /ick/, i.e. any team that contains text ick.
还要别的吗? 当然可以。 在开发过程中,我想连接到我自己的机器上的Mongo实例,但在其他环境(CI,舞台,生产)中,我想连接到专用服务器上的Mongo机器。 为此,我将DataSource.groovy更新为:
environments {
development {
grails {
mongo {
host = "localhost"
port = 27017
username = "test"
password = "test"
databaseName = "mydb"
}
}
dataSource {
dbCreate = "create-drop" // one of 'create', 'create-drop', 'update', 'validate', ''
url = "jdbc:h2:mem:devDb;MVCC=TRUE;LOCK_TIMEOUT=10000"
}
}
ci {
println("In bamboo environment")
grails {
mongo {
host = "10.157.192.99"
port = 27017
username = "shop"
password = "shop"
databaseName = "tony"
}
}
dataSource {
dbCreate = "create-drop" // one of 'create', 'create-drop', 'update', 'validate', ''
url = "jdbc:h2:mem:devDb;MVCC=TRUE;LOCK_TIMEOUT=10000"
}
}
}
您会看到我已经配置了多个数据源(MongoDB和PostGres)。 我不主张同时使用MongoDB和关系数据库,只是指出这是可能的。 另一点是,MongoDB配置始终处于以下状态: grails {mongo {
好的,这是一个简单的介绍性帖子,我将尽快尝试发布更复杂的内容。 在下一次之前,请多保重。
翻译自: https://www.javacodegeeks.com/2014/06/mongodb-and-grails.html