--- title: About the coveoPostItemProcessingPipeline pipeline slug: '2559' canonical_url: https://docs.coveo.com/en/2559/ collection: coveo-for-sitecore-v5 source_format: adoc --- # About the coveoPostItemProcessingPipeline pipeline The `coveoPostItemProcessingPipeline` can be used with items indexed by the Sitecore Search Provider to alter or duplicate them before they're sent to the Coveo index. Adding a processor in the `coveoPostItemProcessingPipeline` pipeline is often an alternative to creating a computed field for an item (see [Create computed fields](https://docs.coveo.com/en/2251/)). This article explains how you can add a processor to the pipeline, followed by a code example. ## Processor requirements You must create a class that implements the `IProcessor` interface and its `Process` method. This method receives a `CoveoPostItemProcessingPipelineArgs` object. `CoveoPostItemProcessingPipelineArgs` objects encapsulate a Sitecore item and the corresponding Coveo index representation of the item (that is, a `CoveoIndexableItem`). The `CoveoPostItemProcessingPipelineArgs` class lets you change the values of fields. You can also use the `CoveoPostItemProcessingPipelineArgs` class to index many items for a single Sitecore item, which can be required when using wildcard items in Sitecore (see [Wildcards and Data Driven URLs](https://community.sitecore.com/community/en/wildcards-and-data-driven-urls?id=community_blog&sys_id=5eb22b6d1b8370d0b8954371b24bcb01)). To add a field to a `CoveoItem` in a `coveoPostItemProcessingPipeline` processor, include a Sitecore template field with a name that matches the metadata name used in your processor (see [Code sample: Adding and populating a field on a Coveo item](#code-sample-adding-and-populating-a-field-on-a-coveo-item)). Finally, to have your code executed, add a `` node that references your class in the `` element of the `Coveo.SearchProvider.Custom.config` file. ### `CoveoPostItemProcessingPipelineArgs` properties The `CoveoPostItemProcessingPipelineArgs` class has the following properties: [%header,cols="3"] |=== |Name |Type |Description |Item |IIndexable |The original Sitecore item. It's typically used to retrieve field values. |CoveoItem |CoveoIndexableItem |The Coveo item currently processed by the pipeline. |IndexConfiguration |CoveoIndexConfiguration |The configuration of the index which defines how Coveo accesses its platform, targets its sources and indexes items. You shouldn't have to modify this property. |OutputCoveoItems |List |The list of items that will be indexed by Coveo, as Coveo for Sitecore only considers this list, and **not** the `CoveoItem` itself, after the pipeline is done. The list is initialized with `CoveoItem`. An empty list means that no items will be indexed, but this won't delete an existing item from the index. |=== ### CoveoIndexableItem properties The following properties can be edited on the `CoveoIndexableItem` type. [%header,cols="3"] |=== |Name |Type |Description |BinaryData |Byte[] |The item body, represented in binary. |BinaryDataMimeType |string |The MIME type of the item binary data. |ClickableUri |string |The ClickableUri used to open a specific location when a search result is clicked. |FileName |string |The item filename, if the item is representing a file. |HasSubItems |bool |Whether the item has sub items or not. |Id |string |The item ID (string representation). |Metadata |Dictionary |A dictionary containing all the item metadata (for example, all the fields values). |ModifiedDate |DateTime |The item last modified date. You shouldn't edit this. |Parent |CoveoIndexableItem |The item parent, if available. |ParentId |string |The item parent ID. |Path |string |The item path (for example,`/sitecore/content/Home`). |Permissions |AccessRulesHierarchy |Data structure containing the security of the item. You should only edit this if you know what you're doing. |PrintablePath |string |The PrintablePath used to show a friendly URL in a search result UI. |Title |string |The item title (also known as display name). |UniqueId |string |The item unique ID, when available. |Uri |string |The item Uri. |=== **Example** You want to create a custom processor to duplicate a `CoveoItem`. To do so, you would add a new `CoveoIndexableItem` to the `OutputCoveoItems` list, using a code snippet such as the following: [source,c#] ``` CoveoIndexableItem duplicateItem = new CoveoIndexableItem(); duplicateItem.Uri = args.CoveoItem.Uri + "/copy"; duplicateItem.Permissions = args.CoveoItem.Permissions; duplicateItem.Title = String.Format("Duplicate of {0}", args.CoveoItem.Title); args.OutputCoveoItems.Add(duplicateItem); ``` > **Note** > > Upstream processors may have created copies of the original Sitecore item already. > Prior to adding a code snippet like the one above, use the `Re-Index Tree` option on a target item in Sitecore and subsequently browse your indexed items by `Indexed date` to verify if copies of the targeted item are already being created in an upstream processor. ## A sample processor This example lives in the context of the Jetstream Sitecore demo, a travel aggregation website offering flights. Several flight items are available in the database and were indexed by Coveo for Sitecore. The default output in a basic search results list looks like this: ![Flight search results showing business first class and economy options with dates | Coveo](https://docs.coveo.com/en/assets/images/c4sc-v5/flight-search-results.png) For each result, you can see the name of the item in Sitecore, which is the flight number followed by the cabin class (for example, first class, business class, or economy class). Out of context, the item title doesn't tell much. However, by leveraging `Flight` item field values, you can easily create a custom processor that displays a better item title. ### Data structure The `Flight` items shown above are all issued from the `Flight` template. The template contains a few fields such as: * `flight number`: Integer * `class`: Single-Line-Text * `departure airport`: Droplist with source on `Airport` Template * `arrival airport`: Droplist with source on `Airport` Template The `Airport` template contains `Airport` items with this single field: * `City`: Single-Line-Text Both the `Flight` and `Airport` template names are unique. These templates could have more fields, but for this example we'll only use those listed above. ### Setting up the Visual Studio project Before you can implement the custom processor itself, you need a suitable project to add it to. You'll need a C# project that targets .NET framework 4. Once you have this project, add the following references to it: * `Coveo.AbstractLayer.dll` * `Coveo.Framework.dll` * `Coveo.SearchProviderBase.dll` * `Sitecore.ContentSearch.dll` * `Sitecore.Kernel.dll` These assemblies are located in the `\bin` folder. ### Implementing the processor class Now that your Visual Studio project is ready, you can move on to implement the custom processor. Create a new class that implements the `IProcessor` interface as in the code sample below. **Skeleton of the custom processor class** [source,c#] ``` using System; using Coveo.Framework.Processor; using Coveo.SearchProvider.Pipelines; using Sitecore; using Sitecore.ContentSearch; using Sitecore.Data; using Sitecore.Data.Items; namespace Samples.Pipelines { public class FlightTitleProcessor : IProcessor { public void Process(CoveoPostItemProcessingPipelineArgs args) { throw new NotImplementedException(); } } } ``` ### Implementing the Process method The hardest task is to implement the `Process` method. Here, we'll alter the item title to include the flight number, the departure city, and the arrival city. Therefore, the flight with the title `9981 - Business` would become `9981 - From Paris to Los Angeles`. Here is the complete implementation of the class. [source,c#] ``` public class FlightTitleProcessor : IProcessor { public void Process(CoveoPostItemProcessingPipelineArgs args) { if (args.Item is SitecoreIndexableItem) { Item item = ((SitecoreIndexableItem) args.Item).Item; // This processing only makes sense for instances of the "Flight" template. if (item != null && item.TemplateName.Equals("Flight", StringComparison.OrdinalIgnoreCase)) { // Get the flight number string flightNumber = item["flight number"]; // Get the linked airport items string departureAirportId = item["departure airport"]; string arrivalAirportId = item["arrival airport"]; string departureCity = GetAirportCity(item.Database, departureAirportId); string arrivalCity = GetAirportCity(item.Database, arrivalAirportId); if (!String.IsNullOrEmpty(flightNumber) && !String.IsNullOrEmpty(departureCity) && !String.IsNullOrEmpty(arrivalCity)) { // Set the new title on the Coveo item args.CoveoItem.Title = String.Format("{0} - From {1} to {2}", flightNumber, departureCity, arrivalCity); } } } } private string GetAirportCity(Database p_Database, string p_AirportId) { string city = ""; Item airport = p_Database.GetItem(p_AirportId); if (airport != null) { city = airport["city"]; } return city; } } ``` First, the `Sitecore.Data.Items.Item` instance is retrieved, as the item needs to be an instance of the `Flight` template and the `Item` class allows you to access the `TemplateName` property. Then, the flight number and the airports are retrieved. For each airport, the `GetAirportCity` method returns the city name for the given airport. The `Item.Database` property is used to ensure that you get the airport from the same database as the flight item itself. Finally, the .NET Framework `System.String` class `Format` method is used to modify the `Title` property of the `CoveoItem` object. [source,c#] ``` args.CoveoItem.Title = String.Format("{0} - From {1} to {2}", flightNumber, departureCity, arrivalCity); ``` > **Important** > > The `Title` of certain search result items might not be altered using the example above, for example, if the Sitecore item has a populated and indexed `Title` field (see [About the search result title selection sequence](https://docs.coveo.com/en/2571/)). ### Enabling the processor in Sitecore The custom processor is ready to be used by Sitecore. Here is how you can add it to the configuration file. . Copy the assembly containing the custom processor into the bin folder of the Sitecore website. Default location is `\website\bin`. . Open the `Coveo.SearchProvider.Custom.config` file in a text editor. The default file location is `\App_Config\Include\Coveo`. . Find the `coveoPostItemProcessingPipeline` element. . Add a processor element that maps the implementation of the custom processor in the pipeline. For example, if you've created a `Samples.dll` file, add the following node in the `coveoPostItemProcessingPipeline` element. ```xml ``` . Save the file and reindex Sitecore items. ### The new search result item titles Now, the results page shows the customized titles. Since you changed the title on the indexed items, a search with the `paris` keyword returns the flight items with `paris` in their title. ![Flight search results showing flights from Boston Paris and Venice with dates and flight numbers | Coveo](https://docs.coveo.com/en/assets/images/c4sc-v5/final-flight-search-results.png) ### Conclusion In this example, you learned how to alter the item title for a specific item type in Sitecore. It's also possible to alter item field values, URLs, and the Quick view, though the latter requires much effort. Almost any field/property on the item can be customized. You can also add as many custom processors as you want, but remember that the more you add, the slower the indexing gets. ## Code sample: Indexing virtual items The following code sample shows you how to index entirely virtual items (that is, items that don't really exist in Sitecore) based on existing items. You'll need to adapt this code sample to make it work in your own environment. [source,c#] ``` using System.Collections.Generic; using Coveo.AbstractLayer.RepositoryItem; using Coveo.Framework.Processor; using Coveo.SearchProvider.Pipelines; using Sitecore.ContentSearch; using Sitecore.Data.Fields; using Sitecore.Data.Items; namespace Coveo.Demos.Pipelines { public class TranslatedItemPostProcessor : IProcessor { public void Process(CoveoPostItemProcessingPipelineArgs args) { // In this example, our goal is to manually translate a specific item from English to French, and index its translated version as a separate item. List itemsToAdd = new List(); // We operate on the indexables in args.CoveoOutputItems in case previous processors added new items. A reference to the original item is already in this list foreach (CoveoIndexableItem coveoIndexableItem in args.OutputCoveoItems) { SitecoreIndexableItem indexableItem = args.Item as SitecoreIndexableItem; IIndexableBuiltinFields builtInFields = indexableItem; if (indexableItem != null) { Item item = indexableItem.Item; // If the item being currently processed is of the expected type (template = Sample Item), we process it. if (builtInFields.TemplateId.ToString().Equals("{76036F5E-CBCE-46D1-AF0A-4143F9B557AA}")) { // If the 'Text' field has the expected value, it means that the current item is indeed the one that we are interested in. Field textField = item.Fields["Text"]; if (textField.Value == "

This is a test!

") { // The value of the original item's UniqueId property has the following format: sitecore://{database}/{item_id}?lang={language}&ver={version}. // But our new item must have a different UniqueId value to be indexed by Coveo, so we change the values of its language and version parameters. string newUniqueId = coveoIndexableItem.UniqueId.Replace("lang=en", "lang=fr").Replace("ver=1", "ver=2"); // To create a new item, we clone the original one and we change the properties and values that we are interested in. CoveoIndexableItem newItem = new CoveoIndexableItem { BinaryData = coveoIndexableItem.BinaryData, BinaryDataMimeType = coveoIndexableItem.BinaryDataMimeType, BinaryDataPath = coveoIndexableItem.BinaryDataPath, ClickableUri = coveoIndexableItem.ClickableUri, FileName = coveoIndexableItem.FileName, HasSubItems = coveoIndexableItem.HasSubItems, Id = coveoIndexableItem.Id, IsDeletedItem = coveoIndexableItem.IsDeletedItem, ModifiedDate = coveoIndexableItem.ModifiedDate, Parent = coveoIndexableItem.Parent, ParentId = coveoIndexableItem.ParentId, Path = coveoIndexableItem.Path, Permissions = coveoIndexableItem.Permissions, PrintablePath = coveoIndexableItem.PrintablePath, Title = "Item Exemple", UniqueId = newUniqueId, Metadata = new Dictionary(coveoIndexableItem.Metadata) }; // Here, we: // -Manually translate the original title to French. // -Change the value of a few metadata fields to reflect the differences in our translated item. newItem.SetMetadata("Text", "Ceci est un test!"); newItem.SetMetadata("Language", "fr"); newItem.SetMetadata("Languages", "fr"); newItem.SetMetadata("Version", "2"); // Since we are using a post-processing pipeline, computed fields such as ParsedLanguage have already been processed. // So here we change its value to 'french'. newItem.SetMetadata("parsedlanguage", "french"); // OutputCoveoItems always contains the original item as its first item. It's actually the same instance of CoveoIndexableItem as in args.CoveoItem, so if you want to // modify the original item, you can do it either through args.CoveoItem or args.OutputCoveoItems. We add the new item to the output items. itemsToAdd.Add(newItem); } } } } args.OutputCoveoItems.AddRange(itemsToAdd); } } } ``` ## Code sample: Adding and populating a field on a Coveo item Following the `coveoItemProcessingPipeline` processor [code sample](https://docs.coveo.com/en/2558#code-sample), you might be looking to add and populate fields on the `relatedMediaItem` object. To achieve this, you want to work with the [CoveoItem](#coveopostitemprocessingpipelineargs-properties) property of the `CoveoPostItemProcessingPipelineArgs` object which is currently being processed. The [Metadata](#coveoindexableitem-properties) property of the `CoveoItem` object contains the key-value pairs that the [Coveo Platform](https://docs.coveo.com/en/186/) uses to populate [fields in your index items](https://docs.coveo.com/en/1712#fields-tab). The code example below populates a media item `mainitem` field with the main item path. [source,c#] ``` using System; using System.Reflection; using Coveo.Framework.Log; using Sitecore; using Coveo.Framework.Processor; using Coveo.SearchProvider.Pipelines; using Sitecore.ContentSearch; using Sitecore.Data; using Sitecore.Data.Items; using Sitecore.Links; namespace MyProcessors { public class RelatedItemProcessor : IProcessor { public void Process(CoveoPostItemProcessingPipelineArgs args) { // In this example, the goal is to set the Coveo "mainitem" metadata value in the related // item (a media item in this example). // To record "mainitem" metadata values in a Coveo field, make sure you have a Sitecore template field // called "mainitem" and that you have included it via the Coveo Command Center (see // https://docs.coveo.com/en/2559#processor-requirements). if (args.Item is SitecoreIndexableItem) { // It's easier to work with the original Sitecore item until we get the related "main" item. Item sitecoreItem = ((SitecoreIndexableItem)args.Item).Item; // If the item being currently processed is of the expected type (template name = 'Jpeg'), we process it. if (sitecoreItem != null && sitecoreItem.TemplateName.Equals("Jpeg", StringComparison.OrdinalIgnoreCase)) { ID mediaItemId = sitecoreItem.ID; // Get the related main Sitecore item string relatedMainItem = GetMainItemPath(sitecoreItem); // Now, we must work with the Coveo item and add the "mainitem" metadata on it. args.CoveoItem.Metadata.Add("mainitem", relatedMainItem); } } } private string GetMainItemPath(Item sitecoreItem) { ItemLink[] itemLinks = Globals.LinkDatabase.GetReferrers(sitecoreItem); string mainItemPath = ""; if (itemLinks != null) { foreach (ItemLink link in itemLinks) { if (link.TargetItemID == sitecoreItem.ID && link.SourceFieldID.ToString() == "{8E4D098E-F358-4D51-A219-84C852BC78DB}") { mainItemPath = link.GetSourceItem().Paths.Path; } } } return mainItemPath; } } } ``` > **Note** > > To populate a `mainitem` Coveo field with the `mainitem` metadata coming from your processor, make sure you have included a Sitecore template field called `mainitem` via the [**Command Center**](https://docs.coveo.com/en/2566#fields). > > ![Including a Sitecore template field called `mainitem` | Coveo](:https://docs.coveo.com/en/assets/images/c4sc-v5/including-field-for-added-metadata.png) ## Code sample: Condition-based binary data processor selection In the following code sample, a condition is set based on the presence of "some.domain" in the item URI to determine whether to use [`BasicHtmlContentInBodyProcessor`](https://docs.coveo.com/en/2324/) instead of [`FetchPageContentProcessor`](https://docs.coveo.com/en/2326/) to generate the body of the item. The [`BasicHtmlContentInBodyProcessor`](https://docs.coveo.com/en/2324/) doesn't perform an HTTP request to retrieve the content of a webpage. It can be used as a fallback processor for an item whose body resides behind a non-negotiable authentication layer. [source,c#] ``` using Coveo.Framework.Processor; using Coveo.SearchProvider.Pipelines; using Coveo.SearchProvider.Processors; namespace MyNamespace { public class DelegateToBinaryDataProcessorBasedOnDomain : IProcessor { public void Process(CoveoPostItemProcessingPipelineArgs args) { // This can be whatever condition you wish. var belongsToAuthenticatedDomain = args.CoveoItem.Uri.Contains("some.domain"); if (belongsToAuthenticatedDomain) { var processor = new BasicHtmlContentInBodyProcessor(); processor.Process(args); } else { var processor = new FetchPageContentProcessor(); // If additional configuration is needed on the processor, such as inbound filters, it can be created programatically here. processor.Process(new CoveoGetBinaryDataPipelineArgs(args.OutputCoveoItems)); } } } } ``` To implement the above processor selection logic, you would: . Create the processor. . Remove the `FetchPageContentProcessor` from the `coveoGetBinaryData` pipeline if it's there, so the out-of-the-box behavior isn't executed. . [Enable](https://docs.coveo.com/en/2559#enabling-the-processor-in-sitecore) the `DelegateToBinaryDataProcessorBasedOnDomain` processor in the `coveoPostItemProcessingPipeline` pipeline. For example, if your processor DLL file is called `MyNamespace.dll`, your configuration would look like this: ```xml ... ... ```