从Mongo Java嵌入式文档中获取值

9

I have the following document in mongo:

>  {    "_id": ObjectId("569afce4b932c542500143ec"),    
>    "date": "2016-1-17T2:31:0Z",    
>    "day": NumberInt(17),    
>    "model1": {
>      "date": "2016-01-17T02:31+0000",
>      "MondayModel": {
>        "gtxdotdot": {
>          "xdotdot": 0,
>          "xdot": 0
>       },
>        "lsxdotdot": {
>          "xdotdot": 0,
>          "xdot": 0
>       },
>        "gtxdot": {
>          "xdotdot": 0,
>          "xdot": 0
>       },
>        "lsxdot": {
>          "xdotdot": 0,
>          "xdot": 0
>       },
>        "modeldotdot": {
>          "mean": 0,
>          "sdvar": 0
>       },
>        "modeldot": {
>          "mean": 0,
>          "sdvar": 0
>       }
>     } 
>     }

我希望能够找到这个文档,然后提取model1.MondayModel.gtxdotdot.xdotdot/xdot/mean/sdvar的值。我的当前代码如下:
MongoCursor<Document>  back = collection.find(and(eq("topic",topic),eq("sp",sp))).limit(1).iterator();
if (back.hasNext())
{
    Document doc = back.next();
    Document tmpddc1 = (Document)doc.get("model1");
    Document tmpddc2 = (Document)tmpddc1.get("MondayModel");

    Document tmpddc3 = (Document)tmpddc2.get("gtxdotdot");
    gtxdotdotXdotdot = tmpddc3.getDouble("xdotdot");
    gtxdotdotXdot    = tmpddc3.getDouble("xdot");

             tmpddc3 = (Document)tmpddc2.get("lsxdotdot");
    lsxdotdotXdotdot = tmpddc3.getDouble("xdotdot");
    lsxdotdotXdot    = tmpddc3.getDouble("xdot");

             tmpddc3 = (Document)tmpddc2.get("gtxdot");
    gtxdotXdotdot = tmpddc3.getDouble("xdotdot");
    gtxdotXdot    = tmpddc3.getDouble("xdot");            

             tmpddc3 = (Document)tmpddc2.get("lsxdot");
    lsxdotXdotdot = tmpddc3.getDouble("xdotdot");
    lsxdotXdot    = tmpddc3.getDouble("xdot");  


             tmpddc3 = (Document)tmpddc2.get("modeldotdot");
    modeldotdotXmean = tmpddc3.getDouble("mean");
    modeldotdotXsdvar    = tmpddc3.getDouble("sdvar");             

             tmpddc3 = (Document)tmpddc2.get("modeldot");
    modeldotXmean = tmpddc3.getDouble("mean");
    modeldotXsdvar    = tmpddc3.getDouble("sdvar");                         

}

除了像上面那样运行文档,是否有一种使用点符号 [model1.MondayModel.gtxdotdot.xdotdot] 获取值的方法?类似于以下内容:

double value =  doc.getDouble("model1.MondayModel.gtxdotdot.xdotdot");
3个回答

9

你可以用三种方法之一来实现。

你可以使用聚合框架,使用点符号投影嵌入字段的值。

使用聚合

 import static com.mongodb.client.model.Aggregates.*;
 import static com.mongodb.client.model.Filters.eq;
 import static com.mongodb.client.model.Projections.computed;
 import static java.util.Arrays.*;
 import static com.mongodb.client.model.Projections.include;

 MongoClient mc = new MongoClient();
 MongoDatabase db = mc.getDatabase("test");
 MongoCollection<Document> collection = db.getCollection("collection");
 Document document = 
     collection.aggregate(asList(
        match(eq("day",17)),
        project(computed("val", "$model1.MondayModel.gtxdotdot.xdotdot")))).
     first();
 Double embeddedField = document.getDouble("val");

Using Distinct

 Double embeddedField = collection.distinct("model1.MondayModel.gtxdotdot.xdotdot", eq("day",17), Double.class).first();

使用“查找”功能

 Document document = collection.find(eq("day",17)).projection(include("model1.MondayModel.gtxdotdot.xdotdot")).first();
 Double embeddedField = document.get("model1", Document.class).get("MondayModel", Document.class).get("gtxdotdot", Document.class).getDouble("xdotdot")

你的前两个例子涉及查询而不是从文档对象中获取值。然而,你在“使用查找”示例的第二行是有效的。 - arxakoulini
1
无法直接从文档中获取值。请参考此处。作为替代方案,我提供了聚合框架查询,您可以使用点符号投影嵌入式值,并在Java端使用简单的get方法读取该值。 - s7vr

2

我认为你不能直接使用点号符号,但你可以创建自己的辅助函数。

解决方案1:使用点号符号获取字段

public static Object getWithDotNotation( Document document, String dots ) 
     throws MongoException{

    String[] keys = dots.split( "\\." );
    Document doc = document;

    for( int i = 0; i < keys.length - 1; i++ ){
        Object o = doc.get( keys[ i ] );
        if( o == null || !( o instanceof Document ) ){
            throw new MongoException( String.format( 
                    "Field '%s' does not exist or s not a Document", keys[ i ] ) );
        }
        doc = ( Document ) o;
    }//end for

    return doc.get( keys[ keys.length - 1 ] );
}

您可以像这样使用它:
String dotNotation = "model1.MondayModel.gtxdotdot.xdotdot";

FindIterable<Document> projection = mongoColl.find()
    .projection( fields( include( dotNotation ) ) );

Double value = ( Double ) getWithDotNotation( projection.first(), dotNotation );

System.out.println( value ); // result: 0.0

这将大大简化你的代码。需要注意的是:
  • 如果不确定点记法,可以使用try catch
  • getWithDotNotation方法可能返回null
  • 强制转换时要小心,只有在数据类型(这里是Double)完全确定时才使用。

解决方案2:展开你的文档

public static Document flattenDoc( Document document ){

    Document flattened = new Document();
    Queue<Pair<String, Document>> queue = new ArrayDeque<>();
    queue.add( new Pair<>( "", document ) );

    while( !queue.isEmpty() ){
        Pair<String, Document> pair = queue.poll();
        String key = pair.getKey();
        for( Map.Entry<String, Object> entry : pair.getValue().entrySet() ){
            if( entry.getValue() instanceof Document ){
                queue.add( new Pair<>( key + entry.getKey() + ".", ( Document ) entry.getValue() ) );

            }else{
                flattened.put( key + entry.getKey(), entry.getValue() );

            }
        }//end for
    }

    return flattened;
}

使用您的示例数据,flattenDoc 的结果如下:

Document{{_id=569afce4b932c542500143ec,
date=2016-1-17T2:31:0Z,
day=17,
model1.date=2016-01-17T02:31+0000,
model1.MondayModel.gtxdotdot.xdotdot=0.0,
model1.MondayModel.gtxdotdot.xdot=0.0,
model1.MondayModel.lsxdotdot.xdotdot=0.0,
model1.MondayModel.lsxdotdot.xdot=0.0,
model1.MondayModel.gtxdot.xdotdot=0.0,
model1.MondayModel.gtxdot.xdot=0.0,
model1.MondayModel.lsxdot.xdotdot=0.0,
model1.MondayModel.lsxdot.xdot=0.0,
model1.MondayModel.modeldotdot.mean=0.0,
model1.MondayModel.modeldotdot.sdvar=0.0,
model1.MondayModel.modeldot.mean=0.0,
model1.MondayModel.modeldot.sdvar=0.0}}

所以你可以直接使用getDouble("model1.MondayModel.gtxdotdot.xdotdot")。如果你需要访问所有字段,这种方法可能更有效率。


这句话不应该是 for(int i = 0; i < keys.length; i++) 吗? - arxakoulini
1
不需要,因为最后一个值是文档属性的名称,而不是文档本身。因此,在最后一个句点之前的名称处停止(希望这个解释清楚了?) - Derlin

1
在MongoDB和Java的较新版本中,您可以使用 Document 上的 getEmbedded()方法,其中 path 是嵌入值的字符串路径。以下简写与上述功能完全相同。对于那些只需要在少数地方使用此功能而不想编写整个函数的人非常有用。
String path = "model1.MondayModel.gtxdotdot.xdotdot";
Double value = document.getEmbedded(Arrays.stream(path.split("\\.")).toList(), Double.class);

我不确定这种方法的效率如何,但它肯定比较简洁。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接