private static void AppendFieldComparers <TItem>(FieldComparerGroup <TItem> sourceFieldComparerGroup, FieldComparerGroup destinationFieldComparerGroup)
            where TItem : class
        {
            if ((sourceFieldComparerGroup.Terms.Count() > 1) && (sourceFieldComparerGroup.Operator == LogicalOp.None))
            {
                throw new ArgumentException($"The logical operator may not be {nameof(LogicalOp.None)} when more than one term is specified!");
            }

            foreach (SearchTerm <TItem> sourceSearchTerm in sourceFieldComparerGroup.Terms)
            {
                FieldComparer <TItem> fieldComparer = sourceSearchTerm as FieldComparer <TItem>;
                if (fieldComparer != null)
                {
                    destinationFieldComparerGroup.FieldComparers.Add(new FieldComparer
                    {
                        FieldName    = fieldComparer.FieldName,
                        Operator     = fieldComparer.Operator,
                        CompareValue = fieldComparer.CompareValue
                    });
                }
                else
                {
                    FieldComparerGroup <TItem> sourceSubFieldComparerGroup = sourceSearchTerm as FieldComparerGroup <TItem>;
                    if (sourceSubFieldComparerGroup != null)
                    {
                        var destinationSubFieldComparerGroup = new FieldComparerGroup
                        {
                            Operator = sourceSubFieldComparerGroup.Operator
                        };

                        AppendFieldComparers(sourceSubFieldComparerGroup, destinationSubFieldComparerGroup);

                        if (destinationSubFieldComparerGroup.FieldComparers.Count + destinationSubFieldComparerGroup.FieldComparerGroups.Count > 0)
                        {
                            destinationFieldComparerGroup.FieldComparerGroups.Add(destinationSubFieldComparerGroup);
                        }
                    }
                    else
                    {
                        throw new ArgumentException($"Unknown search term: {sourceSearchTerm?.GetType()}");
                    }
                }
            }
        }
        private static Expression ExpressionFromFieldComparerGroup <T>(FieldComparerGroup fieldComparerGroup, ParameterExpression param, SearchIntent searchIntent)
            where T : class
        {
            Expression search = null;

            foreach (FieldComparer fieldComparer in fieldComparerGroup.FieldComparers)
            {
                // The expression tree we are building
                PropertyInfo propertyInfo = GetPropertyWithValidation <T>(fieldComparer.FieldName);

                if (propertyInfo == null)
                {
                    throw new Exception($"The column '{fieldComparer.FieldName}' does not exist in my dataclass");
                }

                object convertedSearchValue = GetConvertedSearchValue(propertyInfo, fieldComparer);

                Expression searchWithType;

                // ReSharper disable once PossibleNullReferenceException
                if (propertyInfo.PropertyType == typeof(Guid) && Guid.Empty.Equals((Guid)convertedSearchValue))
                {
                    searchWithType       = Expression.Field(null, typeof(Guid), "Empty");
                    convertedSearchValue = Guid.Empty;
                }
                else
                {
                    searchWithType = Expression.Constant(convertedSearchValue);
                }

                MemberExpression memberExpression = Expression.PropertyOrField(param, fieldComparer.FieldName);

                Expression body;
                switch (fieldComparer.Operator)
                {
                case CompareOp.Equals:
                    body = SearcherExpressionExtensions.Equals(memberExpression, searchWithType, searchIntent);
                    break;

                case CompareOp.NotEquals:
                    body = SearcherExpressionExtensions.NotEqual(memberExpression, searchWithType);
                    break;

                case CompareOp.GreaterThanOrEqual:
                    body = SearcherExpressionExtensions.GreaterThanOrEqual(memberExpression, searchWithType);
                    break;

                case CompareOp.LessThanOrEqual:
                    body = SearcherExpressionExtensions.LessThanOrEqual(memberExpression, searchWithType);
                    break;

                case CompareOp.LessThan:
                    body = SearcherExpressionExtensions.LessThan(memberExpression, searchWithType);
                    break;

                case CompareOp.GreaterThan:
                    body = SearcherExpressionExtensions.GreaterThan(memberExpression, searchWithType);
                    break;

                //case CompareOp.EqualsCaseSensitive:
                //    body = SearcherExpressionExtensions.Equal(memberExpression, searchWithType);
                //    break;
                case CompareOp.StartsWith:
                    body = SearcherExpressionExtensions.StartsWith(memberExpression, searchWithType, searchIntent);
                    break;

                //case CompareOp.StartsWithCaseSensitive:
                //    body = SearcherExpressionExtensions.StartsWith(memberExpression, searchWithType);
                //    break;
                case CompareOp.Contains:
                    body = SearcherExpressionExtensions.Contains(memberExpression, searchWithType, searchIntent);
                    break;

                //case CompareOp.ContainsCaseSensitive:
                //    body = SearcherExpressionExtensions.Contains(memberExpression, searchWithType);
                //    break;
                case CompareOp.EndsWith:
                    body = SearcherExpressionExtensions.EndsWith(memberExpression, searchWithType, searchIntent);
                    break;

                //case CompareOp.EndsWithOrdinalIgnoreCase:
                //    body = SearcherExpressionExtensions.EndsWithOrdinalIgnoreCase(memberExpression, searchWithType);
                //    break;
                case CompareOp.Match:
                    body = SearcherExpressionExtensions.Match(memberExpression, convertedSearchValue, searchIntent);
                    break;

                default:
                    throw new Exception($"Search term {fieldComparer.Operator} not supported");
                }

                if (search == null)
                {
                    search = body;
                }
                else
                {
                    search = fieldComparerGroup.Operator == LogicalOp.And
                        ? Expression.AndAlso(search, body)
                        : Expression.OrElse(search, body);
                }
            }

            foreach (FieldComparerGroup subFieldComparerGroup in fieldComparerGroup.FieldComparerGroups)
            {
                var subExpression = ExpressionFromFieldComparerGroup <T>(subFieldComparerGroup, param, searchIntent);
                if (search == null)
                {
                    search = subExpression;
                }
                else
                {
                    search = fieldComparerGroup.Operator == LogicalOp.And
                        ? Expression.AndAlso(search, subExpression)
                        : Expression.OrElse(search, subExpression);
                }
            }

            return(search ?? Expression.Constant(true));
        }