最近在公司业务上用到了二进制匹配数据,但是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的包中也使用到了这个位运算,因为运算速度快,所以在很多基础框架中用的还是很多的。