Try our conversational search powered by Generative AI!

FilterBuilder question matching comma seperated string with a list

Vote:
 

Hi guys,

my problem:

i have a checkbox property on a catalogitem. 

public class TippingProduct : BaseProductContent
    {        
        [EnumMany(typeof(TippingFrontEndCylinderHeadboardType))]        
        [Display(Order = 8,
            Name = "Headboard Type",
            GroupName = Logic.Constants.TabNames.Filters,
            Description = "Filter Headboard Type - Frontend Cylinder")]        
        public virtual string HeadboardType { get; set; }

}

the enum looks like this:

 public enum TippingFrontEndCylinderHeadboardType
    {
        Slanted,
        Straight
    }

now i want to filter on headboardtype

public static dynamic TippingProducts(TippingSearchData tippingSearchData)
        {
            IClient client = SearchClient.Instance;

            //start of the query
            var query = client.Search()
                .FilterForVisitor()
                .Filter(x => x.ShowInList.Match(true));

            
            //selected headboard type
            if (tippingSearchData.Headboard != null && tippingSearchData.Headboard.Any())
            {
                query = query.Filter(x => GetHeadBoardFilter(tippingSearchData.Headboard));
            }

            
            var res = query.GetContentResult();
            return new
            {
                items = res.Count()
           };
        }
private static FilterBuilder GetHeadBoardFilter(List headboards)
        {
            var filter = SearchClient.Instance.BuildFilter();
           // return headboards.Aggregate(filter, (current, headboard) => current.Or(x => x.HeadboardType.Match(headboard.ToString())));
            foreach (var headboard in headboards)
            {
                filter = filter.Or(tp => tp.HeadboardType.Split(',').Match(headboard.ToString()));
            }
            return filter;
        }

So the property HeadBoardType of my TippingProduct saves the data as comma seperated string. 

When searching using epifind i sent a List headboards and want a filter on all my TippingProduct pages where the HeadBoardType property contains one of the items from the list. 

You can see in my filter builder i had a aggregate uncommented. So that works good if each TippingProduct only has 1 checkbox selected, because then its not a comma seperated string. But once 1 tipping product has 2 checkboxes selected then that one is not found. Which is logical because it will not match. But my second approach trying to split and then check doesnt work either. So i must be missing something trivial here. 

#195430
Jul 24, 2018 21:38
Vote:
 

Not sure if it's trivial or not. But you're missing how expressions work and how they're translated into Elastic queries.

First I would recommend adding an enumerable to your model and then re-index.

public virtual IEnumerable<string> HeadboardTypes => return HeaderboardType.Split(',');

Then you can write your query like this:

if (tippingSearchData.Headboard != null && tippingSearchData.Headboard.Any())
{
    query = query.Filter(x => x.HeaderboardTypes.In( tippingSearchData.Headboard));
}

Following code doesn't work,

tp.HeadboardType.Split(',').Match(headboard.ToString())

since Split cannot be translated into an query that Elastic understands. 

#195431
Edited, Jul 25, 2018 0:54
Vote:
 

Edit, you might have to create a string array of tippingSearchData.Headboard, not sure if there is an In-method that supports an array of enum.

Also, why are you not using enum flags? Since you're allowing multiple values. I think Find has support that when writing queries.

#195432
Jul 25, 2018 1:03
Vote:
 

Hi Johan,

you are absolutely right.. I'm still a nOOb at Epi Find and it was not trivial at all, but you helped me. 

Your tip about the "In" filter in combination with the enumerable added to my model was the correct solution. 

I indeed needed to convert my List<Enums> tippingSearchData.Headboard to a List<string> variant. 

I couldnt follow your last comment about using enum Flags. If i can improve my code more i would love to hear about what you were suggesting exactly. 

#195474
Jul 25, 2018 20:32
Vote:
 

First hit on SO https://stackoverflow.com/questions/8447/what-does-the-flags-enum-attribute-mean-in-c

#195492
Jul 26, 2018 14:48
Vote:
 

Oh i know what enum flags are, but i meant in combination with how you mean to use it using Epi find qeury in this case. How would a sample query look like using enum flags. 

#195493
Jul 26, 2018 15:21
Vote:
 

Something like this

public class TippingProduct : BaseProductContent
{
    [EnumMany(typeof(TippingFrontEndCylinderHeadboardTypes))]
    [Display]
    public virtual TippingFrontEndCylinderHeadboardTypes HeadboardTypes { get; set; }
}

[Flags]
public enum TippingFrontEndCylinderHeadboardTypes
{
    None = 0,
    Slanted = 1,
    Straight = 2
}

public class TippingSearchData
{
    public TippingFrontEndCylinderHeadboardTypes HeadboardTypes { get; set; }
}

public static dynamic TippingProducts(TippingSearchData tippingSearchData)
{
    IClient client = SearchClient.Instance;

    var query = client.Search<TippingProduct>()
        .FilterForVisitor()
        .Filter(x => x.ShowInList.Match(true));

    if (tippingSearchData.HeadboardTypes != TippingFrontEndCylinderHeadboardTypes.None)
    {
        query = query.Filter(x => x.HeadboardTypes.Match(tippingSearchData.HeadboardTypes));
    }

    var res = query.GetContentResult();

    return new
    {
        items = res.Count()
    };
}

Not sure what your EnumMany attribute and edirot supports, so it probably needs to be rewritten. But the main point is that there's no need for an array of enum values, since it's already supported by flags and bit-wise operations.

#195495
Jul 26, 2018 15:41
Vote:
 

Yes now i remember again that i did that, i wanted to make my property indeed of return the Enum instead of a string, but i got a error. 

[EnumMany(typeof(TippingFrontEndCylinderHeadboardType))]
        //[BackingType(typeof(PropertyString))]
        [Display(Order = 8,
            Name = "Headboard Type",
            GroupName = Logic.Constants.TabNames.Filters,
            Description = "Filter Headboard Type - Frontend Cylinder")]        
        public virtual TippingFrontEndCylinderHeadboardType HeadboardType { get; set; }

But i got this error The type Logic.Enums.TippingFrontEndCylinderHeadboardType can not be mapped to a MetaDataType . 

I thought maybe needed to add the backing type property string but with that active i got error: The property 'HeadboardType' on content type 'TippingProduct' is of type 'Logic.Enums.TippingFrontEndCylinderHeadboardType' that cannot be backed by PropertyString, it only only supports types String.

I think it must be my enumMany class that conflicts but could not figure out what. 

public class EnumManyAttribute : SelectManyAttribute, IMetadataAware
    {
        public Type EnumType { get; set; }

        public EnumManyAttribute(Type enumType)
        {
            EnumType = enumType;
        }

        public new void OnMetadataCreated(ModelMetadata metadata)
        {
            SelectionFactoryType = typeof(EnumSelectionFactory<,>).MakeGenericType(EnumType, typeof(string));
            base.OnMetadataCreated(metadata);
        }
    }

It basically a generic class 

public class EnumSelectionFactory<TEnum, TUnderlying> : BaseService, ISelectionFactory
    {
        public IEnumerable<ISelectItem> GetSelections(ExtendedMetadata metadata)
        {
            return Enum.GetValues(typeof(TEnum))
                      .Cast<TEnum>()
                      .Select(s => new SelectItem
                      {
                          Value = Convert.ChangeType(s, typeof(TUnderlying)),
                          Text = ValueName(s as Enum)
                      });
        }

        private static string ValueName(Enum value)
        {
            var culture = WebContext.Current.CurrentCulture;
            //var resourceKey = $"{value.GetType().FullName}.{value}";
            //var translationService = ServiceLocator.Current.GetInstance<LocalizationService>();
            return value.TranslateByCulture(culture);
            
        }
    }   

Because i needed to have the string localized , so i created this, but maybe you can see what could be the issue here. Because the Enum Flags method would be a lot better and easier to understand then my 'workaround'..

Thanks again so far for your input Johan.. Lots of gratitude and appreciation. 

#195532
Jul 27, 2018 13:52
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.