Search code examples
mongodbmongodb-querymongo-javamongo-java-driver

Get value from Embedded Document Mongo Java


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
>       }
>     } 
>     }

I wish to both find this document and extract only the values of model1.MondayModel.gtxdotdot.xdotdot/xdot/mean/sdvar ...

My current code does so with the following:

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");                         

}

Instead of running thought he document (as above) is there a way to get the values using the dot notation [model1.MondayModel.gtxdotdot.xdotdot] ? Something such as:

double value =  doc.getDouble("model1.MondayModel.gtxdotdot.xdotdot");

Solution

  • I don't think you can use dot notation directly, but you can create your own helper function.

    Solution 1: get field with dot notation

    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 ] );
    }
    

    You can then use it like this:

    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
    

    This will simplify your code a whole lot. The only things to care for are:

    • if you are not sure of you dot notation, use a try catch block
    • the method getWithDotNotation might return null
    • be careful about forced casts, use it only if you are 100% sure of the type of your data (here Double).

    Solution 2: flatten your doc

    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;
    }
    

    With your sample data, the result of flattenDoc is the following:

    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}}
    

    So you can use getDouble("model1.MondayModel.gtxdotdot.xdotdot") directly. This approach might be more efficient if you need to access all the fields.