尤物视频网站_国产精品成人在亚洲_国产成人亚洲综合无码不卡精品_丁香五月香婷婷五月_亚洲成AV人片高潮喷水

您的位置:首頁 >聚焦 >

今日視點(diǎn):位圖法在mongodb中的應(yīng)用

2022-11-29 11:46:17    來源:程序員客棧

JAVA前線

歡迎大家關(guān)注公眾號(hào)「JAVA前線」查看更多精彩分享,主要內(nèi)容包括源碼分析、實(shí)際應(yīng)用、架構(gòu)思維、職場分享、產(chǎn)品思考等等,同時(shí)也非常歡迎大家加我微信「java_front」一起交流學(xué)習(xí)


【資料圖】

1 需求背景

假設(shè)系統(tǒng)用戶一共有三種角色:普通用戶、管理員、超級(jí)管理員,現(xiàn)在需要設(shè)計(jì)一張用戶角色表記錄這類信息。我們不難設(shè)計(jì)出如下方案:

idnamesuperadminnormal
101用戶一100
102用戶二010
103用戶三001
104用戶四111

用戶一具有超級(jí)管理員角色,用戶二具有管理員角色,用戶三具有普通用戶角色,用戶四同時(shí)具有三種角色。

2 發(fā)現(xiàn)問題

如果新增加一種角色呢?可以新增一個(gè)字段:

idnamesuperadminnormalnew
101用戶一1000
102用戶二0100
103用戶三0010
104用戶四1110

按照上述一個(gè)字段表示一種角色設(shè)計(jì)表,功能沒有問題,優(yōu)點(diǎn)是容易理解結(jié)構(gòu)清晰,但是我們想一想有沒有什么問題?筆者遇到過如下問題:

在復(fù)雜業(yè)務(wù)環(huán)境一份數(shù)據(jù)可能會(huì)使用在不同場景,例如上述數(shù)據(jù)存儲(chǔ)在MySQL數(shù)據(jù)庫,這一份數(shù)據(jù)還會(huì)被用在如下場景:

檢索數(shù)據(jù)需要同步一份到ES使用此表通過Flink計(jì)算業(yè)務(wù)指標(biāo)訂閱此表Binlog消息進(jìn)行業(yè)務(wù)處理

如果表結(jié)構(gòu)發(fā)生變化,數(shù)據(jù)源之間需要重新對(duì)接,業(yè)務(wù)方也要進(jìn)行代碼修改,這樣開發(fā)成本非常高。有沒有辦法避免此類問題?

3 解決方案

我們可以使用位圖法,同一個(gè)字段可以表示多個(gè)業(yè)務(wù)含義。首先設(shè)計(jì)如下數(shù)據(jù)表,userFlag字段暫時(shí)不填:

idnameuser_flag
101用戶一暫時(shí)不填
102用戶二暫時(shí)不填
103用戶三暫時(shí)不填
104用戶四暫時(shí)不填

位圖每一個(gè)bit表示一種角色:

使用位圖法表示如下數(shù)據(jù):

idnamesuperadminnormal
101用戶一100
102用戶二010
103用戶三001
104用戶四111

用戶一位圖如下,十進(jìn)制數(shù)值等于4:

用戶二位圖如下,十進(jìn)制數(shù)值等于2:

用戶三位圖如下,十進(jìn)制數(shù)值等于1:

用戶四位圖如下,十進(jìn)制數(shù)值等于7:

現(xiàn)在可以填寫數(shù)據(jù)表第三列:

idnameuser_flag
101用戶一4
102用戶二2
103用戶三1
104用戶四7
4 代碼實(shí)例

本文結(jié)合mongodb實(shí)現(xiàn)思路有兩種:

方案一:取出二進(jìn)制字段在應(yīng)用層運(yùn)算方案二:在數(shù)據(jù)層直接運(yùn)算二進(jìn)制字段4.1 用戶實(shí)體

用戶實(shí)體對(duì)應(yīng)數(shù)據(jù)表user:

@Document(collection="user")publicclassUser{@Id@Field("_id")privateStringid;@Field("userId")privateStringuserId;@Field("role")privateLongrole;}

4.2 用戶角色

定義枚舉時(shí)不要直接定義為1、2、4這類數(shù)字,應(yīng)該采用位移方式進(jìn)行定義,這樣使用者可以明白設(shè)計(jì)者的意圖。

publicenumUserRoleEnum{//1->00000001NORMAL(1L<<0,"普通用戶"),//2->00000010MANAGER(1L<<1,"管理員"),//4->00000100SUPER(1L<<2,"超級(jí)管理員"),;privateLongcode;privateStringdescription;privateUserRoleEnum(Longcode,Stringdescription){this.code=code;this.description=description;}//新增角色->位或操作//oldRole->00000001->普通用戶角色//addRole->00000010->新增管理員角色//newRole->00000011->具有普通用戶和管理員角色publicstaticLongaddRole(LongoldRole,LongaddRole){returnoldRole|addRole;}//刪除角色->異或操作//oldRole->00000011->普通用戶和管理員角色//delRole->00000010->刪除管理員角色//newRole->00000001->普通用戶角色publicstaticLongremoveRole(LongoldRole,LongdelRole){returnoldRole^delRole;}//是否具有某種角色->位與操作//allRole->00000011->普通用戶和管理員角色//qryRole->00000001->查詢是否具有管理員角色//resRole->00000001->具有管理員角色publicstaticbooleanhasRole(Longrole,LongqueryRole){LongresRole=(role&queryRole);returnqueryRole==resRole;}}

4.3 數(shù)據(jù)準(zhǔn)備

新增用戶一到用戶四:

db.user.insertMany([{"userId":"user1","role":NumberLong(4)},{"userId":"user2","role":NumberLong(2)},{"userId":"user3","role":NumberLong(1)},{"userId":"user4","role":NumberLong(7)}])

4.4 應(yīng)用層運(yùn)算

應(yīng)用層運(yùn)算有三個(gè)關(guān)鍵步驟:

查詢用戶角色內(nèi)存計(jì)算新角色更新數(shù)據(jù)庫

@ServicepublicclassUserBizService{@ResourceprivateMongoTemplatemongoTemplate;//查詢用戶publicUsergetUser(StringuserId){Queryquery=newQuery();Criteriacriteria=Criteria.where("userId").is(userId);query.addCriteria(criteria);Useruser=mongoTemplate.findOne(query,User.class);returnuser;}//新增角色publicbooleanaddRole(StringuserId,LongaddRole){//查詢用戶角色Useruser=getUser(userId);//計(jì)算新角色LongfinalRole=UserRoleEnum.addRole(user.getRole(),addRole);//更新數(shù)據(jù)庫Queryquery=newQuery();Criteriacriteria=Criteria.where("userId").is(userId);query.addCriteria(criteria);Updateupdate=newUpdate();update.set("role",finalRole);mongoTemplate.updateFirst(query,update,User.class);returntrue;}//刪除角色publicbooleanremoveRole(StringuserId,LongdelRole){//查詢用戶角色Useruser=getUser(userId);//計(jì)算新角色LongfinalRole=UserRoleEnum.removeRole(user.getRole(),delRole);//更新數(shù)據(jù)庫Queryquery=newQuery();Criteriacriteria=Criteria.where("userId").is(userId);query.addCriteria(criteria);Updateupdate=newUpdate();update.set("role",finalRole);mongoTemplate.updateFirst(query,update,User.class);returntrue;}//查詢用戶是否具有某種角色publicbooleanqueryRole(StringuserId,LongqueryRole){//查詢用戶角色Useruser=getUser(userId);//計(jì)算是否具有某種角色returnUserRoleEnum.hasRole(user.getRole(),queryRole);}}

4.5 數(shù)據(jù)層運(yùn)算4.5.1 位運(yùn)算操作符(1) 查詢操作符
操作符含義
bitsAllClear指定二進(jìn)制位全為0
bitsAllSet指定二進(jìn)制位全為1
bitsAnyClear任意指定二進(jìn)制位為0
bitsAnySet任意指定二進(jìn)制位為1

下列語句可以查出用戶四:

0-2三個(gè)位置全部等于1

db.user.find({"role":{$bitsAllSet:[0,1,2]}})

0-2任意一個(gè)位置等于1

db.user.find({"role":{$bitsAnySet:[0,1,2]}})

3-7位置全部等于0

db.user.find({"role":{$bitsAllClear:[3,4,5,6,7]}})

3-7位置任意等于0

db.user.find({"role":{$bitsAnyClear:[3,4,5,6,7]}})

(2) 計(jì)算操作符
操作符含義操作
and位與查詢角色
or位或新增角色
xor位異或刪除角色
user3新增超級(jí)管理員角色

db.user.update({"userId":"user3"},{$bit:{"role":{or:NumberLong(4)}}})

user4刪除普通用戶角色

db.user.update({"userId":"user4"},{$bit:{"role":{xor:NumberLong(1)}}})

4.5.2 代碼實(shí)例

@ServicepublicclassUserBizService{/**新增角色*/publicbooleanaddRoleBit(StringuserId,LongaddRole){Queryquery=newQuery();Criteriacriteria=Criteria.where("userId").is(userId);query.addCriteria(criteria);Updateupdate=newUpdate();update.bitwise("role").or(addRole);mongoTemplate.updateFirst(query,update,User.class);returntrue;}/***刪除角色*/publicbooleanremoveRoleBit(StringuserId,LongaddRole){Queryquery=newQuery();Criteriacriteria=Criteria.where("userId").is(userId);query.addCriteria(criteria);Updateupdate=newUpdate();update.bitwise("role").xor(addRole);mongoTemplate.updateFirst(query,update,User.class);returntrue;}/***查詢r(jià)olePosition位置全部等于0的用戶**表示不具有rolePositions中所有角色的用戶*/publicListqueryRoleAllClear(ListrolePositions){Criteriacriteria=Criteria.where("role").bits().allClear(rolePositions);Listusers=mongoTemplate.query(User.class).matching(criteria).all();returnusers;}/***查詢r(jià)olePosition位置任一等于0的用戶**表示不具有rolePositions中任一角色的用戶*/publicListqueryRoleAnyClear(ListrolePositions){Criteriacriteria=Criteria.where("role").bits().anyClear(rolePositions);Listusers=mongoTemplate.query(User.class).matching(criteria).all();returnusers;}/***查詢r(jià)olePosition位置全部等于1的用戶**表示具有rolePositions中所有角色的用戶*/publicListqueryRoleAllSet(ListrolePositions){Criteriacriteria=Criteria.where("role").bits().allSet(rolePositions);Listusers=mongoTemplate.query(User.class).matching(criteria).all();returnusers;}/***查詢r(jià)olePosition位置任一等于1的用戶**表示具有rolePositions中任一角色的用戶*/publicListqueryRoleAnySet(ListrolePositions){Criteriacriteria=Criteria.where("role").bits().anySet(rolePositions);Listusers=mongoTemplate.query(User.class).matching(criteria).all();returnusers;}}

5 文章總結(jié)

本文我們從一個(gè)簡單案例開始,分析了直接新增字段的優(yōu)缺點(diǎn)。新增字段方案遇到最多問題就是在復(fù)雜業(yè)務(wù)場景中,需要新增數(shù)據(jù)對(duì)接工作量,增加了開發(fā)維護(hù)成本。

我們又介紹了位圖法,一個(gè)字段就可以表示多種業(yè)務(wù)含義,減少了字段冗余,節(jié)省了對(duì)接開發(fā)成本。同時(shí)位圖法增加了代碼理解成本,數(shù)據(jù)庫字段含義不直觀,需要進(jìn)行轉(zhuǎn)義,大家可以根據(jù)業(yè)務(wù)需求場景選擇。

JAVA前線

歡迎大家關(guān)注公眾號(hào)「JAVA前線」查看更多精彩分享,主要內(nèi)容包括源碼分析、實(shí)際應(yīng)用、架構(gòu)思維、職場分享、產(chǎn)品思考等等,同時(shí)也非常歡迎大家加我微信「java_front」一起交流學(xué)習(xí)

關(guān)鍵詞: 超級(jí)管理員 二進(jìn)制位 刪除角色

相關(guān)閱讀