diff --git a/Ch03/Ch03.md b/Ch03/Ch03.md index 403da93c..597b98dc 100644 --- a/Ch03/Ch03.md +++ b/Ch03/Ch03.md @@ -429,7 +429,7 @@ as shown in Listing 3.6. using (var session = store.OpenSession()) { Child alice = session - .Include(c=>c.Grandparents) + .Include(c => c.Grandparents) .Load("children/alice-liddell"); Dictionary gradparents = session.Load(alice.Grandparents); } diff --git a/Ch04/Ch04.md b/Ch04/Ch04.md index 60b05dc8..db49c6d1 100644 --- a/Ch04/Ch04.md +++ b/Ch04/Ch04.md @@ -863,7 +863,7 @@ using (var session = store.OpenSession()) using (var stream = session.Advanced.Stream(callsQuery)) { - while(stream.MoveNext()) + while (stream.MoveNext()) { SupportCall current = stream.Current; // do something with this instance diff --git a/Ch05/Ch05.md b/Ch05/Ch05.md index b28ca95e..325223f3 100644 --- a/Ch05/Ch05.md +++ b/Ch05/Ch05.md @@ -59,7 +59,7 @@ and inspect things. The point is that RavenDB will send those details to your co start handling batches of documents. ```{caption="Opening and using a subscription to process customers" .cs} -using(var subscription = store.Subscriptions +using (var subscription = store.Subscriptions .GetSubscriptionWorker("CustomersSubscription"))) { // wait until the subscription is done @@ -137,9 +137,9 @@ We've previously seen the `Run` method, back in Listing 5.2. But what we haven't go over the batch of documents. Listing 5.3 shows the code to handle the subscription that we redacted from Listing 5.2. ```{caption="Processing customers via subscription" .cs} -await subscription.Run( batch => +await subscription.Run(batch => { - foreach(var item in batch.Items) + foreach (var item in batch.Items) { Customer customer = item.Result; // do something with this customer @@ -205,36 +205,36 @@ in the studio in Figure 5.4. ```{caption="Taking surveys of complex calls" .cs} await subscription.Run(batch => { - foreach (var item in batch.Items) - { - SupportCall call = item.Document; - - var age = DateTime.Today - call.Started; - if (age > DateTime.FromDays(14)) - return; // no need to send survey for old stuff - - using (var session = batch.OpenSession()) - { - var customer = session.Load( - call.CustomerId); - - call.Survey = true; - - session.Store(call, item.ChangeVector, item.Id); - - try - { - session.SaveChanges(); - } - catch(ConcurrenyException) - { - // will be retried by the subscription - return; - } - - SendSurveyEmailTo(customer, call); - } - } + foreach (var item in batch.Items) + { + SupportCall call = item.Document; + + var age = DateTime.Today - call.Started; + if (age > DateTime.FromDays(14)) + return; // no need to send survey for old stuff + + using (var session = batch.OpenSession()) + { + var customer = session.Load( + call.CustomerId); + + call.Survey = true; + + session.Store(call, item.ChangeVector, item.Id); + + try + { + session.SaveChanges(); + } + catch (ConcurrenyException) + { + // will be retried by the subscription + return; + } + + SendSurveyEmailTo(customer, call); + } + } }); ``` @@ -270,28 +270,28 @@ Instead of using explicit concurrency handling, we can also write the code in Li ```{caption="Taking surveys of complex calls, using patches" .cs} await subscription.Run(batch => { - foreach (var item in batch.Items) - { - SupportCall call = item.Document; - var age = DateTime.Today - call.Started; - if (age > DateTime.FromDays(14)) - return; // no need to send survey for old stuff - - using (var session = batch.OpenSession()) - { - var customer = session.Load( - call.CustomerId); - - session.Advanced.Patch( - result.Id, - c => c.Survey, - true); - - SendSurveyEmailTo(customer, call); - - session.SaveChanges(); - } - } + foreach (var item in batch.Items) + { + SupportCall call = item.Document; + var age = DateTime.Today - call.Started; + if (age > DateTime.FromDays(14)) + return; // no need to send survey for old stuff + + using (var session = batch.OpenSession()) + { + var customer = session.Load( + call.CustomerId); + + session.Advanced.Patch( + result.Id, + c => c.Survey, + true); + + SendSurveyEmailTo(customer, call); + + session.SaveChanges(); + } + } }); ``` @@ -378,43 +378,43 @@ We can take advantage of the batch nature of subscriptions to do much better. Tu ```{caption="Efficiently process the batch" .cs} await subscription.Run(batch => { - using (var session = batch.OpenSession()) - { - var customerIds = batch.Items - .Select(item => item.Result.CustomerId) - .Distinct() - .ToList(); - // force load of all the customers in the batch - // in a single request to the server - session.Load(customerIds); - - foreach (var item in batch.Items) - { - SupportCall call = item.Document; - var age = DateTime.Today - call.Started; - if (age > DateTime.FromDays(14)) - return; // no need to send survey for old stuff - - // customer was already loaded into the session - // no remote call will be made - var customer = session.Load( - call.CustomerId); - - // register the change on the session - // no remote call will be made - session.Advanced.Patch( - result.Id, - c => c.Survey, - true); - - SendSurveyEmailTo(customer, call); - - } - - // send a single request with all the - // changes registered on the seession - session.SaveChanges(); - } + using (var session = batch.OpenSession()) + { + var customerIds = batch.Items + .Select(item => item.Result.CustomerId) + .Distinct() + .ToList(); + // force load of all the customers in the batch + // in a single request to the server + session.Load(customerIds); + + foreach (var item in batch.Items) + { + SupportCall call = item.Document; + var age = DateTime.Today - call.Started; + if (age > DateTime.FromDays(14)) + return; // no need to send survey for old stuff + + // customer was already loaded into the session + // no remote call will be made + var customer = session.Load( + call.CustomerId); + + // register the change on the session + // no remote call will be made + session.Advanced.Patch( + result.Id, + c => c.Survey, + true); + + SendSurveyEmailTo(customer, call); + + } + + // send a single request with all the + // changes registered on the seession + session.SaveChanges(); + } }); ``` @@ -444,29 +444,28 @@ special attention. ```{caption="Creating a subscription using JavaScript filtering" .cs} var options = new SubscriptionCreationOptions { - Name = subscriptionName, - Query = @" -declare function isAnnoyedCustomer(call){ - var watchwords = ['annoy', 'hard', 'silly']; - - var lastIndex = call['@metadata']['Last-Monitored-Index'] || 0; - - for (var i = lastIndex; i < call.Comments.length; i++) - { - var comment = call.Comments[i].toLowerCase(); - for (var j = 0; j < watchwords.length; j++) - { - if (comment.indexOf(watchowrd[j]) != -1) - return true; - } - } - return false; + Name = subscriptionName, + Query = @" + declare function isAnnoyedCustomer(call) { + var watchwords = ['annoy', 'hard', 'silly']; + + var lastIndex = call['@metadata']['Last-Monitored-Index'] || 0; + + for (var i = lastIndex; i < call.Comments.length; i++) + { + var comment = call.Comments[i].toLowerCase(); + for (var j = 0; j < watchwords.length; j++) + { + if (comment.indexOf(watchowrd[j]) != -1) + return true; + } + } + return false; + } + from SupportCalls as call + where isAnnoyedCustomer(call) + " } -from SupportCalls as call -where isAnnoyedCustomer(call) -" - } -}; store.Subscriptions.Create(options); ``` @@ -504,36 +503,34 @@ Listing 5.9. ```{caption="Escalating problematic calls" .cs} await subscription.Run(batch => { - const string script = @" -var metadata = this['@metadata']; -var existing = metadata['Last-Monitored-Index'] || 0; -metadata['Last-Monitored-Index'] = Math.max(idx, existing); -"; - using (var session = batch.OpenSession()) - { - foreach (var item in batch.Items) + const string script = @" + var metadata = this['@metadata']; + var existing = metadata['Last-Monitored-Index'] || 0; + metadata['Last-Monitored-Index'] = Math.max(idx, existing); + "; + using (var session = batch.OpenSession()) { - // mark the last index that we - // already observed using Patch - session.Advanced.Defer( - new PatchCommandData( - id: item.Id, - patch: new PatchRequest - { - Script = script, - Values = - { - ["idx"] = item.Result.Comments.Count - } - }, - patchIfMissing: null, - ); - - // actually escalate the call + foreach (var item in batch.Items) + { + // mark the last index that we + // already observed using Patch + session.Advanced.Defer( + new PatchCommandData( + id: item.Id, + patch: new PatchRequest + { + Script = script, + Values = + { + ["idx"] = item.Result.Comments.Count + } + }, + patchIfMissing: null, + ); + } + // actually escalate the call + session.SaveChanges(); } - - session.SaveChanges(); - } }); ``` @@ -567,16 +564,15 @@ of votes for that call. Instead of sending the full document over the wire, we c ```{caption="Getting just the relevant details in the subscription" .cs} var options = new SubscriptionCreationOptions { - Name = subscriptionName, - Query = @" -from SupportCalls as call -where call.Votes >= 10 -select { - Issue: call.Issue, - Votes: call.Votes -} -" - } + Name = subscriptionName, + Query = @" + from SupportCalls as call + where call.Votes >= 10 + select { + Issue: call.Issue, + Votes: call.Votes + }" + } }; store.Subscriptions.Create(options); ``` @@ -592,21 +588,21 @@ how to handle such a thing. This requires a bit of a change in how we open the s ```{caption="Opening subscription with a different target" .cs} public class SupportCallSubscriptionOutput { - public string Issue; - public int Votes; + public string Issue; + public int Votes; } var subscription = store.Subscriptions - .Open(options)); + .Open(options)); await subscription.Run(batch => { - foreach (var item in batch.Items) - { - SupportCallSubscriptionResult result = item.Result; - // do something with the - // result.Issue, result.Votes - } + foreach (var item in batch.Items) + { + SupportCallSubscriptionResult result = item.Result; + // do something with the + // result.Issue, result.Votes + } }); ``` @@ -623,21 +619,19 @@ We can ask RavenDB to handle this as part of the subscription processing directl ```{caption="Getting just the relevant details in the subscription" .cs} var options = new SubscriptionCreationOptions { - Name = subscriptionName, - Query = @" -from SupportCalls as call -where call.Votes >= 10 -load call.CustomerId as customer -select { - Issue: call.Issue, - Votes: call.Votes, - Customer: { - Name: customer.Name, - Email: customer.Email - } -} -" - } + Name = subscriptionName, + Query = @" + from SupportCalls as call + where call.Votes >= 10 + load call.CustomerId as customer + select { + Issue: call.Issue, + Votes: call.Votes, + Customer: { + Name: customer.Name, + Email: customer.Email + } + }" }; store.Subscriptions.Create(options); ``` @@ -651,27 +645,27 @@ When processing the output of this subscription, we can directly process the res ```{caption="Opening subscription with a different target" .cs} public class SupportCallSubscriptionOutput { - public class Customer - { - public string Name; - public string Email; - } - public string Issue; - public int Votes; - public Customer Customer; + public class Customer + { + public string Name; + public string Email; + } + public string Issue; + public int Votes; + public Customer Customer; } var subscription = store.Subscriptions - .Open(options)); + .Open(options)); await subscription.Run(batch => { - foreach (var item in batch.Items) - { - SupportCallSubscriptionResult result = item.Result; - SendEscalationEmail(result.Customer, item.Id); - // other stuff related to processing the call - } + foreach (var item in batch.Items) + { + SupportCallSubscriptionResult result = item.Result; + SendEscalationEmail(result.Customer, item.Id); + // other stuff related to processing the call + } }); ``` @@ -706,77 +700,77 @@ things going on in this listing, so take the time to carefully read through the ```{caption="Retrying subscription on error" .cs} while (true) { - var options = new SubscriptionWorkerOptions(subscriptionName); - - // here we configure that we allow a down time of up to 2 hours - // and will wait for 2 minutes for reconnecting - options.MaxErroneousPeriod = TimeSpan.FromHours(2); - options.TimeToWaitBeforeConnectionRetry = TimeSpan.FromMinutes(2); - - var subscriptionWorker = store.Subscriptions - .GetSubscriptionWorker(options); - - try - { - // here we are able to be informed of any exception that happens during processing - subscriptionWorker.OnSubscriptionConnectionRetry += exception => - { - Logger.Error("Error during subscription processing: " + subscriptionName, exception); - }; - - await subscriptionWorker.Run(async batch => - { - foreach (var item in batch.Items) - { - // we want to force close the subscription processing in that case - // and let the external code decide what to do with that - if (item.Result.Company == "companies/832-A") - throw new UnsupportedCompanyException("Company Id can't be 'companies/832-A', you must fix this"); - await ProcessOrder(item.Result); - } - }, cancellationToken); - - // Run will complete normally if you have disposed the subscription - return; - } - catch (Exception e) - { - Logger.Error("Failure in subscription: " + subscriptionName, e); - - if (e is DatabaseDoesNotExistException || - e is SubscriptionDoesNotExistException || - e is SubscriptionInvalidStateException || - e is AuthorizationException) - throw; // not recoverable - - - if (e is SubscriptionClosedException) - // closed explicitly by admin, probably - return; - - if (e is SubscriberErrorException se) - { - // for UnsupportedCompanyException type, we want to throw an exception, otherwise - // we continue processing - if (se.InnerException is UnsupportedCompanyException) - { - throw; - } - - continue; - } - - // handle this depending on subscription - // open strategy (discussed later) - if (e is SubscriptionInUseException) - continue; - - return; - } - finally - { - subscriptionWorker.Dispose(); - } + var options = new SubscriptionWorkerOptions(subscriptionName); + + // here we configure that we allow a down time of up to 2 hours + // and will wait for 2 minutes for reconnecting + options.MaxErroneousPeriod = TimeSpan.FromHours(2); + options.TimeToWaitBeforeConnectionRetry = TimeSpan.FromMinutes(2); + + var subscriptionWorker = store.Subscriptions + .GetSubscriptionWorker(options); + + try + { + // here we are able to be informed of any exception that happens during processing + subscriptionWorker.OnSubscriptionConnectionRetry += exception => + { + Logger.Error("Error during subscription processing: " + subscriptionName, exception); + }; + + await subscriptionWorker.Run(async batch => + { + foreach (var item in batch.Items) + { + // we want to force close the subscription processing in that case + // and let the external code decide what to do with that + if (item.Result.Company == "companies/832-A") + throw new UnsupportedCompanyException("Company Id can't be 'companies/832-A', you must fix this"); + await ProcessOrder(item.Result); + } + }, cancellationToken); + + // Run will complete normally if you have disposed the subscription + return; + } + catch (Exception e) + { + Logger.Error("Failure in subscription: " + subscriptionName, e); + + if (e is DatabaseDoesNotExistException || + e is SubscriptionDoesNotExistException || + e is SubscriptionInvalidStateException || + e is AuthorizationException) + throw; // not recoverable + + + if (e is SubscriptionClosedException) + // closed explicitly by admin, probably + return; + + if (e is SubscriberErrorException se) + { + // for UnsupportedCompanyException type, we want to throw an exception, otherwise + // we continue processing + if (se.InnerException is UnsupportedCompanyException) + { + throw; + } + + continue; + } + + // handle this depending on subscription + // open strategy (discussed later) + if (e is SubscriptionInUseException) + continue; + + return; + } + finally + { + subscriptionWorker.Dispose(); + } } ``` @@ -829,16 +823,16 @@ Listing 5.15 shows how you can write a self-stopping subscription. ```{caption="Stop the subscription after 15 minutes of inactivity" .cs} var options = new SubscriptionWorkerOptions(subscriptionName) { - CloseWhenNoDocsLeft = true + CloseWhenNoDocsLeft = true } using (var subscription = store - .Subscriptions.GetSubscriptionWorker(options))) + .Subscriptions.GetSubscriptionWorker(options))) { - await subscription.Run(batch => - { - // process batches until we run out of docs - // that match the subscription - }); + await subscription.Run(batch => + { + // process batches until we run out of docs + // that match the subscription + }); } ``` @@ -901,25 +895,25 @@ actually doing this is shown in Listing 5.16. ```{caption="Delete the emails to send after successful send" .cs} using (var subscription = - store.Subscriptions.GetSubscriptionWorker(options)) + store.Subscriptions.GetSubscriptionWorker(options)) { await subscription.Run(async batch => { using (var session = batch.OpenAsyncSession()) { - foreach (var item in batch.Items) + foreach (var item in batch.Items) { - try - { - SendEmail(item.Document); - } - catch - { - // logging / warning / etc - continue; - } + try + { + SendEmail(item.Document); + } + catch + { + // logging / warning / etc + continue; + } session.Delete(item.Id); - } + } await session.SaveChangesAsync(); } }); @@ -975,31 +969,31 @@ when a customer changed its name. ```{caption="Subscribing to versioned customers" .cs} store.Subscriptions.Create>( - new SubscriptionCreationOptions - { - Name = subscriptionName, - } + new SubscriptionCreationOptions + { + Name = subscriptionName, + } ); using (var subscription = store.Subscriptions - .GetSubscriptionWorker>( - subscriptionName - ))) + .GetSubscriptionWorker>( + subscriptionName + ))) { - await subscription.Run(batch => - { - foreach (var item in batch.Items) - { - Revision customer = item.Result; - - if (customer.Previous.Name != - customer.Current.Name) - { - // report customer name change - } - } - }); + await subscription.Run(batch => + { + foreach (var item in batch.Items) + { + Revision customer = item.Result; + + if (customer.Previous.Name != + customer.Current.Name) + { + // report customer name change + } + } + }); } ``` @@ -1037,14 +1031,14 @@ Just like regular subscriptions, we aren't limited to just getting all the data ```{caption="Setting filters on a versioned subscription" .cs} store.Subscriptions.Create( - new SubscriptionCreationOptions - { - Name = subscriptionName, - Query = @" -from Customers (Revisions = true) -where Prervious.Name != Current.Name -" - } + new SubscriptionCreationOptions + { + Name = subscriptionName, + Query = @" + from Customers (Revisions = true) + where Prervious.Name != Current.Name + " + } ); ``` @@ -1065,19 +1059,18 @@ A natural extension of this behavior is to not send the full data to the client ```{caption="Getting changed names using versioned subscription" .cs} store.Subscriptions.Create( - new SubscriptionCreationOptions> - { - Name = subscriptionName, - Query = @" + new SubscriptionCreationOptions> + { + Name = subscriptionName, + Query = @" from Customers (Revisions = true) where Prervious.Name != Current.Name select { - OldName: this.Previous.Name, - NewName: this.Current.Name + OldName: this.Previous.Name, + NewName: this.Current.Name } " - } - } + } ); ``` @@ -1109,32 +1102,32 @@ a great scripting language, and one I'm very fond of.) from pyravendb import RavenDB def process_batch(batch): - - with batch.open_session() as session: - for item in batch.items: - customer = item.result - if customer.WelcomeEmailSent: - continue - - send_welcome_email(customer); - customer.WelcomeEmailSent = True - session.store(customer) + + with batch.open_session() as session: + for item in batch.items: + customer = item.result + if customer.WelcomeEmailSent: + continue + + send_welcome_email(customer); + customer.WelcomeEmailSent = True + session.store(customer) - session.save_changes() + session.save_changes() store = document_store ( - urls = ["http://rvn-srv-2:8080"], - database = "HelpDesk" - ) + urls = ["http://rvn-srv-2:8080"], + database = "HelpDesk" + ) store.initialize() with store.Subscriptions.GetSubscriptionWorker( - subscription_connection_options( - id = "new-customers-welcome-email" - )) as subscription: - - subscription.run(process_batch).join() + subscription_connection_options( + id = "new-customers-welcome-email" + )) as subscription: + + subscription.run(process_batch).join() ``` The code is quite similar to the way we would do things in C#. We open a document store, open the subscription and pass a method to process the document diff --git a/Ch06/CH06.md b/Ch06/CH06.md index 5eb67be6..f8118405 100644 --- a/Ch06/CH06.md +++ b/Ch06/CH06.md @@ -110,7 +110,7 @@ nodes. Let's see how it works in practice. To do this, you'll close any instance and open the command line terminal to the RavenDB folder. We want to reduce the amount of work we have to do per node, so open the `settings.json` file and ensure that it has the following properties: ``` - "Setup.Mode": "None", + "Setup.Mode": "None", "License.Eula.Accepted": true ``` @@ -397,9 +397,9 @@ var store = new DocumentStore { Urls = { - "http://128.0.01:8080","http://128.0.02:8080", - "http://128.0.03:8080","http://128.0.03:8080", - "http://128.0.05:8080" + "http://128.0.01:8080", "http://128.0.02:8080", + "http://128.0.03:8080", "http://128.0.03:8080", + "http://128.0.05:8080" }, Database = "Spade" }; @@ -491,7 +491,7 @@ using (var session = store.OpenSession()) }; session.Store(task); session.Advanced - .WaitForReplicationAfterSaveChanges(replicas: 1); + .WaitForReplicationAfterSaveChanges(replicas: 1); session.SaveChanges(); } ``` @@ -588,11 +588,11 @@ delightful surprise. ```{caption="The classic bank transaction example" .cs } using (var session = store.OpenSession()) { - var you = session.Load("accounts/1234-A"); - var me = session.Load("accounts/4321-C"); + var you = session.Load("accounts/1234-A"); + var me = session.Load("accounts/4321-C"); - you.Amount += 10; - me.Amount -= 10; + you.Amount += 10; + me.Amount -= 10; session.SaveChanges(); } @@ -623,11 +623,11 @@ the other database instances as a single transaction. There's one caveat, howeve ```{caption="The classic bank transaction example" .cs } using (var session = store.OpenSession()) { - var you = session.Load("accounts/1234-A"); - var tax = session.Load("accounts/48879-B"); + var you = session.Load("accounts/1234-A"); + var tax = session.Load("accounts/48879-B"); - you.Amount -= 3; - tax.Amount += 3; + you.Amount -= 3; + tax.Amount += 3; session.SaveChanges(); } @@ -839,24 +839,24 @@ var final = docs[0]; for (var i = 1; i < docs.length; i++) { - var currentCart = docs[i]; - for (var j = 0; j < currentCart.Items.length; j++) - { - var item = currentCart.Items[j]; - var match = final.Items - .find(i => i.ProductId == item.ProductId); - if (!match) - { - // not in cart, add - final.Items.push(item); - } - else - { - match.Quantity = Math.max( - item.Quantity, - match.Quantity); - } - } + var currentCart = docs[i]; + for (var j = 0; j < currentCart.Items.length; j++) + { + var item = currentCart.Items[j]; + var match = final.Items + .find(i => i.ProductId == item.ProductId); + if (!match) + { + // not in cart, add + final.Items.push(item); + } + else + { + match.Quantity = Math.max( + item.Quantity, + match.Quantity); + } + } } return final; @@ -869,24 +869,24 @@ bottles between the versions of the cart that we merged. ```{caption="The merged shopping cart" .js } { - "Name": "Oren Eini", - "Items": [ - { - "ProductId": "products/2134-B", - "Quantity": 2, - "Name": "Milk" - }, - { - "ProductId": "products/9231-C", - "Quantity": 1, - "Name": "Bread" - }, - { - "ProductId": "products/8412-B", - "Quantity": 2, - "Name": "Eggs" - } - ] + "Name": "Oren Eini", + "Items": [ + { + "ProductId": "products/2134-B", + "Quantity": 2, + "Name": "Milk" + }, + { + "ProductId": "products/9231-C", + "Quantity": 1, + "Name": "Bread" + }, + { + "ProductId": "products/8412-B", + "Quantity": 2, + "Name": "Eggs" + } + ] } ``` @@ -911,7 +911,7 @@ var cmd = new PutCompareExchangeValueOperation( var result = await store.Operations.SendAsync(cmd); if (result.Successful) { - // users/1 now owns the username 'john' + // users/1 now owns the username 'john' } ``` @@ -945,32 +945,32 @@ const string key = "engineers/available"; public void RegisterEngineerAvailability(string engineer) { - while (true) - { - var get = new GetCompareExchangeValueOperation>( - key); - var getResult = _store.Operations.Send(get); - if (getResult == null) + while (true) { - // empty, so create a new one - getResult = new CompareExchangeValue>( - key, - index: 0, // new - value: new List { engineer } + var get = new GetCompareExchangeValueOperation>( + key); + var getResult = _store.Operations.Send(get); + if (getResult == null) + { + // empty, so create a new one + getResult = new CompareExchangeValue>( + key, + index: 0, // new + value: new List { engineer } ); + } + getResult.Value.Add(engineer); + var put = new PutCompareExchangeValueOperation>( + key, + getResult.Value, + getResult.Index); + + var putResult = _store.Operations.Send(put); + if (putResult.Successful) + return; + + // someone pushed a new engineer, retry... } - getResult.Value.Add(engineer); - var put = new PutCompareExchangeValueOperation>( - key, - getResult.Value, - getResult.Index); - - var putResult = _store.Operations.Send(put); - if (putResult.Successful) - return; - - // someone pushed a new engineer, retry... - } } ``` @@ -995,45 +995,45 @@ const string key = "engineers/available"; public string PullAvailableEngineer() { - while (true) - { - var get = new GetCompareExchangeValueOperation>( - key); - var getResult = _store.Operations.Send(get); - if (getResult == null) + while (true) { - // no support engineers, wait and retry - Thread.Sleep(500); - continue; - } - string engineer = getResult.Value[0]; - getResult.Value.RemoveAt(0); - - CompareExchangeResult> result; - if (getResult.Value.Count == 0) - { - var del = - new DeleteCompareExchangeValueOperation>( - key, - getResult.Index); - result = _store.Operations.Send(del); - } - else - { - var put = - new PutCompareExchangeValueOperation>( - key, - getResult.Value, - getResult.Index); - result = _store.Operations.Send(put); + var get = new GetCompareExchangeValueOperation>( + key); + var getResult = _store.Operations.Send(get); + if (getResult == null) + { + // no support engineers, wait and retry + Thread.Sleep(500); + continue; + } + string engineer = getResult.Value[0]; + getResult.Value.RemoveAt(0); + + CompareExchangeResult> result; + if (getResult.Value.Count == 0) + { + var del = + new DeleteCompareExchangeValueOperation>( + key, + getResult.Index); + result = _store.Operations.Send(del); + } + else + { + var put = + new PutCompareExchangeValueOperation>( + key, + getResult.Value, + getResult.Index); + result = _store.Operations.Send(put); + } + + if (result.Successful) + return engineer; + + // someone took an available engineer + // while we were running, let's try again... } - - if (result.Successful) - return engineer; - - // someone took an available engineer - // while we were running, let's try again... - } } ``` diff --git a/Ch08/Ch08.md b/Ch08/Ch08.md index 3412638e..e4b5fd2c 100644 --- a/Ch08/Ch08.md +++ b/Ch08/Ch08.md @@ -130,7 +130,7 @@ However, when you provide your own script, you need to take responsibility for t ```{caption="Creating a subscription to process customers" .js} var managerName = null; -if(this.ReportsTo !== null) +if (this.ReportsTo !== null) { var manager = load(this.ReportsTo); managerName = manager.FirstName + " " + manager.LastName; @@ -167,16 +167,16 @@ Looking on the other side, you'll be able to see the replicated document, as sho ```{caption="ETL'ed document on the side" .json} { - "BornOn": 1966, - "Manager": "Steven Buchanan", - "Name": "Anne Dodsworth", - "Title": "Sales Representative", - "@metadata": { - "@collection": "Employees", - "@change-vector": "A:84-4Xmt8lVCrkiCDii/CfyaWQ", - "@id": "employees/9-A", - "@last-modified": "2017-12-04T12:02:53.8561852Z" - } + "BornOn": 1966, + "Manager": "Steven Buchanan", + "Name": "Anne Dodsworth", + "Title": "Sales Representative", + "@metadata": { + "@collection": "Employees", + "@change-vector": "A:84-4Xmt8lVCrkiCDii/CfyaWQ", + "@id": "employees/9-A", + "@last-modified": "2017-12-04T12:02:53.8561852Z" + } } ``` @@ -359,9 +359,9 @@ with the appropriate table. You can see the table creation script in Listing 8.4 ```{caption="Table creation script for the sample ETL process" .sql} CREATE TABLE Employees ( - DocumentId NVARCHAR(50) NOT NULL PRIMARY KEY, - Name NVARCHAR(50) NOT NULL, - Birthday DATETIME NOT NULL + DocumentId NVARCHAR(50) NOT NULL PRIMARY KEY, + Name NVARCHAR(50) NOT NULL, + Birthday DATETIME NOT NULL ) ``` @@ -413,20 +413,20 @@ represent that in the SQL destination. We'll start by first defining the tables ```{caption="Table creation script for multiple tables ETL process" .sql} CREATE TABLE Orders ( - OrderId NVARCHAR(50) NOT NULL PRIMARY KEY, - Company NVARCHAR(50) NOT NuLL, - OrderedAt DATETIME NOT NULL, - Total FLOAT NOT NULL, - ShipToCountry NVARCHAR(50) NOT NULL + OrderId NVARCHAR(50) NOT NULL PRIMARY KEY, + Company NVARCHAR(50) NOT NuLL, + OrderedAt DATETIME NOT NULL, + Total FLOAT NOT NULL, + ShipToCountry NVARCHAR(50) NOT NULL ) CREATE TABLE OrderLines ( - OrderLIneId int identity NOT NULL PRIMARY KEY NONCLUSTERED, - OrderId NVARCHAR(50) NOT NULL INDEX IX_OrderLines_OrderId CLUSTERED, - Price FLOAT NOT NULL, - Product NVARCHAR(50) NOT NULL, - Quantity INT NOT NULL + OrderLIneId int identity NOT NULL PRIMARY KEY NONCLUSTERED, + OrderId NVARCHAR(50) NOT NULL INDEX IX_OrderLines_OrderId CLUSTERED, + Price FLOAT NOT NULL, + Product NVARCHAR(50) NOT NULL, + Quantity INT NOT NULL ) ``` @@ -444,7 +444,7 @@ var total = 0; for (var i = 0; i < this.Lines.length; i++) { var line = this.Lines[i]; - total += line.PricePerUnit * line.Quantity * (1 - line.Discount); + total += line.PricePerUnit * line.Quantity * (1 - line.Discount); loadToOrderLines({ Quantity: line.Quantity, Product: line.Product, diff --git a/Ch09/Ch09.md b/Ch09/Ch09.md index 66976a90..06ad616f 100644 --- a/Ch09/Ch09.md +++ b/Ch09/Ch09.md @@ -155,8 +155,8 @@ Queries can also use more then a single field, as you can see in Listing 9.2. ```{caption="Querying over several fields at the same time" .sql} from Employees -where (FirstName = 'Andrew' or LastName = 'Callahan') -and Address.Country = 'USA' +where (FirstName = 'Andrew' or LastName = 'Callahan') +and Address.Country = 'USA' ``` Using the sample data set, this should give two results, as shown in Figure 9.3. In that figure, you can also see some of the options available to inspect @@ -553,14 +553,14 @@ from Orders as o select { TopProducts: o.Lines .sort((a, b) => - (b.PricePerUnit * b.Quantity) - - (a.PricePerUnit * a.Quantity) + (b.PricePerUnit * b.Quantity) - + (a.PricePerUnit * a.Quantity) ) - .map(x=>x.ProductName) + .map(x => x.ProductName) .slice(0,2), Total: o.Lines.reduce( - (acc, l) => acc += l.PricePerUnit * l.Quantity, - 0) + (acc, l) => acc += l.PricePerUnit * l.Quantity, + 0) } ``` @@ -597,20 +597,20 @@ Listing 9.13 shows how we can use the function declaration to properly compute t ```{caption="Using functions to consolidate logic" .sql} declare function lineItemPrice(l) { return l.PricePerUnit * - l.Quantity * - (1 - l.Discount); + l.Quantity * + (1 - l.Discount); } from Orders as o select { TopProducts: o.Lines .sort((a, b) => - lineItemPrice(b) - - lineItemPrice(a) ) - .map( x => x.ProductName) + lineItemPrice(b) - + lineItemPrice(a) ) + .map(x => x.ProductName) .slice(0,2), Total: o.Lines.reduce((acc, l) => - acc + lineItemPrice(l), - 0) + acc + lineItemPrice(l), + 0) } ``` @@ -775,7 +775,7 @@ RQL supports the `order by` clause, which allows you to dictate how results are ```{caption="Sorting by multiple fields in RQL" .sql} from Employees -where Address .Country = 'USA' +where Address.Country = 'USA' order by FirstName asc, LastName asc ``` @@ -784,7 +784,7 @@ the query in Listing 9.19. ```{caption="Sorting by multiple fields in RQL with different orders" .sql} from Employees -where Address .Country = 'USA' +where Address.Country = 'USA' order by LastName asc, FirstName asc ``` @@ -881,9 +881,9 @@ from Employees where spatial.within( spatial.point(Address.Location.Latitude, Address.Location.Longitude), spatial.wkt("POLYGON((-0.38726806640625 51.72477396651261,0.1483154296875 51.67881439742299,0.2911376953125 51.579928527080114,0.2581787109375 - 51.439756376733676,0.1483154296875 51.347212267024645,0.1483154296875 51.288847685894844,-0.1153564453125 51.25448088572911,-0.4669189453125 - 51.3094554292733,-0.560302734375 51.41578143396663,-0.494384765625 51.494509016952534,-0.538330078125 51.61064031418932,-0.490264892578125 - 51.677111294565,-0.38726806640625 51.72477396651261))") + 51.439756376733676,0.1483154296875 51.347212267024645,0.1483154296875 51.288847685894844,-0.1153564453125 51.25448088572911,-0.4669189453125 + 51.3094554292733,-0.560302734375 51.41578143396663,-0.494384765625 51.494509016952534,-0.538330078125 51.61064031418932,-0.490264892578125 + 51.677111294565,-0.38726806640625 51.72477396651261))") ) ``` diff --git a/Ch10/Ch10.md b/Ch10/Ch10.md index 451d812c..3d674b9c 100644 --- a/Ch10/Ch10.md +++ b/Ch10/Ch10.md @@ -166,7 +166,7 @@ from order in docs.Orders select new { order.Employee, order.Company, - Total = order.Lines.Sum(l=>(l.Quantity * l.PricePerUnit) * ( 1 - l.Discount)) + Total = order.Lines.Sum(l => (l.Quantity * l.PricePerUnit) * ( 1 - l.Discount)) } ``` @@ -240,12 +240,12 @@ For example, let's assume we have the JSON document in Listing 10.8 and we query ```{caption="Sample document that is about to be indexed" .json} { - 'Name': 'Arava Eini', - 'Nick': 'Dawg', - '@metadata': { - '@id': 'dogs/1', + 'Name': 'Arava Eini', + 'Nick': 'Dawg', + '@metadata': { + '@id': 'dogs/1', '@collection': 'Dogs' - } + } } ``` What will happen is that RavenDB will produce an index entry from this document that @@ -253,11 +253,11 @@ will have the structure `{'Name': 'Arava Eini'}` and will mark the `Name` field additional processing, and the actual terms that will be indexed are shown in Listing 10.9. ```{caption="Index terms after the analyzing for full text search" .json} -index = { - 'Name': { - 'arava': ['dogs/1'], - 'eini': ['dogs/1'] - } +index = { + 'Name': { + 'arava': ['dogs/1'], + 'eini': ['dogs/1'] + } } ``` @@ -697,7 +697,7 @@ We'll start by defining the `Orders/Search` index, as shown in Listing 10.18. from o in docs.Orders select new{ Address = new[]{o.ShipTo.City, o.ShipTo.Country}, - Products = o.Lines.Select(x=>x.Product) + Products = o.Lines.Select(x => x.Product) } ``` diff --git a/Ch11/Ch11.md b/Ch11/Ch11.md index 303dc259..40b7341f 100644 --- a/Ch11/Ch11.md +++ b/Ch11/Ch11.md @@ -25,8 +25,8 @@ from Orders as o group by o.Company where o.Company = 'companies/1-A' select count() as NumberOfOrders, - sum(o.Lines[].Quantity) as ItemsPurchased, - o.Company + sum(o.Lines[].Quantity) as ItemsPurchased, + o.Company ``` The query in Listing 11.1 should be familiar to anyone who's used SQL before. Now, let's analyze what RavenDB must do in @@ -152,7 +152,7 @@ takes us. Go to `Documents` and then `Patch` and run the update script in Listin ```{caption="Increase the number of Orders documents by a hundred" .sql} from Orders update { - for(var i = 0; i < 100; i++ ){ + for (var i = 0; i < 100; i++ ){ put("orders/", this); } } @@ -230,8 +230,8 @@ group r by r.Product into g select new { Product = g.Key, - Quantity = g.Sum(x=>x.Quantity), - Total = g.Sum(x=>x.Total) + Quantity = g.Sum(x => x.Quantity), + Total = g.Sum(x => x.Total) } ``` @@ -312,11 +312,11 @@ group r by r.Product into g select new { Product = g.Key, - Quantity = g.Sum(x=>x.Quantity), - Count = g.Sum(x=>x.Count), - Total = g.Sum(x=>x.Total), - Average = g.Average(x=>x.Total), - Debug = g.Select(x=>x.Total).ToArray() + Quantity = g.Sum(x => x.Quantity), + Count = g.Sum(x => x.Count), + Total = g.Sum(x => x.Total), + Average = g.Average(x => x.Total), + Debug = g.Select(x => x.Total).ToArray() } ``` @@ -353,13 +353,13 @@ as we are reducing the data and compute the `Average` using these. You can see i // reduce from r in results group r by r.Product into g -let qty = g.Sum(x=>x.Quantity) -let total = g.Sum(x=>x.Total) +let qty = g.Sum(x => x.Quantity) +let total = g.Sum(x => x.Total) select new { Product = g.Key, Quantity = qty, - Count = g.Sum(x=>x.Count), + Count = g.Sum(x => x.Count), Total = total, Average = total / qty } @@ -391,7 +391,7 @@ from l in o.Lines select new { o.Company, - Products = new []{ new { l.Product, l.Quantity } }, + Products = new [] { new { l.Product, l.Quantity } }, Total = l.Quantity } @@ -401,14 +401,14 @@ group r by r.Company into g select new { Company = g.Key, - Products = g.SelectMany(x=>x.Products) - .GroupBy(x=>x.Product) + Products = g.SelectMany(x => x.Products) + .GroupBy(x => x.Product) .Select(p => new { Product = p.Key, - Quantity = p.Sum(x=>x.Quantity) + Quantity = p.Sum(x => x.Quantity) }), - Total = g.Sum(x=>x.Total) + Total = g.Sum(x => x.Total) } ``` @@ -432,7 +432,7 @@ numbers of items purchased for each product, and this mechanism allows us to agg > > Let's look at an example where the amount of information that passed through `reduce` doesn't go down and discuss its > implications. In the `Companies/Purchases` index, if the `Products` field was defined just as -> `Products = g.SelectMany(x=>x.Products)`, we'll have a problem. Calling `reduce` won't actually reduce the amount of data +> `Products = g.SelectMany(x => x.Products)`, we'll have a problem. Calling `reduce` won't actually reduce the amount of data > we're working with and, as time goes by, the `reduce` function will have to operate on larger and larger values. > > This isn't an efficient way to do things, and since we need to keep track of intermediate values, it will lead to a marked @@ -458,7 +458,7 @@ way to get more information at very little cost. ```{caption="Using functions to enrich the results from a MapReduce index" .sql} declare function addProductName(result){ - for(var i = 0; i < result.Products.length; i++){ + for (var i = 0; i < result.Products.length; i++){ var p = load(result.Products[i].Product); result.Products[i].ProductName = p.Name; } @@ -542,7 +542,7 @@ select new { g.Key.Date, g.Key.Product, - Count = g.Sum(x=>x.Count) + Count = g.Sum(x => x.Count) } ``` @@ -608,7 +608,7 @@ select new g.Key.Year, g.Key.Month, g.Key.Product, - Count = g.Sum(x=>x.Count) + Count = g.Sum(x => x.Count) } ``` @@ -708,9 +708,9 @@ into g select new { City = g.Key, - Companies = g.Sum(x=>x.Companies), - Suppliers = g.Sum(x=>x.Suppliers), - Employees = g.Sum(x=>x.Employees), + Companies = g.Sum(x => x.Companies), + Suppliers = g.Sum(x => x.Suppliers), + Employees = g.Sum(x => x.Employees), } ``` @@ -745,7 +745,7 @@ by also having it operate on orders. > it _begins_. We'll first add another entry for each of the existing maps `OrderTotal = 0`. And we'll add the same logic for the reduce -function: `OrderTotal = g.Sum(x=>x.OrderTotal)`. The first step is required because all the maps and reduces in an index +function: `OrderTotal = g.Sum(x => x.OrderTotal)`. The first step is required because all the maps and reduces in an index must have the same output. The second is required to actually sum up the information we'll shortly add. Now click on the `Add map` button on the index edit page and add the map shown in Listing 11.15. @@ -913,7 +913,7 @@ select new g.Key.Supplier, g.Key.Month, g.Key.Year, - Total = g.Sum(x=>x.Total) + Total = g.Sum(x => x.Total) } ``` @@ -927,7 +927,7 @@ A sample query for this index can be seen in Listing 11.19. ```{caption="This query can be used to render the results in Figure 11.16." .sql} from index 'Sales/ByCityAndSupplier' as t -where t.City= 'London' and t.Month = 2 and t.Year = 1997 +where t.City = 'London' and t.Month = 2 and t.Year = 1997 order by t.Total as double desc load t.Supplier as s select s.Name, t.City, t.Supplier , t.Month, t.Year, t.Total diff --git a/Ch12/Ch12.md b/Ch12/Ch12.md index a0f0c26b..76781be2 100644 --- a/Ch12/Ch12.md +++ b/Ch12/Ch12.md @@ -54,34 +54,34 @@ using Orders; namespace Northwind { - class Program - { - static void Main(string[] args) + class Program { - var store = new DocumentStore - { - Urls = new [] + static void Main(string[] args) { - "http://localhost:8080" - }, - Database = "Northwind" - }; - store.Initialize(); - - using(var session = store.OpenSession()) - { - var londonEmployees = - from emp in session.Query() - where emp.Address.City == "London" - select emp; - - foreach(var emp in londonEmployees) - { - Console.WriteLine(emp.FirstName); + var store = new DocumentStore + { + Urls = new [] + { + "http://localhost:8080" + }, + Database = "Northwind" + }; + store.Initialize(); + + using (var session = store.OpenSession()) + { + var londonEmployees = + from emp in session.Query() + where emp.Address.City == "London" + select emp; + + foreach (var emp in londonEmployees) + { + Console.WriteLine(emp.FirstName); + } + } } - } } - } } ``` @@ -95,13 +95,13 @@ the query. How can we select the index we want to use for a query from the clien ```{caption="Specifying the index to use for a query (using strings)" .cs} var ordersForEmployee1A = - from order in session.Query("Orders/Totals") - where order.Employee == "employees/1-A" - select order; + from order in session.Query("Orders/Totals") + where order.Employee == "employees/1-A" + select order; -foreach(var order in ordersForEmployee1A) +foreach (var order in ordersForEmployee1A) { - Console.WriteLine(order.Id); + Console.WriteLine(order.Id); } ``` @@ -117,18 +117,19 @@ in Listing 12.4. (You'll need to add a `using Raven.Client.Documents.Indexes;` t ```{caption="The index is defined using a strongly typed class" .cs} public class My_Orders_Totals - : AbstractIndexCreationTask + : AbstractIndexCreationTask { public My_Orders_Totals() { - Map = orders => - from o in orders - select new - { - o.Employee, - o.Company, - Total = o.Lines.Sum(l => (l.Quantity * l.PricePerUnit) * (1 - l.Discount)) - }; + Map = orders => + from o in orders + select new + { + o.Employee, + o.Company, + Total = o.Lines.Sum(l => + (l.Quantity * l.PricePerUnit) * (1 - l.Discount)) + }; } } ``` @@ -175,9 +176,9 @@ see in Listing 12.6. ```{caption="Specifying the index to use for a query (strongly typed)" .cs} var ordersForEmployee1A = - from order in session.Query() - where order.Employee == "employees/1-A" - select order; + from order in session.Query() + where order.Employee == "employees/1-A" + select order; ``` The second generic parameter to `Query` is the index we want to use, and the first one is the item we're querying on. @@ -233,24 +234,24 @@ We need to introduce a type, just for querying, to satisfy the compiler. An exam ```{caption="Using a dedicate type for strongly typed queries" .cs} public class My_Orders_Totals : - AbstractIndexCreationTask + AbstractIndexCreationTask { - public class Result - { - public string Employee; - public string Company; - public double Total; - } - - // class constructor shown in Listing 12.4 + public class Result + { + public string Employee; + public string Company; + public double Total; + } + + // class constructor shown in Listing 12.4 } var bigOrdersForEmployee1A = ( - from o in session.Query() - where o.Employee == "employees/1-A" && - o.Total > 1000 - select o + from o in session.Query() + where o.Employee == "employees/1-A" && + o.Total > 1000 + select o ).OfType().ToList(); ``` @@ -269,21 +270,21 @@ or to select the sort order for the results, as you can see in Listing 12.9. ```{caption="Adding projection and sorting after calling OfType" .cs} var bigOrdersForEmployee1A = ( - from o in session.Query() - where o.Employee == "employees/1-A" && - o.Total > 1000 - orderby o.Total descending - select o + from o in session.Query() + where o.Employee == "employees/1-A" && + o.Total > 1000 + orderby o.Total descending + select o ).OfType(); var results = - from o in bigOrdersForEmployee1A - orderby o.Employee - select new - { - o.Company, - Total = o.Lines.Sum(x=>x.Quantity) - }; + from o in bigOrdersForEmployee1A + orderby o.Employee + select new + { + o.Company, + Total = o.Lines.Sum(x => x.Quantity) + }; ``` The RQL generated by the query in Listing 12.9 is shown in Listing 12.10. @@ -293,9 +294,9 @@ from index 'My/Orders/Totals' as o where o.Employee = $p0 and o.Total > $p1 order by Total as double desc, Employee select { - Company : o.Company, - Total : o.Lines.map(function(x){return x.Quantity;}) - .reduce(function(a, b) { return a + b; }, 0) + Company : o.Company, + Total : o.Lines.map(function(x) { return x.Quantity; }) + .reduce(function(a, b) { return a + b; }, 0) } ``` @@ -320,38 +321,38 @@ the second type as a generic argument to the index. Listing 12.11 shows such a M ```{caption="Defining a map-reduce index from code code" .cs} public class My_Products_Sales : - AbstractIndexCreationTask + AbstractIndexCreationTask { - public class Result - { - public string Product; - public int Count; - public double Total; - } - - public My_Products_Sales() - { - Map = orders => - from order in orders - from line in order.Lines - select new - { - Product = line.Product, - Count = 1, - Total = (line.Quantity * line.PricePerUnit) - }; - - Reduce = results => - from result in results - group result by result.Product - into g - select new - { - Product = g.Key, - Count = g.Sum(x=>x.Count), - Total = g.Sum(x=>x.Total) - } - } + public class Result + { + public string Product; + public int Count; + public double Total; + } + + public My_Products_Sales() + { + Map = orders => + from order in orders + from line in order.Lines + select new + { + Product = line.Product, + Count = 1, + Total = (line.Quantity * line.PricePerUnit) + }; + + Reduce = results => + from result in results + group result by result.Product + into g + select new + { + Product = g.Key, + Count = g.Sum(x => x.Count), + Total = g.Sum(x => x.Total) + } + } } ``` @@ -379,9 +380,9 @@ pretty much the same as we've already seen. Listing 12.12 has the full details. ```{caption="Querying a map-reduce index using Linq"} var salesSummaryForProduct1A = - from s in session.Query() - where s.Product == "products/1-A" - select s; + from s in session.Query() + where s.Product == "products/1-A" + select s; ``` The `Query` method in Listing 12.12 takes two generic parameters. The first is the type of the query—in this case, the `Result` @@ -411,7 +412,7 @@ to use the appropriate base class, `AbstractMultiMapIndexCreationTask`, as yo ```{caption="Defining a multimap index from the client side" .cs} public class People_Search : - AbstractMultiMapIndexCreationTask + AbstractMultiMapIndexCreationTask { public class Result { @@ -467,23 +468,23 @@ on. Luckily for us, we already defined that shape: the `People_Search.Result` ne ```{caption="Querying a multimap index with heterogeneous results from the client" .cs} var results = session.Query() - .Where(item => item.Name.StartsWith("Mar")) - .OfType(); + .Where(item => item.Name.StartsWith("Mar")) + .OfType(); -foreach(var result in results) +foreach (var result in results) { - switch(result) - { - case Employee e: - RenderEmployee(e); - break; - case Supplier s: - RenderSupplier(s); - break; - case Company c: - RenderCompany(c); - break; - } + switch(result) + { + case Employee e: + RenderEmployee(e); + break; + case Supplier s: + RenderSupplier(s); + break; + case Company c: + RenderCompany(c); + break; + } } ``` @@ -531,8 +532,8 @@ we'll use in this section will use the `SearchResult` class defined in Listing 1 ```{caption="A simple data class to hold the results of queries" .cs} public class SearchResult { - public string ContactName; - public string Collection; + public string ContactName; + public string Collection; } ``` @@ -541,16 +542,16 @@ we used in Listing 10.13 a few chapters ago. ```{caption="Querying using raw RQL" .cs} List results = session.Advanced - .RawQuery(@" - from index 'People/Search' as p - where StartsWith(Name, $name) - select - { - Collection: p['@metadata']['@collection'], - ContactName: ( - p.Contact || { Name: p.FirstName + ' ' + p.LastName } - ).Name - } + .RawQuery(@" + from index 'People/Search' as p + where StartsWith(Name, $name) + select + { + Collection: p['@metadata']['@collection'], + ContactName: ( + p.Contact || { Name: p.FirstName + ' ' + p.LastName } + ).Name + } ") .AddParameter("$name", "Mar") .ToList(); @@ -588,15 +589,15 @@ the Linq query. ```{caption="Using Linq queries with a bit of RQL sprinkled in" .cs} List results = - from item in session.Query() - where item.Name.StartsWith("Mar")) - select new SearchResult - { - Collection = RavenQuery.Raw("item['@metadata']['@collection']"), - ContactName = RavenQuery.Raw(@"( - item.Contact || { Name: item.FirstName + ' ' + item.LastName } - ).Name") - } + from item in session.Query() + where item.Name.StartsWith("Mar")) + select new SearchResult + { + Collection = RavenQuery.Raw("item['@metadata']['@collection']"), + ContactName = RavenQuery.Raw(@"( + item.Contact || { Name: item.FirstName + ' ' + item.LastName } + ).Name") + } ``` Listing 12.17 isn't a representative example, mostly because it's probably easier to write it as a @@ -610,17 +611,17 @@ This is easy to do, as you can see in Listing 12.18. ```{caption="Using a custom projection with the `DocumentQuery` API" .cs} List results = - session.Advanced.DocumentQuery() - .WhereStartsWith(x=>x.Name, "Mar") - .SelectFields(QueryData.CustomFunction( - alias: "item", - func: @"{ - Collection: p['@metadata']['@collection'], - ContactName: ( - p.Contact || { Name: p.FirstName + ' ' + p.LastName } - ).Name - }") - ).ToList(); + session.Advanced.DocumentQuery() + .WhereStartsWith(x => x.Name, "Mar") + .SelectFields(QueryData.CustomFunction( + alias: "item", + func: @"{ + Collection: p['@metadata']['@collection'], + ContactName: ( + p.Contact || { Name: p.FirstName + ' ' + p.LastName } + ).Name + }") + ).ToList(); ``` The queries in Listing 12.16, 12.17 and 12.18 produce the exact same query and the same results, so @@ -657,11 +658,11 @@ public class People_Search : { public People_Search() { - // AddMap & Reduce calls from Listing 12.14 - // removed for brevity + // AddMap & Reduce calls from Listing 12.14 + // removed for brevity - Index(x => x.Name, FieldIndexing.Search); - Suggestion(x=>x.Content); + Index(x => x.Name, FieldIndexing.Search); + Suggestion(x => x.Content); } } ``` @@ -677,29 +678,29 @@ field should have suggestions applied to it. > creation direction, such as with the following code: > > ``` -> public class People_Search : AbstractIndexCreationTask +> public class People_Search : AbstractIndexCreationTask > { -> public override IndexDefinition CreateIndexDefinition() -> { -> return new IndexDefinition() -> { -> Maps = { -> @"from e in docs.Employees select new { -> Name = e.FirstName + ' ' + e.LastName -> }", -> @"from c in docs.Companies select new { -> c.Contact.Name -> }",@"from s in docs.Suppliers select new { -> s.Contact.Name -> }" -> }, -> Fields = { -> ["Name"] = new IndexFieldOptions { -> Indexing = FieldIndexing.Search -> } -> } -> }; -> } +> public override IndexDefinition CreateIndexDefinition() +> { +> return new IndexDefinition() +> { +> Maps = { +> @"from e in docs.Employees select new { +> Name = e.FirstName + ' ' + e.LastName +> }", +> @"from c in docs.Companies select new { +> c.Contact.Name +> }",@"from s in docs.Suppliers select new { +> s.Contact.Name +> }" +> }, +> Fields = { +> ["Name"] = new IndexFieldOptions { +> Indexing = FieldIndexing.Search +> } +> } +> }; +> } > } > ``` > @@ -723,59 +724,59 @@ is pretty straightforward, given what we've done so far. We need to define an in public class Cities_Details : AbstractMultiMapIndexCreationTask { - public class Result - { - public string City; - public int Companies, Employees, Suppliers; - } - - public Cities_Details() - { - AddMap(emps => - from e in emps - select new Result - { - City = e.Address.City, - Companies = 0, - Suppliers = 0, - Employees = 1 - } - ); - - AddMap(companies => - from c in companies - select new Result - { - City = c.Address.City, - Companies = 1, - Suppliers = 0, - Employees = 0 - } - ); + public class Result + { + public string City; + public int Companies, Employees, Suppliers; + } + + public Cities_Details() + { + AddMap(emps => + from e in emps + select new Result + { + City = e.Address.City, + Companies = 0, + Suppliers = 0, + Employees = 1 + } + ); + + AddMap(companies => + from c in companies + select new Result + { + City = c.Address.City, + Companies = 1, + Suppliers = 0, + Employees = 0 + } + ); + + AddMap(suppliers => + from s in suppliers + select new Result + { + City = s.Address.City, + Companies = 0, + Suppliers = 1, + Employees = 0 + } + ); - AddMap(suppliers => - from s in suppliers + Reduce = results => + from result in results + group result by result.City + into g select new Result { - City = s.Address.City, - Companies = 0, - Suppliers = 1, - Employees = 0 + City = g.Key, + Companies = g.Sum(x => x.Companies), + Suppliers = g.Sum(x => x.Suppliers), + Employees = g.Sum(x => x.Employees) } - ); - - Reduce = results => - from result in results - group result by result.City - into g - select new Result - { - City = g.Key, - Companies = g.Sum(x=>x.Companies), - Suppliers = g.Sum(x=>x.Suppliers), - Employees = g.Sum(x=>x.Employees) - } - } + } } ``` @@ -1065,15 +1066,15 @@ public class Employees_Managers { public Employees_Managers() { - Map = emps => - from e in emps - let manager = LoadDocument(e.ReportsTo) - select new - { - Name = e.FirstName + " " + e.LastName, - HasManager = manager != null, - Manager = manager.FirstName + " " + manager.LastName - }; + Map = emps => + from e in emps + let manager = LoadDocument(e.ReportsTo) + select new + { + Name = e.FirstName + " " + e.LastName, + HasManager = manager != null, + Manager = manager.FirstName + " " + manager.LastName + }; } } ``` @@ -1099,13 +1100,13 @@ public class Employees_PostalCode { public Employees_PostalCode() { - Map = emps => - from e in emps - select new - { - Name = e.FirstName + " " + e.LastName, - Postal = int.Parse(e.Address.PostalCode) - }; + Map = emps => + from e in emps + select new + { + Name = e.FirstName + " " + e.LastName, + Postal = int.Parse(e.Address.PostalCode) + }; } } ``` diff --git a/Ch13/Ch13.md b/Ch13/Ch13.md index 294da512..0afa53d5 100644 --- a/Ch13/Ch13.md +++ b/Ch13/Ch13.md @@ -656,8 +656,8 @@ You can see an example of how this can be done in Listing 13.1. ```{caption="Partial configuration of shelling out to a user defined method for obtaining the certificate" .json} { - "ServerUrl": "https://0.0.0.0", - "PublicServerUrl": "https://a.raven.development.run", + "ServerUrl": "https://0.0.0.0", + "PublicServerUrl": "https://a.raven.development.run", "Security.Certificate.Exec": "powershell", "Security.Certificate.Exec.Arguments": "get-cert-by-id.ps1 90F4BC16CA5E5CB535A6CD8DD78CBD3E88FC6FEA" } @@ -676,7 +676,7 @@ try $exportedCertBinary = $cert.Export("Pfx") $stdout = [System.Console]::OpenStandardOutput() $stdout.Write($exportedCertBinary, 0, - $exportedCertBinary.Length) + $exportedCertBinary.Length) } catch { diff --git a/Ch18/Ch18.md b/Ch18/Ch18.md index a98f74e6..40a3d9aa 100644 --- a/Ch18/Ch18.md +++ b/Ch18/Ch18.md @@ -254,8 +254,8 @@ Console, you'll need to execute the command shown in Listing 18.2 (remember to u ```{caption="Emergency cluster secession in favor of a paritcular cluster topology" .js} server.ServerStore.Engine.HardResetToPassive( - // the topology id from Listing 18.1 goes here - "xxxxxxxx-c4d2-494e-bc36-02274ccc6e4c" + // the topology id from Listing 18.1 goes here + "xxxxxxxx-c4d2-494e-bc36-02274ccc6e4c" ); ```