Можно ли соединить одно поле массива с раскруткой на раскрученный массив? - PullRequest
0 голосов
/ 16 ноября 2018

Довольно новый для Монго и не смог понять, как выполнить запрос.

У меня есть коллекция 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"
        },
    ],
}

Iхранить только exchangeRateUSD для каждой валюты, а не exchangeRateXXX, где XXX - имя валюты, поскольку может быть произвольное количество валют и валютных пар.Но когда я запрашиваю коллекцию счетов, она всегда запрашивается валютной парой, например, BTC-USD.На данный момент все просто, я могу предположить, что валютная пара всегда будет XXX-USD.

Когда я запрашиваю коллекцию счетов, я хотел бы добавить «виртуальное» поле для каждого объекта счета: exchangeRateCryptoа затем в документ об учетных записях верхнего уровня я хотел бы добавить totalCrypto, который будет просто общим значением учетной записи в данном крипто.Например: баланс счета в долларах США * exchangeRateCrypto + баланс крипто-счета * exchangeRateCrypto (который будет равен 1).

Мой текущий запрос без exchangeRateCrypto и totalCrypto выглядит следующим образом:

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' } ] } }
    }
  }
]);

Я пытаюсь выяснить, как «добраться» до строки BTC и вычислить exchangeRateCrypto, просто выполнив 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"
        },
    ],
}

, ноЯ не смог найти хороший способ сделать это.

Кажется, это должно быть довольно просто, но все еще изучать Монго.

Есть какие-нибудь советы?

Спасибо!

1 Ответ

0 голосов
/ 16 ноября 2018

Решение может быть немного длинным и, вероятно, его можно сократить, однако я хочу, чтобы вы поняли предложенный способ мышления шаг за шагом.

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 с $ сокращение , чтобы получить сумму всех счетов в валюте 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
}
...