javascript - Can't figure out how to use MongoDB's $bitsAnySet operator
问题描述
I am working on a little project where I need to store business hours in my MongoDB database. I decided to use a bitmask for the entire week represented by 336bits which means that every bit represents 30 minutes of the day. (Using a bitmask has no special reason. I am willing to change the way I'm storing business hours, but I'm curious anyway.)
I thought it would be convenient if I'd use the $bitsAnySet operator to query based on the bitmask, because I could just figure out the position that needed to be set if a customer queries for open businesses a t 4pm for example.
Reading the docs about the operator I found out that you can provide a array of positions you want checked, which would be the perfect fit for my needs.
But here's the catch. I can't figure out how those positions work. The docs state, that position 0 will be on the least significant bit (the one most right) meaning a document with a number like 254 (0b1111 1110) stored as the business hours would not be found if querying $bitsAnySet: [0], because the far most right bit is set to 0 in this example.
Using the operator this way, testing it on my test data I always ran into problems, because the positioning didn't seem to match.
So I wrote myself a little script (in JavaScript running on NodeJS v12 connecting to MongoDB using Mongoose) that was simply trying all the possible positions:
Here my Script:
require ('mongoose').connect ('mongodb://localhost/test', {useCreateIndex: true, useNewUrlParser: true, useFindAndModify: false, useUnifiedTopology: true}).then (() => {
let buffer = Buffer.alloc (6, 0);
// 0xC0 => 0b1100 0000
buffer.writeUInt8 (0xC0, 0);
// 0x07 => 0b0000 0111
buffer.writeUInt8 (0x07, 5);
// buffer should be: 11000000 00000000 00000000 00000000 00000000 00000111
new SomeModel ({data: buffer}).save ().then (model => {
let promises = [];
// just trying out each position
for (let index = 0; index < 48; index ++)
promises.push (SomeModel.findOne ({data: {$bitsAnySet: [index]}}))
Promise.all (promises).then (resolved => {
resolved = resolved.map ((item, index) => {
if (item !== null)
return {index, item: item.data.toString ('hex')};
else return null;
});
// yes, I know it's not very performant to go over the entire array a second time,
// because I could have done it in the first round,
// but this was just some quick and dirty testing :D
resolved = resolved.filter (item => item !== null);
console.log (resolved);
})
}).catch (console.log);
}).catch (console.log);
This was my little testing script, and I expected an output like:
[
{index: 0, item: 'c00000000007'},
{index: 1, item: 'c00000000007'},
{index: 2, item: 'c00000000007'},
{index: 41, item: 'c00000000007'},
{index: 42, item: 'c00000000007'},
]
because when looking at the binary representation coming from the right it should first find the three ones at the positions 0, 1 and 2 and then it should find the last two ones at the very left end.
But I got this:
[
{ index: 6, item: 'c00000000007' },
{ index: 7, item: 'c00000000007' },
{ index: 40, item: 'c00000000007' },
{ index: 41, item: 'c00000000007' },
{ index: 42, item: 'c00000000007' }
]
This blew my mind, because it was totally unexpected for me. Suddenly it started looking like MongoDB is starting from the left. It found the two ones before the three ones, and the two ones are located at the very left of the binary data. Which also confuses me is that it doesn't seem to start at position 0.
If I'm not wrong my data starts with two ones and ends with three. So a $bitsAnySet: [0] should be true either way.
Does anyone of you know what I'm doing wrong here? I'm very thankful for your help!
Note: Correct me if I'm wrong, but I believe that Promise.all does not change the order of the array, meaning that it couldn't just be that one promise just resolved before the other changing its position in the 'promises' array.
Note: The model does not have any additional fields. It just has the default _id field and the data field.
解决方案
Fortunately I was able to figure this one out myself. This has to do with endianness. In my mind I was thinking in the big endian way therefore assuming it to be stored as follows:
Hex: c0 00 00 00 00 07
Bin: 11000000 00000000 00000000 00000000 00000000 00000111
According to the BSON Specification BSON is
serialized in little-endian format.
therefore its actually stored like this:
Hex: 07 00 00 00 00 C0
Bin: 00000111 00000000 00000000 00000000 00000000 11000000
This explains why the indexes where mixed around.
推荐阅读
- reactjs - 无限循环或 useEffect 中缺少依赖项
- kubernetes - cudaErrorInsufficientDriver:CUDA 驱动程序版本对于 CUDA 运行时版本不足
- javascript - openWeatherMap 不显示传单图层和 geoJSON
- bots - 如何在 discord.py 中将 guild_subscriptions 事件设置为 true?
- python - 在硒中找不到元素(python)
- node.js - 尝试使用 typescript 构建 node.js 应用程序时构建或测试错误
- python - 按月份分组数据
- reactjs - 从 WebSocket 客户端通过 WebSocket 多次调用 RPC 服务器
- android - 使用 Smack 连接 XMPP 服务器的延迟
- bigtable - 设计 Cloud BigTable:数百万行 X 数百万列?