最近在公司业务上用到了二进制匹配数据,但是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位

bit11111111
position76543210

更新操作

使用$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" }

二进制对应

1000110 0100
200001 0100
1311000 0011
80000 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 8
	filter.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的包中也使用到了这个位运算,因为运算速度快,所以在很多基础框架中用的还是很多的。

 

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐