Code Example: Verses
pmarkert edited this page May 17, 2012
·
1 revision
For me, sometimes the quickest way to get familiar with something is to see it in action. Additionally, I'm always looking for a chance to share the Gospel. :) Here is a compact, but complete sample project that demonstrates using Mongol to save and query some records. It showcases a few features, and provides enough of an example to understand basic operations. Create a new console application, add Mongol using Nuget, and add a Mongol.Url config setting. Then paste these classes in and hit run. The project downloads one of the easiest to parse copies of the bible I could find and loads the verses into Mongo, using them to demonstrate some CRUD and query operations.
// Class to represent a single bible-verse
public class Verse {
[BsonId]
public string Id { get; set; }
public string Book { get; set; }
public int Chapter { get; set; }
public int Number { get; set; }
public string Text { get; set; }
}
// Class to demonstrate storing an object with an array property
public class Person {
[BsonId]
public string Id { get; set; }
public string[] FavoriteVerses { get; set; }
}
public class VerseManager : RecordManager<Verse> {
protected override void Initialize() {
// Unique index on Book, Chapter, Verse
EnsureIndex(IndexKeys
.Ascending(PropertyName(x => x.Book))
.Ascending(PropertyName(x => x.Chapter))
.Ascending(PropertyName(x => x.Number))
, IndexOptions.SetUnique(true));
}
public IEnumerable<Verse> GetByBook(string book) {
// Example of a simple single-field query
return Find(Query.EQ(PropertyName(x => x.Book), book));
}
public Verse GetByReference(string book, int chapter, int verse) {
// Exmaple of a multi-field query
return FindSingle(
Query.EQ(PropertyName(x => x.Book), book)
.And(Query.EQ(PropertyName(x => x.Chapter), chapter))
.And(Query.EQ(PropertyName(x => x.Number), verse)));
}
public IEnumerable<Verse> GetByBookAndVerseRange(string book, int lowChapter, int lowVerse, int highChapter, int highVerse) {
// Example of a range query joined with multiple conditions
return Find(Query.EQ(PropertyName(x => x.Book), book)
.And((Query.GTE(PropertyName(x => x.Chapter), lowChapter)
.And(Query.GTE(PropertyName(x => x.Number), lowVerse)))
.And((Query.LTE(PropertyName(x => x.Chapter), highChapter)
.And(Query.LTE(PropertyName(x => x.Number), highVerse))))
));
}
public void ReloadAll(IEnumerable<Verse> verses) {
// Reloads all records in the collection, so you can run the program more than once
collection.RemoveAll();
collection.InsertBatch(verses);
}
// Returns the number of verses in each book, sorted by descending count of verses.
public Dictionary<string, int> CalculateBookStats() {
// Demonstrates the new Aggregation Framework and Mongol's DSL wrapper
return Aggregate(
Aggregation.Group(
Aggregation.Grouping.By(PropertyName(x => x.Book)),
Aggregation.Grouping.Count("Count")),
Aggregation.Sort(Aggregation.Sorting.By("Count", false))
).ToDictionary(x => x[ID_FIELD].AsString, x => x["Count"].AsInt32);
}
}
class Program {
static void Main(string[] args) {
var verseManager = new VerseManager();
// I prefer more modern translations than KJV, but this was freely available and easy to parse with a few lines.
verseManager.ReloadAll(parseVerses(new StreamReader(new GZipStream(new WebClient().OpenRead("https://s3.amazonaws.com/mongol.ephisys.com/Data/kjv.rawtxt.gz"), CompressionMode.Decompress))));
// Print some verse-count statistics by book using aggregation framework and Mongol's DSL wrapper (requires Mongo version>=2.1, otherwise comment these out)
foreach (var entry in verseManager.CalculateBookStats()) {
Console.WriteLine(entry.Key + " - " + entry.Value);
}
// Print verses containing the word love using a linq query
var re = new Regex(@"\blove\b", RegexOptions.IgnoreCase);
foreach (var verse in verseManager.AsQueryable.Where(x => re.IsMatch(x.Text))) {
Console.WriteLine(verse.Id + " - " + verse.Text);
}
// Print a famous verse using a multi-field lookup
var john316 = verseManager.GetByReference("John", 3, 16);
Console.WriteLine(john316.Id + " - " + john316.Text);
// Print a range of verses using a range query
foreach (var verse in verseManager.GetByBookAndVerseRange("Eph", 6, 10, 6, 18)) {
Console.WriteLine(verse.Id + " - " + verse.Text);
}
// Practice some delete and save operations
var rev2219 = verseManager.GetById("Rev22:19");
verseManager.DeleteById(rev2219.Id); // Remove a verse
Console.WriteLine("DELETED - " + rev2219.Text);
verseManager.Save(rev2219); // On second thought, after reading the verse, maybe we had better put it back. :)
// Maybe we could just add some extra ones instead?
verseManager.Save(new Verse() { Id="Opn2:21", Book = "Opinions", Chapter = 2, Number = 21, Text = "God helps those who help themselves." }); // Was actually Ben Franklin
verseManager.Save(new Verse() { Id = "Fab6:5", Book = "Fabrications", Chapter = 6, Number = 5, Text = "God doesn't care what we call him, as long as we believe in something." }); // Not true at all.
// Actually Rev22:18 says that's a bad idea too, so maybe we just stick straight to the true.
verseManager.DeleteById("Opn2:21");
verseManager.DeleteById("Fab6:5");
var personManager = new RecordManager<Person>(); // Finally show an example of an on-the-fly RecordManager
// Demonstrate saving some array properties
var phillip = new Person() { Id = "Phillip Markert", FavoriteVerses = new string[] { "Col3:23", "Rom8:28", "Rev3:20" } };
personManager.Save(phillip);
personManager.Save(new Person() { Id = "Ryan Caskey", FavoriteVerses = new string[] { "John3:16", "Eph2:8", "Eph2:9" } });
// Now demonstrate multi-valued ID lookup (closest equivalent to a 1-Many Join), uses a $in clause
Console.WriteLine("Some of Phillip's favorite verses are...");
foreach (var verse in verseManager.GetManyByIds(phillip.FavoriteVerses)) {
Console.WriteLine(verse.Id + " - " + verse.Text);
}
// Find people who have John 3:16 in their favorite-verses array.
foreach (var person in personManager.AsQueryable.Where(p => p.FavoriteVerses.Contains("John3:16"))) {
Console.WriteLine(person.Id + " likes John 3:16");
}
Console.ReadKey();
}
private static IEnumerable<Verse> parseVerses(StreamReader sr) {
// Iterator to return verses as long as we keep finding more. Loads about 31,000 verses.
while (!sr.EndOfStream) {
var match = Regex.Match(sr.ReadLine(), @"(?<Reference>(?<Book>\d*[A-Za-z]+)(?<Chapter>\d+):(?<Verse>\d+))\s(?<Text>.+$)");
if (match.Success) {
yield return new Verse() { Id = match.Groups["Reference"].Value, Book = match.Groups["Book"].Value, Chapter = int.Parse(match.Groups["Chapter"].Value), Number = int.Parse(match.Groups["Verse"].Value), Text = match.Groups["Text"].Value };
}
}
}
}