在MongoDB中查询场景
问题描述:
因为我是mongoDB的新手,我在想以下问题:在我的文档中有一个字段(让我们打电话为fieldA
)。该字段的值存储为数字表示的十六进制字符串。基于数字表示,是否有可能检索出值落在给定值范围内的那些文档的子集?我假设这个字段被存储为一个字符串,mongoDB会根据给定的查询限制按字典顺序进行比较fieldA
,这与我的需求不一致。在MongoDB中查询场景
比方说,我的文件范围是:fieldA >= "0x12f"
和fieldA <= "0x12ea"
在这种情况下,我假设的MongoDB会比较字典顺序作出决定,但在这种情况下,我想基础上的数值进行比较。
答
在MongoDB中,如果将十六进制字符串转换为二进制数据,则可以执行大于64位的十六进制范围查询。 Java的BigInteger类允许这样做。
考虑下面的代码中插入和查询的十六进制值作为二进制
private static void bigIntTest(MongoCollection<Document> bigIntCollection) {
// BigInteger cannot parse 0x, so it must be stripped off
// the "16" argument represent base 16 radix
BigInteger floor = new BigInteger("0x12f".substring(2), 16);
BigInteger ceiling = new BigInteger("0x12ea".substring(2), 16);
BigInteger between = new BigInteger("0x555".substring(2), 16);
// The toByteArray() method on BigInteger returns a byte array
// Containing the two's-complement representation of this BigInteger.
// MongoDB will store the byte array as a binary data field
bigIntCollection.insertOne(new Document("value", floor.toByteArray()));
bigIntCollection.insertOne(new Document("value", between.toByteArray()));
bigIntCollection.insertOne(new Document("value", ceiling.toByteArray()));
rangeQuery(bigIntCollection, floor, ceiling);
// Test with values greater than Long.MAX_VALUE
BigInteger newFloor = new BigInteger("8000000000000000", 16);
BigInteger newBetween = new BigInteger("1dcd64ffffffffffe58250e3", 16);
BigInteger newCeiling = new BigInteger("4563918244f3fffff538dcfb7617ffff", 16);
List<Document> newDocuments = Arrays.asList(new Document[] { new Document("value", newFloor.toByteArray()),
new Document("value", newBetween.toByteArray()), new Document("value", newCeiling.toByteArray()) });
bigIntCollection.insertMany(newDocuments);
rangeQuery(bigIntCollection, newFloor, newCeiling);
}
private static void rangeQuery(MongoCollection<Document> bigIntCollection, BigInteger floor, BigInteger ceiling) {
Document range = new Document("$gt", floor.toByteArray());
range.append("$lt", ceiling.toByteArray());
Document filter = new Document("value", range);
FindIterable<Document> find = bigIntCollection.find().filter(filter);
find.iterator().forEachRemaining(new Consumer<Document>() {
@Override
public void accept(Document t) {
byte[] data = ((Binary) t.get("value")).getData();
System.out.println(new BigInteger(data).toString(16));
}
});
}
第二组十六进制值超过Long.MAX_VALUE
,它仍然工作就像一个魅力。然而,我会建议在生产中使用这种技术之前进行广泛的测试,特别是如果负数可能和溢出边界附近。可能会发现完整代码here
此解决方案附带一个附加警告。对于大的二进制值,索引在RAM和性能方面可能会变得昂贵。请确保您查看关于此主题的MongoDB docs。
我不认为这是可能的。为什么不使用相应的十进制数字具有不同的字段并将其用于范围查询? – cjungel
@cjungel这将是一个很好的解决方案是的。但我担心这个字段的值增加得太快,以至于十进制数字表示无法处理它。基本上这就是场地设计的原因。当我在Java中进行编程时,我认为Long是数字表示,但是是的,关注这个事实。 –
java和mongo都支持64位整数。你需要支持更大的数字吗?如果这足以满足您的域,您可以利用本机索引并执行有效的范围查询。如果你绝对需要更大的数字,你可以将它们表示为固定长度的字符串(前面的0)。这样你可以表示任意大的数字,但仍然能够使用本地索引来执行范围查询,因为在您的情况下字母数字字符串排序是正确的。 – cjungel