Finding products using properties on Products and Variants.

Vote:
 

Hello!

I am wanting to search for products and variants in Episerver Find. I have inherited from ProductContent and VariationContent and created an index. I am able to pull back products using code like this:

var productSearch = SearchClient.Instance.Search<FoodserviceProduct>()
.FilterFacet("BrandFacet", x => x.Brand.Match("Acme"))

This returns results successfully. However, in addition to being able to search by properties on my ProductContent, I also need to be able to search on properties that are apart of the product variants (VariationContent).

Can someone please help me understand how to do that? In other words, if my Product looks like this:

*Product Name
*Brand

and that product has several variants underneath it with properties like this:

*Size
*Color

How do I construct my query so that I can search based on Brand and Color (for instance)?

#206832
Aug 30, 2019 22:11
Vote:
 

You can create extension methods on your product content like .Colors() or .Sizes() which basically loads the variants and get the properties of variants. Then include thoses in your convention and use them to match in your query

#206839
Aug 31, 2019 16:33
Vote:
 

@Quan Mai, thanks for your response.

I considered doing something like you suggested, but that approach is not ideal from the standpoint that the built-in .Take() and .Skip() methods won't work for the case where I need to have paged data. I'll have to pull back the entire result set with all variants, filter "client" side, and then calculate which records to provide based on what page the user is requesting.

I found this page: https://world.episerver.com/documentation/developer-guides/find/NET-Client-API/searching/nested-queries/ which seems to present the concept that I am asking about, but I don't know how to create my ProductContent and VariationContent in a way that allows me to create filters in the same way that is being described.

#206842
Aug 31, 2019 19:25
Vote:
 

You can always use extension methods to index custom fields such as described https://world.episerver.com/documentation/developer-guides/find/NET-Client-API/Customizing-serialization/Including-fields/ which can index some custom information such as the fields from the varaitns that you want added to the main Product model. Then you can just filter directly of the parent object. Or even create a custom object with all the data you need, that's what we did for a recent project.

https://world.episerver.com/blogs/scott-reed/dates/2019/3/episerver-find---performance-reloading-custom-data-and-getcontent-vs-getcontentresult/

#206846
Sep 02, 2019 10:04
Vote:
 

@Scott Reed, thanks for the reply!

Following along on your example using IInitializableModule: How does the data get re-indexed if/when it changes? In our case, we'll be loading new product catalog content via Commerce API. Is there an API that I can use to force a re-indexing?

Related question: I found this article: https://world.episerver.com/documentation/developer-guides/commerce/search/find-integration/indexing-variants-in-a-product-document/, specifically under the heading "Including related variant content items in product content", but it does not show how I can consume that on the SearchClient side. Do you know how I would use that?

Related question: I am seeing some confusing messaging on the Episerver Find .Net Client API https://world.episerver.com/documentation/class-libraries/find/ "Note: Class libraries for versions 9 and higher are not available for Episerver Find.". How should I interpret that? Is there another place I should be looking for up to date Client API docs?

#206862
Sep 02, 2019 16:28
Vote:
 

Extension methods will be indexed whenever the usual indexing happens. So that will be on a change to the product or during the reindexing job. Obviously if content is changing on the variant that you want updating on the product you'll have to handle that. All you need to do is hook in to the publishing events via an initialziation module and when it's a variant trigger the product to reindex. Then any custom fields will be updated as normal.

The class libraries aren't really that useful, I suggest using ILSpy or DotPeek to navigate through the assemblies. You should always follow https://world.episerver.com/documentation/developer-guides/find/ for any implementation

#206863
Sep 02, 2019 16:32
Vote:
 

I accepted Quan's answer. In addition, here is some sample code I used to use the answer in practice:

* Create an initialization module to make Episerver Find aware of the extension methods I want to index along with other ProductContent data:

[InitializableModule]
[ModuleDependency(typeof(FindCommerceInitializationModule))]
public class FindInitializationModule : IConfigurableModule
{
public void Initialize(InitializationEngine context)
{
}
public void Uninitialize(InitializationEngine context)
{
}
public void ConfigureContainer(ServiceConfigurationContext context)
{
context.Services.AddTransient<CatalogContentClientConventions, SiteCatalogContentClientConventions>();
}
}

public class SiteCatalogContentClientConventions : CatalogContentClientConventions
{
// Informs the find index that we want to add some properties from the Variants to the index.
protected override void ApplyProductContentConventions(TypeConventionBuilder<ProductContent> conventionBuilder)
{
base.ApplyProductContentConventions(conventionBuilder);
conventionBuilder
.IncludeField(x => x.Colors())
}
}

An example of an extension method:

public static class FoodserviceProductExtensions
{
private static Injected<IRelationRepository> _relationRepository;
private static Injected<IContentLoader> _contentLoader;

// These extension methods are used to load additional data into the Find index.
// Each extension method will return data that will be indexed along with the product definition
// in the Find index. These fields need to be included in the index by adding to FindInitializationModule class.
public static IEnumerable<string> Colors(this ProductContent product)
{
var variants = product.GetVariants(_relationRepository.Service)
.Select(contentLink => _contentLoader.Service.Get<FoodserviceVariation>(contentLink));

return variants.Where(x => x.ProductColor != null).Select(x => x.ProductColor).ToList();
}

}

#207272
Sep 16, 2019 13:51
Quan Mai - Sep 16, 2019 14:07
Appreciate that you go back and share your solution :)