I have a schema like this:
parentId: Number
name:[
new Schema({
language: { type: String, enum: ['en-US', 'fr-CA'] },
text: String,
},{ _id: false }
);
],
isActive: Boolean
...
and sample data like the following:
{
parentId:1,
"name": [
{"language": "en-US", "text": "Book"},
{"language": "fr-CA", "text": "livre"}
],
isActive:true
// and so many other fields
},
{
parentId:1,
"name": [
{"language": "en-US", "text": "Pen"}
],
isActive:true
// and so many other fields
}
my mongoose searching for French texts:
db.langs.find({
"name.language":"fr-CA", parentId: 1
})
Q1. How can I return the name in French like this:
{
parentId:1,
"name": "livre",
isActive:true
// and so many other fields
}
Q2. Is there any possibility in mongoose that I can return the french text and if the french is not there, it returns English?
{
parentId:1,
"name": "livre",
isActive:true
// and so many other fields
},
{
parentId:1,
"name": "pen",
isActive:true
// and so many other fields
}
CodePudding user response:
Question 1:
You can use $unwind
to deconstruct the array and $match
like an object. And least do $addFields
to overwrite name
field with the desired value (if there are multiple fields is better options than use $project
). All this in an aggregation pipeline like this:
db.collection.aggregate([
{
"$match": {
"parentId": 1
}
},
{
"$unwind": "$name"
},
{
"$match": {
"name.language": "fr-CA"
}
},
{
"$addFields": {
"name": "$name.text"
}
}
])
Example here
Question 2:
You can use $facet
to create "two ways". One if exists french result and another if not exists. And then check if exists to output one value or another like this:
db.collection.aggregate([
{
"$match": {
"parentId": 1
}
},
{
"$unwind": "$name"
},
{
"$facet": {
"french": [
{
"$match": {
"name.language": "fr-CA"
}
}
],
"notFrench": [
{
"$match": {
"name.language": "en-US"
}
}
]
}
},
{
"$project": {
"result": {
"$cond": {
"if": {
"$eq": [
{
"$size": "$french"
},
0
]
},
"then": "$notFrench",
"else": "$french"
}
}
}
}
])
Example here
CodePudding user response:
You can do it with array operations also.
Query1
- match parent 1
- fitler to keep only french, and take the
text
- match to have a name (to remove documents that didn't had a french book)
aggregate(
[{"$match": {"parentId": {"$eq": 1}}},
{"$set":
{"name":
{"$getField":
{"field": "text",
"input":
{"$arrayElemAt":
[{"$filter":
{"input": "$name",
"cond": {"$eq": ["$$this.language", "fr-CA"]}}},
0]}}}}},
{"$match": {"$expr": {"$eq": [{"$type": "$name"}, "string"]}}}])
Query2
- match parent 1
- reduce
- if value(i found it before) french i keep it
- else if the current is france i keep this
- else if the current is us i keep this
- else value (keep the initial value that is empty {})
- match to have a name (to remove documents that didn't had a french or us book)
aggregate(
[{"$match": {"parentId": {"$eq": 1}}},
{"$set":
{"name":
{"$getField":
{"field": "text",
"input":
{"$reduce":
{"input": "$name",
"initialValue": {},
"in":
{"$switch":
{"branches":
[{"case": {"$eq": ["$$value.language", "fr-CA"]},
"then": "$$value"},
{"case": {"$eq": ["$$this.language", "fr-CA"]},
"then": "$$this"},
{"case": {"$eq": ["$$this.language", "en-US"]},
"then": "$$this"}],
"default": "$$value"}}}}}}}},
{"$match": {"$expr": {"$eq": [{"$type": "$name"}, "string"]}}}])