Life is an Adventure
— Your Database Shouldn’t Be
Used in production by 1000+ customers
Most common RavenDB use cases
Full Table Scan is the #1 enemy for each backend developer. With RavenDB, you cannot go wrong. There is no possibility to perform a query without an index.
It doesn’t mean that you need to create and index for each query in your application! RavenDB learn from your application behaviour and will create auto indexes for you. RavenDB will create the optimal set of indexes to cover your application needs.
Being Boring means that you can sleep at night instead of waiting for the next Full Table Scan nightmare to happen.
// New index will be created
from Companies
where Name = "Island Trading"
// Previous index will get merged with new one
from Companies
where Address.Country = "UK"
// I still have one index, but all three queries can be handled by it
// Amazing!
from Companies
where Phone = "(171) 555-0297"
// New index will be created
List<Company> companies1 = session.Query<Company>()
.Where(x => x.Name == "Island Trading")
.ToList();
// Previous index will get merged with new one
List<Company> companies2 = session.Query<Company>()
.Where(x => x.Address.Country == "UK")
.ToList();
// I still have one index, but all three queries
// can be handled by it
// Amazing!
List<Company> companies3 = session.Query<Company>()
.Where(x => x.Phone == "(171) 555-0297")
.ToList();
# New index will be created
companies1 = list(
session.query(object_type=Company).where_equals("name", "Island Trading")
)
# Previous index will get merged with new one
companies2 = list(
session.query(object_type=Company).where_equals("address.country", "UK")
)
# I still have one index, but all three queries can be handled by it
# Amazing !
companies3 = list(
session.query(object_type=Company).where_equals("phone", "(171) 555-0297")
)
// New index will be created
List < Company > companies1 = session.query(Company.class)
.whereEquals("Name", "Island Trading")
.toList();
// Previous index will get merged with new one
List < Company > companies2 = session.query(Company.class)
.whereEquals("Address.Country", "UK")
.toList();
// I still have one index, but all three queries can be handled by it
// Amazing!
List < Company > companies3 = session.query(Company.class)
.whereEquals("Phone", "(171) 555-0297")
.toList();
// New index will be created
$companies1 = $session->query(Company::class)
->whereEquals("Name", "Island Trading")
->toList();
// Previous index will get merged with new one
$companies2 = $session->query(Company::class)
->whereEquals("Address.Country", "UK")
->toList();
// I still have one index, but all three queries can be handled by it
// Amazing!
$companies3 = $session->query(Company::class)
->whereEquals("Phone", "(171) 555-0297")
->toList();
const companies1: Company[] = await session.query(Company)
.whereEquals("Name", "Island Trading")
.all();
// Previous index will get merged with new one
const companies2: Company[] = await session.query(Company)
.whereEquals("Address.Country", "UK")
.all();
// I still have one index, but all three queries can be handled by it
// Amazing!
const companies3: Company[] = await session.query(Company)
.whereEquals("Phone", "(171) 555-0297")
.all();
// New index will be created
var companies1 []*Company
err = session.QueryCollectionForType(reflect.TypeOf(&Company{})).
WhereEquals("Name", "Island Trading").
GetResults(&companies1)
// Previous index will get merged with new one
var companies2 []*Company
err = session.QueryCollectionForType(reflect.TypeOf(&Company{})).
WhereEquals("Address.Country", "UK").
GetResults(&companies2)
// I still have one index, but all three queries can be handled by it
// Amazing!
var companies3 []*Company
err = session.QueryCollectionForType(reflect.TypeOf(&Company{})).
WhereEquals("Phone", "(171) 555-0297").
GetResults(&companies3)
Use time-series in your next finance, IoT, or healthcare application, where chronological order of data points is crucial for trend analysis. anomaly detection, and predictive modelling.
Take advantage of:
- Incremental time-series which avoids conflicts and a cost of synchronization for real-time updates.
- Built-in RQL support for data extraction.
- Indexing capabilities for advanced querying scenarios.
- Rollup and retention for data aggregation and cleanup.
from Companies
where id() = 'companies/55-A'
select timeseries(
from StockPrices
between '2020-01-01' and '2020-06-30'
group by '1 month'
select min(), max()
) as StockPrices
var stocks =
session.TimeSeriesFor("companies/55-A")
.Get(from
: new DateTime(2020, 1, 1), to
: new DateTime(2020, 6, 30))
.GroupBy(g => g.Timestamp.Month)
.Select(g => new {Month = g.Key, Min = g.Min(x => x.Value.Close),
Max = g.Max(x => x.Value.Close)})
.ToList();
from itertools import groupby
with self.store.open_session() as session:
stocks = session.typed_time_series_for(StockPrice, "users/karmel").get()
grouped_stocks = groupby(stocks, key=lambda x: x.timestamp.month)
def get_min_max(group):
values = [x for x in group]
close_values = [x.value.close for x in values]
return {
"Month": values[0].timestamp.month,
"Min": min(close_values),
"Max": max(close_values),
}
stocks = [get_min_max(group) for _, group in grouped_stocks]
IRawDocumentQuery<StockPrice> stocks = session.advanced()
.rawQuery(StockPrice.class, """
from Companies
where id() = 'companies/55-A'
select timeseries(
from StockPrices
between '2020-01-01' and '2020-06-30'
group by '1 month'
select min(), max()
) as StockPrices
""");
$query = <<<EOQ
from Companies
where id() = 'companies/55-A'
select timeseries(
from StockPrices
between '2020-01-01' and '2020-06-30'
group by '1 month'
select min(), max()
) as StockPrices
EOQ;
$stocks = $session -> advanced() -> rawQuery(StockPrice:: class, $query);
const stocks: StockPrice[] = await session.advanced
.rawQuery(
`from Companies
where id() = 'companies/55-A'
select timeseries(
from StockPrices
between '2020-01-01' and '2020-06-30'
group by '1 month'
select min(), max()
) as StockPrices`,
StockPrice).all();
With no additional lines of code, search for language-aware text values, execute highlighting, retrieve suggestions, and set up value boosting in single, multiple or complex fields.
Improve your search capabilities with our brand new tailored Corax solution to push the performance to the limits or take advantage of the industry battle-tested Lucene search engine.
If this is not enough, build your own custom analyzers or custom sorters and use them in your static indexes to achieve desired search outcome.
// Single term
from "Employees"
where search(Notes, "University")
// Multiple term
from "Employees"
where search(Notes, "University Sales Japanese")
// Complex objects with multiple terms
from "Companies" where search(Address, "USA London")
// Nested values with boosting
from "Companies"
where boost(search(Address.City, "Po*"), 100)
or boost(search(Address.Country, "Po*"), 1000)
// Single term
List employees = session.Query()
.Search(x => x.Notes, "University")
.ToList();
// Multiple term
List employees = session.Query()
.Search(x => x.Notes, "University Sales Japanese")
.ToList();
// Complex objects with multiple terms
List companies = session.Query()
.Search(x => x.Address, "USA London")
.ToList();
// Nested values with boosting
List<Company> companies2 = session.Query<Company>()
.Search(x => x.Address.City, "Po*", boost: 100)
.Search(x => x.Address.Country, "Po*", boost: 1000)
.ToList();
# Single term
employees1 = list(session.query_collection("Employees").search("Notes", "University"))
# Multiple term
employees2 = list(
session.query_collection("Employees").search("Notes", "University Sales Japanese")
)
# Complex objects with multiple terms
companies1 = list(session.query_collection("Companies").search("Address", "USA London"))
# Nested values with boosting
companies2 = list(
session.query_collection("Companies")
.search("Address.City", "Po*")
.boost(100)
.or_else()
.search("Address.Country", "Po*")
.boost(1000)
)
// Single term
List employees = session.query(Employee.class)
.search("Notes", "University")
.toList();
// Multiple term
List employees = session.query(Employee.class)
.search("Notes", "University Sales Japanese")
.toList();
// Complex objects with multiple terms
List companies = session.query(Company.class)
.search("Address", "USA London")
.toList();
// Nested values with boosting
List < Company > companies2 = session.query(Company.class, Query.collection("Companies"))
.search("Address.City", "Po*")
.boost(100)
.orElse()
.search("Address.Country", "Po*")
.boost(1000)
.toList();
// Single term
$employees = $session->query(Employee::class)
->search("Notes", "University")
->toList();
// Multiple term
$employees = $session->query(Employee::class)
->search("Notes", "University Sales Japanese")
->toList();
// Complex objects with multiple terms
$companies = $session->query(Company::class)
->search("Address", "USA London")
->toList();
// Single term
const employees: Employee[] = await session.query(Employee)
.search("Notes", "University")
.all();
// Multiple term
const employees: Employee[] = await session.query(Employee)
.search("Notes", "University Sales Japanese")
.all();
// Complex objects with multiple terms
const employees: Employee[] = await session.query(Employee)
.search("Address", "USA London")
.all();
// Nested values with boosting
const companies2: Company[] = await session.query({
collection: "Companies",
documentType: Company
})
.search("Address.City", "Po*")
.boost(100)
.orElse()
.search("Address.Country", "Po*")
.boost(1000)
.all();
// Single term
var employees1 []*Employee
err = session.QueryCollectionForType(&employees1, "Employees").
Search("Notes", "University").
GetResults(&employees1)
// Multiple term
var employees2 []*Employee
err = session.QueryCollectionForType(&employees2, "Employees").
Search("Notes", "University Sales Japanese").
GetResults(&employees2)
// Complex objects with multiple terms
var employees3 []*Employee
err = session.QueryCollectionForType(&employees3, "Employees").
Search("Address", "USA London").
GetResults(&employees3)
// Nested values with boosting
var companies2 []*Company
err = session.QueryCollectionForType(&companies2, "Companies").
Search("Address.City", "Po*").
Boost(100).
OrElse().
Search("Address.Country", "Po*").
Boost(1000).
GetResults(&companies2)
Are you looking for a place to eat, or maybe a nearby taxi?
Or are you just browsing through some online stores?
Our built-in Spatial and Faceted Search support can help you achieve this, with a vast set of supported standards like WKT for Spatial or range aggregations for Facets.
All of that is integrated into our RQL querying language.
// Find all Employees in a 20km circle from 47.623473, -122.3060097 coordinates
from Employees
where spatial.within(
spatial.point(Address.Location.Latitude, Address.Location.Longitude),
spatial.circle(20, 47.623473, -122.3060097)
// Return facets aggregated by
// - Brand
// - Five Ranges
from index "Cameras/ByFeatures"
select facet(Brand,
sum(UnitsInStock), avg(Price), min(Price), max(MegaPixels)),
facet(Price < 200.0,
Price >= 200.0 and Price < 400.0,
Price >= 400.0 and Price < 600.0,
Price >= 600.0 and Price < 800.0,
Price >= 800.0,
sum(UnitsInStock), avg(Price), min(Price), max(MegaPixels))
// Find all Employees in a 20km circle from 47.623473, -122.3060097 coordinates
List<Employee> employees =
session.Query<Employee>()
.Spatial(
factory => factory.Point(
x => x.Address.Location.Latitude,
x => x.Address.Location.Longitude),
factory => factory.WithinRadius(20, 47.623473, - 122.3060097)
).ToList();
// Return facets aggregated by
// - Brand
// - Five Ranges
Dictionary<string, FacetResult> results = session.Query<Camera>("Cameras/ByFeatures")
.AggregateBy(f => f
.ByField(c => c.Brand)
.SumOn(c => c.UnitsInStock).AverageOn(c => c.Price).MinOn(c => c.Price).MaxOn(c => c.MegaPixels))
.AndAggregateBy(f => f.ByRanges(
range => range.Price < 200.0,
range => range.Price >= 200.0 && range.Price < 400.0,
range => range.Price >= 400.0 && range.Price < 600.0,
range => range.Price >= 600.0 && range.Price < 800.0,
range => range.Price >= 800.0
)
.SumOn(c => c.UnitsInStock).AverageOn(c => c.Price).MinOn(c => c.Price).MaxOn(c => c.MegaPixels))
.Execute();
# Find all Employees in a 20km circle from 47.623473, -122.3060097 coordinates
employees = list(
session.query(object_type=Employee).spatial(
PointField("address.location.latitude", "address.location.longitude"),
lambda f: f.within_radius(20, 47.623473, -122.3060097),
)
)
# Return facets aggregated by
# - Brand
# - Five Ranges
range = RangeBuilder.for_path("price")
product_aggregations = (
session.query_index("Cameras/ByFeatures")
.aggregate_by(
lambda f: f.by_field("brand")
.sum_on("units_in_stock").average_on("price").min_on("price").max_on("mega_pixels")
)
.and_aggregate_by(
lambda f: f.by_ranges(
range.is_less_than(200),
range.is_greater_than_or_equal_to(200).is_less_than(400),
range.is_greater_than_or_equal_to(400).is_less_than(600),
range.is_greater_than_or_equal_to(600).is_less_than(800),
range.is_greater_than_or_equal_to(800),
)
.sum_on("units_in_stock").average_on("price").min_on("price").max_on("mega_pixels")
)
.execute()
)
// Find all Employees in a 20km circle from 47.623473, -122.3060097 coordinates
List<Employee> query = session.query(Employee.class)
.spatial(
new PointField("Address.Location.Latitude", "Address.Location.Longitude"),
f -> f.withinRadius(20, 47.623473, -122.3060097))
.toList();
// Return facets aggregated by
// - Brand
// - Five Ranges
RangeBuilder<Integer> range = new RangeBuilder<>("price");
Map<String, FacetResult> results = session.query(Object.class, Query.index("Cameras/ByFeatures"))
.aggregateBy(f -> f
.byField("brand")
.sumOn("units_in_stock").averageOn("price").minOn("price").maxOn("mega_pixels"))
.andAggregateBy(f -> f.byRanges(
range.isLessThan(200),
range.isGreaterThanOrEqualTo(200).isLessThan(400),
range.isGreaterThanOrEqualTo(400).isLessThan(600),
range.isGreaterThanOrEqualTo(600).isLessThan(800),
range.isGreaterThanOrEqualTo(800)
)
.sumOn("units_in_stock").averageOn("price").minOn("price").maxOn("mega_pixels"))
.execute();
// Find all Employees in a 20km circle from 47.623473, -122.3060097 coordinates
$query = $session->query(Employee::class)
->spatial(
new PointField("Address.Location.Latitude", "Address.Location.Longitude"),
function($f) { return $f->withinRadius(20, 47.623473, -122.3060097); }
)
->toList();
// Return facets aggregated by
// - Brand
// - Five Ranges
/** @var array $results */
$range = new RangeBuilder("price");
$results = $session->query(null, Query::index("Cameras/ByFeatures"))
->aggregateBy(function($f) {
return $f
->byField("brand")
->sumOn("units_in_stock")->averageOn("price")->minOn("price")->maxOn("mega_pixels");
})
->spatial("LocationCoordinates", )
->andAggregateBy(function($f) use ($range) { return
$f->byRanges(
$range->isLessThan(200),
$range->isGreaterThanOrEqualTo(200)->isLessThan(400),
$range->isGreaterThanOrEqualTo(400)->isLessThan(600),
$range->isGreaterThanOrEqualTo(600)->isLessThan(800),
$range->isGreaterThanOrEqualTo(800)
)
->sumOn("units_in_stock")->averageOn("price")->minOn("price")->maxOn("mega_pixels");
})
->execute();
// Find all Employees in a 20km circle from 47.623473, -122.3060097 coordinates
const query: Employee[] = await session.query(Employee)
.spatial(new PointField("Address.Location.Latitude", "Address.Location.Longitude"),
f => f.withinRadius(20, 47.623473, -122.3060097))
.all();
// Return facets aggregated by
// - Brand
// - Five Ranges
const range = new RangeBuilder("price");
const results: Map < string, FacetResult > = await session.query({
indexName: "Cameras/ByFeatures"
})
.aggregateBy(f => f
.byField("brand")
.sumOn("units_in_stock").averageOn("price").minOn("price").maxOn("mega_pixels"))
.andAggregateBy(f => f
.byRanges(
range.isLessThan(200),
range.isGreaterThanOrEqualTo(200).isLessThan(400),
range.isGreaterThanOrEqualTo(400).isLessThan(600),
range.isGreaterThanOrEqualTo(600).isLessThan(800),
range.isGreaterThanOrEqualTo(800)
)
.sumOn("units_in_stock").averageOn("price").minOn("price").maxOn("mega_pixels"))
.execute();
Writing a piece of code that reliably process data in a distributed environment is a cumbersome and non-trivial task. With our Data Subscriptions, RavenDB takes care of all the heavy lifting and guarantees that every single item will be processed without any additional infrastructure.
Why waste time maintaining the code, when you can just subscribe to an RQL-based subscription and focus on the business code.
// Create
var name = await DocumentStore.Subscriptions.CreateAsync();
// Open
var worker = DocumentStore.Subscriptions.GetSubscriptionWorker(name);
// Subscribe
_ = worker.Run(batch =>
{
// Issue an Invoice or Send an Email
});
# Create
name = self.store.subscriptions.create_for_class(Order)
# Open
worker = self.store.subscriptions.get_subscription_worker(SubscriptionWorkerOptions(name), Order)
# Subscribe
def _do_work(batch: SubscriptionBatch):
with batch.open_session() as session:
for item in batch.items:
order = item.result
# Issue an Invoice, Send an Email
order.shipped_at = datetime.now()
session.save_changes()
worker.run(_do_work)
// Create
SubscriptionCreationOptions subscriptionCreationOptions = new SubscriptionCreationOptions();
subscriptionCreationOptions.setQuery("from Orders where ShippedAt is null");
String name = documentStore.subscriptions().create(subscriptionCreationOptions);
// Open
SubscriptionWorker < Order > worker = documentStore.subscriptions().getSubscriptionWorker(Order.class, name);
// Subscribe
worker.run(batch -> {
try (IDocumentSession session = batch.openSession()) {
for (SubscriptionBatchBase.Item < Order > item: batch.getItems()) {
Order result = item.getResult();
// Issue an Invoice, Send an Email
result.setShippedAt(new Date());
session.saveChanges();
}
}
});
// Create
const subscriptionCreationOptions: SubscriptionCreationOptions = {
query: "from Orders where ShippedAt is null"
};
const name = await documentStore.subscriptions.create(subscriptionCreationOptions);
// Open
const worker = documentStore.subscriptions.getSubscriptionWorker({
documentType: Order,
subscriptionName: name
});
// Subscribe
worker.on("batch", async (batch, callback) => {
const session = batch.openSession();
try {
for (const item: Item of batch.items) {
const result: Order = item.result;
// Issue an Invoice, Send an Email
result.shippedAt = new Date();
await session.saveChanges();
}
} finally {
session.dispose();
}
callback();
});
Creating a voting or pooling system can be challenging due to the synchronization and aggregation issues involved. We are simplifying this process to just a few lines of code and taking the heavy burden on ourselves.
With counters in a distributed environment, you can consume a tremendous amount of data in a short time span and still enjoy the final results in a swift manner.
// Vote
var counters = session.CountersFor("candidate/1");
counters.Increment("Votes");
session.SaveChanges();
// Check results
var counters = session.CountersFor("candidate/1");
var votes = counters.Get("Votes");
# Vote
counters = session.counters_for("candidate/1")
counters.increment("Votes")
session.save_changes()
# Check results
counters = session.counters_for("candidate/1")
votes = counters.get("Votes")
// Vote
ISessionDocumentCounters counters = session.countersFor("candidate/1");
counters.increment("Votes");
session.saveChanges();
// Check results
ISessionDocumentCounters counters = session.countersFor("candidate/1");
Long votes = counters.get("Votes");
// Vote
$counters = $session->countersFor("candidate/1");
$counters->increment("Votes");
$session->saveChanges();
// Check results
$counters = $session->countersFor("candidate/1");
$votes = $counters->get("Votes");
// Vote
const counters = session.countersFor("candidate/1");
counters.increment("Votes");
await session.saveChanges();
// Check results
const counters = session.countersFor("candidate/1");
const votes: number = await counters.get("Votes");
Get started in minutes,
comfortable in days, master it in weeks.
RavenDB is explicitly meant to get out of your way. You should just write your system and not worry about persistence. With RavenDB there isn't a huge chasm of complexity to cross. The burden of running your database in production and under load is on RavenDB.
Used in Production
by Fortune 500 Enterprises
Under load, RavenDB matches or exceeds Couchbase performance at the 99.99 percentile with third of the hardware resources. In the cloud this translates to 80% cost savings.
Deployed to millions of locations across the entire world
12000+ customers across multiple countries
15+ years on the market
- ISO27001
- SOC2 & SOC3
- GDPR
- HIPAA
The highest security and compliance standards
Resilient to failures
Real stories of RavenDB’s impact
See how Rakuten Kobo kept their costs down without sacrificing performance
RavenDB has shown to deliver remarkable performance improvements and cost savings, as evidenced by Rakuten Kobo's experience where it led to significant infrastructure cost reduction.
Deepening Configuration Capabilities
Explore how Configit harnesses the power of RavenDB to enhance the depth and efficiency of their configuration solutions.
See how other businesses improved their systems
Setup RavenDB now
We built a database that works for you,
instead of making you work.