The Big Fat Disclaimer – This has not been thoroughly tested for a production environment. I have also removed references in my snippets below to caching and error handling to try and keep it brief. The downloadable version uses both caching and error handling, but it is still really just a proof of concept and you should TEST it before you deploy it! I take no responsibility if your production servers blow up!

I must have seen this requirement dozen of times on different projects, having search results which either:

Filter using a User Profile Property of the current user

Filter using a dynamic date range (e.g. using the “TODAY” token)

Specify the Sort-By (which is normally restricted to either “Relevance” or “Modified Date”

The requirement for functionality of this nature come up extremely frequently on Intranet projects. For example:

“Show News Articles from the past 7 days which filter based on the user’s location”“Show events coming up in the next 3 months”“Show discussions / wiki entries / blog posts which include the current user’s Ask Me About values”

ExampleFixedQuery used in the Web Part: Author:[UPP-PreferredName] AND Write:[TODAY-180]..[TODAY] AND ContentType:Event

Well .. on my current client project these very requirements came up .. so this time I decided to knock together the basics of the web part in my spare time and then “donate” it to the project… and this post describes how I built it, what goes on under the hood, and also includes both the source code as well as a downloadable WSP package with the working Web Part in it.

Step 1 – Extending the Search Core Results Web PartSo .. to get us started, lets kick off by creating our actual Web Part. I am going to be extending the Search CoreResultsWebPart (link to MSDN).This is easy enough to achieve by simply creating a new Web Part in Visual Studio and inheriting from the CoreResultsWebPart class. This will make sure our web part gets all of the functionality and properties that the normal Search Results web part does without any additional effort.

1: [ToolboxItemAttribute(false)]

2: publicclass ExtendedSearchWebPart : CoreResultsWebPart

3: {

4:

5: }

That is the easy bit …

Step 2 – Overriding the Query and SortOrderNow the next bit to tackle is how to override the actual query that gets executed. Well the best place to do this is to override the ConfigureDataSourceProperties method. This method gets called before the query is actually executed against the Search engine itself.

You can then leverage the CoreResultsWebPart.DataSource property (which is of type CoreResultsDataSource). This is what allows all of the magic to happen.

First off we want to make sure we are only executing our custom code when we are actually trying to retrieve search results. This is a fail-safe block as some instances this will be false (such as when you are editing the web part or if you are viewing the “Design” view in SharePoint Designer). We also need to call the base method (as you typically would when overriding a method call!).

Then things get interesting. Line 11 has us create our “CoreResultsDataSource” object from the local “DataSource” property. This has two properties which we are modifying:

Query (line 14) – This allows us to change or completely override the query which is being executed. This will be the entire query including the Fixed Query, Appended Query and whatever the user typed into their search box (if you are using this on a Search Results page). In my example above I am simply overriding the result so that it just searches for items created by “Martin Hatch” (me!)

SortOrder (lines 17 and 18) – This allows us to override the sort order, allowing us to select ANY indexed Search Property you want (I expect excluding rich text fields of course!). In my example, I am sorting by Title in Ascending order.This can then be easily extended to provide custom Web Part properties to allow the Sort functionality to be specified by the page editor.

Step 3 – Making it re-usable Part 1 – Dynamic Date ranges So now that we can override the query easily we can move on to adding some of the good stuff. I decided to go with a relatively simple Token Replacement function using a simple [TODAY] token to represent the current date:

[TODAY] (todays date)

[TODAY+7] (today plus 7 days)

[TODAY-7] (today minus 7 days)

So .. how do we code this in? Well .. I am quite lazy and don’t really get on with regular expressions (if you are reading this and you are a RegEx guru.. by all means download the source code, refactor it and send it back, cheers!).

So I started off by creating a bunch of class level constants which I would use to recognise the tokens that we are looking for above:

privateconststring TODAY_PLACEHOLDER = "[TODAY]";

privateconststring TODAY_ADD_STARTSTRING = "[TODAY+";

privateconststring TODAY_SUBTRACT_STARTSTRING = "[TODAY-";

privateconststring TOKEN_ENDSTRING = "]";

The following code can then be swapped out for our ConfigureDataSourceProperties method.

48: thrownew FormatException("The format of the [TODAY] string is invalid. Could not convert the days value to an integer.", ex);

49: }

50: }

So you should be able to see we are using simple String.IndexOf() method calls to find out if our Tokens are present.

If they are then we simply calculate the DateTime value based on the static DateTime.UtcNow property and use String.Replace() methods to swap out these into our query text.

When we are using [TODAY+X] or [TODAY-X] we simply use DateTime.UtcNow.AddDays(X) or DateTime.UtcNow.AddDays(0-X) and use the same String.Replace() method.

The search syntax is exactly the same as it was previously, and the Keyword Syntax is very powerful.

Example: Using [TODAY] Token query syntax

Write:[TODAY] – this will return all items that were modified todayWrite>[TODAY-7] – this will return all items that were modified in the past weekWrite:[TODAY-14]..[TODAY-7] – this will return all items that were modified between 2 weeks ago and 1 week ago

So we already have a powerful and reusable search component .. but there is more!

Step 4 – Making it re-usable Part 2 – Dynamic User Profile PropertiesThe next one is to allow us to pull in User Profile Properties so that we can start doing searches based on the current user’s profile values. For this we needed to create new replacable Tokens, for which I decided to use:

[UPP-{User Profile Property Internal Name}]

[UPP-PreferredName] (swaps out for the users name)

[UPP-SPS-Responsibility] (swaps out for their “Ask Me About” values)

etc ..

So .. we add another class level constant (same as we did for our DateTime tokens)

privateconststring USER_PROFILE_PROP_STARTSTRING = "[UPP-";

We can then use this in our code, in exactly the way we did before (using String.IndexOf(), String.SubString() and String.Replace() methods).

So we add the following additional code to our ConfigureDataSourceProperties method;

We are using the UserProfileManager.IsAvailable() method to find out if we have a user profile service application provisioned and assigned to the current Web Application.

At the moment this code throws an error if the current user doesn’t have a User Profile. You may want to handle this differently for your environment?

Handling of multi-value fields. At the moment all we do is take the string values and concatenate them with “OR” in the middle. So if you had “Value1; Value2” as your property value the Token replacement would put “Value1 OR Value2” as the search query.

As long as the content editors are aware of the behaviour this allows us to create quite complex queries.

Example if we now used the Fixed Query:

([UPP-SP-Responsibility]) AND Write:[TODAY-14]..[TODAY]

Then for a user who’s “Ask Me About” properties were “SharePoint” and “IT Administration” then the resulting Search Query would be:

(SharePoint OR “IT Administration”) AND Write:13/05/2012..17/05/2012

If another user comes along whose “Ask Me About” property was just set to “Marketing” then the resulting Search Query would be:

(Marketing) AND Write:13/05/2012..17/05/2012

This is without changing any of the web part properties, and allows us to drive dynamic content from a single web part to our entire user base.

Hopefully you can see that this is incredibly powerful and flexible.

Step 5 – Making it re-usable Part 3 – Controllable Sort ByThe final step is to allow our content editors to control the “Sort By” functionality. The default OOTB webpart only allows us to sort by “Relevance” or “Last Modified”, which is fine when you are looking at general search results, but when you are building custom components (such as news, links or event feeds) you typically want to control the order by date or title or something a little more usable for the specific component.

So this bolt-in allows you to control the Sort By. First off we need to add some Web Part Properties so that the user can modify their values:

ContentType:Event AND ([UPP-SPS-Responsibility]) AND Write:[TODAY-7]..[TODAY]becomesContentType:Event AND (SharePoint OR “Solution Architecture” OR Code) AND Write:12/05/2012..17/05/2012Returns all events which were updated within the past week, and contain the current user’s “Ask Me About” values.

Author:[UPP-PreferredName] IsDocument:1becomesAuthor:”Martin Hatch” IsDocument:1Returns all documents written by the current user

Author:[UPP-PreferredName] Write>=[TODAY-14]becomesAuthor:”Martin Hatch” Write>=02/05/2012Returns all content created by the current user and updated within the past 2 weeks

Ok, this post was about filtering based on the User Profile of the current user.

So what you should be doing is use the same Managed Metadata TermSet in both your lists and libraries as you do in the User Profile Service Properties.

Then when a user edits their profile (and selects a department) you can then pull that value out and use it to provide a personalised set of Search results.

Lets assume you have added a Site Column called "CustomDepts" and added it to a document library to allow tagging. You have also created a User Profile Property called "MyDept" and made it available in the User Profile editing screen.

You could build a search query using:CustomDepts:[UPP-MyDept]

I then edit my profile and select "Information Technology" in the "MyDept" field.

When I visit the page with your webpart then it will execute the query:CustomDepts:"Information Technology"

This is giving me personal results.

Does this make sense?

ajit bhandari

Hi Martin ,

Thanks a lot for the prompt response .

Ill just brief you about my requirement , i need to customize my search results based on some filters , i have a list of departments(metadata property column) with check boxes and based on the check boxes selected by the user , the documents belonging to that department need to displayed on the core results web part .

Very Sorry if i have made it complicated .

Martin Hatch

Sorry, not entirely sure what you mean?

Do you mean "what department the user is in?" .. or some other department related information?

If you are simply trying to get the department of the current user then you just pull out the "Department" profile property (which would be something like [UPP-Department] .. assuming "Department" is the internal name of the profile property)

ajit bhandari

how obtain the search results based on the department metadata dynamically . ?

Martin Hatch

Regarding the Office 365 question, I have had confirmation from @nellymo (who is an MCM in the O365 team) that the Content Search Web Part is going to come to Office 365 SharePoint Online "when they have finished testing".

I believe they are doing performance and capacity planning before they roll it out to make sure their search infrastructure can cope with thousands of CSWP popping up 🙂

Martin Hatch

Glad this was of help.. although bear in mind this is for SharePoint 2010 farm solutions only.

If you are using SharePoint 2013 then you can use the same features basically "out of the box" with either Search Core Results or the Content by Search Web Part

ajit bhandari

Hi,

Thank you for the great article .

Just had a query as to how obtain the search results based on the department metadata dynamically .

Thanks in advance

Anonymous

great article! Greetings from Slovakia!

Martin Hatch

@Luke – You should be able to use "Modified" instead?

@Daniel – this won't work in the sandbox because it uses the User Profile namespace which won't be accessible.

DanielALevine

Martin,

Great work on this is a great need for the enchanced Search Core WP is there a possiblity to deploy it on O365 through a Sandbox Solution?

Best Dan

Luke Lewandowski

Hi I have been trying to modify your webpart example to show latest results sorted by Date (or Write) property. I tried using both 'Date' and 'Write' even tried using 'SearchResults_CRDate' and no luck so far. Order seems to be random. Any ideas?

Martin Hatch

Oh .. and for those of you wondering about the screenshot with the Ratings Stars in the search results.