In Part 1, we talked about interacting and consuming the search service. In Part 2, we discussed a Tagging strategy to allow for a Filtered Search to be possible. Today, we are going to look at the service layer that is called by the presentation layer.
SearchService.cs
Here, we have the method signature that is called by the client. So as we went over in Part 1, if the user searches for "cost savings" and selects the Industries of "Construction" and "Energy", the following URL is used in the Ajax call on the client end.
http://www.domainname.com/Services/SearchService.svc/SearchInsightsJson?&Category2={017E7503-83A1-4A45-9053-4C6755715162}|{66310DCB-3BD1-4C4C-9AFA-FBBC8D4BA967}&page=1&perPage=8&searchString=cost%20savings
This URL ends up calling the SearchInsightsJson
method within the SearchService
web service class and it will pass in the GUID
s associated with Construction and Energy, the current page the user is on, the results the client wants per page the searchString
is set to "cost savings".
You will notice the method has two attributes that mark the method as an OperationContract
(available via the web service) and the WebGet
attribute is stating that the response should be in a JSON format.
Prevent Client-side Caching
The three lines of code above help prevent any caching that may occur by the client. Technically, because the URL would be unique for each search request, we could remove this block of code altogether with little impact but this ensures if a new item has been published since the client ran the query that it would get picked up in the search results.
Building-up the Criteria
The code above shows the approach to calling the Search service. We are populating a SearchParam
object with the information needed by the Search Service to run the query.
LocationIds
= The top folder or folders within the CMS we want to search TemplateIds
= The page types we want included in our search FullTextQuery
= The key word or phrase the user entered in the search form ShowAllVersions
= This specifies if we want to search all versions of a page or just the latest. Language
= Sets the language context of our search (for multi-lingual sites)
We then check each Category
parameter and if it is not null
, we will add the pipe delimited GUID
s to the Tags
collection.
Search Call and Setup Paging
The first piece is calling our Search Service and passing in our Criteria. We can then set some parameter values that we will include in our JSON result so our presentation can provide paging functionality without having to do the logic on their end.
We set the TotalResults
to the hits.Count
and the CurrentPage
is the page that was passed in originally so the client doesn't have maintain state.
The results we get back from the Search Service is actually just a List
of what we are calling a SkinnyItem
, that is to say, it is just a pointer to the actual Item
along with some other meta data. SkinnyItem
has a GetItem()
method that will retrieve the full Item
from Sitecore but if we have 1000+ items that match our search and we truly only need to fetch the ones that are being displayed on the current page (8 of them), it would be a lot of unnecessary overhead to do this.
We end up creating an s and an e variable that we will use in the next code snippet. e is either going to be (perPage * page
) or the total results from the search whichever is less. s is either ((perPage * page) - perPage)
or the hits.Count - perPage
whichever is less (as long as it is >= 0
).
Above, we are iterating through the SkinnyItems
between our s and or e count and grabbing the full Item out of Sitecore
in order to populate a SearchResultItem
and add it to our resultItems
collection. This seems like an extra step because we are going from a SkinnyItem
to get a Sitecore
Item to get a SearchResultItem
but there is a logical reason. The SkinnyItem
is essentially a representation of the raw search result from Lucene. The Sitecore
Item is acting as full data record or Domain
Object and the SearchResultItem
is a simple Data Transfer Object containing only the fields needed by the view.
We set the JSON resultData.Results
equal to the collection of SearchResultItems
we just created and we set the TotalPages
equal to the total records divided by items per page and round up.
We set the SearchUrl
with the value that was passed in and provide it back to the client so it doesn't have to worry about state.
The PageStart
is what the starting page in the page navigation should be. This is hard coded right now with a paging of 5 but this could come from a config file or even the CMS. This allows our client to setup paging of 6 through 10 when a current page of 7 is passed in (see below).
Next Up
So that is what our web service looks like. While writing this blog post and taking screenshots, I see some areas of improvement and refactoring but you should get the idea of what the service layer should be doing. Part 4 will focus on our actual Search service. This will go into detail about how we customized our Crawler, setup our Indexes and leveraged the advanced searcher / crawler from the Sitecore
Shared Source library - if you want to get a headstart on that, I recommend this video.
CodeProject
Permalink | Leave a comment ยป