问题
Having data 'localized' like this:
{
"cs": [
{
"cID": "00001",
"title": {
"en": "title en1",
"de": "title de1"
},
"desc": {
"en": "desc en1",
"de": "desc de1"
},
"startDate": 20210801,
"endDate": 20210809,
"numDays": 8
},
{
"cID": "00002",
"title": {
"en": "title en2",
"de": "title de2"
},
"desc": {
"en": "desc en2",
"de": "desc de2"
},
"startDate": 20210701,
"endDate": 20210715,
"numDays": 14
}
]
}
What would be the best way to query this taking user locale (passed to query as param) into account? For example, if "en" was passed, the query should return this for "cID": "00001" :
{
"cID": "00001",
"title": "title en1",
"desc": "desc en1",
"startDate": 20210801,
"endDate": 20210809,
"numDays": 8
}
Ideally, the query should be 'generic' and not filter specifically the 'title' and 'desc' objects.
I know that:
db.cs.find({
"cID": "00001"
},
{
"_id": 0,
"cID": 1,
"title": "$title.en",
"desc": "$desc.en",
"startDate": 1,
"endDate": 1,
"numDays": 1
})
will give me:
[
{
"cID": "00001",
"desc": "desc en1",
"endDate": 2.0210809e+07,
"numDays": 8,
"startDate": 2.0210801e+07,
"title": "title en1"
}
]
but it would be tricky to handle it with many locales and different data models in different queries.
Mongo playground: https://mongoplayground.net/p/9erh-VYiOO4
回答1:
You can try,
$reduce
to iterate loop oftitle
array,$cond
to matchlocal
and return match result to value, same process for description array
{
$addFields: {
title: {
$reduce: {
input: "$title",
initialValue: "",
in: {
$cond: [{ $eq: ["$$this.locale", "pl"] }, "$$this.value", "$$value"]
}
}
},
description: {
$reduce: {
input: "$description",
initialValue: "",
in: {
$cond: [{ $eq: ["$$this.locale", "pl"] }, "$$this.value", "$$value"]
}
}
}
}
}
for the generic option you can make a function with two parameters first one is input field with $
sign and second one is locale:
function languageFilter(inputField, locale) {
return {
$reduce: {
input: inputField,
initialValue: "",
in: {
$cond: [{ $eq: ["$$this.locale", locale] }, "$$this.value", "$$value"]
}
}
};
}
Your final query would be:
let locale = "pl";
db.cs.aggregate([
{ $match: { cID: "00001" } },
{
$addFields: {
title: languageFilter("$title", locale)
description: languageFilter("$description", locale)
}
}
]);
回答2:
To create a generic query one would need the aggregation framework as it has some handy operators to help you with this. To start with, you would need to convert the embedded document into array of key/value pairs and then filter the array on the key field passing in the locale as the parameter.
For example, convert the document
"title": {
"en": "title en2",
"de": "title de2"
},
to an array
"title": [
{ "k": "en", '"v": "title en2" },
{ "k": "de", "v": "title de2" }
],
using $objectToArray operator. You can then filter this array on the key field using the $filter operator as
{
'$filter': {
'input': { '$objectToArray': '$title' },
'cond': { '$eq': ['$$this.k', locale] }
}
}
where the variable locale is derived from the passed parameter.
Once you have the filtered array, getting the value field requires the $arrayElemAt operator applied on the value key as
{
'$arrayElemAt': ['$title.v', 0]
}
So in the end you will have to run a pipeline like this:
var locale = 'en';
db.cs.aggregate([
{ '$match': { "cID" : "00001" } },
{ '$addFields': {
'title': {
'$filter': {
'input': { '$objectToArray': '$title' },
'cond': { '$eq': ['$$this.k', locale] }
}
},
'desc': {
'$filter': {
'input': { '$objectToArray': '$desc' },
'cond': { '$eq': ['$$this.k', locale] }
}
}
} },
{ '$addFields': {
'title': {
'$arrayElemAt': ['$title.v', 0]
},
'desc': {
'$arrayElemAt': ['$desc.v', 0]
}
} }
]);
And with some refactoring:
var locale = 'en';
var getFilterOperatorExpression = function (field) {
return {
'$filter': {
'input': { '$objectToArray': '$'+ field },
'cond': { '$eq': ['$$this.k', locale] }
}
}
};
var getValueOperatorExpression = function (field) {
return {
'$arrayElemAt': ['$'+ field +'.v', 0]
}
};
db.cs.aggregate([
{ '$match': { "cID" : "00001" } },
{ '$addFields': {
'title': getFilterOperatorExpression('title'),
'desc': getFilterOperatorExpression('desc'),
} },
{ '$addFields': {
'title': getValueOperatorExpression('title'),
'desc': getValueOperatorExpression('desc')
} }
]);
来源:https://stackoverflow.com/questions/64742175/querying-localized-data-in-mongodb