武裝你的WEBAPI-OData聚合查詢

波多爾斯基發表於2023-03-27

本文屬於OData系列

目錄


ODATA v4提出了新的聚合查詢功能,這對於ODA他的基本查詢能力($expand等)是一個非常大的補充。ODATA支援的聚合查詢功能,可以對資料進行統計分析,例如求和、平均值、最大/最小值、分組等。

聚合查詢是透過$apply關鍵字實現的。使用$apply關鍵字可以指定一系列的聚合操作,以對資料進行處理。

GET /odata/Products?$apply=groupby((Category), aggregate(Price with sum as TotalSales))

該請求將返回按照產品類別(Category)分組的資料,並計算每個組的銷售總額(TotalSales)。

需要注意,聚合查詢出來的結果一般都不在EDM中註冊,因此無法對結果應用更多ODATA查詢,如果想解鎖這個能力,請手動在EDM中註冊這個型別。

聚合查詢示例

ODA他的強大之處在於可以賦予前端更多的自主權,讓他們自己獲得自己需要的資料,透過聚合查詢,我們可以實現非常多複雜的、以前只能在後端單獨實現的查詢。舉例說明,我們有以下三個實體類。

public class AttachDeviceType
    {
        /// <summary>
        /// 
        /// </summary>
        [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int Id { get; set; }
        /// <summary>
        /// 附加裝置的型別
        /// </summary>
        public string Name { get; set; }
        /// <summary>
        /// 描述
        /// </summary>
        public string Description { get; set; }
    }
   public abstract class AttachDeviceInfo
    {
        /// <summary>
        /// 
        /// </summary>
        [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int Id { get; set; }
        [Required]
        public string AttachDeviceId { get; set; }
        public string Name { get; set; }
        /// <summary>
        /// 附加裝置的型別
        /// </summary>
        public virtual AttachDeviceType AttachDeviceType { get; set; }
        public string? Description { get; set; }

        public virtual DeviceInfo DeviceInfo { get; set; }
    }
   public class DeviceInfo
    {
        /// <summary>
        /// 
        /// </summary>
        [Key]
        [MaxLength(200)]
        public string DeviceId { get; set; }
        /// <summary>
        /// 地域位置的程式碼
        /// </summary>
        public int? Adcode { get; set; }

        public virtual ICollection<AttachDeviceInfo> AttachDevices { get; set; }
    }

以上三個類定義了主裝置、附加裝置與從屬裝置的型別,三者之間透過導航屬性進行連線。假定我們需要按照地域位置程式碼進行分組查詢,查詢出每個地域的所有主裝置的、附加裝置的和不同的型別的數量。

/odata/DeviceInfos?$apply=groupby((Adcode), aggregate(AttachDevices/$count as NumAttachDevices, $count as NumDeviceInfos, AttachDevices/AttachDeviceType/$countdistinct as NumAttachDeviceTypes))

或者我們使用以attachdevice作為目標

/odata/AttachDeviceInfo?$apply=groupby((DeviceInfo/Adcode), aggregate(
        $count as TotalDeviceInfo,
        attachdevices/$count as TotalAttachDevices,
        AttachDeviceType/$countdistinct as TotalAttachDeviceTypes))

思路沒有問題,但是實際執行會提示$count不是可用的aggregatebinder之類的錯誤(Binding OData QueryNode of kind 'Count' is not supported by 'AggregationBinder'.)。這是因為$count已經是OData進行查詢得到的結果,這個結果不能在進行的聚合查詢了。

換一個思路,我們使用每一個實體物件的屬性作為統計物件。

odata/attachdeviceinfos?$apply=groupby((deviceinfo/Adcode), aggregate($count as NumAttachDevices, deviceinfo/deviceid with countdistinct as NumDevices, attachdevicetype/id with countdistinct as NumTypes))

那麼就可以得到正確的結果:

[

    {

        "deviceInfo": {

            "adcode": 110105

        },

        "NumTypes": 1,

        "NumDevices": 1,

        "NumAttachDevices": 2

    },

    {

        "deviceInfo": {

            "adcode": 110108

        },

        "NumTypes": 1,

        "NumDevices": 1,

        "NumAttachDevices": 1

    }
]

當然,我們還可以組合使用filter查詢:

/odata/attachdeviceinfos?$apply=filter(deviceinfo/Adcode eq 110105)/groupby((deviceinfo/Adcode), aggregate($count as NumAttachDevices, deviceinfo/deviceid with countdistinct as NumDevices, attachdevicetype/id with countdistinct as NumTypes))

注意:在AXIOS中,前端傳送如/之類的特殊字元會自動進行轉移,導致查詢請求失敗,請前端搜尋禁止AXIOS自動轉移的方法,以保證傳送的請求同POSTMAN傳送的請求一致。

總結

使用聚合查詢可以方便將原來需要後端單獨程式設計的分類、統計等操作簡化,給予了前端非常大的自由度,同時減少了後端的介面數量。需要注意的是,很多操作在聚合查詢中語法可能有一些變化,請一定參閱ODA他的OASIS標準的文件

相關文章