是否可以将数组的一个字段展开到展开的数组上?

时间:2018-11-16 03:24:12

标签: mongodb mongodb-query aggregation-framework

对mongo来说还很陌生,还无法弄清楚如何执行查询。

我有一个accounts集合,看起来像这样:

{
    "_id" : ObjectId("1"),
    "time" : ISODate("2018-10-20T05:57:15.372Z"),
    "profileId" : "1",
    "totalUSD" : "1015.5513030613",
    "accounts" : [
        {
            "_id" : ObjectId("2"),
            "accountId" : "1",
            "currency" : "USD",
            "balance" : "530.7934159683763000",
            "available" : "530.7934159683763",
            "hold" : "0.0000000000000000",
            "exchangeRateUSD" : "1"
        },
        {
            "_id" : ObjectId("5"),
            "accountId" : "4",
            "currency" : "BTC",
            "balance" : "0.0759214200000000",
            "available" : "0.07592142",
            "hold" : "0.0000000000000000",
            "exchangeRateUSD" : "6384.995"
        },
    ],
}

我只存储每种货币的exchangeRateUSD,而不存储XXX是货币名称的exchangeRateXXX,因为可以有任意数量的货币和货币对。但是,当我查询帐户集合时,将始终以货币对查询它,例如:BTC-USD。暂时简化一下,我可以假设货币对始终为XXX-USD。

查询帐户集合时,我想向每个帐户对象添加一个“虚拟”字段:exchangeRateCrypto,然后在顶级帐户文档中添加totalCrypto这只是给定加密货币中的总账户值。例如:美元帐户余额* exchangeRateCrypto +加密帐户余额* exchangeRateCrypto(等于1)。

我当前没有exchangeRateCryptototalCrypto的查询如下:

db.accounts.aggregate([
  { $unwind: '$accounts' },
  { $match: { 'accounts.currency': { $in: [ 'USD', 'BTC' ] }}},
  {
    $group: {
      _id: '$_id',
      time: { $first: '$time' },
      profileId: { $first: '$profileId' },
      accounts:  { $push: '$accounts' },
      totalUSD: { $sum: { $multiply: [ { $toDouble: '$accounts.balance' }, { $toDouble: '$accounts.exchangeRateUSD' } ] } }
    }
  }
]);

我试图通过简单地执行exchangeRateCrypto然后以如下方式投影/返回帐户文档和子文档来弄清楚如何进入BTC行并计算1 / exchangeRateUSD

{
    "_id" : ObjectId("1"),
    "time" : ISODate("2018-10-20T05:57:15.372Z"),
    "profileId" : "1",
    "totalUSD" : "1015.5513030613",
    "totalCrypto" : "0.1590527953",   // 530.7934159683763 * 0.0001566171939 + 0.07592142 * 1
    "accounts" : [
        {
            "_id" : ObjectId("2"),
            "accountId" : "1",
            "currency" : "USD",
            "balance" : "530.7934159683763000",
            "available" : "530.7934159683763",
            "hold" : "0.0000000000000000",
            "exchangeRateUSD" : "1",
            "exchangeRateCrypto" : "0.0001566171939",   //  1 / 6384.995
        },
        {
            "_id" : ObjectId("5"),
            "accountId" : "4",
            "currency" : "BTC",
            "balance" : "0.0759214200000000",
            "available" : "0.07592142",
            "hold" : "0.0000000000000000",
            "exchangeRateUSD" : "6384.995",
            "exchangeRateCrypto" : "1"
        },
    ],
}

但是还没有找到一种好的方法。

似乎应该很简单,但仍要学习Mongo。

有什么提示吗?

谢谢!

1 个答案:

答案 0 :(得分:1)

解决方案可能会有点长,可能可以缩短,但是我希望您逐步理解建议的思维方式。

var secondCurrency = "BTC";
var secondCurrencyFieldName = "exchangeRate" + secondCurrency;
var secondCurrencyFieldNameRef = "$" + secondCurrencyFieldName;
var totalFieldName = "total" + secondCurrency;

db.accounts.aggregate([
    { $unwind: "$accounts" },
    { $match: { "accounts.currency": { $in: [ "USD", secondCurrency ] }}},
    {
        $group: {
            _id: "$_id",
            time: { $first: "$time" },
            profileId: { $first: "$profileId" },
            accounts:  { $push: "$accounts" },
            totalUSD: { $sum: { $multiply: [ { $toDouble: "$accounts.balance" }, { $toDouble: "$accounts.exchangeRateUSD" } ] } }
        }
},
{
    $addFields: {
        [secondCurrencyFieldName]: {
            $filter: {
                input: "$accounts",
                as: "account",
                cond: { $eq: [  "$$account.currency", secondCurrency ] }
            }
        }
    }
},
{
    $addFields: {
        [secondCurrencyFieldName]: {
            $let: {
                vars: { first: { $arrayElemAt: [ secondCurrencyFieldNameRef, 0 ] } },
                in: { $toDouble: "$$first.exchangeRateUSD" }
            }
        }
    }
},
{
    $addFields: {
        accounts: {
            $map: {
                input: "$accounts",
                as: "account",
                in: {
                    $mergeObjects: [
                        "$$account",
                            { 
                            [secondCurrencyFieldName]: {
                                $cond: [ { $eq: [ "$$account.currency", secondCurrency ] }, 1, { $divide: [ 1, secondCurrencyFieldNameRef ] } ]
                                } 
                            }
                    ]
                }
            }
        }
    }
},
{
    $addFields: {
        [totalFieldName]: {
            $reduce: {
                input: "$accounts",
                initialValue: 0,
                in: {
                    $add: [
                        "$$value",
                        { $multiply: [ { $toDouble: "$$this.balance" }, "$$this." + secondCurrencyFieldName ] }
                    ]
                }
            }
        }
    }
}
]).pretty()

所以我们可以从$addFields开始,它可以将新字段添加到现有文档中或替换现有字段。在$group阶段之后,您必须找到USD-XXX汇率(在下一个管道阶段使用$filter$let + $arrayElemAt)。有了这个值,您可以再次将$addFields$map$mergeObjects结合使用,将新字段添加到嵌套数组中,该字段将表示 USD 和<强> XXX 货币。然后,您可以再次将$addFields$reduce一起使用,以获取XXX货币的所有帐户的总数。

输出:

{
    "_id" : ObjectId("5beeec9fef99bb86541abf7f"),
    "time" : ISODate("2018-10-20T05:57:15.372Z"),
    "profileId" : "1",
    "accounts" : [
            {
                    "_id" : ObjectId("5beeec9fef99bb86541abf7d"),
                    "accountId" : "1",
                    "currency" : "USD",
                    "balance" : "530.7934159683763000",
                    "available" : "530.7934159683763",
                    "hold" : "0.0000000000000000",
                    "exchangeRateUSD" : "1",
                    "exchangeRateBTC" : 0.00015661719390539853
            },
            {
                    "_id" : ObjectId("5beeec9fef99bb86541abf7e"),
                    "accountId" : "4",
                    "currency" : "BTC",
                    "balance" : "0.0759214200000000",
                    "available" : "0.07592142",
                    "hold" : "0.0000000000000000",
                    "exchangeRateUSD" : "6384.995",
                    "exchangeRateBTC" : 1
            }
    ],
    "totalUSD" : 1015.5513030612763,
    "exchangeRateBTC" : 6384.995,
    "totalexchangeRateBTC" : 0.15905279535242806
}