MongoDB $project: $filter子数组

11

有一个物品(mongoose)模式,看起来像这样(简化到问题所在的部分):

{
    brand: {
        name: String,
    },
    title: String,
    description: [{ lang: String, text: String }],
    shortDescription: [{ lang: String, text: String }],
    variants: {
        cnt: Number,
        attrs: [
            {
                displayType: String,
                displayContent: String,
                displayName: [{ lang: String, text: String }],
                name: String,
            },
        ],
    }
}

我正在尝试通过语言筛选项目,因此我构建了以下查询:

db.items.aggregate([
    { $match: { 'description.lang': 'ca', 'shortDescription.lang': 'ca' } },
    { $project: {
        'brand.name': 1,
        title: 1,
        description: {
            '$filter': {
                input: '$description',
                as: 'description',
                cond: { $eq: ['$$description.lang', 'ca'] }
            }
        },
        shortDescription: {
            '$filter': {
                input: '$shortDescription',
                as: 'shortDescription',
                cond: { $eq: ['$$shortDescription.lang', 'ca'] }
            }
        },
        'variants.cnt': 1,
        'variants.attrs': 1
    } }
])

它按预期工作:通过语言过滤descriptionshortDescription。我现在想知道是否也可以过滤每个variants.attrs.$.displayName。有什么方法可以做到吗?

我一直在尝试使用$unwindvariant.attrs,但是当我尝试再次$group时完全迷失了方向,我不确定这是否是最佳方法...


我有一个问题:description、shortDescription、variants.attrs和variants.attrs.displayName都是列表。这4个列表都可以有多个元素吗? - sergiuz
是的,它们包含每种语言的文本。 description: [{ lang:'en', text: 'This item is awful' }, { lang: 'es', text: 'Este producto es increíble' }, ...] - Miquel
1个回答

17

你快要成功了。请尝试以下步骤:

  • use $unwind stage before $project stage to expand the outer array of documents, i.e. variants.attrs
  • Add filter for the sub array variants.attrs.displayName in the $project stage.
  • You will have to project all the sub fields of variants key.
  • Next add $group stage and group by all the elements except the sub-array. Use $push to rebuild the sub array in group by stage.
  • Lastly, add $project stage to rebuild the document to its original structure.

    db.items.aggregate([
      { $match: { 'description.lang': 'ca', 'shortDescription.lang': 'ca' } },
      { $unwind : "$variants.attrs" },
      { $project: {
         '_id' : 1,
         'brand.name': 1,
         title: 1,
         description: {
            '$filter': {
                input: '$description',
                as: 'description',
                cond: { $eq: ['$$description.lang', 'ca'] }
            }
         },
         shortDescription: {
           '$filter': {
               input: '$shortDescription',
               as: 'shortDescription',
               cond: { $eq: ['$$shortDescription.lang', 'ca'] }
            }
         },
         'variants.attrs.displayName' : {
            '$filter' : {
               input: '$variants.attrs.displayName',
               as: 'variants_attrs_displayName',
               cond: { $eq : ['$$variants_attrs_displayName.lang','ca']}
            }
          },
    
          'variants.cnt': 1,
          'variants.attrs.displayType': 1,
          'variants.attrs.displayContent' : 1,
          'variants.attrs.name' : 1
        } 
     } , { $group:
              {
                _id : { 
                    _id: "$_id",
                    title: "$title",
                    brand:"$brand",
                    description:"$description",
                    shortDescription:"$shortDescription", 
                    variants_cnt : "$variants.cnt"
                    },
                variants_attrs : { $push : 
                { 
                  displayType : "$variants.attrs.displayType",
                  displayContent : "$variants.attrs.displayContent",
                  displayName : "$variants.attrs.displayName",
                  name: "$variants.attrs.name" 
                }
              }
            }
        },
    { $project : 
     {
        "_id" : 0,
        brand : "$_id.brand",
        title : "$_id.title",
        description : "$_id.description",
        shortDescription : "$_id.shortDescription",
        variants : {
          cnt : "$_id.variants_cnt" ,
          attrs : "$variants_attrs"
         }
       }  
     }
    ])
    
根据您的使用情况,您应重新考虑数据模型设计,以避免过滤值的重复。即:'description.lang': 'ca', 'shortDescription.lang': 'ca', 'variants.attrs.displayName.lang': 'ca'。

这是其中一种解决方案。缺点是需要在本地化之前了解模型(因此不太标准化)。最终我采用了一种使用ref字段并填充选定语言的解决方案。另一个解决方案的缺点是需要额外进行一次读取以获取翻译,但它是可扩展的。 - Miquel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接