London Dev Meetup Rescheduled! Due to unavoidable reasons, the event has been moved to 21st May. Speakers remain the same—any changes will be communicated. Seats are limited—register here to secure your spot!

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.