MAIN GOAL
I want to retrieve translations, which are projected as terms (Term
) with a single relation (Relation
) in between.
STATUS
I'm describing terms Term
. Think of it as words (usually nouns).
const TermSchema: Schema = new Schema({
_id: mongoose.Schema.Types.ObjectId,
term: { type: String, required: true },
lang: { type: String, required: true},
relations: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Relation' }],
});
export default mongoose.model<Term & mongoose.Document>('Term', TermSchema);
The Relation
schema describes the relations of these terms.
const RelationSchema: Schema = new Schema({
_id: mongoose.Schema.Types.ObjectId,
relationType: { type: String, required: true, default: RelationEnum.TRANSLATION },
termSrc: { type: mongoose.Schema.Types.ObjectId, ref: 'Term' },
termTrg: { type: mongoose.Schema.Types.ObjectId, ref: 'Term' },
});
export default mongoose.model<Relation & mongoose.Document>('Relation', RelationSchema);
I want to retrieve all translations from a language combination. Let's say German (de
) and Arabic (ar
). That is:
relationType
of value RelationEnum.TRANSLATION
.Which led me to write the following mongoose statement.
var query = Relation.find({relationType: 'TRANSLATION' }, null)
.populate( {path: 'termSrc', select: 'term lang', model: Term, match: {lang: {$in: ['ar', 'de']}}})
.populate( {path: 'termTrg', select: 'term lang', model: Term, match: {lang: {$in: ['ar', 'de']}}});
In most cases that certainly results in what I'm aiming for. Here is a desired sample of a single records, which is returned.
{
"_id": "41224d776a326fb40f000023",
"relationStatus": "QUALIFIED",
"termSrc": {
"_id": "41224d776a326fb40f000021",
"term": "Verhaftung",
"lang": "de"
},
"termTrg": {
"_id": "41224d776a326fb40f000022",
"term": "اعتقالات",
"lang": "ar"
}
}
PROBLEM STATEMENT
Since the population of termSrc and termTrg are fully independent (it is like an OR statement, a disjunction), it will also include relations where only one filter applies to the population. Thus the query will also return records for French-Arabic translations. Of course, setting the fr
term to null (because we look for 'ar', 'de'
) as in this sample record.
{
"_id": "01224d776a326fb40f000050",
"relationStatus": "QUALIFIED",
"termSrc": null,
"termTrg": {
"_id": "01224d776a326fb40f000034",
"term": "تقالا",
"lang": "ar"
}
}
Which is not what I desire and leads to an after-math cleanup. I only want de-ar and ar-de translations.
As my main goal expresses, I'd like to have only relations returned where BOTH terms are populated (none of them should be null
).
find
then both populate
then 2) run a filter on the intermediate result to avoid null
records?You can use below $lookup
version
Relation.aggregate([
{ $match: { relationType: "TRANSLATION" } },
{
$lookup: {
from: Term.collection.name,
let: { termSrc: "$termSrc" },
pipeline: [{ $match: { $expr: { $eq: ["$_id", "$$termSrc"] } } }],
as: "termSrc"
}
},
{ $unwind: "$termSrc" },
{
$lookup: {
from: Term.collection.name,
let: { termTrg: "$termTrg" },
pipeline: [{ $match: { $expr: { $eq: ["$_id", "$$termTrg"] } } }],
as: "termTrg"
}
},
{ $unwind: "$termTrg" }
]);