在Java中查询具有完全匹配字段MongoDB的数组元素
Posted
技术标签:
【中文标题】在Java中查询具有完全匹配字段MongoDB的数组元素【英文标题】:Query an element of an array with exactly matching fields MongoDB in Java 【发布时间】:2014-10-28 03:52:41 【问题描述】:我想查询一个/多个匹配到文档中的数组元素(也可以包括子文档)的字段。
例如:
我的收藏包括以下文档:
"_id": 1,
"index": 1,
"elements":[
"name":"test",
"date":"Mon Sep 01 01:00:00 EEST 2014" ,
"tag":1
,
"name": "test2",
"date": "Mon Sep 01 01:00:00 EEST 2014",
"tag": 2
,
"name": "test",
"date": "Mon Sep 01 02:00:00 EEST 2014",
"tag": 3
]
,
"_id":2,
"index":2,
"elements":[
"name":"test",
"date":"Mon Sep 01 01:00:00 EEST 2014" ,
"tag":1
,
"name": "test2",
"date": "Mon Sep 01 01:00:00 EEST 2014",
"tag":2
,
"name":"test",
"date":"Mon Sep 01 01:10:00 EEST 2014",
"tag":3
]
,
"_id": 3,
"index": 3,
"elements": [
"name": "test",
"date": "Mon Sep 01 01:00:00 EEST 2014",
"tag":1
,
"name": "test2",
"date": "Mon Sep 01 01:00:00 EEST 2014",
"tag":2
,
"name": "test",
"date": "Mon Sep 01 01:10:00 EEST 2014",
"tag":3
]
我希望我的查询结果返回给我如下文档:
"_id":1,
"index": 1,
"elements":[
"name":"test",
"date":"Mon Sep 01 02:00:00 EEST 2014" ,
"tag":3
]
为了提供这个,我写了一个查询
Date dCriteria = new SimpleDateFormat("dd/MM/yy HH:mm:ss").parse("01/09/2014 05:00:00");
Query find = new Query( Criteria.where("index").is(3) ); //To find matching documents
find.fields().elemMatch(
"elements",
Criteria.where("name").is("test").and("date").gte(dCriteria)));
mongotemplate.findOne( find, Document.class );
这在 MongoDB shell 命令中意味着:
db.collection.find(
"index": 3 ,
"elements":
"$elemMatch":
"name": "test",
"date":
"$gte": "$date":"2014-09-01T02:00:000Z"
)
但它返回以下结果:
"_id": 0,
"index": 0,
"elements":[
"name": "test",
"date": "Mon Sep 01 01:00:00 EEST 2014",
"tag":1
]
可以省略 _id 和索引字段,但由于匹配条件,它返回数组的第一个 macting 元素,匹配“名称”:“测试”或“日期”大于等于 dCriteria,但我想要的是同时匹配两个条件。
为了做到这一点,我必须使用 $elemMatch 运算符来查询一个数组元素同时匹配多个字段。但我不知道如何在我的投影中使用它的语法。
【问题讨论】:
【参考方案1】:MongoDB 将日期存储为 UTC,这通常也是存储日期的最佳实践,因为它允许在不同时区之间进行转换,而无需计算出原始起点。
因此,最确定的是此处的实际日期确实是 UTC,但您传递的是从不同时区构造的值。驱动程序实际上使用纪元时间戳序列化 BSON 请求,并注明“日期”BSON 类型。这意味着您的输入现在被时区差异“扭曲”。在这种情况下,将 3 小时用于表示 UTC,因此转换后的日期比您想象的要早 3 小时。
如果您有特定时区的某个日期的源数据,那么如果您打算以当地时间显示值,则需要先将其转换为输入和用户界面中的呈现。
所以要么转换为 UTC,要么构造为 UTC:
// org.joda.time as Date constructor is deprecated
Date dCriteria = new DateTime(2014,9,1,1,10,DateTimeZone.UTC).toDate();
Query find = new Query( Criteria.where("index").is(3) ); //To find matching documents
find.fields().elemMatch(
"elements",
Criteria.where("name").is("test").and("date").gte(dCriteria)
);
System.out.println(find);
如果您不确定结果查询的结构,请将其打印出来以进行一般调试。您确实应该在可以在生产中抑制的日志记录级别执行此操作。
如上所述,这与您引用的 shell 查询相同,但您的代码不是因为它使用的是本地时间而不是 UTC。
您几乎总是希望在查询中而不是在投影中进行元素匹配。一旦将$elemMatch
移动到语句的查询部分,就可以使用positional $
运算符预测获得的“第一个”匹配。这样可以确保当数组中没有任何元素真正符合您的条件时,您不会得到一个空数组。
【讨论】:
嗨,尼尔,其实我的问题不在于日期。我也知道 mongo 使用 UTC 格式,不关心日期。我的问题是查询结果。我也可以将 $elemMatch 标准放入搜索查询中,而不是在投影之前得到一个空文档,你说得对。但是问题来自于仅在数组元素中获取匹配的数组元素。这意味着我应该使用(不确定)$elemMatch 和另一个 elemMatch 运算符来为我的查询获取匹配元素,其中既是字段名称又是测试。 @C.Kaya 您的问题在于日期。你得到错误元素的原因是因为 TimeZone 差异的调整时间。您应该能够通过基本上做我在定义查询结束时所做的事情来判断。记录输出以查看序列化表单。您的代码不会产生与您向 shell 发出的查询相同的结果。我正在做的事情会。 我编辑了我在帖子中实际使用的时间格式。下面是我使用的匹配元素的查询: Query: "index" : 1, "alarms" : "$elemMatch" : "name" : "Test1" , "date" : "$gte" : “$date”:“2014-09-01T02:00:00.000Z”,字段:“alarms”:“$elemMatch”:“name”:“Test1”,“date”: "$gte" : "$date" : "2014-09-01T02:00:00.000Z",排序:null以上是关于在Java中查询具有完全匹配字段MongoDB的数组元素的主要内容,如果未能解决你的问题,请参考以下文章