最近在公司業務上用到了二進制匹配數據,但是MongoDB進行二進制運算(Bitwise)沒用過,網上博客文章少,所以就上官網看API,因此記錄一下,順便在普及一下使用二進制位運算的一些應用。
在MongoDB的V3.2版本以后才支持的位運算,在這個版本之前是不支持,所以想要用位運算,需先將MongoDB的版本升級至V3.2。
說明
查詢操作
在官方文檔中,在查詢操作中共支持四種位運算方法,官方文檔鏈接
$bitsAllClear | 所有指定二進制的位數都為0 |
$bitsAllSet | 所有指定二進制的位數都為1 |
$bitsAnyClear | 任意一位指定二進制的位數為0 |
$bitsAnySet | 任意一位指定二進制的位數為1 |
這個指定的位數是不是有點難以理解
例如255這個數字的二進制有8位,那么位數則是position指定的位置,Integer的最大值為2的31次方,那么指定的位置(position)最大為31位
bit | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
position | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
更新操作
使用$bit進行更新操作,支持and|or|xor?三種位運算符?即對應位運算符的? &(與) 、|(或) 、~(異或)
將指定列的值(field)通過指定的位運算計算出的結果更新至數據庫中,下面有示例
更新語法如下:
{ $bit: { <field>: { <and|or|xor>: <int> } } }
位運算的使用
查詢操作
在MongoDB數據庫中user集合的數據為:
{ "_id" : "03ddb005-6e8e-45b9-be74-9c840f925f1f", "name" : "Tomcar", "bitData" : NumberLong(100), "_class" : "com.mongodb.mongo_test.bean.User" }
{ "_id" : "dccb1549-bbaf-438b-9c52-dd9549de6fde", "name" : "Java", "bitData" : NumberLong(20), "_class" : "com.mongodb.mongo_test.bean.User" }
{ "_id" : "1bcf44ba-92ee-4dda-ae1b-4b0b28816fc1", "name" : "python", "bitData" : NumberLong(131), "_class" : "com.mongodb.mongo_test.bean.User" }
{ "_id" : "50b76890-d533-4455-8a13-fce98bbd96fe", "name" : "php", "bitData" : NumberLong(8), "_class" : "com.mongodb.mongo_test.bean.User" }
二進制對應
100 | 0110 0100 |
20 | 0001 0100 |
131 | 1000 0011 |
8 | 0000 1000 |
$bitsAllSet
執行命令:
db.user.find({"bitData":{$bitsAllSet:[1,4]}}) //將二進制的第2位以及第5位上為1的匹配出來
?結果:
{ "_id" : "1bcf44ba-92ee-4dda-ae1b-4b0b28816fc1", "name" : "python", "bitData" : NumberLong(131), "_class" : "com.mongodb.mongo_test.bean.User" }
$bitsAllClear
執行命令:
db.user.find({"bitData":{$bitsAllClear:[1,4]}}); //將二進制的第2位以及第5位上為0的匹配出來
結果:
{ "_id" : "03ddb005-6e8e-45b9-be74-9c840f925f1f", "name" : "Tomcar", "bitData" : NumberLong(100), "_class" : "com.mongodb.mongo_test.bean.User" }
{ "_id" : "50b76890-d533-4455-8a13-fce98bbd96fe", "name" : "php", "bitData" : NumberLong(8), "_class" : "com.mongodb.mongo_test.bean.User" }
?$bitsAnyClear
執行命令:
db.user.find({"bitData":{$bitsAnyClear:[1,4]}}); //將第2位或者第5位上為0的查詢出來
結果:
{ "_id" : "03ddb005-6e8e-45b9-be74-9c840f925f1f", "name" : "Tomcar", "bitData" : NumberLong(100), "_class" : "com.mongodb.mongo_test.bean.User" }
{ "_id" : "dccb1549-bbaf-438b-9c52-dd9549de6fde", "name" : "Java", "bitData" : NumberLong(20), "_class" : "com.mongodb.mongo_test.bean.User" }
{ "_id" : "1bcf44ba-92ee-4dda-ae1b-4b0b28816fc1", "name" : "python", "bitData" : NumberLong(131), "_class" : "com.mongodb.mongo_test.bean.User" }
{ "_id" : "50b76890-d533-4455-8a13-fce98bbd96fe", "name" : "php", "bitData" : NumberLong(8), "_class" : "com.mongodb.mongo_test.bean.User" }
?$bitsAnySet
執行命令:
db.user.find({"bitData":{$bitsAnySet:[1,4]}}); //將第2位或者第5位上為1的查詢出來
結果:
{ "_id" : "dccb1549-bbaf-438b-9c52-dd9549de6fde", "name" : "Java", "bitData" : NumberLong(20), "_class" : "com.mongodb.mongo_test.bean.User" }
{ "_id" : "1bcf44ba-92ee-4dda-ae1b-4b0b28816fc1", "name" : "python", "bitData" : NumberLong(131), "_class" : "com.mongodb.mongo_test.bean.User" }
通過數字進行位運算
如果不是用過指定位數進行運算的話,而是指定數字進行的話,查詢結果$bitsAllClear與$bitsAnyClear一致;$bitsAllSet與$bitsAnySet一致。
示例:
命令:
db.user.find({"bitData":{$bitsAnySet:4}})
結果:
{ "_id" : "03ddb005-6e8e-45b9-be74-9c840f925f1f", "name" : "Tomcar", "bitData" : NumberLong(100), "_class" : "com.mongodb.mongo_test.bean.User" }
{ "_id" : "dccb1549-bbaf-438b-9c52-dd9549de6fde", "name" : "Java", "bitData" : NumberLong(20), "_class" : "com.mongodb.mongo_test.bean.User" }命令:
db.user.find({"bitData":{$bitsAnyClear:4}})
結果:
{ "_id" : "1bcf44ba-92ee-4dda-ae1b-4b0b28816fc1", "name" : "python", "bitData" : NumberLong(131), "_class" : "com.mongodb.mongo_test.bean.User" }
{ "_id" : "50b76890-d533-4455-8a13-fce98bbd96fe", "name" : "php", "bitData" : NumberLong(8), "_class" : "com.mongodb.mongo_test.bean.User" }
大家可以各自去試一下。
更新操作
在命令中指定更新某個ID的bitData列,當前列數據為8,這里通過使用與(&)運算做示例:
db.user.update({"_id":"50b76890-d533-4455-8a13-fce98bbd96fe"},{$bit:{"bitData":{and:NumberLong(4)}}})
結果集:
{ "_id" : "03ddb005-6e8e-45b9-be74-9c840f925f1f", "name" : "Tomcar", "bitData" : NumberLong(100), "_class" : "com.mongodb.mongo_test.bean.User" }
{ "_id" : "dccb1549-bbaf-438b-9c52-dd9549de6fde", "name" : "Java", "bitData" : NumberLong(20), "_class" : "com.mongodb.mongo_test.bean.User" }
{ "_id" : "1bcf44ba-92ee-4dda-ae1b-4b0b28816fc1", "name" : "python", "bitData" : NumberLong(131), "_class" : "com.mongodb.mongo_test.bean.User" }
{ "_id" : "50b76890-d533-4455-8a13-fce98bbd96fe", "name" : "php", "bitData" : NumberLong(0), "_class" : "com.mongodb.mongo_test.bean.User" }
其他的兩個命令也可以各自調試。
在Java代碼中使用位運算
查詢操作
在Spring Boot封裝好的MongoTemplate中帶條件查詢使用Query以及Criteria配合使用,使用位運算的話需使用Criteria的bits()方法:這些方法對應上面的四個方法
而采用Mongo客戶端查詢位運算:
MongoCollection<Document> collection = mongoTemplate.getCollection("user");Document filter = new Document();//0110 0100 100//0001 0100 20//1000 0011 131//0000 1000 8filter.append("bitData", new Document("$bitsAllClear", Arrays.asList(1,4)) );/*** $bitsAllClear 進行位運算并且計算結果為 0 的匹配出來* $bitsAllSet 進行位運算并且計算結果不為 0 的匹配出來* $bitsAnyClear 進行位運算并且計算結果為 0 的匹配出來* $bitsAnySet 進行位運算并且計算結果不為 0 的匹配出來*/FindIterable<Document> iterable = collection.find(filter);MongoCursor<Document> iterator = iterable.iterator();System.out.println("采用Bson形式查詢");while(iterator.hasNext()) {Document next = iterator.next();Object object = next.get("bitData");if(object instanceof Long)System.out.println(Long.toBinaryString((Long) object));System.out.println(next);}
?更新操作
在SpringBoot的MongoTemplate的更新操作為:
Update update = new Update();update.bitwise("bitData").and(2);mongoTemplate.updateFirst(Query.query(Criteria.where("_id").is("50b76890-d533-4455-8a13-fce98bbd96fe")), update, User.class);
?采用mongo的客戶端為:
MongoCollection<Document> collection = mongoTemplate.getCollection("user");
collection.updateOne(new Document().append("_id", "50b76890-d533-4455-8a13-fce98bbd96fe"),Updates.bitwiseAnd("bitData", Long.valueOf(4)));
位運算的應用
位運算即使用二進制進行計算,所以這個的計算效率是最快的,先看一下常用使用的位運算
public static void main(Stringa rgs []){//位與 &(1&1=1 1&0=0 0&0=0)
System.out.println("the 4 is "+Integer.toBinaryString(4));
System.out.println("the 6 is "+Integer.toBinaryString(6));
System.out.println("the 4&6 is "+Integer.toBinaryString(4&6));
//位或 | (1|1=1 1|0=1 0|0=0)
System.out.println("the 4|6 is "+Integer.toBinaryString(4|6));
//位非~(~1=0 ~0=1)
System.out.println("the ~4 is "+Integer.toBinaryString(~4));
//位異或 ^ (1^1=0 1^0=1 0^0=0)
System.out.println("the 4^6 is "+Integer.toBinaryString(4^6));
// <<有符號左移 >>有符號的右移 >>>無符號右移
//取模的操作 a % (2^n) 等價于 a&(2^n-1)
System.out.println("the 345 % 16 is "+(345%16)+ " or "+(345&(16-1)));
}
運算結果為:
the 4 is 100
the 4 is 100
the 6 is 110
the 4&6 is 100
the 4|6 is 110
the ~4 is 11111111111111111111111111111011
the 4^6 is 10
the 345 % 16 is 9 or 9
雖然看起來平常開發用不到,但是可以使用到權限這一塊由于Integer最大值可以到2的31次方,所以單個數字就可以存儲31中權限,不僅僅可以在業務上使用;
在JDK的反射包中有個類Modifier.class,這個類是采集每個Class的中修飾符以及類型。不僅僅是反射在JUC的包中也使用到了這個位運算,因為運算速度快,所以在很多基礎框架中用的還是很多的。
?