introduction to ravendb
DESCRIPTION
Presentation from SDP 2014 covering RavenDB - the .NET document database. Discussing basic CRUD operations, indexes, and full-text search.TRANSCRIPT
© Copyright SELA software & Education Labs Ltd. | 14-18 Baruch Hirsch St Bnei Brak, 51202 Israel | www.selagroup.com
SELA DEVELOPER PRACTICEJUNE 29 – JULY 3, 2014
Sasha Goldshtein blog.sashag.netCTO, Sela Group @goldshtn
Introduction to RavenDB
NoSQLThe Zen-like answer: No one can tell you what NoSQL is, they can only tell you what it isn’t
It doesn’t use SQLIt usually is less consistent than RDBMSIt doesn’t put an emphasis on relationsIt emphasizes size and scale over structure
Classes of NoSQL Databases
Document DB
Key-Value
DB
Graph DB
Column DB
RavenDBTransactional document databaseOpen sourcehttps://github.com/ravendb/ravendb with licensing for commercial projectsSchema-less documents, JSON storageRESTful endpointsLINQ-style .NET APIImplicit (usage-based) or explicit indexingPowerful text search based on LuceneReplication and sharding support
Oren Eini(Ayende Rahien)
Hosting RavenDBRaven.Server.exeWindows ServiceIntegrated in IISEmbedded client for stand-alone appsCloud-hosted (e.g. RavenHQ)
Management Studio
Demo
RavenDB Management Studio
Opening a Session
DocumentStore is the session factory; one per application is enoughSupports .NET connection strings or direct initialization:
var ds = new DocumentStore{ Url = "http://localhost:8888"};ds.Initialize();
CRUD Operations on Documentsusing (var session = documentStore.OpenSession()){ session.Store(new Speaker(“Sasha”, “Jerusalem”)); session.SaveChanges();}
using (var session = documentStore.OpenSession()){ Speaker sasha = session.Query<Speaker>() .Where(e => e.City == “Jerusalem”).First(); sasha.City = “Tel-Aviv”; session.SaveChanges();}
Collections and IDsDocuments are stored in JSON formatDocuments have metadata that includes the entity typeA collection is a set of documents with the same entity typeDocuments have unique ids, often a combination of collection name + id
speakers/1conferences/7
Demo
Basic Operations
Modeling Data as Documents
Don’t be tempted to use a document store like a relational database
Documents should be aggregate rootsReferences to other documents are OK but (some) data duplication (denormalization) is also OK
“conference/11” : { tracks: [ { title: “Web”, days: { 1, 2 }, sessions: [ ... ] }, ... ]} Should the tracks
be references?
…But Don’t Go Too Far
Is this a reasonable document?
“blogs/1” : { tags : [ “Windows”, “Visual Studio”, “VSLive” ], posts : [ { title: “Migrating to RavenDB”, content: “When planning a migration to Raven…”, author: “Sasha Goldshtein”, comments: [ ... ] }, ... ]}
My blog has 500 posts
One More Example“orders/1783”: { customer: { name: “James Bond”, id: “customers/007” }, items: [ { product: “Disintegrator”, cost: 78.3, qty: 1 }, { product: “Laser shark”, cost: 99.0, qty: 3 } ]} What if we always
need the customer’s address?
What if the customer’s address changes
often?
What if we always need to know whether
the product is in stock?
Include
Load the referenced document when the referencing document is retrieved
Also supports arrays of referenced documents
Order order = session.Include<Order>(o => o.Customer.Id) .Load(“orders/1783”);Customer customer = session.Load<Customer>( order.Customer.Id);
Order[] orders = session.Query<Order>() .Customize(q => q.Include<Order>(o => o.Customer.Id)) .Where(o => o.Items.Length > 5) .ToArray();
Demo
Include and Load
Indexes
RavenDB automatically creates indexes for you as you run your queries
The indexing happens in the backgroundIndexes can become staleCan wait for non-stale results (if necessary)
RavenQueryStatistics stats;var results = session.Query<Speaker>() .Statistics(out stats) .Where(s => s.Experience > 3) .ToArray();if (stats.IsStale) ...
ACID?If indexes can become stale, does it mean RavenDB is not ACID?
The document store is ACIDThe index store is not
You can insert lots of data very quickly and load it quickly, but indexes take a while to catch up
Indexing FundamentalsA document has fields that are indexed individuallyAn index points from sorted field values to matching documents
"orders/1" : { customer: "Dave", price: 200, items: 3}
"orders/2" : { customer: "Mike", price: 95, items: 1}
"orders/3" : { customer: "Dave", price: 150, items: 2}
Customer
Document IDs
Dave orders/1, orders/3
Mike orders/2PriceRange
Document IDs
0-99 orders/2
100-199 orders/3
200-299 orders/1
Static (Manual) Indexes
Static indexes can provide map and reduce functions to specify what to indexThe simplest form specifies a map function with the fields to index:
ds.DatabaseCommands.PutIndex(“Speaker/ByCity”, new IndexDefinitionBuilder<Speaker> { Map = speakers => from speaker in speakers select new { speaker.City } });
Map/Reduce Index
We often need the speaker count for each of our conferences:
ds.DatabaseCommands.PutIndex(“Conferences/SpeakerCount”, new IndexDefinitionBuilder<Conference, SpeakerCount> { Map = conferences => from conf in conferences from speaker in conf.Speakers select new { Item1 = speaker.Name, Item2 = 1 },
Reduce = results => from result in results group result by result.Item1 into g select new { Item1 = g.Key, Item2 = g.Sum(x => x.Item2) }
});
class SpeakerCount : Tuple<string, int> {}
Using Indexes
In most cases you simply run a query and it will implicitly use or create an indexOr, instruct the query to use your index:
var d = session.Query<SpeakerCount>(“Conferences/SpeakerCount”) .FirstOrDefault(s => s.Item1 == “Dave”);Console.WriteLine(“Dave spoke at {0} conferences”, d.Item2);
var posts = session.Query<Comment>(“CommentsIndex”) .Where(c => c.Author == “Mike”) .OfType<Post>();
Demo
Using Indexes
Full-Text Search Indexes
Made possible by the underlying Lucene.NET engine
public class SpeakerIndex : AbstractIndexCreationTask<Speaker>{ public SpeakerIndex() { Map = speakers => from speaker in speakers select new { speaker.Name }; Index("Name", FieldIndexing.Analyzed); }}
Using Full-Text Search and Query Suggestionsvar query = session.Query<Speaker, SpeakerIndex>() .Where(s => s.Name == name);var speaker = query.FirstOrDefault();
if (speaker == null){ string[] suggestions = query.Suggest().Suggestions;}
Will find “Dave Smith” when searching for “dave” or “smith”
Will suggest “dave” when searching for “david”
Using Lucene Directly
You can also query Lucene directly on any analyzed fieldsE.g., fuzzy search for sessions:
string query = String.Format("Title:{0}*", term);
session.Advanced.LuceneQuery<Session>("SessionIndex") .Where(query) .ToList();
Demo
Full-Text Search and Suggestions
Advanced FeaturesBatch operations by indexAsync API (OpenAsyncSession, await)AttachmentsPatching (partial document updates)Change notifications (IDatabaseChanges)Transactions (TransactionScope)…and many others
http://ravendb.net/docs
Upcoming Features in RavenDB 3.0
Management Studio rewrite in HTML5Web API-based infrastructureFirst-class Java client SDKCustom storage engine (Voron)
© Copyright SELA software & Education Labs Ltd. | 14-18 Baruch Hirsch St Bnei Brak, 51202 Israel | www.selagroup.com
SELA DEVELOPER PRACTICEJUNE 29 – JULY 3, 2014
Thank You!
Sasha [email protected]