iT邦幫忙

2022 iThome 鐵人賽

DAY 18
0
自我挑戰組

被MongoDB用Aggregate暴打的後端小菜雞日記系列 第 18

被MongoDB用Aggregate暴打的後端小菜雞日記-day18-陣列操作符(下)

  • 分享至 

  • xImage
  •  

今天是最後一天介紹陣列操作符,當初在查資料的時候,完全沒有想到這個主題可以寫三天,但事實上我只有拉出,我比較有興趣的操作符來說明。

被我跳過的操作符,例如:

  1. $first:選出陣列第一個元素
  2. $firstN:選出陣列前N個元素
  3. $isArray:判定是否是陣列
  4. $last:選出陣列最後一個元素
  5. $lastN:選出陣列最後N個元素
  6. $zip:整合陣列中的兩個陣列(建議看官方文件的範例,才比較懂這個操作符在幹嘛)

大家有興趣可以自行點選連結,去官網看一下。


接下來進入正題,先用一個比較簡單的例子,一次介紹$slice(選取特定範圍的陣列資料) 、$reverseArray(將陣列反轉)、$indexOfArray(查詢資料在陣列的位置)三種操作符。

假設現在有一筆英文成績的資料

  {
    _id: 1,
    english: [58, 96, 88, 23, 69, 100, 45, 87]
  }

對它執行以下指令

test.aggregate([
  {
    $project: {
      slice_array: { $slice: ["$english", 1, 3] },
      reverse: { $reverseArray: "$english" },
      find_100: { $indexOfArray: ["$english", 100] }
    }
  }
]);

// 最後回傳的資料
  {
    _id: 1,
    slice_array: [96, 88, 23],
    reverse: [87, 45, 100, 69, 23, 88, 96, 58],
    find_100: 5
  }

其中$slice的指令有兩種寫法,一種是直接指定切割的範圍,{ $slice: [ <array>, <position>, <n> ] },透過<position>選擇要從陣列哪一個位置開始切割,搭配<n>決定要選取幾個資料出來。

或是直接寫{ $slice: [ <array>, <n> ] },透過<n>是正數或負數,決定要從陣列的開頭或結尾選出n筆資料。

另外$indexOfArray除了用{ $indexOfArray: [ <array expression>, <search expression> ] }這種比較簡單的寫法外,後面還可以再多待兩個參數,決定要搜尋範圍,例如:{ $indexOfArray: ["$english", 100, 1, 4] },這時候會從陣列1~4的位置看有沒有100這個值,如果剛好找不到資料,會回傳-1。


再來如果我們想要快速建立陣列資料,可以透過$range這個操作符,後面會帶入一個陣列,第一個參數決定回傳新陣列的開頭數值,第二個參數決定結尾的數值,第三個參數決定要新陣列每一個元素的數值間隔。

詳細範例如下

db.collection.aggregate([
  {
    $project: {
      new_array_1: { $range: [0, 10, 2] },
      new_array_2: { $range: [10, 0, -1] }
    }
  }
]);

// 最後回傳的資料
  {
    _id: 1,
    new_array_1: [0, 2, 4, 6, 8],
    new_array_2: [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
  }

最後來介紹$reduce,它可以幫助我們快速使用陣列的每一筆資料,進行運算。
假設數學老師一道題目,要求學生計算1到10連加以及連乘的結果,這時候我們可以用以下指令計算。

db.collection.aggregate([
  {
    $addFields: {
      new_array: { $range: [1, 10, 1] } // 先建立一個陣列,裡面有1~10
    }
  },
  {
    $project: {
      result: {
        $reduce: {
          input: "$new_array", // 帶入要進行運算的陣列
          initialValue: { sum: 0, multiply: 1 }, // 設定一開始運算的初始值
          in: { // 寫入要運算的程式碼
            // "$$value" 代表前一次計算的結果,"$$this" 代表目前陣列的值
            sum: { $add: ["$$value.sum", "$$this"] }, 
            multiply: { $multiply: ["$$value.multiply", "$$this"] } 
          }
        }
      }
    }
  }
]);

// 最後回傳的結果
  {
    _id: 1,
    result: { sum: 45, multiply: 362880 }
  }

讓我們用計算連加的過程,來詳細說明上方程式碼是如何運行。

一開始我們有設定初始值是{ sum: 0, multiply: 1 },因此第一次在下方in計算的時候,$$value.sum的值會是0,這時會取出陣列第一個元素1,會用$$this做代表。

因為我們有寫{ $add: ["$$value.sum", "$$this"] },所以0+1=1,在下一輪時$$value.sum的值就會變成1,在與第二個陣列元素2做相加,接下來的就一直延續以上步驟,直到把整個陣列都遍歷過一次,最後回傳計算結果。

本篇文章同步放在我的部落格,大家有空可以進來逛逛


上一篇
被MongoDB用Aggregate暴打的後端小菜雞日記-day17-陣列操作符(中)
下一篇
被MongoDB用Aggregate暴打的後端小菜雞日記-day19-日期時間操作符(上)
系列文
被MongoDB用Aggregate暴打的後端小菜雞日記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言