Lucene Boost with LINQ in Sitecore 7 ContentSearch

Finally figured out how to boost certain fields using Sitecore 7’s ContentSearch LINQ provider. Boosting provides relevance enhancements when a search term is found within a field. For example, a search term found in a documents title or keywords field is probably way more relevant than if its found in the body.

You may have stumbled across the Boost extension method but couldn’t get it to work.

The issue is that the Boost extension method does not work with the == operator: you have to use a method like .Equals or .Matches. (I think I’ll throw up another post with the other provided LINQ operators).

So if you have a document type like BlogSearchResultItem:

BlogSearchResultItem.cs
using Sitecore.ContentSearch; using Sitecore.ContentSearch.SearchTypes; public class BlogSearchResultItem : SearchResultItem { [IndexField("title")] public string Title { get; set; } [IndexField("keywords")] public string Keywords { get; set; } [IndexField("body")] public string Body { get; set; } }

You can boost the search term using Boost:

Search.cs
public SearchResults<BlogSearchResultItem> Search(string term) { using (var context = ContentSearchManager.GetIndex("blog_posts").CreateSearchContext()) { var query = context.GetQueryable<BlogSearchResultItem>(); return query.Where(b => b.Title.Equals("test").Boost(2.0f) || b.Keywords.Equals("test").Boost(1.5f) || b.Body == "test").GetResults(); } }

This should yield the following Lucene query:

Search.log.txt
title:test^2.0 keywords:test^1.5 body:test

Be Careful Subclassing DbContext

i.e. Why doesn’t Entity Framework find my connection string?

Remember that if you subclass from a DbContext, the default constructor takes the name of the class as the connection string name.

Your normal WidgetContext is working just fine: it finds a connection string called WidgetContext in the web.config and loads it right on up.

But then you start adding some instrumentation, and you decide to create a customized TracingWidgetContext that inherits from WidgetContext. Suddenly it starts trying to create a new database on your local SQLServer instance.

What just happened, I don’t even…

The default, parameter-less DbContext constructor takes the class name as the expected connection string name. In this case, the subclass looked for a TracingWidgetContext connection string, and not finding one, it just connects to your local SQLServer. This fall back behavior is probably not expected, and personally I’d prefer it to just throw.

Anyway, the solution is to pass the name of the desired connection string in the constructor:

TracingWidgetContext.cs
class TracingWidgetContext : WidgetContext { public TracingWidgetContext() : base("WidgetContext") { } }

If you can’t do that, because you’re WidgetContext doesn’t expose that overloaded constructor, well you should fix it.

But if you can’t you can hack at it and manually set the connection string:

TracingWidgetContext.cs
class TracingWidgetContext : WidgetContext { public TracingWidgetContext() { // Here be dragons this.Database.Connection.ConnectionString = ConfigurationManager.ConnectionStrings["WidgetContext"].ConnectionString; } }

3 Things I Learned This Week 2015-02-15

Nimbletext is neat

It’s kind of a text transform and templating tool. You can enter text in one pane and apply a transformation on it. An example application is generating INotifyPropertyChanged properties. These are tedious to right manually even if you are using a framework that provides some shortcut and utility functions.

Check it out at nimbletext.com.

Wifi Interference is Horrible

So after ranting and complaining on twitter about my internet speed with Comcast, I finally got on a live chat with their tech support looking for answers. The tech asked if I was on wireless or a wired connection. So while he was sending a “reset signal” to my router, I ran upstairs and got a Ethernet patch cable and connected the laptop directly to the router. Suddenly my speeds were far more than the 50Mbps I was paying for.

After some research I determined that my problem is wireless interference. My neighborhood is all 4-unit duplexes: so each building has found individual wireless networks. Switching devices to a network in the 5GHz band greatly increased my speeds.

Sorry Comcast, while there are still plenty of reasons to be upset with you, this turns out to not be one of them.

git merge-base

git merge-base finds a common ancestor between two branches. Useful for some merge operations or tricky rebases.

3 Things I Learned This Week 2015-02-06

stree command in cygwin

Sometimes you just wanna launch SourceTree from your current cygwin command prompt.

No reason you can’t.

Accessing the clipboard on cygwin

Too much cygwin this week, I know. But I found out you can access the clipboard through the magic /dev/clipboard pseudo-file. Just cat stuff in to copy, and cat stuff out to paste.

You can set up some OSX-like pbcopy and pbpaste commands too. See an example at Command line clipboard in OSX and Cygwin

Visual Studio is a pain sometimes

VS2013 has forgotten how to let me set the fonts and colors for the code editor. The option just doesn’t appear. There are a few StackOverflow posts about it, but no working resolution for me. I think I’ve reinstalled a dozen times over the last couple weeks.

I just can’t live with Consolas.

Oh, and WPF development crashes my whole machine. Can’t begin to imagine whats going on there.

Open Repository in SourceTree from Cygwin

SourceTree is my go-to Windows git client. It has pretty good UI and exposes most of the more advanced features that I need (git add -p being chief among them).

But I still spend a lot of time in the command line via Cygwin and want the ability to open SourceTree to whatever repo I happen to be in at the time.

I don’t like the manual process of loading up SourceTree and adding a new repository bookmark. A lot of times, I don’t even care to book mark the repo: I just want to pull it up and look at some diffs or quickly add parts of a file.

Luckily, SourceTree added a few command line arguments a few versions back that allow you to open it right to a repository.

It’s a little clunky in cygwin, like most things, but with a small script it becomes very accessible.

~/bin/stree
#!/bin/bash # Open SourceTree to the file status window for the repo # in the provided directory. # # stree [path] # # if no path is provided, the current working directory is used # Cygwin path to the source tree executable SOURCE_TREE="/cygdrive/c/Program Files (x86)/Atlassian/SourceTree/SourceTree.exe" # Determine the path. If a path is provided, convert it to # (w)indows (a)bsolute path with cygpath. Otherwise, convert # the current directory if [ "$1" != "" ]; then cwd=$(cygpath -wa $1) else cwd=$(cygpath -wa $PWD) fi # Override the HOME environment variable to be the windows # HOME instead of cygwin's $HOME. Then execute source tree # See https://blog.sourcetreeapp.com/2014/01/29/sourcetree-for-windows-1-4-released/ # for options HOME="$HOMEPATH" cygstart -v "$SOURCE_TREE" -f $cwd status

There’s a couple of tricks involved

  1. Use cygpath -wa to convert the Unix file paths for the directory to the absolute windows path. So ~/repos/test/ will become C:\cygwin64\home\<username>\repos\test\.
  2. Override the $HOME environment variable. Cygwin will be default pass the full windows path to ~ as the new HOME environment variable to Source Tree. This caused problems for me when SourceTree in turn shelled out to git and git complained about the .gitconfig. I fixed it by making sure SourceTree used C:\users\<username> as its $HOME instead of C:\cygwin64\home\<username>.

3 Things I Learned This Week 2015-01-30

HTML Entity Decode in Javascript

Apparently javascript doesn’t have an out of the box function to decode html entities. But you can do it by forcing setting the innerHTML of a disconnected DOM element to your string, and then reading it back out. Quite a hack!

html-entity-decode.js
function htmlDecode(s) { var textarea = document.createElement("textarea"); textarea.innerHTML = s; return textarea.value; }

Or if you have jQuery its a one-liner:

html-entity-decode-jquery.js
function htmlDecode(s) { return $("<textarea/>").html(s).val(); }

Note that you have to use a text area or you open up some XSS vulnerabilities. Most browsers will start downloading assets and scripts even before the disconnected DOM element is added to the document.

BlockingCollection is nice for .NET producer-consumer contexts.

I’m building a tool that could benefit from a publish-subscribe threading model. BlockingCollection is one nice trick for that. It provides an IEnumerable interface that you can use as a task queue.

Threads will have their foreach block until items are added to the queue. The items will be yielded to only one of the waiting threads. You can mark the queue as complete and all the waiting threads will stop enumerating gracefully.

See Justin Etheredge’s BlockingCollection and IProducerConsumerCollection and especially note the first comment.

Building ASP.NET HttpModules

I had to create an HttpModule so we could check the status of something on each request. You just implement IHttpModule, wire up whatever events you want, and do your work. Register it in the web.config httpModules section and you’re good to go.

3 Things I Learned This Week 2015-01-23

You can use jQuery to create events on plain objects

There are some caveats, but you can totally use jQuery to create events on plain objects.

Firefox Developer Edition shows event bindings right in the DOM element list

The little ev icon indicates javascript events are bound to the element.

When viewing the DOM in the elements panel, any element with an event bound to it shows a little ev icon. Hover on the item, and it shows you the functions that handle the event. Chrome also has this feature, but its buried in another tab on the sidebar.

The really cool thing, that even Chrome doesn’t yet have, is that it shows your code instead of just the jQuery event. In chrome, all you see is some minified jQuery source which doesn’t tell you very much. It does confirm there is an event bound, but you probably already knew that.

ASP.NET RenderPartial with a null model unexpectedly passes the parent view’s model

I got bit by this in some code that was like this:

FooList.cshtml
@model IEnumerable<Foo> <ul> @foreach(var foo in Model) { <li> @{ Html.RenderPartial("_FooListElement", foo); } </li> } </ul>

Somehow there was some null Foos in my IEnumerable. When calling RenderPartial, if the foo local is null, it passes Model to the partial view rather than just a null. This is completely unexpected, and results in a strange error about the partial view receiving an IEnumerable<Foo> instead of a Foo. You stare at the code thinking “How in the heck did that happen!?”

I think a better result would be ArgumentNullException

Bind jQuery Events to Plain Objects

Sometimes you need a simple little pub-sub event model for your application. It’s fairly easy to write your own or include a small library, but if you’ve already got jQuery on the page, this might be good enough for you.

3 Things I Learned This Week 2015-01-16

HtmlAgilityPack

Cleaning HTML Snippets with HtmlAgilityPack demonstrates how to use HtmlAgilityPack for parsing and modifying HTML documents and snippets. It’s pretty straight forward and easy to use.

Git Inline Diffs

Did you know you could use git to show inline diffs on the command line? I didn’t either, until this week.

inheritInChildApplications in web.config

I was having trouble with a Telerik control that comes with Sitecore not loading in the admin section. It kept yelling that it needed its Telerik.Web.UI.WebResource.axd added to the <handlers> section of <system.webServer>. And I’m like, “It’s right there you idiot machine!”

Oh, turns out there was a inheritInChildApplications="false" attribute on the section. Normally web.config settings inherit and override the deeper into the file system hierarchy they are located.

Git Inline Diff

TIL Git has inline diffs (word by word) with --word-diff or --color-words

Normal Diff

Inline diff when adding a word

Inline diff when changing a word