Skip to content

Commit

Permalink
Add a few new Filter_Conditions (#8539)
Browse files Browse the repository at this point in the history
- Closes #8045
  • Loading branch information
radeusgd committed Dec 16, 2023
1 parent 9428d12 commit d4714af
Show file tree
Hide file tree
Showing 18 changed files with 161 additions and 22 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -595,6 +595,8 @@
- [Initial Enso Cloud APIs.][8006]
- [Errors thrown inside `map` are wrapped in `Map_Error`.][8307]
- [Support for loading big Excel files.][8403]
- [Added new `Filter_Condition`s - `Equal_Ignore_Case`, `Is_Nan`, `Is_Infinite`
and `Is_Finite`.][8539]

[debug-shortcuts]:
https://github.com/enso-org/enso/blob/develop/app/gui/docs/product/shortcuts.md#debug
Expand Down Expand Up @@ -852,6 +854,7 @@
[8235]: https://github.com/enso-org/enso/pull/8235
[8307]: https://github.com/enso-org/enso/pull/8307
[8403]: https://github.com/enso-org/enso/pull/8403
[8539]: https://github.com/enso-org/enso/pull/8539

#### Enso Compiler

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import project.Any.Any
import project.Data.Locale.Locale
import project.Data.Set.Set
import project.Data.Text.Case_Sensitivity.Case_Sensitivity
import project.Data.Text.Regex.Regex
Expand Down Expand Up @@ -38,6 +39,21 @@ type Filter_Condition
## Is between (inclusive) two values (or columns, in case of Table operations)?
Between lower:Any upper:Any

## Is equal to another value, ignoring case (Text only)?

? Table Operations
It accepts a Text value to check if the value equals to it. In case of
Table operations, it can accept another column - then the corresponding
values from the source column and the provided column are checked.

! Unicode Equality
The definition of equality includes Unicode canonicalization. I.e. two
texts are equal if they are identical after canonical decomposition.
This ensures that different ways of expressing the same character in
the underlying binary representation are considered equal.
@locale Locale.default_widget
Equal_Ignore_Case (to : Text | Any) (locale:Locale=Locale.default)

## Does the value start with a prefix (Text only)?

? Table Operations
Expand Down Expand Up @@ -76,6 +92,17 @@ type Filter_Condition
## Is not equal to Nothing?
Not_Nothing

## Is the value a NaN (Number only)?
Is_Nan

## Is the value infinite (Number only)?
Is_Infinite

## Is the value finite (Number only)?

Finite numbers are ones that are not infinite nor NaN.
Is_Finite

## Is the value equal to True (Boolean only)?
Is_True

Expand Down Expand Up @@ -167,6 +194,7 @@ type Filter_Condition
Not_Equal value -> !=value
Between lower upper -> elem ->
(lower <= elem) && (elem <= upper)
Equal_Ignore_Case value locale -> elem-> elem.equals_ignore_case value locale
Starts_With prefix case_sensitivity -> _.starts_with prefix case_sensitivity
Ends_With suffix case_sensitivity -> _.ends_with suffix case_sensitivity
Contains substring case_sensitivity -> _.contains substring case_sensitivity
Expand All @@ -177,6 +205,9 @@ type Filter_Condition
Not_Nothing -> elem -> case elem of
Nothing -> False
_ -> True
Is_Nan -> .is_nan
Is_Infinite -> .is_infinite
Is_Finite -> .is_finite
Is_True -> ==True
Is_False -> ==False
Is_Empty -> elem -> case elem of
Expand Down Expand Up @@ -215,26 +246,32 @@ type Filter_Condition
Greater value -> ">" + value.to_display_text
Not_Equal value -> "!=" + value.to_display_text
Between lower upper -> "Between " + lower.to_display_text + " And " + upper.to_display_text
Equal_Ignore_Case value locale ->
suffix = if locale == Locale.default then "" else " (within locale " + locale.to_display_text + ")"
"Equal Ignore Case " + value.to_display_text + suffix
Starts_With prefix case_sensitivity -> "Starts With " + prefix.to_display_text + (render_case case_sensitivity)
Ends_With suffix case_sensitivity -> "Ends With " + suffix.to_display_text + (render_case case_sensitivity)
Contains substring case_sensitivity -> "Contains " + substring.to_display_text + (render_case case_sensitivity)
Not_Contains substring case_sensitivity -> "Not Contains " + substring.to_display_text + (render_case case_sensitivity)
Not_Contains substring case_sensitivity -> "not Contains " + substring.to_display_text + (render_case case_sensitivity)
Is_Nothing -> "is Nothing"
Not_Nothing -> "is Not Nothing"
Not_Nothing -> "is not Nothing"
Is_Nan -> "is NaN"
Is_Infinite -> "is Infinite"
Is_Finite -> "is Finite"
Is_True -> "is True"
Is_False -> "is False"
Is_Empty -> "is Empty"
Not_Empty -> "is Not Empty"
Not_Empty -> "is not Empty"
Like sql_pattern -> "Like " + sql_pattern.to_display_text
Not_Like sql_pattern -> "Not Like " + sql_pattern.to_display_text
Not_Like sql_pattern -> "not Like " + sql_pattern.to_display_text
Is_In values -> "is in " + values.to_display_text
Not_In values -> "is not in " + values.to_display_text
"Filter Condition: " + condition

## PRIVATE
Creates a Single_Choice Widget for delimiters.
default_widget : Boolean -> Boolean -> Boolean -> Boolean -> Widget
default_widget include_comparable=True include_text=True include_boolean=True include_nullable=True =
default_widget : Boolean -> Boolean -> Boolean -> Boolean -> Boolean -> Widget
default_widget include_comparable=True include_text=True include_boolean=True include_nullable=True include_numeric=True =
options_builder = Vector.new_builder
if include_comparable then
options_builder.append "Less"
Expand All @@ -244,6 +281,10 @@ type Filter_Condition
options_builder.append "Greater"
options_builder.append "Not_Equal"
options_builder.append "Between"
if include_numeric then
options_builder.append "Is_Nan"
options_builder.append "Is_Infinite"
options_builder.append "Is_Finite"
if include_boolean then
options_builder.append "Is_True"
options_builder.append "Is_False"
Expand All @@ -253,6 +294,7 @@ type Filter_Condition
if include_text then
options_builder.append "Is_Empty"
options_builder.append "Not_Empty"
options_builder.append "Equal_Ignore_Case"
options_builder.append "Starts_With"
options_builder.append "Ends_With"
options_builder.append "Contains"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,13 @@ type Number
_ : Float -> Double.isInfinite self
_ -> False

## GROUP Math
Checks if the given number is finite, i.e. not infinite nor NaN.
is_finite : Boolean
is_finite self = case self of
_ : Float -> (Double.isInfinite self).not && (Double.isNaN self).not
_ -> True

## GROUP Math
Returns the sign of the number.
signum : Integer
Expand Down
5 changes: 3 additions & 2 deletions distribution/lib/Standard/Base/0.0.0-dev/src/Data/Text.enso
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ type Text

Arguments:
- that: The text to compare `self` for case-insensitive equality with.
- locale: The locale to use for case-insensitive comparison.

Two texts are considered equal ignoring case if they are of the same length
and corresponding characters are equal ignoring case.
Expand All @@ -75,7 +76,7 @@ type Text
(('É' . equals_ignore_case 'é') && ('é' . equals_ignore_case 'e\u0301')) == True
@locale Locale.default_widget
equals_ignore_case : Text -> Locale -> Boolean
equals_ignore_case self that locale=Locale.default =
equals_ignore_case self (that : Text) (locale : Locale = Locale.default) =
Text_Utils.equals_ignore_case self that locale.java_locale

## PRIVATE
Expand All @@ -85,7 +86,7 @@ type Text
used to perform case-insensitive comparisons.
@locale Locale.default_widget
to_case_insensitive_key : Locale -> Text
to_case_insensitive_key self locale=Locale.default =
to_case_insensitive_key self (locale : Locale = Locale.default) =
Text_Utils.case_insensitive_key self locale.java_locale

## Compare two texts to discover their ordering.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ type Case_Sensitivity

Arguments:
- locale: The locale used for the comparison.
Insensitive locale=Locale.default
Insensitive (locale : Locale = Locale.default)

## PRIVATE
Convert Case_Sensitivity to a friendly string.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -562,7 +562,7 @@ unify_condition_for_index_of (condition : Date | Filter_Condition | Function) =

## PRIVATE
date_range_default_filter_condition_widget =
Filter_Condition.default_widget include_text=False include_boolean=False include_nullable=False
Filter_Condition.default_widget include_text=False include_boolean=False include_numeric=False include_nullable=False

## PRIVATE
Vector.from (that:Date_Range) = that.to_vector
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ type Column
- other: The value to compare `self` against. If `other` is a column, the
comparison is performed pairwise between corresponding elements of
`self` and `other`.
- locale: The locale to use for the case-insensitive comparison.

Returns a column with results of comparing this column's elements against
`other`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ type Dialect
Unimplemented.throw "This is an interface only."

## PRIVATE
Specifies if the Database distinguishes a seprarate `NaN` value for
Specifies if the Database distinguishes a separate `NaN` value for
floating point columns. Some databases will not be able to distinguish
NaN from NULL.
supports_separate_nan : Boolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ type Column
- other: The value to compare `self` against. If `other` is a column, the
comparison is performed pairwise between corresponding elements of
`self` and `other`.
- locale: The locale to use for the case-insensitive comparison.

Returns a column with results of comparing this column's elements against
`other`.
Expand Down
22 changes: 13 additions & 9 deletions distribution/lib/Standard/Table/0.0.0-dev/src/Errors.enso
Original file line number Diff line number Diff line change
Expand Up @@ -424,15 +424,19 @@ type Invalid_Value_Type

Create a human-readable version of the error.
to_display_text : Text
to_display_text self = case self of
Invalid_Value_Type.Column expected actual related_column ->
"Expected type "+expected.to_display_text+", but got a column ["+related_column+"] of type "+actual.to_display_text+"."
Invalid_Value_Type.Value expected actual value ->
"Expected type "+expected.to_display_text+", but got a value "+value.to_display_text+" of type "+actual.to_display_text+"."
Invalid_Value_Type.Not_Ordered actual ->
"Type "+actual.to_display_text+" does not support comparisons."
Invalid_Value_Type.Incomparable type_1 type_2 ->
"Types "+type_1.to_display_text+" and "+type_2.to_display_text+" cannot be compared to each other."
to_display_text self =
format_expected_type expected = case expected of
_ : Text -> expected + " type"
_ -> "type " + expected.to_display_text
case self of
Invalid_Value_Type.Column expected actual related_column ->
"Expected "+(format_expected_type expected)+", but got a column ["+related_column+"] of type "+actual.to_display_text+"."
Invalid_Value_Type.Value expected actual value ->
"Expected "+(format_expected_type expected)+", but got a value "+value.to_display_text+" of type "+actual.to_display_text+"."
Invalid_Value_Type.Not_Ordered actual ->
"Type "+actual.to_display_text+" does not support comparisons."
Invalid_Value_Type.Incomparable type_1 type_2 ->
"Types "+type_1.to_display_text+" and "+type_2.to_display_text+" cannot be compared to each other."

## An error representing an invalid JSON format for conversion.
type Invalid_JSON_Format
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ type Table_Ref
Filter_Condition.Greater value -> Filter_Condition.Greater (self.resolve value)
Filter_Condition.Equal_Or_Greater value -> Filter_Condition.Equal_Or_Greater (self.resolve value)
Filter_Condition.Between lower upper -> Filter_Condition.Between (self.resolve lower) (self.resolve upper)
Filter_Condition.Equal_Ignore_Case value locale -> Filter_Condition.Equal_Ignore_Case (self.resolve value) locale
Filter_Condition.Starts_With prefix case_sensitivity -> Filter_Condition.Starts_With (self.resolve prefix) case_sensitivity
Filter_Condition.Ends_With prefix case_sensitivity -> Filter_Condition.Ends_With (self.resolve prefix) case_sensitivity
Filter_Condition.Contains prefix case_sensitivity -> Filter_Condition.Contains (self.resolve prefix) case_sensitivity
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ make_filter_column source_column filter_condition on_problems = case filter_cond
Greater value -> (source_column > value)
Between lower upper -> source_column.between lower upper
# Text
Equal_Ignore_Case value locale ->
source_column.equals_ignore_case value locale
Starts_With prefix case_sensitivity ->
source_column.starts_with prefix case_sensitivity
Ends_With suffix case_sensitivity ->
Expand All @@ -54,6 +56,16 @@ make_filter_column source_column filter_condition on_problems = case filter_cond
source_column.like pattern
Not_Like pattern ->
source_column.like pattern . not
# Numeric
Is_Nan -> source_column.is_nan
Is_Infinite -> source_column.is_infinite
Is_Finite ->
is_infinite_column = source_column.is_infinite
is_nan_column = source_column.is_nan
## We check is_nan_column for error, since some Database backends may
actually not support it and throw Unsupported_Database_Operation here.
if is_nan_column.is_error then is_infinite_column.not else
(is_infinite_column || is_nan_column).not
# Vector
Is_In values -> source_column.is_in values
Not_In values -> source_column.is_in values . not
Original file line number Diff line number Diff line change
Expand Up @@ -120,12 +120,16 @@ make_filter_condition_selector table display=Display.Always =
builder.append (Option "Greater Than" fqn+".Greater" [["than", col_names]])
builder.append (Option "Greater Than Or Equal" fqn+".Greater_Or_Less" [["than", col_names]])
builder.append (Option "Between" fqn+".Between" [["lower", col_names], ["upper", col_names]])
builder.append (Option "Equals Ignore Case" fqn+".Equal_Ignore_Case" [["to", col_names]])
builder.append (Option "Starts With" fqn+".Starts_With" [["prefix", col_names]])
builder.append (Option "Ends With" fqn+".Ends_With" [["suffix", col_names]])
builder.append (Option "Contains" fqn+".Contains" [["substring", col_names]])
builder.append (Option "Not Contains" fqn+".Not_Contains" [["substring", col_names]])
builder.append (Option "Is Nothing" fqn+".Is_Nothing")
builder.append (Option "Is Not Nothing" fqn+".Not_Nothing")
builder.append (Option "Is Finite" fqn+".Is_Finite")
builder.append (Option "Is Infinite" fqn+".Is_Infinite")
builder.append (Option "Is NaN" fqn+".Is_Nan")
builder.append (Option "Is True" fqn+".Is_True")
builder.append (Option "Is False" fqn+".Is_False")
builder.append (Option "Is Empty" fqn+".Is_Empty")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,7 @@ spec setup =
t = table_builder [["A", [1, 2, 3]], ["B", ["a", "b", "c"]], ["C", [True, False, True]]]
r1 = t.at "A" . year
r1.should_fail_with Invalid_Value_Type
r1.catch . to_display_text . should_start_with "Expected type Date or Date_Time, but got a column [A] of type Integer"
r1.catch . to_display_text . should_start_with "Expected Date or Date_Time type, but got a column [A] of type Integer"
t.at "B" . month . should_fail_with Invalid_Value_Type
t.at "C" . day . should_fail_with Invalid_Value_Type

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ spec setup =
t2.set (Column_Operation.If (Column_Ref.Name "A") (Filter_Condition.Greater than=(Column_Ref.Name "B"))) "Z" . at "Z" . to_vector . should_equal [False, True]
t2.set (Column_Operation.If (Column_Ref.Name "A") (Filter_Condition.Greater than=(Column_Ref.Name "B")) (Column_Ref.Name "C") 0) "Z" . at "Z" . to_vector . should_equal [0, 55]
t2.set (Column_Operation.If "A" (Filter_Condition.Greater than="B") (Column_Ref.Name "C") 0) "Z" . at "Z" . to_vector . should_equal [0, 0]
t2.set (Column_Operation.If (Column_Ref.Name "A") (Filter_Condition.Equal_Ignore_Case "C") "==" "!=") "Z" . at "Z" . to_vector . should_equal ["!=", "=="]

t2.set (Column_Operation.If (Column_Ref.Name "A") (Filter_Condition.Is_In ["x", "a", "dd"]) "TT" "FF") "Z" . at "Z" . to_vector . should_equal ["TT", "FF"]
t2.set (Column_Operation.If (Column_Ref.Name "A") (Filter_Condition.Not_In ["x", "a", "dd"]) "TT" "FF") "Z" . at "Z" . to_vector . should_equal ["FF", "TT"]
Expand Down
Loading

0 comments on commit d4714af

Please sign in to comment.