2014-09-03 46 views
0

我想要查询文档中的数组元素的一个/多个匹配字段(也可以包括子文档)。查询具有完全匹配字段的数组的元素Java中的MongoDB

例如:

我的收藏包括以下文件:

{ 
    "_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和指数的Fileds但它返回的第一macting元素数组由于匹配条件,要么匹配“名称”:“测试”或“日期”大于等于dCriteria,但我想要的是在同一时间匹配两个标准。

为了这一点,我必须使用$ elemMatch运营商查询exatcly多个字段在一个数组元素的同时匹配。但我不知道如何在我的投影中使用它的语法。

回答

1

MongoDB中保存日期,UTC,这也是一般的存储日期,因为它允许转换和来自各时区没有工作离开原点原始点一般最佳实践。

因此,它是最肯定的是,这里的实际日期确实是UTC,但是您传递从不同的时区构建的价值。驱动程序实际上序列化具有纪元时间戳的BSON请求并注明“日期”BSON类型。这意味着您的输入现在被时区差异“倾斜”。在这种情况下,取消3个小时来表示UTC,因此转换后的日期比您想象的早3个小时。

如果您有附带在特定时区的日期源的数据,那么你需要先转换这一点,既作为输入,并在用户界面演示,如果你打算显示的本地时间值。

因此,无论转换还是构建为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); 

你在哪里不确定的结果查询将如何构建,打印出来的一般调试。您应该在日志级别执行此操作,您可以在生产中对其进行压制。

如上所述,这形成是一样的,你引用的外壳查询,但因为它代表你的代码是不是因为它使用的是本地时间,而不是UTC。

你也几乎总是真的想在查询中做你的元素匹配而不是在投影中。一旦您将您的$elemMatch移动到您的语句的查询部分,即可使用positional $运算符来计算获得的“第一个”匹配。这可以确保当数组中没有任何元素实际符合条件时,不会获得空数组。

+0

嗨尼尔,其实我的问题是不符合日期。我也知道mongo使用UTC格式,不关心日期。我的问题是查询结果。我可以将$ elemMatch条件放入搜索查询中,以免在投影之前得到空白文档,这很重要。但是问题来自于获取数组元素中唯一匹配的数组元素。这意味着我应该使用(不确定)$ elemMatch与另一个elemMatch操作符来获取我的查询的匹配元素,这是字段名称和测试。 – 2014-09-05 06:51:01

+0

@ C.Kaya您的问题与日期。你得到错误元素的原因是因为TimeZone差异的调整时间。您应该能够基本上完成我在定义查询结束时所做的工作。记录输出以查看序列化表单。您的代码不会与您发布到shell的查询产生相同的结果。我在做什么。 – 2014-09-05 07:01:08

+0

我编辑我的时间格式,实际上我在我的帖子中使用。下面是我用过的匹配元素的查询:Query:{“index”:1,“alarms”:{“$ elemMatch”:{“name”:“Test1”,“date”:{“$ gte”: {“$ date”:“2014-09-01T02:00:00.000Z”}}}}},Fields:{“alarms”:{“$ elemMatch”:{“name”:“Test1”,“date”: {“$ gte”:{“$ date”:“2014-09-01T02:00:00.000Z”}}}}},排序:null – 2014-09-05 07:20:19