在MongoDB的单个字段中连接数组中的字符串值

时间:2015-03-05 16:16:51

标签: javascript mongodb mapreduce mongodb-query aggregation-framework

假设我有一系列具有以下格式的文档:

{
    "_id": "3_0",
    "values": ["1", "2"]
}

我希望获得在单个字段中连接的数组中的值的投影。我想以下列格式获取每个文档。

{
    "_id": "3_0",
    "values": "1_2"
}

这可能吗?我尝试使用$concat,但我想我无法使用$values作为$concat的数组。

3 个答案:

答案 0 :(得分:8)

在Modern MongoDB版本中,您可以。您仍然无法“直接”将数组应用于$concat,但是您可以使用$reduce来处理数组元素并生成:

db.collection.aggregate([
  { "$addFields": {
    "values": { 
      "$reduce": {
        "input": "$values",
        "initialValue": "",
        "in": {
          "$cond": {
            "if": { "$eq": [ { "$indexOfArray": [ "$values", "$$this" ] }, 0 ] },
            "then": { "$concat": [ "$$value", "$$this" ] },
            "else": { "$concat": [ "$$value", "_", "$$this" ] }
          }    
        }
      }        
    }
  }}
])

当它是数组的“第一个”索引时,将当然与$indexOfArray结合,以便“连接”与"_"下划线。

我的额外“愿望”也已通过$sum

回答
db.collection.aggregate([
  { "$addFields": {
    "total": { "$sum": "$items.value" }
  }}
])

通常使用带有一系列项的聚合运算符会引发这种情况。这里的区别在于它意味着编码表示中提供的“aguments”的“数组”与当前文档中存在的“数组元素”相对。

你可以真正在文档中存在的数组中实现项目串联的唯一方法是执行某种JavaScript选项,如mapReduce中的此示例所示:

db.collection.mapReduce(
    function() {
        emit( this._id, { "values": this.values.join("_") } );
    },
    function() {},
    { "out": { "inline": 1 } }
)

当然,如果您实际上没有聚合任何内容,那么最好的方法可能就是在后处理查询结果时在客户端代码中执行“加入”操作。但如果需要在文档中使用它,那么mapReduce将是唯一可以使用它的地方。


我可以补充说“例如”我希望这样的事情可以发挥作用:

{
    "items": [
        { "product": "A", "value": 1 },
        { "product": "B", "value": 2 },
        { "product": "C", "value": 3 }
    ]
}

总的来说:

db.collection.aggregate([
    { "$project": {
        "total": { "$add": [
            { "$map": {
                "input": "$items",
                "as": "i",
                "in": "$$i.value"
            }}
        ]}
    }}
])

但它不起作用,因为$add需要参数而不是文档中的数组。叹! :(。部分“按设计”推理可以说“只是因为”它是一个数组或“列表”的奇异值从转换的结果传入它不是“保证”那些是实际上是操作符期望的“有效”奇异数值类型值。至少不是当前实现的“类型检查”方法。

这意味着现在我们仍然必须这样做:

db.collection.aggregate([
   { "$unwind": "$items" },
   { "$group": {
       "_id": "$_id",
        "total": { "$sum": "$items.value" }
   }}
])

而且遗憾的是,没有办法应用这样的分组运算符来连接字符串。

因此,您可以希望对此进行某种更改,或希望进行一些更改,以允许以某种方式在$map操作的范围内更改外部范围的变量。更好的是,新的$join操作也会受到欢迎。但是这些在写作时并不存在,并且可能在一段时间内不会存在。

答案 1 :(得分:1)

您可以将reduce运算符与substr运算符一起使用。

db.collection.aggregate([
{
    $project: {
        values: {
            $reduce: {
              input: '$values',
              initialValue: '',
              in: {
                $concat: ['$$value', '_', '$$this']
              }
            }
        }   
    }       
},
{
    $project: {
        values: { $substr: ['$values', 1 , -1]}
    }       
}])

答案 2 :(得分:0)

Mongo 4.4开始,$function聚合运算符允许应用自定义javascript函数来实现MongoDB查询语言不支持的行为。

例如,为了连接字符串数组:

// { "_id" : "3_0", "values" : [ "1", "2" ] }
db.collection.aggregate(
  { $set:
    { "values":
      { $function: {
          body: function(values) { return values.join('_'); },
          args: ["$values"],
          lang: "js"
      }}
    }
  }
)
// { "_id" : "3_0", "values" : "1_2" }

$function具有3个参数:

  • body,这是要应用的函数,其参数是要加入的数组。
  • args,其中包含body函数作为参数的记录中的字段。在我们的情况下,"$values"
  • lang,这是编写body函数的语言。当前仅js可用。
相关问题