MongoDB 中的自连接查询
Self joining query in MongoDB
我们正在探索从关系数据库迁移到 MongoDB 的可能性,并且在处理查询时遇到困难,数据模式是这样的:
data_store:
id,
userId,
study,
form,
formData: {
element1: value1,
element2: value2,
.....
}
formData 是一个 json 动态数组 element:value 对,不同的表单有不同的预定义元素列表。
要求是在一个研究中给定2个表单,我们需要为同一用户在一行中显示它们所有元素的数据,即我们需要通过userId加入并以平面结构显示formData。
另外,一个用户可能对同一个表单有多个数据输入,所以如果一个用户对表单 A 有 3 个条目,对表单 B 有 4 个条目,我们预计结果中有 12 行。
示例数据:
id: "id1",
user: "user1",
study: "study",
form: "f1",
formData: [
{ "e11": "value1" },
{ "e12": "value2" },
{ "e13": "value3" }
]
,
id: "id2",
user: "user1",
study: "study",
form: "f1",
formData: [
{ "e11": "value4" },
{ "e12": "value5" },
{ "e13": "value6" }
]
,
id: "id3",
user: "user1",
study: "study",
form: "f2",
formData: [
{ "e21": "value7" },
{ "e22": "value8" }
]
,
id: "id4",
user: "user1",
study: "study1",
form: "f2",
formData: [
{ "e21": "value9" },
{ "e22": "value10" }
]
,
id: "id2",
user: "user2",
study: "study1",
form: "f2",
formData: [
{ "e21": "value11" },
{ "e22": "value12" }
以上示例数据的预期结果是:
行#
学习
用户
f1.e11
f1.e12
f1.e13
f2.e21
f2.e22
1
学习
用户 1
值 1
值2
值3
值7
值8
2
学习
用户 1
值 1
值2
值3
值9
值 10
3
学习
用户 1
值4
值5
值6
值7
值8
4
学习
用户 1
值4
值5
值6
值9
值 10
5
学习
用户 2
值 11
值 12
关系数据库中类似的查询可能是这样的:
select t1.*, t2.*
from data_store t1, data_store t2
where t1.form = 'f1'
and t2.form = 'f2'
and t1.userId = t2.userId;
我很难将其转换为 MongoDB 查询,任何人都可以提供一些帮助,将不胜感激。
您可以使用下面的聚合查询来获得所需的结果。
db.collection.aggregate([
{
$group: {
_id: {
"user": "$user",
"form": "$form",
},
"formData": {
"$push": {
"$concatArrays": [
"$formData",
[
{
"study": "$study"
}
]
]
},
},
}
},
{
$group: {
_id: {
"user": "$_id.user",
},
formData: {
"$push": "$formData"
}
}
},
{
$project: {
"formData": {
"$map": {
"input": {
"$arrayElemAt": [
"$formData",
0
]
},
"as": "f1",
"in": {
"$cond": {
"if": {
"$gt": [
{
"$size": "$formData"
},
1
]
},
"then": {
"$map": {
"input": {
"$arrayElemAt": [
"$formData",
1
]
},
"as": "f2",
"in": {
"$concatArrays": [
"$$f1",
"$$f2"
],
},
},
},
"else": [
"$$f1"
]
},
},
},
},
}
},
{
$unwind: {
path: "$formData",
}
},
{
$unwind: {
path: "$formData",
}
},
{
$replaceRoot: {
newRoot: {
"$mergeObjects": [
{
"user": "$_id.user"
},
{
"$reduce": {
"input": "$formData",
"initialValue": {},
"in": {
"$mergeObjects": [
"$$this",
"$$value"
],
}
},
},
],
}
}
}
])
Mongo Playground Working Sample
如果你想要每个阶段的解释,请告诉我。
限制:
这仅在有两种形式的研究 f1
和 f2
.
时才有效
我正在努力使这个动态化,完成后会更新这个答案。
编辑:
利用这个动态运行的更新查询。
db.collection.aggregate([
{
"$match": {
"form": {
"$in": [
"f1",
"f2"
]
},
},
},
{
"$group": {
"_id": "$user",
"formData": {
"$push": {
"$mergeObjects": [
{
"$reduce": {
"input": "$formData",
"initialValue": {},
"in": {
"$mergeObjects": [
"$$value",
{
"$arrayToObject": {
"$map": {
"input": {
"$objectToArray": "$$this"
},
"as": "formValue",
"in": {
"k": {
"$concat": [
"$form",
"-",
"$$formValue.k"
]
},
"v": "$$formValue.v"
}
},
},
},
]
},
}
},
{
"study": "$study",
"user": "$user",
"form": "$form"
},
],
},
},
},
},
{
"$project": {
"formData": {
"$cond": {
"if": {
"$gt": [
{
"$size": "$formData"
},
1
]
},
"then": {
$reduce: {
input: {
$range: [
0,
{
$size: "$formData"
}
]
},
initialValue: [],
in: {
$concatArrays: [
"$$value",
{
$let: {
vars: {
i: "$$this"
},
in: {
$map: {
input: {
$range: [
{
$add: [
1,
"$$i"
]
},
{
$size: "$formData"
}
]
},
in: [
{
"$let": {
"vars": {
"arrayElem1": {
$arrayElemAt: [
"$formData",
"$$i"
]
},
"arrayElem2": {
$arrayElemAt: [
"$formData",
"$$this"
]
},
},
"in": {
"$cond": {
"if": {
"$and": [
{
"$ne": [
"$$arrayElem1.form",
"$$arrayElem2.form"
]
},
// {
// "$eq": [
// "$$arrayElem1.user",
// "$$arrayElem2.user"
// ]
// },
],
},
"then": {
"$mergeObjects": [
"$$arrayElem2",
"$$arrayElem1",
],
},
"else": "$$REMOVE",
},
},
}
}
]
}
}
}
}
]
}
}
},
"else": [
"$formData"
]
},
},
},
},
{
"$project": {
"formData": {
"$reduce": {
"input": "$formData",
"initialValue": [],
"in": {
"$concatArrays": [
"$$value",
{
"$cond": {
"if": {
"$ne": [
"$$this",
[
null
]
]
},
"then": "$$this",
"else": []
}
}
]
}
}
}
}
},
{
"$unwind": "$formData"
},
{
"$replaceRoot": {
"newRoot": "$formData"
}
},
])
我们正在探索从关系数据库迁移到 MongoDB 的可能性,并且在处理查询时遇到困难,数据模式是这样的:
data_store:
id,
userId,
study,
form,
formData: {
element1: value1,
element2: value2,
.....
}
formData 是一个 json 动态数组 element:value 对,不同的表单有不同的预定义元素列表。
要求是在一个研究中给定2个表单,我们需要为同一用户在一行中显示它们所有元素的数据,即我们需要通过userId加入并以平面结构显示formData。
另外,一个用户可能对同一个表单有多个数据输入,所以如果一个用户对表单 A 有 3 个条目,对表单 B 有 4 个条目,我们预计结果中有 12 行。
示例数据:
id: "id1",
user: "user1",
study: "study",
form: "f1",
formData: [
{ "e11": "value1" },
{ "e12": "value2" },
{ "e13": "value3" }
]
,
id: "id2",
user: "user1",
study: "study",
form: "f1",
formData: [
{ "e11": "value4" },
{ "e12": "value5" },
{ "e13": "value6" }
]
,
id: "id3",
user: "user1",
study: "study",
form: "f2",
formData: [
{ "e21": "value7" },
{ "e22": "value8" }
]
,
id: "id4",
user: "user1",
study: "study1",
form: "f2",
formData: [
{ "e21": "value9" },
{ "e22": "value10" }
]
,
id: "id2",
user: "user2",
study: "study1",
form: "f2",
formData: [
{ "e21": "value11" },
{ "e22": "value12" }
以上示例数据的预期结果是:
行# | 学习 | 用户 | f1.e11 | f1.e12 | f1.e13 | f2.e21 | f2.e22 |
---|---|---|---|---|---|---|---|
1 | 学习 | 用户 1 | 值 1 | 值2 | 值3 | 值7 | 值8 |
2 | 学习 | 用户 1 | 值 1 | 值2 | 值3 | 值9 | 值 10 |
3 | 学习 | 用户 1 | 值4 | 值5 | 值6 | 值7 | 值8 |
4 | 学习 | 用户 1 | 值4 | 值5 | 值6 | 值9 | 值 10 |
5 | 学习 | 用户 2 | 值 11 | 值 12 |
关系数据库中类似的查询可能是这样的:
select t1.*, t2.*
from data_store t1, data_store t2
where t1.form = 'f1'
and t2.form = 'f2'
and t1.userId = t2.userId;
我很难将其转换为 MongoDB 查询,任何人都可以提供一些帮助,将不胜感激。
您可以使用下面的聚合查询来获得所需的结果。
db.collection.aggregate([
{
$group: {
_id: {
"user": "$user",
"form": "$form",
},
"formData": {
"$push": {
"$concatArrays": [
"$formData",
[
{
"study": "$study"
}
]
]
},
},
}
},
{
$group: {
_id: {
"user": "$_id.user",
},
formData: {
"$push": "$formData"
}
}
},
{
$project: {
"formData": {
"$map": {
"input": {
"$arrayElemAt": [
"$formData",
0
]
},
"as": "f1",
"in": {
"$cond": {
"if": {
"$gt": [
{
"$size": "$formData"
},
1
]
},
"then": {
"$map": {
"input": {
"$arrayElemAt": [
"$formData",
1
]
},
"as": "f2",
"in": {
"$concatArrays": [
"$$f1",
"$$f2"
],
},
},
},
"else": [
"$$f1"
]
},
},
},
},
}
},
{
$unwind: {
path: "$formData",
}
},
{
$unwind: {
path: "$formData",
}
},
{
$replaceRoot: {
newRoot: {
"$mergeObjects": [
{
"user": "$_id.user"
},
{
"$reduce": {
"input": "$formData",
"initialValue": {},
"in": {
"$mergeObjects": [
"$$this",
"$$value"
],
}
},
},
],
}
}
}
])
Mongo Playground Working Sample
如果你想要每个阶段的解释,请告诉我。
限制:
这仅在有两种形式的研究 f1
和 f2
.
我正在努力使这个动态化,完成后会更新这个答案。
编辑:
利用这个动态运行的更新查询。
db.collection.aggregate([
{
"$match": {
"form": {
"$in": [
"f1",
"f2"
]
},
},
},
{
"$group": {
"_id": "$user",
"formData": {
"$push": {
"$mergeObjects": [
{
"$reduce": {
"input": "$formData",
"initialValue": {},
"in": {
"$mergeObjects": [
"$$value",
{
"$arrayToObject": {
"$map": {
"input": {
"$objectToArray": "$$this"
},
"as": "formValue",
"in": {
"k": {
"$concat": [
"$form",
"-",
"$$formValue.k"
]
},
"v": "$$formValue.v"
}
},
},
},
]
},
}
},
{
"study": "$study",
"user": "$user",
"form": "$form"
},
],
},
},
},
},
{
"$project": {
"formData": {
"$cond": {
"if": {
"$gt": [
{
"$size": "$formData"
},
1
]
},
"then": {
$reduce: {
input: {
$range: [
0,
{
$size: "$formData"
}
]
},
initialValue: [],
in: {
$concatArrays: [
"$$value",
{
$let: {
vars: {
i: "$$this"
},
in: {
$map: {
input: {
$range: [
{
$add: [
1,
"$$i"
]
},
{
$size: "$formData"
}
]
},
in: [
{
"$let": {
"vars": {
"arrayElem1": {
$arrayElemAt: [
"$formData",
"$$i"
]
},
"arrayElem2": {
$arrayElemAt: [
"$formData",
"$$this"
]
},
},
"in": {
"$cond": {
"if": {
"$and": [
{
"$ne": [
"$$arrayElem1.form",
"$$arrayElem2.form"
]
},
// {
// "$eq": [
// "$$arrayElem1.user",
// "$$arrayElem2.user"
// ]
// },
],
},
"then": {
"$mergeObjects": [
"$$arrayElem2",
"$$arrayElem1",
],
},
"else": "$$REMOVE",
},
},
}
}
]
}
}
}
}
]
}
}
},
"else": [
"$formData"
]
},
},
},
},
{
"$project": {
"formData": {
"$reduce": {
"input": "$formData",
"initialValue": [],
"in": {
"$concatArrays": [
"$$value",
{
"$cond": {
"if": {
"$ne": [
"$$this",
[
null
]
]
},
"then": "$$this",
"else": []
}
}
]
}
}
}
}
},
{
"$unwind": "$formData"
},
{
"$replaceRoot": {
"newRoot": "$formData"
}
},
])