RavenDB Client for Java
Installation
Maven
<dependency>
<groupId>net.ravendb</groupId>
<artifactId>ravendb</artifactId>
<version>5.0.2</version>
</dependency>
Gradle
implementation 'net.ravendb:ravendb:5.0.2'
Documentation
Please find the official documentation on RavenDB Documentation page.
Bug tracker
You can report bug/feature: http://issues.hibernatingrhinos.com/issues/RDBC
Getting started
- Initialize document store (you should have one DocumentStore instance per application)
DocumentStore store = new DocumentStore("http://live-test.ravendb.net", "databaseName");
store.initialize();
- Open a session
try (IDocumentSession session = store.openSession()) {
// here goes your code
}
- Call
saveChanges()
once you're done:
User user = session.load(User.class, "users/1-A");
user.setPassword("new password");
session.saveChanges();
// data is persisted
// you can proceed e.g. finish web request
CRUD example
Storing documents
Product product = new Product();
product.setTitle("iPhone X");
product.setPrice(999.99);
product.setCurrency("USD");
product.setStorage(64);
product.setManufacturer("Apple");
product.setInStock(true);
session.store(product, "products/");
System.out.println(product.getId()); // products/1-A
session.saveChanges();
Loading documents
Product product = session.load(Product.class, "products/1-A");
System.out.println(product.getTitle()); // iPhone X
System.out.println(product.getId()); // Products/1-A
Loading documents with includes
try (IDocumentSession session = store.openSession()) {
// users/1
// {
// "name": "John",
// "kids": ["users/2", "users/3"]
// }
User user1 = session.include("kids")
.load(User.class, "users/1");
// Document users/1 is going to be pulled along
// with docs referenced in "kids" field within a single request
User user2 = session.load(User.class, "users/2"); // this won't call server again
assertThat(user1)
.isNotNull();
assertThat(user2)
.isNotNull();
assertThat(session.advanced().getNumberOfRequests())
.isEqualTo(1);
}
Updating documents
Product product = session.load(Product.class, "products/1-A");
product.setInStock(false);
product.setLastUpdate(new Date());
session.saveChanges();
// ...
product = session.load(Product.class, "products/1-A");
System.out.println(product.getInStock()); // false
System.out.println(product.getLastUpdate()) // 2018-06-05T13:52:31.633Z
Deleting documents
- Using entity
Product product = session.load(Product.class, "products/1-A");
session.delete(product);
session.saveChanges();
product = session.load(Product.class, "products/1-A");
assertThat(product)
.isNull();
- Using document ID
session.delete("products/1-A");
Querying documents
- Use
query()
session method:
By collection:
IDocumentQuery<Product> query = session.query(Product.class);
By index name:
IDocumentQuery<Product> query = session.query(Product.class, Products_ByCategory.class);
- Build up the query - apply conditions, set ordering etc. Query supports chaining calls:
query
.waitForNonStaleResults()
.usingDefaultOperator(QueryOperator.AND)
.whereEquals("manufacturer", "Apple")
.whereEquals("inStock", true)
.whereBetween("lastUpdate", lastYearDate, new Date())
.orderBy("price");
- Finally, you may get query results:
List<Product> products = query.toList();
// ...
Product firstOne = query.first(); // gets first result
// ...
Product single = query.single(); // gets single result
DocumentQuery methods overview
selectFields() - projections using a single field
// RQL
// from users select name
List<String> userNames = session.query(User.class)
.selectFields(String.class, "name")
.toList();
// John,Stefanie,Thomas
selectFields() - projections using multiple fields
// RQL
// from users select name, age
List<NameAndAge> result = session.query(User.class)
.selectFields(NameAndAge.class)
.toList();
// [ { name: 'John', age: 30, id: 'users/1-A' },
// { name: 'Stefanie', age: 25, id: 'users/2-A' },
// { name: 'Thomas', age: 25, id: 'users/3-A' } ]
distinct()
// RQL
// from users select distinct age
session.query(User.class)
.selectFields(String.class, "age")
.distinct()
.toList(); // [ 25, 30 ]
whereEquals() / whereNotEquals()
// RQL
// from users where age = 30
session.query(User.class)
.whereEquals("age", 30)
.toList()
// [ User {
// name: 'John',
// age: 30,
// kids: [...],
// registeredAt: 2017-11-10T23:00:00.000Z } ]
whereIn()
// RQL
// from users where name in ("John", "Thomas")
session.query(User.class)
.whereIn("name", Arrays.asList("John", "Thomas"))
.toList()
// [ User {
// name: 'John',
// age: 30,
// registeredAt: 2017-11-10T23:00:00.000Z,
// kids: [...],
// id: 'users/1-A' },
// User {
// name: 'Thomas',
// age: 25,
// registeredAt: 2016-04-24T22:00:00.000Z,
// id: 'users/3-A' } ]
whereStartsWith() / whereEndsWith()
// RQL
// from users where startsWith(name, 'J')
session.query(User.class)
.whereStartsWith("name", "J")
.toList();
// [ User {
// name: 'John',
// age: 30,
// kids: [...],
// registeredAt: 2017-11-10T23:00:00.000Z } ]
whereBetween()
// RQL
// from users where registeredAt between '2016-01-01' and '2017-01-01'
session.query(User.class)
.whereBetween("registeredAt", lastYearStartDate, new Date())
.toList()
// [ User {
// name: 'Thomas',
// age: 25,
// registeredAt: 2016-04-24T22:00:00.000Z,
// id: 'users/3-A' } ]
whereGreaterThan() / whereGreaterThanOrEqual() / whereLessThan() / whereLessThanOrEqual()
// RQL
// from users where age > 29
session.query(User.class)
.whereGreaterThan("age", 29)
.toList();
// [ User {
// name: 'John',
// age: 30,
// registeredAt: 2017-11-10T23:00:00.000Z,
// kids: [...],
// id: 'users/1-A' } ]
whereExists()
Checks if the field exists.
// RQL
// from users where exists("age")
session.query(User.class)
.whereExists("kids")
.toList();
// [ User {
// name: 'John',
// age: 30,
// registeredAt: 2017-11-10T23:00:00.000Z,
// kids: [...],
// id: 'users/1-A' } ]
containsAny() / containsAll()
// RQL
// from users where kids in ('Mara')
session.query(User.class)
.containsAll("kids", Arrays.asList("Mara", "Dmitri"))
.toList();
// [ User {
// name: 'John',
// age: 30,
// registeredAt: 2017-11-10T23:00:00.000Z,
// kids: ["Dmitri", "Mara"]
// id: 'users/1-A' } ]
search()
Performs full-text search.
// RQL
// from users where search(kids, 'Mara')
session.query(User.class)
.search("kids", "Mara Dmitri")
.toList();
// [ User {
// name: 'John',
// age: 30,
// registeredAt: 2017-11-10T23:00:00.000Z,
// kids: ["Dmitri", "Mara"]
// id: 'users/1-A' } ]
openSubclause() / closeSubclause()
// RQL
// from users where exists(kids) or (age = $p0 and name != $p1)
session.query(User.class)
.whereExists("kids")
.orElse()
.openSubclause()
.whereEquals("age", 25)
.whereNotEquals("name", "Thomas")
.closeSubclause()
.toList();
// [ User {
// name: 'John',
// age: 30,
// registeredAt: 2017-11-10T23:00:00.000Z,
// kids: ["Dmitri", "Mara"]
// id: 'users/1-A' },
// User {
// name: 'Stefanie',
// age: 25,
// registeredAt: 2015-07-29T22:00:00.000Z,
// id: 'users/2-A' } ]
not()
// RQL
// from users where age != 25
session.query(User.class)
.not()
.whereEquals("age", 25)
.toList();
// [ User {
// name: 'John',
// age: 30,
// registeredAt: 2017-11-10T23:00:00.000Z,
// kids: ["Dmitri", "Mara"]
// id: 'users/1-A' } ]
andAlso() / orElse()
// RQL
// from users where age != 25
session.query(User.class)
.whereExists("kids")
.orElse()
.whereLessThan("age", 30)
.toList();
// [ User {
// name: 'John',
// age: 30,
// registeredAt: 2017-11-10T23:00:00.000Z,
// kids: [ 'Dmitri', 'Mara' ],
// id: 'users/1-A' },
// User {
// name: 'Thomas',
// age: 25,
// registeredAt: 2016-04-24T22:00:00.000Z,
// id: 'users/3-A' },
// User {
// name: 'Stefanie',
// age: 25,
// registeredAt: 2015-07-29T22:00:00.000Z,
// id: 'users/2-A' } ]
usingDefaultOperator()
Sets default operator (which will be used if no andAlso()
/ orElse()
was called. Just after query instantiation, OR
is used as default operator. Default operator can be changed only adding any conditions.
orderBy() / randomOrdering()
// RQL
// from users order by age
session.query(User.class)
.orderBy("age") // .randomOrdering()
.toList();
// [ User {
// name: 'Stefanie',
// age: 25,
// registeredAt: 2015-07-29T22:00:00.000Z,
// id: 'users/2-A' },
// User {
// name: 'Thomas',
// age: 25,
// registeredAt: 2016-04-24T22:00:00.000Z,
// id: 'users/3-A' },
// User {
// name: 'John',
// age: 30,
// registeredAt: 2017-11-10T23:00:00.000Z,
// kids: [ 'Dmitri', 'Mara' ],
// id: 'users/1-A' } ]
take()
Limits the number of result entries to count
.
// RQL
// from users order by age
session.query(User.class)
.orderBy("age")
.take(2)
.toList();
// [ User {
// name: 'Stefanie',
// age: 25,
// registeredAt: 2015-07-29T22:00:00.000Z,
// id: 'users/2-A' },
// User {
// name: 'Thomas',
// age: 25,
// registeredAt: 2016-04-24T22:00:00.000Z,
// id: 'users/3-A' } ]
skip()
Skips first count
results.
// RQL
// from users order by age
session.query(User.class)
.orderBy("age")
.take(1)
.skip(1)
.toList();
// [ User {
// name: 'Thomas',
// age: 25,
// registeredAt: 2016-04-24T22:00:00.000Z,
// id: 'users/3-A' } ]
Getting query statistics
To obtain query statistics use statistics()
method.
Reference<QueryStatistics> statsRef = new Reference<>();
List<User> users = session.query(User.class)
.whereGreaterThan("age", 29)
.statistics(statsRef)
.toList();
// QueryStatistics {
// isStale: false,
// durationInMs: 744,
// totalResults: 1,
// skippedResults: 0,
// timestamp: 2018-09-24T05:34:15.260Z,
// indexName: 'Auto/users/Byage',
// indexTimestamp: 2018-09-24T05:34:15.260Z,
// lastQueryTime: 2018-09-24T05:34:15.260Z,
// resultEtag: 8426908718162809000 }
toList() / first() / single() / count()
toList()
- returns all results
first()
- first result
single()
- first result, throws error if there's more entries
count()
- returns the count of the results (not affected by take()
)
Attachments
Store attachments
User doc = new User();
doc.setName("John");
// track entity
session.store(doc);
// get read stream or buffer to store
FileInputStream fileStream = new FileInputStream("../photo.png")
// store attachment using entity
session.advanced().attachments()
.store(doc, "photo.png", fileStream, "image/png");
// OR using document ID
session.advanced().attachments()
.store(doc.getId(), "photo.png", fileStream, "image/png");
session.saveChanges();
Get attachments
try (CloseableAttachmentResult closeableAttachmentResult
= session.advanced().attachments().get(documentId, "photo.png")) {
// closeableAttachmentResult.getDetails() contains information about the attachemnt
// {
// name: 'photo.png',
// documentId: 'users/1-A',
// contentType: 'image/png',
// hash: 'MvUEcrFHSVDts5ZQv2bQ3r9RwtynqnyJzIbNYzu1ZXk=',
// changeVector: '"A:3-K5TR36dafUC98AItzIa6ow"',
// size: 4579
// }
InputStream is = closeableAttachmentResult.getData();
// is contains attachment data
}
Check if attachment exists
session.advanced().attachments().exists(doc.getId(), "photo.png");
// true
session.advanced().attachments().exists(doc.getId(), "not_there.avi");
// false
Get attachment names
// use a loaded entity to determine attachments' names
AttachmentName[] names = session.advanced().attachments().getNames(doc);
// [ { name: 'photo.png',
// hash: 'MvUEcrFHSVDts5ZQv2bQ3r9RwtynqnyJzIbNYzu1ZXk=',
// contentType: 'image/png',
// size: 4579 } ]
Bulk Insert
// create bulk insert instance using DocumentStore instance
try (BulkInsertOperation bulkInsert = store.bulkInsert()) {
// insert your documents
for (String name: Arrays.asList("Anna", "Maria", "Miguel", "Emanuel", "Dayanara", "Aleida")) {
User user = new User();
user.setName(name);
bulkInsert.store(user);
}
// auto flush by closing bulkInsert operation
}
// User { name: 'Anna', id: 'users/1-A' }
// User { name: 'Maria', id: 'users/2-A' }
// User { name: 'Miguel', id: 'users/3-A' }
// User { name: 'Emanuel', id: 'users/4-A' }
// User { name: 'Dayanara', id: 'users/5-A' }
// User { name: 'Aleida', id: 'users/6-A' }
Changes API
Listen for database changes e.g. document changes.
CleanCloseable subscription = store
.changes()
.forAllDocuments()
.subscribe(Observers.create(change -> {
System.out.println(change.getType() + " on document " + change.getId());
}));
// close when you are done
subscription.close();
Streaming
Stream documents with ID prefix
try (CloseableIterator<StreamResult<User>> stream
= session.advanced().stream(User.class, "users/")) {
while (stream.hasNext()) {
StreamResult<User> next = stream.next();
User document = next.getDocument();
// do something with document
}
}
Stream query results
// create a query
IDocumentQuery<User> query = session.query(User.class)
.whereGreaterThan("age", 29);
Reference<StreamQueryStatistics> statsRef = new Reference<>();
try (CloseableIterator<StreamResult<User>> stream
= session.advanced().stream(query, statsRef)) {
while (stream.hasNext()) {
StreamResult<User> item = stream.next();
User user = item.getDocument();
}
}
Revisions
NOTE: Please make sure revisions are enabled before trying one of the below.
User user = new User();
user.setName("Marcin");
user.setAge(30);
user.setPet("users/4");
try (IDocumentSession session = store.openSession()) {
session.store(user, "users/1");
session.saveChanges();
// modify the document to create a new revision
user.setName("Roman");
user.setAge(40);
session.saveChanges();
}
// get revisions
List<User> revisions = session.advanced()
.revisions()
.getFor(User.class, "users/1");
// [ { name: 'Roman',
// age: 40,
// pet: 'users/4',
// '@metadata': [Object],
// id: 'users/1' },
// { name: 'Marcin',
// age: 30,
// pet: 'users/4',
// '@metadata': [Object],
// id: 'users/1' } ]
Suggestions
// users collection
// [ User {
// name: 'John',
// age: 30,
// registeredAt: 2017-11-10T23:00:00.000Z,
// kids: [Array],
// id: 'users/1-A' },
// and a static index like:
public static class UsersIndex extends AbstractIndexCreationTask {
public UsersIndex() {
map = "from user in docs.users select new { user.name }";
suggestion("name");
}
}
// ...
try (IDocumentSession session = store.openSession()) {
Map<String, SuggestionResult> results = session.query(User.class, UsersIndex.class)
.suggestUsing(x -> x.byField("name", "Jon"))
.execute();
}
// { name: { name: 'name', suggestions: [ 'john' ] } }
Advanced patching
session.advanced().increment("users/1", "age", 1);
// increments *age* field by 1
session.advanced().patch("users/1", "underAge", false);
// sets *underAge* field to *false*
session.saveChanges();
Working with secured server
// load certificate
KeyStore clientStore = KeyStore.getInstance("PKCS12");
clientStore.load(new FileInputStream("c:\\ravendb\\client-cert.pfx"), "passwordToPfx".toCharArray());
try (DocumentStore store = new DocumentStore()) {
store.setCertificate(clientStore);
store.setDatabase("Northwind");
store.setUrls(new String[]{ "https://my_secured_raven" });
store.initialize();
// do your work here
}