Web applications tend to be more fun if there's some sort of social aspect to them. Usually a diary application would be a private affair. Let's add a social aspect to this to make it a little more interesting.
The application should break down each diary entry into a list of words. Every user will have a list of every word they have ever written in a diary entry. Each word will also have a user list associated with it, effectively keeping track of every user who has ever said that word in one of their diary entries.
Add the content of Listing 19 to the end of the
MakeEntry action, just before the return
statement.
Listing 19, Creating the word lists.
// Finally we want to store each word in the diary entry // into the database such that a map of words to users // is produced in order to make this application a little // more social. var wordList = Regex.Split(entry.Text, @"\W+") .ToList() .ConvertAll(d => d.ToLower()); var wordsKey = user.UserId + ".Words"; foreach (var word in wordList) { AddToListWithCas(client, wordsKey, word); AddToListWithCas(client, word + ".Users", user.UserId); }
This block of code breaks the entry text down into words using a
regular expression, and uses a little LINQ to turn the array of
strings into a list and convert every word to lower case. A key is
created simply be appending ".Words" to the user's
UserId guide. It doesn't matter what is in the
key, as long as you remember what the key was. This effectively
makes a relation between the list of words and the user.
The code then iterates over the list of words, and reuses the
AddToListWithCas method discussed earlier to
add them first to the user's word list, and finally to a global
word list that relates each word to all of the users who have ever
said it. This is definitely where the power of a NoSQL database
shines. We can just make links between information by making
specially formatted keys pointing to objects. In this case we're
adding the userId to a list of objects pointed
to by the key word.Users where the word is
replaced by the current word.
Now you will need to make a final modification to the Index method
of the HomeController to include the logic for
turning these lists of words into a list of names of the users who
are using the same language you are.
Listing 20, update Index in HomeController.cs
// Now, do a calculation to discover the top three users who use // similar words in their diary entries. // Get the user's word list var wordlist = client.Get<List<string>>(user.UserId + ".Words"); if (wordlist != null) { var userListKeys = wordlist.ConvertAll(k => k + ".Users"); var userLists = client.Get(userListKeys) .Values .Cast<List<Guid>>(); // Calculate the count of users who have used // the same words that the current user has var userCounts = new Dictionary<Guid, int>(); foreach (var list in userLists) { foreach (var userId in list) { // Skip ourselves if (userId == user.UserId) continue; if (userCounts.ContainsKey(userId)) { userCounts[userId] = userCounts[userId] + 1; } else { userCounts[userId] = 1; } } } // Make a final list of users by ordering by the counts // in the userCounts dictionary. var kindred = new List<User>(); foreach (var item in userCounts.OrderByDescending(key => key.Value)) { kindred.Add(client.Get<User>(item.Key.ToString())); if (kindred.Count > 2) break; } ViewData["KindredUsers"] = kindred; }
Effectively each user's word lists are obtained, and the keys to
the global list of words to users is created. This is used in
another multi-get operation whose values are converted to a lists
of Guids. Then these lists are iterated, to tabulate a total count
of the userIds. We're making the assumption that these counts are
all we need to determine who has been using similar words to us.
These are then ordered by maximum count first, and added to a
KindredUsers list.
The Index view must now be modified to present
these kindred users. Listing 21 should be added before the last
line of the Index.cshtml file.
Listing 21, update Index.cshtml.
@if (ViewData.ContainsKey("KindredUsers")) { var kindred = ((IList<User>)ViewData["KindredUsers"]); <h2>Kindred Users</h2> if (kindred.Count > 0) { <ul> @foreach (var user in kindred) { <li>@user.FullName</li> } </ul> } else { <p>You currently have no kindred users.</p> } }
Improvements could easily be made to this application. You may want to allow users to contact each other by providing a way to drop notes. You may also improve the word ratings by removing common stop-words (such as 'a', 'an', 'or', 'the', 'in' and so on) or maybe skipping all short words altogether.
I'm sure you've got all sorts of ideas about how to apply Couchbase to your latest projects, and we hope you've enjoyed this tutorial. You will find the final source code in the file dotnet-couchbase-tutorial.zip.