我有一个调查表,看起来像这样:
{
id: Id,
date: Date,
clients: [{
client_id: Id,
contacts: [{
contact_id: Id,
score: Number,
feedback: String,
email: String
}]
}]
}
我需要在特定联系人下更新score
和feedback
字段。目前,我正在运行这样的更新:
function saveScore(obj){
var dfd = q.defer();
var survey = surveys.get(obj.survey_id);
survey
.pluck({ clients: 'contacts' })
.run()
.then(results => {
results.clients.forEach((item, outerIndex) => {
item.contacts.forEach((item, index, array) => {
if(Number(item.contact_id) === Number(obj.contact_id)) {
array[index].score = obj.score;
console.log(outerIndex, index);
}
});
});
return survey.update(results).run()
})
.then(results => dfd.resolve(results))
.catch(err => dfd.resolve(err));
return dfd.promise;
};
当我查看update方法时,它指定了如何更新嵌套的键:值对。但是,我找不到任何更新数组中单个项的示例。
是否有更好的,更有希望更新的方式来更新嵌套数组中的项目?
您可能需要将数组filter
从数组中取出所需的值,然后再将其追加到数组中。然后,您可以将更新的数组传递给update
方法。
例
假设您有一个包含两个客户端的文档,这两个客户端都有name
和score
,并且您希望更新其中一个得分:
{
"clients": [
{
"name": "jacob" ,
"score": 200
} ,
{
"name": "jorge" ,
"score": 57
}
] ,
"id": "70589f08-284c-495a-b089-005812ec589f"
}
您可以获取该特定文档,使用匿名函数运行update
命令,然后将新的更新数组传递到clients
属性。
r.table('jacob').get("70589f08-284c-495a-b089-005812ec589f")
.update(function (row) {
return {
// Get all the clients, expect the one we want to update
clients: row('clients').filter(function (client) {
return client('name').ne('jorge')
})
// Append a new client, with the update information
.append({ name: 'jorge', score: 57 })
};
});
我认为这有点麻烦,并且可能有更好,更优雅的方式,但这应该可以解决您的问题。
数据库架构
也许值得为您的所有联系人创建一个contacts
表,然后对您的数据进行某种加入。然后你的contacts
数组中的clients
属性看起来像:
{
id: Id,
date: Date,
clients: [{
client_id: Id,
contact_scores: {
Id: score(Number)
},
contact_feedbacks: {
Id: feedback(String)
}
}]
}
这个对我有用
r.table(...).get(...).update({
contacts: r.row('Contacts').changeAt(0,
r.row('Contacts').nth(0).merge({feedback: "NICE"}))
})
数据库架构
{
"clients": [
{
"name": "jacob" ,
"score": 200
} ,
{
"name": "jorge" ,
"score": 57
}
] ,
"id": "70589f08-284c-495a-b089-005812ec589f"
}
然后你可以使用map
和branch
查询这样做。
r.db('users').table('participants').get('70589f08-284c-495a-b089-005812ec589f')
.update({"clients": r.row('clients').map(function(elem){
return r.branch(
elem('name').eq("jacob"),
elem.merge({ "score": 100 }),
elem)})
})
在ReThinkDB(和大多数查询语言)中创建查询以就地更新JSON对象数组是一个相当复杂的过程。我所知道的ReQL中最好(也是唯一)的解决方案是使用update
,offsetsOf
,do
,changeAt
和merge
函数的组合。此解决方案将保留数组中对象的顺序,并仅修改offsetsOf
方法中匹配的对象的值。
以下代码(或类似的东西)可用于更新包含对象数组(即clients
)的对象数组(即contracts
)。
必须提供
'%_databaseName_%'
,'%_tableName_%'
,'%_documentUUID_%'
,%_clientValue_%
和%_contractValue_%
。
r.db('%_databaseName_%').table('%_tableName_%').get('%_documentUUID_%').update(row =>
row('clients')
.offsetsOf(clients => client('client_id').eq('%_clientValue_%'))(0)
.do(clientIndex => ({
clients: row('clients')(clientIndex)
.offsetsOf(contacts => contact('contact_id').eq('%_contactValue_%')))(0)
.do(contactIndex => ({
contacts: row(clientIndex)
.changeAt(contractIndex, row(clientIndex)(contractIndex).merge({
'score': 0,
'feedback': 'xyz'
}))
})
}))
)
survey
.pluck({ clients: 'contacts' }).run()
.then(results => {
results.clients.forEach((item, outerIndex) => {
item.contacts.forEach((item, index, array) => {
if(Number(item.contact_id) === Number(obj.contact_id)) {
array[index].score = obj.score;
console.log(outerIndex, index);
}
});
});
return survey.update(results).run()
})
虽然Jacob(在上面显示的Stack Overflow上提问题的用户)提供的代码可能看起来更简单,但性能可能不如ReQL解决方案。
1)ReQL解决方案在查询服务器(即数据库端)上运行,因此在数据库写入期间优化代码(更高性能)。鉴于上面的代码,没有充分利用查询服务器,并且在运行pluck().run()
查询之后,在客户端请求端(即NodeJs端)处理读取和写入请求update().run()
和pluck()
(更低)性能)。
2)上述代码要求查询服务器将所有数据发送回客户端请求端(即NodeJs侧),因此响应有效载荷(因特网带宽使用/下载大小)可以是几兆字节。虽然ReQL解决方案是在查询服务器上处理的,因此响应有效负载通常只是确认写入已完成,换句话说,只有几个字节被发送回客户端请求端。这是在单个请求中完成的。
但是,在使用JSON时,ReQL(尤其是SQL)似乎过于复杂,在我看来,在使用JSON时应该使用JSON。
我还建议ReThinkDB社区采用替代ReQL而不是使用JSON(https://github.com/rethinkdb/rethinkdb/issues/6736)。
更新嵌套JSON数组的解决方案应该像...一样简单
r('database.table').update({
clients: [{
contacts: [{
score: 0,
feedback: 'xyz',
}]
}]
});