DevNation'15 - Using Lambda Expressions to Query a Datastore
-
Upload
xavier-coulon -
Category
Technology
-
view
330 -
download
1
Transcript of DevNation'15 - Using Lambda Expressions to Query a Datastore
![Page 1: DevNation'15 - Using Lambda Expressions to Query a Datastore](https://reader031.fdocuments.net/reader031/viewer/2022020113/58ed17691a28abe6558b4675/html5/thumbnails/1.jpg)
Using Lambda Expressions to Query a
Datastore
Xavier Coulon - Red Hat @xcoulon
![Page 2: DevNation'15 - Using Lambda Expressions to Query a Datastore](https://reader031.fdocuments.net/reader031/viewer/2022020113/58ed17691a28abe6558b4675/html5/thumbnails/2.jpg)
The idea
![Page 3: DevNation'15 - Using Lambda Expressions to Query a Datastore](https://reader031.fdocuments.net/reader031/viewer/2022020113/58ed17691a28abe6558b4675/html5/thumbnails/3.jpg)
The idea
Lambda Expressions:
• provide type safety
• describe the "what", not the "how"
![Page 4: DevNation'15 - Using Lambda Expressions to Query a Datastore](https://reader031.fdocuments.net/reader031/viewer/2022020113/58ed17691a28abe6558b4675/html5/thumbnails/4.jpg)
The idea
![Page 5: DevNation'15 - Using Lambda Expressions to Query a Datastore](https://reader031.fdocuments.net/reader031/viewer/2022020113/58ed17691a28abe6558b4675/html5/thumbnails/5.jpg)
The idea
u -> ( u.firstName.equals("john") && u.lastName.equals("doe")) || u.userName.equals("jdoe"))
final List<User> users = userCollection.filter( u -> ( u.firstName.equals("john") && u.lastName.equals("doe")) || u.userName.equals("jdoe")).toList();
![Page 6: DevNation'15 - Using Lambda Expressions to Query a Datastore](https://reader031.fdocuments.net/reader031/viewer/2022020113/58ed17691a28abe6558b4675/html5/thumbnails/6.jpg)
Introducing project
Lambdamatic
![Page 7: DevNation'15 - Using Lambda Expressions to Query a Datastore](https://reader031.fdocuments.net/reader031/viewer/2022020113/58ed17691a28abe6558b4675/html5/thumbnails/7.jpg)
Part I. The building blocks
![Page 8: DevNation'15 - Using Lambda Expressions to Query a Datastore](https://reader031.fdocuments.net/reader031/viewer/2022020113/58ed17691a28abe6558b4675/html5/thumbnails/8.jpg)
Building blocks
• Metamodel generation • Lambda Expression analysis
![Page 9: DevNation'15 - Using Lambda Expressions to Query a Datastore](https://reader031.fdocuments.net/reader031/viewer/2022020113/58ed17691a28abe6558b4675/html5/thumbnails/9.jpg)
Metamodel generation (à la JPA)
![Page 10: DevNation'15 - Using Lambda Expressions to Query a Datastore](https://reader031.fdocuments.net/reader031/viewer/2022020113/58ed17691a28abe6558b4675/html5/thumbnails/10.jpg)
package com.example.domain;
public class User { private String id; private String firstName; private String lastName; private String userName;
}
Metamodel generation
package com.example.domain;
@Document(collection="users") public class User {
@DocumentId private String id;
@DocumentField private String firstName;
private String lastName;
@DocumentField(name="uName") private String userName; }
package com.example.domain;
@Generated(value="LambdamaticAnnotationsProcessor") public class QUser implements QueryMetadata<User> {
@DocumentField(name="_id") public StringField firstName;
@DocumentField(name="firstName") public StringField firstName;
@DocumentField(name="lastName") public StringField lastName;
@DocumentField(name="uName") public StringField userName; }
Query Metadata class generation
![Page 11: DevNation'15 - Using Lambda Expressions to Query a Datastore](https://reader031.fdocuments.net/reader031/viewer/2022020113/58ed17691a28abe6558b4675/html5/thumbnails/11.jpg)
Metamodel generation
package com.example.domain;
@Generated(value="o.l.m.a.DocumentAnnotationProcessor") public class UserCollection extends LambdamaticMongoCollectionImpl<User, QUser> {
public UserCollection(final MongoClient mongoClient, final String databaseName) { super(mongoClient, databaseName, "users", User.class); }
}
Collection classpackage com.example.domain;
@Document(collection="users") public class User {
@DocumentId private String id;
@DocumentField private String firstName;
private String lastName;
@DocumentField(name="uName") private String userName; }
![Page 12: DevNation'15 - Using Lambda Expressions to Query a Datastore](https://reader031.fdocuments.net/reader031/viewer/2022020113/58ed17691a28abe6558b4675/html5/thumbnails/12.jpg)
Metamodel generation
CDI Integration
@Generated(value="org.lambdamatic.mongodb.apt.DocumentAnnotationProcessor") @ApplicationScoped public class UserCollectionProducer { @Produces public UserCollection getUserCollection(final MongoClient mongoClient, final MongoClientConfiguration mongoClientConfiguration) { return new UserCollection(mongoClient, mongoClientConfiguration.getDatabaseName(), "users"); }
}
![Page 13: DevNation'15 - Using Lambda Expressions to Query a Datastore](https://reader031.fdocuments.net/reader031/viewer/2022020113/58ed17691a28abe6558b4675/html5/thumbnails/13.jpg)
final List<User> users = userCollection.filter( u -> ( u.firstName.equals("john") && u.lastName.equals("doe")) || u.userName.equals("jdoe")).toList();
Metamodel in action
public class UserCollection extends LambdamaticMongoCollectionImpl<User, QUser> { ... }
package com.example.domain;
@Generated(value="LambdamaticAnnotationsProcessor") public class QUser implements QueryMetadata<User> {
@DocumentField(name="_id") public StringField firstName;
@DocumentField(name="firstName") public StringField firstName;
... }
![Page 14: DevNation'15 - Using Lambda Expressions to Query a Datastore](https://reader031.fdocuments.net/reader031/viewer/2022020113/58ed17691a28abe6558b4675/html5/thumbnails/14.jpg)
Metamodel benefits
Geospatial queries
![Page 15: DevNation'15 - Using Lambda Expressions to Query a Datastore](https://reader031.fdocuments.net/reader031/viewer/2022020113/58ed17691a28abe6558b4675/html5/thumbnails/15.jpg)
Metamodel benefits
public class Location {
/** The latitude value.*/ private double latitude;
/** The longitude value.*/ private double longitude;
}
public List<BikeStation> findWithin(final Location[] corners) { return bikeStationCollection.find(s -> s.location.geoWithin(corners)) .toList(); }
public class QBikeStation implements QueryMetadata<BikeStation> {
@DocumentField(name="location") public LocationField location;
...
}
@Document(collection="bikestations") public class BikeStation { @DocumentField private Location location;
...
}
public interface LocationField {
public boolean geoWithin(final Location[] corners);
... }
![Page 16: DevNation'15 - Using Lambda Expressions to Query a Datastore](https://reader031.fdocuments.net/reader031/viewer/2022020113/58ed17691a28abe6558b4675/html5/thumbnails/16.jpg)
Lambda Expression Analysis
(this is the fun part, really)
![Page 17: DevNation'15 - Using Lambda Expressions to Query a Datastore](https://reader031.fdocuments.net/reader031/viewer/2022020113/58ed17691a28abe6558b4675/html5/thumbnails/17.jpg)
Lambda Expression Analysis
![Page 18: DevNation'15 - Using Lambda Expressions to Query a Datastore](https://reader031.fdocuments.net/reader031/viewer/2022020113/58ed17691a28abe6558b4675/html5/thumbnails/18.jpg)
![Page 19: DevNation'15 - Using Lambda Expressions to Query a Datastore](https://reader031.fdocuments.net/reader031/viewer/2022020113/58ed17691a28abe6558b4675/html5/thumbnails/19.jpg)
Lambda Expression Analysis
@FunctionalInterfacepublic interface FilterExpression<T> extends Predicate<T>, Serializable { }
Step#1 : Use a Serializable Functional Interface
![Page 20: DevNation'15 - Using Lambda Expressions to Query a Datastore](https://reader031.fdocuments.net/reader031/viewer/2022020113/58ed17691a28abe6558b4675/html5/thumbnails/20.jpg)
Lambda Expression Analysis
public static <T> SerializedLambda getSerializedLambda( final FilterExpression<T> expression) { ... final Class<?> cl = expression.getClass(); final Method m = cl.getDeclaredMethod("writeReplace"); m.setAccessible(true); return (SerializedLambda) m.invoke(expression); ... }
Step#2 : Locate the generated method
![Page 21: DevNation'15 - Using Lambda Expressions to Query a Datastore](https://reader031.fdocuments.net/reader031/viewer/2022020113/58ed17691a28abe6558b4675/html5/thumbnails/21.jpg)
Lambda Expression Analysis
Step#3 : read the generated bytecode Label L1125736023 VarInsn ALOAD #0 FieldInsn GETFIELD com/sample/QUser.firstName (desc=Lorg/lambdamatic/mongodb/metadata/StringField;) LdcInsn john MethodInsn INVOKEVIRTUAL java/lang/Object.equals (desc=(Ljava/lang/Object;)Z) JumpInsn IFEQ L1636182655 VarInsn ALOAD #0 FieldInsn GETFIELD com/sample/QUser.lastName (desc=Lorg/lambdamatic/mongodb/metadata/StringField;) Label L1122606666 LdcInsn doe MethodInsn INVOKEVIRTUAL java/lang/Object.equals (desc=(Ljava/lang/Object;)Z) JumpInsn IFNE L350068407 Label L1636182655 VarInsn ALOAD #0 FieldInsn GETFIELD com/sample/QUser.userName (desc=Lorg/lambdamatic/mongodb/metadata/StringField;) LdcInsn jdoe MethodInsn INVOKEVIRTUAL java/lang/Object.equals (desc=(Ljava/lang/Object;)Z) JumpInsn IFNE L350068407 Insn ICONST_0 JumpInsn GOTO L1820383114 Label L350068407 Insn ICONST_1 Label L1820383114 Insn IRETURN Label L369049246 LocalVariable u (desc=Lcom/sample/QUser;) index=0
![Page 22: DevNation'15 - Using Lambda Expressions to Query a Datastore](https://reader031.fdocuments.net/reader031/viewer/2022020113/58ed17691a28abe6558b4675/html5/thumbnails/22.jpg)
Lambda Expression Analysis
Step#4 : build an AST
if(u.firstName.equals("john")) { if(u.lastName.equals("doe")) { return true } else { if(u.userName.equals("jdoe")) { return true } else { return false } } } else { if(u.userName.equals("jdoe")) { return true } else { return false } }
![Page 23: DevNation'15 - Using Lambda Expressions to Query a Datastore](https://reader031.fdocuments.net/reader031/viewer/2022020113/58ed17691a28abe6558b4675/html5/thumbnails/23.jpg)
if(u.firstName.equals("john")) { if(u.lastName.equals("doe")) { return true } else { if(u.userName.equals("jdoe")) { return true } else { return false } } } else { if(u.userName.equals("jdoe")) { return true } else { return false } }
if(u.firstName.equals("john")) { if(u.lastName.equals("doe")) { return true } else { if(u.userName.equals("jdoe")) { return true } else { return false } } } else { if(u.userName.equals("jdoe")) { return true } else { return false }
(u.firstName.equals("john") && u.lastName.equals("doe"))
|| (u.firstName.equals("john") && !u.lastName.equals("doe") && u.userName.equals("jdoe"))
|| (!u.firstName.equals("john") && u.userName.equals("jdoe"))
Lambda Expression Analysis
Step#5 : Thin out the AST
![Page 24: DevNation'15 - Using Lambda Expressions to Query a Datastore](https://reader031.fdocuments.net/reader031/viewer/2022020113/58ed17691a28abe6558b4675/html5/thumbnails/24.jpg)
(u.firstName.equals("john") && u.lastName.equals("doe"))
|| (u.firstName.equals("john") && !u.lastName.equals("doe") && u.userName.equals("jdoe"))
|| (!u.firstName.equals("john") && u.userName.equals("jdoe"))
Lambda Expression Analysis
Step#6 : Further simplify the AST
(u.firstName.equals("john") && u.lastName.equals("doe"))
|| (u.firstName.equals("john") && !u.lastName.equals("doe") && u.userName.equals("jdoe"))
|| (!u.firstName.equals("john") && u.userName.equals("jdoe"))
![Page 25: DevNation'15 - Using Lambda Expressions to Query a Datastore](https://reader031.fdocuments.net/reader031/viewer/2022020113/58ed17691a28abe6558b4675/html5/thumbnails/25.jpg)
Lambda Expression Analysis
Boolean algebra to the rescue !a.(b.c) = a.b.c a + (b + c) = a + b + c a.(!a + b).(!a + c) = a.b.c a + (!a.b) + (!a.c) = a + b + c a.(a + b).(a + c) = a a + (a.b) + (a.c) = a (a.b) + (a.c) + (a.d) = a.(b + c + d) (a + b).(a + c).(a + d) = a + (b.c.d) a + a = a a.a = a a.!a = O a + !a = I O + a = a O.a = O 1 + a = 1 1.a = a a.(b + c + d) = (a.b) + (a.c) + (a.d) a + (b.c.d) = (a + b).(a + c).(a + d)
![Page 26: DevNation'15 - Using Lambda Expressions to Query a Datastore](https://reader031.fdocuments.net/reader031/viewer/2022020113/58ed17691a28abe6558b4675/html5/thumbnails/26.jpg)
![Page 27: DevNation'15 - Using Lambda Expressions to Query a Datastore](https://reader031.fdocuments.net/reader031/viewer/2022020113/58ed17691a28abe6558b4675/html5/thumbnails/27.jpg)
Lambda Expression Analysis
Step#6 : further simplify the statement tree
(u.firstName.equals("john") && u.lastName.equals("doe"))
|| (u.firstName.equals("john") && !u.lastName.equals("doe") && u.userName.equals("jdoe"))
|| (!u.firstName.equals("john") && u.userName.equals("jdoe"))
(u.firstName.equals("john") && u.lastName.equals("doe")) || u.userName.equals("jdoe")
![Page 28: DevNation'15 - Using Lambda Expressions to Query a Datastore](https://reader031.fdocuments.net/reader031/viewer/2022020113/58ed17691a28abe6558b4675/html5/thumbnails/28.jpg)
![Page 29: DevNation'15 - Using Lambda Expressions to Query a Datastore](https://reader031.fdocuments.net/reader031/viewer/2022020113/58ed17691a28abe6558b4675/html5/thumbnails/29.jpg)
Performances ?
• Byte code is analyzed on first access to Lambda Expression ( <100ms)
• "Raw" AST is cached for subsequent calls
• Placeholders in AST for captured arguments
![Page 30: DevNation'15 - Using Lambda Expressions to Query a Datastore](https://reader031.fdocuments.net/reader031/viewer/2022020113/58ed17691a28abe6558b4675/html5/thumbnails/30.jpg)
Part II. Managing data on MongoDB
![Page 31: DevNation'15 - Using Lambda Expressions to Query a Datastore](https://reader031.fdocuments.net/reader031/viewer/2022020113/58ed17691a28abe6558b4675/html5/thumbnails/31.jpg)
Managing Data on MongoDB
• Providing Codecs for Lambda Expressions and Documents
• Skipping the "DBObject" DTOs layer
MongoDB Java Driver Integration
![Page 32: DevNation'15 - Using Lambda Expressions to Query a Datastore](https://reader031.fdocuments.net/reader031/viewer/2022020113/58ed17691a28abe6558b4675/html5/thumbnails/32.jpg)
Querying MongoDB
Service Layer
Lambda Expression
MongoDB Java Driver
BSON Document
MongoDB
FilterExpression Codec
BSON Document
Domain object(s)
Document Codec
![Page 33: DevNation'15 - Using Lambda Expressions to Query a Datastore](https://reader031.fdocuments.net/reader031/viewer/2022020113/58ed17691a28abe6558b4675/html5/thumbnails/33.jpg)
Demo #1 Geolocation queries
![Page 34: DevNation'15 - Using Lambda Expressions to Query a Datastore](https://reader031.fdocuments.net/reader031/viewer/2022020113/58ed17691a28abe6558b4675/html5/thumbnails/34.jpg)
Three more things (yes, three)
![Page 35: DevNation'15 - Using Lambda Expressions to Query a Datastore](https://reader031.fdocuments.net/reader031/viewer/2022020113/58ed17691a28abe6558b4675/html5/thumbnails/35.jpg)
Query on Arrays
![Page 36: DevNation'15 - Using Lambda Expressions to Query a Datastore](https://reader031.fdocuments.net/reader031/viewer/2022020113/58ed17691a28abe6558b4675/html5/thumbnails/36.jpg)
Query on Arrays
![Page 37: DevNation'15 - Using Lambda Expressions to Query a Datastore](https://reader031.fdocuments.net/reader031/viewer/2022020113/58ed17691a28abe6558b4675/html5/thumbnails/37.jpg)
Query on Arrays
final List<BlogEntry> blogEntries = blogEntryCollection.filter( e -> e.comments.elementMatch(
c -> c.author.equals("anonymous") && c.date.greaterOrEquals(twoDaysAgo))) .toList();
final List<BlogEntry> blogEntries = blogEntryCollection.filter( e -> e.comments.author.equals("anonymous") && e.comments.date.greaterOrEquals(twoDaysAgo)) .toList();
![Page 38: DevNation'15 - Using Lambda Expressions to Query a Datastore](https://reader031.fdocuments.net/reader031/viewer/2022020113/58ed17691a28abe6558b4675/html5/thumbnails/38.jpg)
Query Projections
![Page 39: DevNation'15 - Using Lambda Expressions to Query a Datastore](https://reader031.fdocuments.net/reader031/viewer/2022020113/58ed17691a28abe6558b4675/html5/thumbnails/39.jpg)
Query Projection
Projection Metadata class generation
@Generated(value="o.l.m.a.DocumentAnnotationProcessor") public abstract class PBlogEntry implements ProjectionMetadata<BlogEntry> {
@DocumentField(name="_id") public ProjectionField id; @DocumentField(name="authorName") public ProjectionField authorName; @DocumentField(name="publishDate") public ProjectionField publishDate;
... }
@Document(collection="blog") public class BlogEntry {
@DocumentId private String id;
@DocumentField private String authorName;
private Date publishDate;
... }
![Page 40: DevNation'15 - Using Lambda Expressions to Query a Datastore](https://reader031.fdocuments.net/reader031/viewer/2022020113/58ed17691a28abe6558b4675/html5/thumbnails/40.jpg)
Query Projection
import static org.lambdamatic.mongodb.Projection.include;
final BlogEntry blogEntry = blogEntries.filter(e -> e.id.equals("1")).projection(e -> include(e.authorName, e.title, e.publishDate))
.first();
@Generated(value="o.l.m.a.DocumentAnnotationProcessor") public abstract class PBlogEntry implements ProjectionMetadata<BlogEntry> {
@DocumentField(name="_id") public ProjectionField id; @DocumentField(name="authorName") public ProjectionField authorName; @DocumentField(name="publishDate") public ProjectionField publishDate; ... }
public interface Projection { @IncludeFields public static void include(final ProjectionField... fields) {}
@ExcludeFields public static void exclude(final ProjectionField... fields) {} }
![Page 41: DevNation'15 - Using Lambda Expressions to Query a Datastore](https://reader031.fdocuments.net/reader031/viewer/2022020113/58ed17691a28abe6558b4675/html5/thumbnails/41.jpg)
Query Projection
Projecting array elements
final BlogEntry blogEntry = blogEntryCollection.filter(e -> e.id.equals("1")) .projection(e -> include(e.authorName, e.title, e.publishDate, e.comments.elementMatch(c -> c.author.equals("anonymous") && c.date.greaterOrEquals(twoDaysAgo)))) .first();
![Page 42: DevNation'15 - Using Lambda Expressions to Query a Datastore](https://reader031.fdocuments.net/reader031/viewer/2022020113/58ed17691a28abe6558b4675/html5/thumbnails/42.jpg)
Update Operations
![Page 43: DevNation'15 - Using Lambda Expressions to Query a Datastore](https://reader031.fdocuments.net/reader031/viewer/2022020113/58ed17691a28abe6558b4675/html5/thumbnails/43.jpg)
Update Operations
@Document(collection="blog") public class BlogEntry {
@DocumentId private String id; /** Name of author of the blog entry. */ private String authorName; /** Title of the Blog Entry */ private String title; /** list of comments. */ private List<BlogEntryComment> comments; ... }
Update Metadata class generation@Generated(value="o.l.m.a.DocumentAnnotationProcessor") public abstract class UBlogEntry implements UpdateMetadata<BlogEntry> {
@DocumentField(name="_id") public String id; @DocumentField(name="authorName") public String authorName; @DocumentField(name="title") public String title; @DocumentField(name="comments") public UBlogEntryCommentArray comments; ... }
![Page 44: DevNation'15 - Using Lambda Expressions to Query a Datastore](https://reader031.fdocuments.net/reader031/viewer/2022020113/58ed17691a28abe6558b4675/html5/thumbnails/44.jpg)
Update Operations
BlogEntryComment comment = ... ; blogEntries.filter(e -> e.id.equals("1")). forEach(e -> { e.lastUpdate = new Date(); e.commentsNumber++; e.comments.push(comment);});
@Document(collection="blog") public class BlogEntry {
@DocumentId private String id; /** date of last update. */ private Date lastUpdate; /** Number of comments */ private int commentsNumber; /** list of comments. */ private List<BlogEntryComment> comments;
@EmbeddedDocument public class BlogEntryComment {
/** comment author. */ private String author; /** comment date. */ private Date date; /** comment content. */ private String content;
![Page 45: DevNation'15 - Using Lambda Expressions to Query a Datastore](https://reader031.fdocuments.net/reader031/viewer/2022020113/58ed17691a28abe6558b4675/html5/thumbnails/45.jpg)
Demo #2 Filter with projections
and Update
![Page 46: DevNation'15 - Using Lambda Expressions to Query a Datastore](https://reader031.fdocuments.net/reader031/viewer/2022020113/58ed17691a28abe6558b4675/html5/thumbnails/46.jpg)
Project Status
Experimental
![Page 47: DevNation'15 - Using Lambda Expressions to Query a Datastore](https://reader031.fdocuments.net/reader031/viewer/2022020113/58ed17691a28abe6558b4675/html5/thumbnails/47.jpg)
Project Info
http://github.com/lambdamatic
• Give it a try !
• Clone it / Fork it / Star it
• Open issues to discuss about API and features
![Page 48: DevNation'15 - Using Lambda Expressions to Query a Datastore](https://reader031.fdocuments.net/reader031/viewer/2022020113/58ed17691a28abe6558b4675/html5/thumbnails/48.jpg)
Thanks ! Q/A