<field name="ExpiryDate_dyn_s_dte" type="date" indexed="true" stored="true" />
<!-- ContentAuthoring.Translation -->
<field name="TranslationRequired_dyn_s_bln" type="boolean" indexed="true" stored="true" />
<field name="ParentRevisionDate_dyn_s_dte" type="date" indexed="true" stored="true" />
<!-- ContentAuthoring.PublishedTo -->
<field name="PublishedToTargetIds_dyn_mvl_txt" type="string" indexed="true" stored="false" />
<field name="PublishedToTargetTitles_dyn_mvl_txt" type="text" indexed="true" stored="false" />
</fields>
In the example you see three fields defined. Expiry Date, and Published To Target Ids and Titles.
The name of the field needs to be set in a specific format to allow to parse the field value in the result set. E.g.: ExpiryDate_dyn_s_dte
Besides the name of the field, the name attribute contains information on whether or not the field is dynamic (_dyn), stored (_s) or multivalued (_mvl).
NB: Making a field stored will return it in the search result.
The last part of the field name must contain a type keyword:
public void
Configure(SearchIndexingHandlerSettings
settings)
public void
ExtractIndexFields(IdentifiableObjectData
subjectData, Item item)
The Configure method can be left empty, and ExtractIndexFields contains the actual implementation of your search index handler.
The following example indexes any ExpiryDate metadata field present in the Component:
public sealed
class ExpiryDateSearchIndexingHandler:
ISearchIndexingHandler
{
const string
ExpiryDateFieldName = "ExpiryDate";
public void
Configure(SearchIndexingHandlerSettings
settings)
{
}
public void
ExtractIndexFields(IdentifiableObjectData
subjectData, Item item)
{
var
itemData = subjectData as VersionedItemData;
if
(SubjectHasMetadata(itemData))
{
AddExpiryDateToIndexItem(itemData,
item);
}
}
public void
Dispose()
{
}
static bool
SubjectHasMetadata(RepositoryLocalObjectData
itemData)
{
return
itemData != null &&
!string.IsNullOrEmpty(itemData.Metadata);
}
static void
AddExpiryDateToIndexItem(RepositoryLocalObjectData
itemData, Item item)
{
var
expiryDate = GetExpiryDate(itemData.Metadata);
if (expiryDate.HasValue)
{
item.Add(new
DateField(ExpiryDateFieldName, expiryDate, true));
}
}
static DateTime?
GetExpiryDate(string xml)
{
var xe
= XElement.Parse(xml);
var
expiryDateValue =
xe.Descendants().Where(
el => string.Equals(el.Name.LocalName, ExpiryDateFieldName,
StringComparison.InvariantCultureIgnoreCase)).
Select(el =>
el.Value).FirstOrDefault();
DateTime
expiryDate;
return DateTime.TryParse(expiryDateValue, out expiryDate) ? expiryDate : default(DateTime?);
}
}
Configuring a Search Indexing Handler
The third step is to configure your search indexing handler in the content manager configuration.
You can configure the handler in the searchIndexer section:
<searchIndexer heartbeatTimeout="300" indexingThreadCount="1" hostUrl="http://localhost:8983/tridion" hostUsername="[Domain]\[Account
Name]" hostPassword="[Password]">
<searchIndexingHandlers>
<add type="Tridion.ContentManager.Search.Indexing.Handling.DefaultSearchIndexingHandler" assembly="Tridion.ContentManager.Search.Indexing,
Version=6.1.0.996, Culture=neutral, PublicKeyToken=ddfc895746e5ee6b" />
<add type="[Customer Name].ContentManager.Extensions.ContentAuthoring.ExpiryDates.ExpiryDateSearchIndexingHandler" assembly="[Customer Name].ContentManager.Extensions,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=9d4a72f7ed42e8d3,
processorArchitecture=MSIL" />
</searchIndexingHandlers>
</searchIndexer>
It might be possible that in your implementation these elements are encrypted. If this is the case you can encrypt the section again by resetting the host password using the SDL Tridion MMC Snap-in.
Expired Content Search Folders
When you create a Search Folder you need to query the entire name as specified in the Sol-r Schema.
The following example shows the Search Folder source to find the content that is expiring in the coming week:
<SearchFolder xmlns="http://www.tridion.com/ContentManager/5.1/SearchFolder"> <GeneralParameters> <SearchQuery>ExpiryDate_dyn_s_dte:[NOW TO NOW+1WEEK]</SearchQuery> <SearchIn xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="tcm:0-6-1" Recursive="true"></SearchIn> </GeneralParameters> <AdvancedParameters></AdvancedParameters></SearchFolder>
The following example shows the Search Folder source to find the Component published to two specific targets, Live (Public) and Live Secure:
<SearchFolder xmlns="http://www.tridion.com/ContentManager/5.1/SearchFolder">
<GeneralParameters>
<SearchQuery>PublishedToTargetTitles_dyn_mvl_txt:Live AND
PublishedToTargetTitles_dyn_mvl_txt:"Live Secure"</SearchQuery>
<SearchIn xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="tcm:0-4-1" Recursive="1"></SearchIn>
</GeneralParameters>
<AdvancedParameters></AdvancedParameters>
</SearchFolder>
Wrapping up and some additional advice
Now restart the COM+ application and search related services and call TcmReIndex.exe /all to reindex your entire search index.
To wrap up I would like to share some points I came across:
- Updating Application data does not trigger updating the Search Catalog. You need to force this manually, which is not possible using the API, and the solution I found is unsupported.
- You receive a Tridion.ContentManager.CoreService.Client.IdentifiableObjectData data object in the handler, and not the actual TOM.NET Tridion Item. This means that when you need to do anything with the item (e.g. retrieve Application Data) you need to use the Core Service.
- When searching for these custom fields you need to use the full name as specified in the Sol-r Schema.
- You can define multivalued index fields (nice to keep track of all publication targets a component is published to!), but they can’t be returned in the search result. Tridion doesn’t know how to handle these results, so set the stored attribute in the Schema configuration to false.
Good luck! I hope you find this article useful, and when you need any help, let me know.