Filter on ContentAreaItems

Vote:
 

Hi,

I have a Find query where I search for a specific page type and I want to filter that query on items in the page's ContentArea. I´ll try to explain:

There is a page type called PartnerPage and on that page there is a ContentArea property called ProductAreas where editors can drag and drop pages of page type SystemTagPage. I need to filter on the SystemTagPage's Name property against a product area (string) which is selected from a list in the view.

This is the Find query:

var result = SearchClient.Instance.Search()
                                                  .Filter(x => x.Ancestors().Match(ContentReference.StartPage.ID.ToString()))
                                                  .Filter(filterBuilder)
                                                  .FilterForVisitor()
                                                  .OrderByDescending(x => x.StartPublish)
                                                  .GetPagesResult(LanguageSelector.AutoDetect());

And the filter (the filterBuilder variable in the query) is created like this:

var filterBuilder = SearchClient.Instance.BuildFilter();
            if (!string.IsNullOrWhiteSpace(country))
            {
                filterBuilder = filterBuilder.And(x => x.Country.Match(country));
            }
            if (!string.IsNullOrWhiteSpace(productArea))
            {
                filterBuilder = filterBuilder.And(x => x.ProductAreas.Items.Select(c => c.GetContent().Name).Match(productArea));
            }
            return filterBuilder;

I can´t get the filter to work, when debugging I see that the Field of the filter is called "GetContent.Name.ProductAreas.Items.Select$$string" and I guess that´s why I don´t get any hits, there is no property with that name. But I´m not sure how the filter should be written to accomplish this, any ideas? Might not even be possible since a ContentArea and its items are a bit different from "normal" properties.

My solution right now is to skip the filter for product area and do a Linq query on the finished result from Find instead but that doesn´t feel like a good solution, like this:

items.AddRange(from page in result
                    from item in page.ProductAreas.Items
                    where item.GetContent().Name == productArea
                    select page);

Any ideas and information is welcome! Thanks!

BR, Petra

#147068
Apr 05, 2016 17:08
Vote:
 

Been some time since I was working with the filtering bit but I think you have two options.

One is to index the content in the contentareas http://world.episerver.com/documentation/Items/Developers-Guide/EPiServer-Find/8/Integration/EPiServer-75/Indexing-content-in-a-content-area/

Or you could add the logic to the page during indexing and setting a bool property that you then filter on.

Hope that gives you some new angles to solving the issue.

/Petter

#147076
Edited, Apr 05, 2016 18:16
Vote:
 

Okay, thanks, I´ll try that. But I think the FilterBuilder code needs to change some how since the field in the created filter was called GetContent.Name.ProductAreas.Items.Select$$string and there will still not be any property with that name even if I index the content in the ContentArea, right?

//Petra

#147079
Apr 05, 2016 19:50
Vote:
 

Yes right you are. 

So going down option one and adding the content in the contentarea to the index should give a a json response that looks something like

{
...
"MainContentArea": {
"ReferencedPermanentLinkIds": [
"bla bla bla"
],
"Contents": [
{
....

"MasterLanguage.Name$$string": "sv",
"ContentTypeName$$string": "YourSuperDuperBlock",
"Status": 4,
...
}
... 
}

and in that case the filter would be 

            filterBuilder = filterBuilder.And(x => x.MainContentArea.Contents.Select(c => c.ContentTypeName).Match(productArea));
/Petter
    
#147080
Apr 05, 2016 20:23
Vote:
 

Okay, thanks, the indexing of the items in the content area worked fine, but the Linq query didn´t work. Contents is marked as deprecated and the Select part didn´t work, I got an error saying "The type arguments for method 'Enumerable.Select<TSource, TResult>(IEnumerable<TSource>, Func<TSource, TResult>)' cannot be inferred from the usage. Try specifying the type arguments explicitly." and I couldn´t get it to compile without changing it to:

filterBuilder = filterBuilder.And(x => x.ProductAreas.Items.Select(c => c.GetContent().ContentTypeName()).Match(productArea));

This led me to getting a field name on the filter called GetContent.ContentTypeName.ProductAreas.Items.Select$$string instead of the previous which was GetContent.Name.ProductAreas.Items.Select$$string.

Starting to think that what I want to do isn´t possible...

//Petra

#147121
Apr 06, 2016 15:19
Vote:
 

Right well that's a shame. 

Guess we have to do it on indexing then instead. 

Just add a prop to your pagetype that runs through the items and checks if you got a productArea or not. Then you should be able to use that prop in your querying.

/Petter

#147125
Apr 06, 2016 15:40
Vote:
 

I haven´t tried that yet, but I still won´t get the names of the page types in the content area, just a true or false if there are items in the content area or not, right? I need to filter on the name of the page type in the content area.

//Petra

#147126
Apr 06, 2016 15:44
Vote:
 

Well was thinking something like this.

   public bool HasSystemTagPageWithRightName
        {
            get
            {
                var result = false;
                
                if (ProductAreas != null && ProductAreas.Items.Any())
                {
                    foreach (var item in ProductAreas.Items)
                    {
                        var blockContent = item.GetContent() as SystemTagPage;
                        if (blockContent != null && !string.IsNullOrEmpty(blockContent.Name))
                        {
                            result = true;
                        }
                    }
                }
                
				return result;
            }
        }

Then in your filter you would just modify the builder to look at the bool

#147130
Apr 06, 2016 16:29
Vote:
 

I suspect we are thinking of different things, with this solution it´s required that the product area to use in the filter is always the same and it´s not. I don´t know what product area to filter on until the end user chooses that in a select list, so one time it might be x and the next time y. So I can´t really have a property on the page type that does the matching.

//Petra

#147152
Apr 07, 2016 8:50
Vote:
 

Right so I missunderstood what you where after.

But if you modify the code abow to just add the names of the SystemTagPage as a list of strings and then filter on that?

public List<string> SystemTagPages
     {
         get
         {
             var result = new List<string>();
              
             if (ProductAreas != null && ProductAreas.Items.Any())
             {
                 foreach (var item in ProductAreas.Items)
                 {
                     var blockContent = item.GetContent() as SystemTagPage;
                     if (blockContent != null && !string.IsNullOrEmpty(blockContent.Name))
                     {
                         result = result.Add(blockContent.Name)
                     }
                 }
             }
              
             return result;
         }
     }
#147229
Apr 08, 2016 8:22
Vote:
 

Hmm, that would probably be a solution. Why didn´t I think of that :) Performance wise I won´t actually get rid of the iteration that I now do on the result after the Find question is finished since it´ll be in the property of the page type instead, but at least I´ll get rid of the ugly post-query Linq-filter which was my goal so, win!

I´ll try it out! Thanks Petter! (will mark as solution once verified :))

#147270
Apr 08, 2016 16:09
This topic was created over six months ago and has been resolved. If you have a similar question, please create a new topic and refer to this one.
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.