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");
You can do it one of three ways.
You can use aggregation framework to project the value of embedded field using dot notation.
Using Aggregation
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();
Using Find
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")
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:
try catch
blockgetWithDotNotation
might return nullSolution 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With