Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
1933173
WIP
paddymul Nov 10, 2022
63046b2
WIP
paddymul Nov 10, 2022
2cdfdc6
feat: initial add of narrative ExpressionLang explanation
paddymul Nov 10, 2022
5f5dfad
feat: WIP two small fies
paddymul Nov 11, 2022
f54a0f4
feat: better names for validate functions
paddymul Nov 11, 2022
d6504ed
updated docs to reflect 'ErrorWhen' and other MessageConditional func…
paddymul Nov 11, 2022
f20dc5f
chore: jest now searches examples directory
paddymul Nov 15, 2022
4dbb90f
feat: updates to expression lang
paddymul Nov 15, 2022
fbcb4ca
feat: sheetTester that cna test for info messages
paddymul Nov 15, 2022
52a9ca6
feat: testing expression language on an actual sheet
paddymul Nov 15, 2022
be77d85
chore: finished typing fixes on validate functions
paddymul Nov 15, 2022
6445b2b
merged in latest main
paddymul Nov 21, 2022
89736bd
removed trash file
paddymul Nov 21, 2022
22086ba
WIP
paddymul Nov 22, 2022
68a7ffd
first working sheetcompute test in -starter
paddymul Nov 22, 2022
435f345
WIP
paddymul Nov 22, 2022
606544a
WIP
paddymul Nov 22, 2022
94f8958
all tests converted and pass
paddymul Nov 22, 2022
0693b7d
all ExpressionLanguage converted to Expr
paddymul Nov 22, 2022
4d0a857
feat: working examples of SheetTester being used with GroupByField
paddymul Nov 22, 2022
ea1a7ca
added key explanation note
paddymul Nov 22, 2022
ab39185
chore:merged latest main
paddymul Dec 1, 2022
4654b27
added more tests
paddymul Dec 1, 2022
8acc6fe
chore:bumped to latest versions
paddymul Dec 6, 2022
6132746
non-unique works
paddymul Dec 16, 2022
a2b155c
merged in latest main
paddymul Dec 20, 2022
2e47a6f
added NonUnique EXPR
paddymul Dec 20, 2022
18757a4
updated the argument so that it's always 'funcname', 'rowset', extra …
paddymul Dec 21, 2022
15ee8f0
added a complex unique test
paddymul Dec 22, 2022
14958c5
merged in latest main
paddymul Dec 28, 2022
4aced6a
example sheet with SumField
paddymul Dec 28, 2022
17d1e56
WIP, sample deploy showing row being returned by GroupConstraintItem
paddymul Dec 29, 2022
bfef5ca
WIP
paddymul Jan 3, 2023
6e8dc83
WIP
paddymul Jan 4, 2023
4863ee6
merged in latest main
paddymul Jan 18, 2023
959f8c8
WIP
paddymul Jan 18, 2023
4408de9
reduced diff to main
paddymul Jan 18, 2023
4de7230
updating the directory structure to be more in line with main
paddymul Jan 19, 2023
1b23b71
naming more in common with main
paddymul Jan 19, 2023
0b859b8
updates to tests to show a demo of making an HTTP request and stuffin…
paddymul Jan 20, 2023
44d8172
added Uniq
paddymul Jan 20, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 145 additions & 0 deletions examples/fields/GroupByField.README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
# The expression language by example

The expression language is the flatfile creation that allows concise code to be evaluated with the same syntax, whether that code is executing in a test in node, in a datahook in a validate function, or on our server for a sheetCompute.

Let's dive in

```
When(GreaterThan(Val(), 5), Error(" val is greater than 5"))
```

What's going on there... a lot

`Val()` is a special function, that references a variable for this interpret context.. more on that later.
We'll start from the inside `GreaterThan(Val(), 5)` runs `Val()` and returns true or false if it is greater or less than 5.

Let me explain a simpler expression first
```
When(true, Error(" val is greater than 5")
```
`When` is a conditional, based on the value of the first argument, it will conditionally return the expression in the second argument.

This all seems complex and a long way of saying
`if(val > 5) {return error("val greater than 5");}`

The magic happens with this, because we aren't actually executing the code immediately. We can execute the code immmediately, or we can send that same parse tree to the server and execute it there.

for a `validate` hook we execute it immediately, and `Val()` is the value being validated.

For a sheetCompute, or groupConstraint, we send the parse tree to the server for execution there.

# The expression language for validate Field hooks

The easiest place to use the expression language is for `validate` field hooks.

let's take the above example and put it in a test Sheet
```
const SimpleSheet = new Sheet(
'SimpleSheet',
{age: NumberField({validate: ErrorWhen(LessThan(Val(), 21), "too young to drink")},
)
```


note, when used in a `validate` function, there are special MessageConditionals `ErrorWhen`,`ErrorUnless`, `WarnWhen`, `WarnUnless`, `InfoWhen`, `InfoUnless` that should be used. `ErrorWhen` returns a function that accepts a value and calls the validate function on the expression, making `Val()` a valid way of accessing the argument for `validate`.

# Expression language for sheet wide operations

There are many operations and validations that need to be run across an entire sheet. These leverage expression language to allow declarative statements describing what you want done. GroupByField is the only way to accomplish this now.

When thinking about using GroupByField, keep in mind the order of operations

1. field cast
2. field compute
3. recordCompute
4. batchRecordsCompute
5. field validate
6. field egressFormat
----- Back to the server ----
7. uniqueness checks
8. sheetCompute (groupByField)


Given that order of operations. Use the other hooks to prepare data for the groupByField. The other hooks are better suited to most tasks, but sheetwide operations require GroupByField.

GroupByField is best suited for simple calculations (sum, count) and validating properties about a group.

When developing with GroupByField, we recommend using local testing. Look at [GroupByField.spec.ts]( https://flatfile.com/docs/get-started/quickstart/)

# GroupByField


```
const ItemSummary = new Sheet(
'ItemSummary',
{
orderID: NumberField({}), //parent item
itemId: NumberField({}),
price: NumberField({}),
orderTotal: GroupByField('orderId', compute: Sum(GetColumn('price', Group())))
}
)
```

this sheet looks fairly straight forward until we get to GroupByField.

the first argument is the column to group on
`compute` for GroupByField will be interpreted on the Flatfile server, once per group, with all relevant rows exposed as `Group()`. `GetColumn` returns a list of values for the column from the set sent in. `Sum` returns the sum of values passed in


# GroupConstraintSet
```
const MixedRowSheet = new Sheet(
'MixedRows',
{
parentId: NumberField({}),
rowType: TextField({}), //can be header or item
colForHeader: TextField({}), // required per header row
colForItem: NumberField({}), //unique per parentId for rowType = item
//the following field is just a placeholder to recieve errors
validityErrors: GroupByField('parentId', compute:
GroupConstraintSet(
[Group(), When(Equal(Count(Match({'rowType':'header'}), Group())), 0),
Error("At least 1 rowType='header' required")],
[Group(), When(Equal(Count(Match({'rowType':'item'}), MatchResult())), 0),
Error("At least 1 rowType='item' required")],
[Match({'rowType':'item'}),
Unless(Equal(Count(MatchResult()), Count(Uniq(GetColumn('colForItem', MatchResult())))),
Error("colForItem must be unique across parentId"))]))
}
)
```


Wow, a lot going on there
`GroupConstraintSet` -- take arguments as (applicableSet, condition), add all Messages together and apply to field.

note we use `When` here to differentiate from `ValidateWhen`. Still not sure how to handle this naming collision.

for the first argument
```
[Group(), When(Equal(Count(Match({'rowType':'header'}), Group())), 0),
Error("At least 1 rowType='header' required")],
```
this will match each row in Group with `rowType`=`'header'` and count them. if the count is 0, add an Error message of "header required".


```
[Group(), When(Equal(Count(Match({'rowType':'item'}), MatchResult())), 0),
Error("At least 1 rowType='item' required")],
```
the second argument does much the same for rowType. note `MatchResult()` which is the same as the condition used as the first argument of the array


```
[Match({'rowType':'item'}),
Unless(Equal(Count(MatchResult()), Count(Uniq(GetColumn('colForItem', MatchResult())))),
Error("colForItem must be unique across parentId"))]))
```
the third argument only applies to rows where `rowType`=`'item'`. this enforces that `colForItem` is unique within the group.






Loading