Using a property value from a document to access a property of another document inside a find query with mongoose

Viewed 74

I have two collections, letters and settings, as defined below:

const mongoose = require("mongoose");
const Schema = mongoose.Schema;

const letterSchema = new Schema({
  language: { type: String, required: true },
  content: { type: String, required: true },
  annotations: { type: Number, required: true }
});

const settingsSchema = new Schema({
  maxAnnotations: {
    toAllLanguages: {
      enabled: { type: Boolean, required: true },
      amount: { type: Number, required: true }
    }
    english: { type: Number, required: true },
    french: { type: Number, required: true },
    spanish: { type: Number, required: true },
    german: { type: Number, required: true },
    italian: { type: Number, required: true }
  },
  allowNewAnnotations: { type: Boolean, required: true }
}, capped: { max: 1 } });

module.exports = mongoose.model("Letter", letterSchema);
module.exports = mongoose.model("Setting", settingsSchema);

I'm writing a REST api that will be consumed by a react frontend. Each schema is written in a separate file, but I wrote them together here to simplify things. I'm trying to write a query where I can get all letters whose number of annotations did not exceeded the maximum amount defined in the settings according to the language of the letter. And only certain languages are allowed to have annotations. Let's suppose only letters in english and french are allowed to have annotations, like this:

const allowedLanguages = ["english", "french"];

So, based on these constraints, I came up with this code:

const Letters = require("../models/lettters");
const Settings = require("../models/settings");

exports.getLetters = async (req, res, next) => {
  const pageSize = +query.pagesize;
  const currentPage = +query.currentPage;

  const settings = await Settings.findOne();
  const toAllLanguages = settings.maxAnnotations.toAllLanguages.enabled;
  
  if (toAllLanguages ) {
    const letters = await Letters.find({
      language: { $in: allowedLanguages },
      annotations: { $lt: settings.maxAnnotations.toAllLanguages.amount }
    })
    .skip(pageSize * (currentPage - 1))
    .limit(pageSize);
  } else {
    const letters = await Letters.find({
      language: { $in: allowedLanguages },
      $expr: {
        $lt: ["$annotations", settings.maxAnnotations["$language"]]
      }
    })
    .skip(pageSize * (currentPage - 1))
    .limit(pageSize);
  }
}

This code doesn't work when toAllLanguages is set to false because there is no property "$language" in the settings collection. I wonder if it is possible to get the value of language property in the letters document, and use this value to access a property in the settings document with the same name in the same find query.

To give a clearer idea of what I want to achieve, consider this json object to be the current configuration selected:

{
  "maxAnnotations": {
    "toAllLanguages": {
      "enabled": false,
      "amount": 3
    },
    "english": 2,
    "french": 4,
    "spanish": 5,
    "german": 2,
    "italian": 3
  },
  "allowNewAnnotations": true
}

Following this configuration, since maxAnnotations.toAllLanguages.enabled is set to false, I have to compare the language of the letter and the matching property of the configuration json object. In this example, the the find query should retrieve letters obeying these rules:

  • letters in english and german must have less than 2 annotations;
  • letters in french must have less than 4 annotations;
  • letters in spanish must have less than 5 annotations;
  • letters in italian must have less than 3 annotations
0 Answers
Related