Выберите такое же поле из списка похожих вложенных полей в мангусте - PullRequest
2 голосов
/ 13 июля 2020

У меня есть схема.

const placeSchema = new Schema({
    description: {
      fr: String,
      en: String,
    },
    comment: {
      fr: String,
      en: String,
    },
    ...
    ...
});

const Place= mongoose.model('Place', placeSchema);

module.exports = Place;

Если я хочу получить только значение 'en', я сейчас использую

await Place.find({}, '-description.fr -comment.fr ...')

Если количество количество похожих полей увеличивается, поэтому увеличивается и длина запроса. Есть ли способ выбрать все похожие поля, например $ field .fr?

Ответы [ 3 ]

1 голос
/ 13 июля 2020

Следующая агрегация проверяет все поля верхнего уровня на наличие подполя en. Если это правда (должно работать, если у вас строго указаны строковые значения для свойств языка), подполе будет { field: { en: fieldValue.en } }, иначе будет { field: fieldValue }

db.collection.aggregate([
  {
    $replaceRoot: {
      newRoot: {
        $arrayToObject: {
          $map: {
            input: { $objectToArray: "$$ROOT" },
            in: {
              k: "$$this.k",
              v: {
                $cond: [
                  "$$this.v.en", // works for string values, otherwise you will have to check more explicitly
                  {
                    en: "$$this.v.en"
                  },
                  "$$this.v"
                ]
              }
            }
          }
        }
      }
    }
  }
])

Пн go Площадка

1 голос
/ 13 июля 2020

Оба приведенных выше ответа - это именно то, что искал вопрос. Это может быть более «хакерский» способ делать что-то.

Сначала создайте функцию, которая генерирует строку запроса '-description.fr -comment.fr ...'

let select = '';
const selectLanguage = (fields, lang) => {
   switch (true) {
        case lang === 'fr':
            fields.forEach(field => {
                select= `${select} -${field}.en `;
            });
            break;
        case lang === 'en':
            fields.forEach(field => {
                select = `${select} -${field}.fr `;
            });
            break;
        default: 
            break;
    }
    
    return select;
}

Это сгенерирует строку типа ' -fieldName1.fr -fieldName2.fr ..' для engli sh и ' -fieldName1.en ..' для французского. Затем мы можем использовать этот оператор в запросе выше.

const select = selectLanguage(['description', 'comment', ..], 'en')

await Place.find({}, select)   //await Place.find({}, ' -description.fr -comment.fr ..')
1 голос
/ 13 июля 2020

Технически да, способ есть. используя $ objectToArray и выполняя некоторые манипуляции со структурой.

Это будет выглядеть примерно так:

db.collection.aggregate([
  {
    $match: {} //match your document.
  },
  {
    $addFields: {
      rootArr: {
        $objectToArray: "$$ROOT"
      }
    }
  },
  {
    $unwind: "$rootArr"
  },
  {
    $match: {
      "rootArr.v.en": {
        $exists: true
      }
    }
  },
  {
    $group: {
      _id: "$_id",
      data: {
        $push: {
          k: "$rootArr.k",
          v: "$rootArr.v.en"
        }
      }
    }
  },
  {
    $replaceRoot: {
      newRoot: {
        $arrayToObject: "$data"
      }
    }
  }
])

Mon go Playground

Это небольшая «хакерская» мысль, насколько строгие требования к вашей схеме? Думали ли вы о том, чтобы построить его под следующую структуру?:

const placeSchema = new Schema({
    data: [
        {
            lang: String,
            description: String,
            comment: String,
            ...
        }
    ]
});
...