如何使用Java从Mongodb中的子文档数组中获取所需字段

如何使用Java从Mongodb中的子文档数组中获取所需字段,java,arrays,mongodb,Java,Arrays,Mongodb,我刚刚开始使用Mongo Db。下面是我的数据结构 它有一个skillID数组,每个skillID都有一个activeCampaign数组,每个activeCampaign都有一个callsByTimeZone数组 我在SQL术语中寻找的是: Select activeCampaigns.callsByTimeZone.label, activeCampaigns.callsByTimeZone.loaded from X where skillID=50296 and act

我刚刚开始使用Mongo Db。下面是我的数据结构

它有一个skillID数组,每个skillID都有一个
activeCampaign
数组,每个
activeCampaign
都有一个
callsByTimeZone
数组

我在SQL术语中寻找的是:

Select activeCampaigns.callsByTimeZone.label,
       activeCampaigns.callsByTimeZone.loaded 
from X 
where skillID=50296 and activeCampaigns.campaign_id= 11371940 
      and activeCampaigns.callsByTimeZone='PT'
我期望得到的输出是

{"label":"PT", "loaded":1 }
我使用的命令是

db.cd.find({ "skillID" : 50296 , "activeCampaigns.campaignId" : 11371940,
"activeCampaigns.callsByTimeZone.label" :"PT" },
{ "activeCampaigns.callsByTimeZone.label" : 1 , 
"activeCampaigns.callsByTimeZone.loaded" : 1 ,"_id" : 0})
我得到的输出是
activeCampaigns.callsByTimeZone
下的所有内容,而我只期望
PT

数据结构:

{
   "skillID":50296,
   "clientID":7419,
   "voiceID":1,
   "otherResults":7,
   "activeCampaigns":
   [{
       "campaignId":11371940,
       "campaignFileName":"Aaron.name.121.csv",
       "loaded":259,
       "callsByTimeZone":
       [{
           "label":"CT",
           "loaded":6
       },
       {
           "label":"ET",
           "loaded":241
       },
       {
           "label":"PT",
           "loaded":1
       }]
   }]
}
我在Java中也尝试过同样的方法

QueryBuilder query = QueryBuilder.start().and("skillID").is(50296)
                    .and("activeCampaigns.campaignId").is(11371940)
                    .and("activeCampaigns.callsByTimeZone.label").is("PT");

BasicDBObject fields = new BasicDBObject("activeCampaigns.callsByTimeZone.label",1)
                    .append("activeCampaigns.callsByTimeZone.loaded",1).append("_id", 0);

DBCursor cursor = coll.find(query.get(), fields);
String campaignJson = null;
while(cursor.hasNext()) {
    DBObject campaignDBO = cursor.next();
    campaignJson = campaignDBO.toString();
    System.out.println(campaignJson);
}
获得的值是
callsByTimeZone
array下的所有内容。我目前正在解析获得的JSON,只得到
PT
值。有没有办法只查询
activeCampaigns.callsByTimeZone
中的
PT
字段

提前谢谢。很抱歉,如果这个问题已经在论坛上提出,我已经搜索了很多,没有找到合适的解决方案。
提前感谢。

将Java代码中的while循环替换为以下内容似乎会将“PT”作为输出

   `while(cursor.hasNext()) {
        DBObject campaignDBO = cursor.next();
        campaignJson = campaignDBO.get("activeCampaigns").toString();
        int labelInt = campaignJson.indexOf("PT", -1);
        String label = campaignJson.substring(labelInt, labelInt+2);
        System.out.println(label);
      }`

有几种方法可以做到这一点,但您不应该使用字符串操作(即
indexOf
),性能可能会很糟糕

游标中的结果是嵌套映射,表示数据库中的文档-映射是键-值对的良好Java表示。因此,您可以导航到文档中所需的位置,而不必将其作为字符串进行解析。我已经测试了以下内容,它对您的测试数据有效,但如果您的数据与示例不完全相同,则可能需要对其进行调整:

while (cursor.hasNext()) {
    DBObject campaignDBO = cursor.next();
    List callsByTimezone = (List) ((DBObject) ((List) campaignDBO.get("activeCampaigns")).get(0)).get("callsByTimeZone");
    DBObject valuesThatIWant;
    for (Object o : callsByTimezone) {
        DBObject call = (DBObject) o;
        if (call.get("label").equals("PT")) {
            valuesThatIWant = call;
        }
    }
}
根据您的数据,您可能还需要添加针对空值的保护

您要查找的内容(
{“label”:“PT”,“loaded”:1}
)位于变量
中,该变量是我想要的。请注意,这也是一个DBObject,即一个Map,因此如果您想查看其中的内容,需要使用
get

valuesThatIWant.get("label");  // will return "PT"
valuesThatIWant.get("loaded"); // will return 1
因为DBObject实际上是字符串到对象的映射(即
映射
),所以您需要转换从中产生的值(因此我的答案中的第一位代码很难看)-使用数字,这将取决于数据如何加载到数据库中,它可能是int或double:

String theValueOfLabel = (String) valuesThatIWant.get("label");   // will return "PT"
double theValueOfLoaded = (Double) valuesThatIWant.get("loaded"); // will return 1.0
我还想从我的回答中指出以下几点:

((List) campaignDBO.get("activeCampaigns")).get(0)
这假设“activeCampaigns”是a)一个列表,在本例中b)只有一个条目(我正在执行
get(0)

您还将注意到,您设置的
字段
值几乎完全被忽略,结果是文档的大部分,而不仅仅是您要求的字段。我很确定您只能定义希望查询返回的顶级字段,因此您的代码:

BasicDBObject fields = new BasicDBObject("activeCampaigns.callsByTimeZone.label",1)
    .append("activeCampaigns.callsByTimeZone.loaded",1)
    .append("_id", 0);
实际上与:

BasicDBObject fields = new BasicDBObject("activeCampaigns", 1).append("_id", 0);
我认为以下几点将有助于您使用Java和MongoDB:

  • 当您查询数据库时,它将返回您的整个文档 与您的查询匹配的内容,即“skillID”中的所有内容 向下。如果要选择要返回的字段,我认为这些字段将仅为顶级字段。有关更多详细信息,请参阅
  • 要导航结果,您需要知道返回了一个
    DBObject
    s,并且它们实际上是Java中的一个
    Map
    ——您可以使用
    get
    导航到正确的节点, 但您需要将值转换为正确的形状

IMO用java编写mongodb查询真是太枯燥了!非常感谢您对游标工作原理的详细解释。在发布这个问题之前,我已经通过使用BasicDBList完成了同样的任务。通过这样做,我猜我实际上会得到整个callsByTimeZone数组,然后将其解析出来,但我的意图是编写一个查询,它可以直接获得标签为PT的元素。我想我需要改变模式来实现同样的效果。我有两个选择,1。通过在activeCampaigns(又称activeCampaigns{callsByTZPacific{},callsByTZEaster{})下创建每个时区的数据作为字段,或2.为CallsbytimeZone创建另一个集合不要使用单独的集合,这违背了MongoDB的目的;您的第一个选项将不起作用,因为您仍然要检索所有activeCampaigns—请记住,“字段”按顶级字段过滤。您可能希望看到的是-这使您能够更好地控制返回的数据。现在,我可以使用$elemMatch将数组元素降低一级//db.activitData.find({“skillID”:50297},{activeCampaigns:{$elemMatch:{“activitid”:11433464}})。这是好的,但它仅限于一个级别$elemMatch不支持向下钻取到数组的数组元素//不要使用单独的集合,这会破坏MongoDb的用途;当查询嵌套结构变得越来越复杂,有时甚至不可能的时候,你能分享一下你的想法吗?为什么我们不应该使用平面结构。