33
Seeking Life Beyond Relational: RavenDB Sasha Goldshtein CTO, Sela Group Level: Intermediate

Introduction to RavenDB

Embed Size (px)

DESCRIPTION

Visual Studio Live presentation on RavenDB. Introduces the concept of NoSQL databases and then takes you on a quick tour of the RavenDB .NET SDK - simple LINQ queries, loading related documents, dynamic and static indexes, and full-text search with Lucene and query suggestions.

Citation preview

Page 1: Introduction to RavenDB

Seeking Life Beyond Relational: RavenDB

Sasha GoldshteinCTO, Sela Group

Level: Intermediate

Page 2: Introduction to RavenDB

NoSQL

• The Zen-like answer: No one can tell you what NoSQL is, they can only tell you what it isn’t

• It doesn’t use SQL• It usually is less consistent than RDBMS• It doesn’t put an emphasis on relations• It emphasizes size and scale over

structure

Page 3: Introduction to RavenDB

Classes of NoSQL Databases

Document DB

Key-Value

DB

Graph DB

Column DB

Page 4: Introduction to RavenDB

RavenDB

• Transactional document database• Open source

https://github.com/ravendb/ravendb with licensing for commercial projects

• Schema-less documents, JSON storage• RESTful endpoints• LINQ-style .NET API• Implicit (usage-based) or explicit indexing• Powerful text search based on Lucene• Replication and sharding support

Oren Eini(Ayende Rahien)

Page 5: Introduction to RavenDB

Hosting RavenDB

• Raven.Server.exe• Windows Service• Integrated in IIS• Embedded client for stand-alone apps• Cloud-hosted (e.g. RavenHQ)

Page 6: Introduction to RavenDB

Management Studio

Page 7: Introduction to RavenDB

DEMORavenDB Management Studio

Page 8: Introduction to RavenDB

Opening a Session

• DocumentStore is the session factory; one per application is enough

• Supports .NET connection strings or direct initialization:

var ds = new DocumentStore{ Url = "http://localhost:8888"};ds.Initialize();

Page 9: Introduction to RavenDB

CRUD Operations on Documents

• Unit of work pattern (ORM-style)

using (var session = documentStore.OpenSession()){ session.Store(new Speaker(“Sasha”, “Tel-Aviv”)); session.SaveChanges();}using (var session = documentStore.OpenSession()){ Speaker sasha = session.Query<Speaker>() .Where(e => e.City == “Tel-Aviv”).First(); sasha.City = “Orlando”; session.SaveChanges();}

Page 10: Introduction to RavenDB

Collections and IDs

• Documents are stored in JSON format• Documents have metadata that includes

the entity type• A collection is a set of documents with the

same entity type• Documents have unique ids, often a

combination of collection name + id– speakers/1– conferences/7

Page 11: Introduction to RavenDB

DEMOBasic Operations

Page 12: Introduction to RavenDB

Modeling Data as Documents

• Don’t be tempted to use a document store like a relational database– Documents should be aggregate roots– References 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?

Page 13: Introduction to RavenDB

…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

Page 14: Introduction to RavenDB

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?

Page 15: Introduction to RavenDB

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();

Page 16: Introduction to RavenDB

DEMOInclude and Load

Page 17: Introduction to RavenDB

Indexes

• RavenDB automatically creates indexes for you as you run your queries– The indexing happens in the background after

changes are made to the database– Indexes can become stale– Can 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) ...

Page 18: Introduction to RavenDB

ACID?

• If indexes can become stale, does it mean RavenDB is not ACID?

• The document store is ACID• The index store is not

• You can insert lots of data very quickly and load it quickly, but indexes take a while to catch up

Page 19: Introduction to RavenDB

Indexing Fundamentals

• A document has fields that are indexed individually

• An 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/2

Price Document IDs

95 orders/2

150 orders/3

200 orders/1

Page 20: Introduction to RavenDB

Static (Manual) Indexes

• Static indexes can provide map and reduce functions to specify what to index

• The 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 } });

Page 21: Introduction to RavenDB

Hierarchical Data

• How to index the following hierarchy of comments by author and text?

public class Post{ public string Title { get; set; } public Comment[] Comments { get; set; }}public class Comment{ public string Author { get; set; } public string Text { get; set; } public Comment[] Comments { get; set; }}

Page 22: Introduction to RavenDB

Hierarchical Index with Recurse

public class CommentsIndex : AbstractIndexCreationTask<Post>{ public CommentsIndex() { Map = posts => from post in posts from comment in Recurse(post, c=>c.Comments) select new { Author = comment.Author, Text = comment.Text } }}

This is an index over Post objects but the output produces Comment objects!

Page 23: Introduction to RavenDB

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> {}

Page 24: Introduction to RavenDB

Using Indexes

• In most cases you simply run a query and it will implicitly use or create an index

• Or, 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>();

Page 25: Introduction to RavenDB

Indexing Related Documents

• Use the LoadDocument method

public class OrderCustomerCityIndex : AbstractIndexCreationTask<Order, OrderCustomerCityIndex.Result>{ public class Result { public string City; } public OrderCustomerCityIndex() { Map = orders => from order in orders select new { City = LoadDocument(order.Customer.Id).City } }}

session.Query<OrderCustomerCityIndex.Result, OrderCustomerCityIndex>() .Where(c => c.City == “Orlando”) .OfType<Order>() .ToList();

Page 26: Introduction to RavenDB

DEMOUsing Indexes

Page 27: Introduction to RavenDB

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); }}

Page 28: Introduction to RavenDB

Using Full-Text Search and Query Suggestions

var 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”

Page 29: Introduction to RavenDB

Using Lucene Directly

• You can also query Lucene directly on any analyzed fields

• E.g., fuzzy search for sessions:

string query = String.Format("Title:{0}*", term);

session.Advanced.LuceneQuery<Session>("SessionIndex") .Where(query) .ToList();

Page 30: Introduction to RavenDB

DEMOFull-Text Search and Suggestions

Page 31: Introduction to RavenDB

Advanced Features

• Batch operations by index• Async API (OpenAsyncSession, await)• Attachments• Patching (partial document updates)• Change notifications (IDatabaseChanges)• Transactions (TransactionScope)• …and many others

http://ravendb.net/docs

Page 32: Introduction to RavenDB

Upcoming Features in RavenDB 3.0

• Management Studio rewrite in HTML5• Web API-based infrastructure• First-class Java client SDK• Custom storage engine (Voron)

Page 33: Introduction to RavenDB

Thank You!

Sasha Goldshtein

@goldshtn

blog.sashag.net