从猫鼬的类似嵌套字段列表中选择一个相同的字段

时间:2020-07-13 09:53:55

标签: mongodb mongoose mongoose-schema

我有一个架构。

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 个答案:

答案 0 :(得分:1)

从技术上讲是有办法的。使用$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"
      }
    }
  }
])

Mongo Playground

有点“棘手”的想法,您的架构需求有多严格? 您是否考虑过按照以下结构进行构建?

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

答案 1 :(得分:1)

以下汇总将检查所有顶级字段中的子字段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"
                ]
              }
            }
          }
        }
      }
    }
  }
])

Mongo Playground

答案 2 :(得分:1)

以上两个答案正是问题的根源。这可能是一种更“ hacky”的处理方式。

首先创建一个生成查询字符串'-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 ..',对于法语生成一个字符串' -fieldName1.en ..'。然后,我们可以在上面的查询中使用此语句。

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

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