Skip to content

Instantly share code, notes, and snippets.

@ecwyne
Last active August 7, 2019 19:35
Show Gist options
  • Save ecwyne/e20c96fdba1750f2d5fb3670e35e378a to your computer and use it in GitHub Desktop.
Save ecwyne/e20c96fdba1750f2d5fb3670e35e378a to your computer and use it in GitHub Desktop.

Revisions

  1. ecwyne revised this gist Apr 22, 2019. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion Fauna Index Lessons Learned.md
    Original file line number Diff line number Diff line change
    @@ -105,7 +105,7 @@ Add(
    )
    )
    ```
    We are now able to sum all of the pre-computed commissions and return the total commission _all using only a single `read` operation_.
    We are now able to sum all of the pre-computed commissions and return the total commission _all using only a single `read` operation and minimizing bandwidth usage for the result_.

    # Key Takeaways
    - Try to keep the number of `read` operations to a minimum by avoiding using `Get` when possible.
  2. ecwyne revised this gist Apr 22, 2019. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion Fauna Index Lessons Learned.md
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,6 @@
    Consider the following example. We have a `sales` class that has instances with the following shape.

    ```json
    ```javascript
    {
    date: '2019-04-21',
    total: 1050,
  3. ecwyne created this gist Apr 22, 2019.
    120 changes: 120 additions & 0 deletions Fauna Index Lessons Learned.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,120 @@
    Consider the following example. We have a `sales` class that has instances with the following shape.

    ```json
    {
    date: '2019-04-21',
    total: 1050,
    salesperson: Ref(Class('users'), '1234')
    }
    ```

    Salespeople get a commission on each sale that is equal to 5% of the total. We're creating a page that can calculate the commission for a given salesperson between two dates.
    Eg. "How much commission did Susan earn between Jan 1st, 2019 and April 21st, 2019?"

    # First Try
    A first attempt may simply group all sales by salesperson
    ```javascript
    CreateIndex({
    name: 'salesperson_commission',
    source: Class('sales'),
    terms: [{field: ['data', 'salesperson']}]
    });
    ```
    ```javascript
    Filter(
    Map(
    Paginate(
    Match(Index('salesperson_commission'), Ref(Class('users'), '1234')),
    {size: 1000}
    ),
    ref => Get(ref),
    ),
    sale =>
    And(
    GTE(Select(['data', 'date'], sale), '2019-01-01'),
    LTE(Select(['data', 'date'], sale), '2019-04-21'),
    ),
    );

    ```
    The main problem with this index is that we're running `Get(ref)` on *every* sale associated with this salesperson. This could result in *many* reads and increase the cost of our query. Additionally, we still need to calculate the commission for each sale every time we run the query.

    # Second Try
    To reduce the number of reads we can add `values` to the index. This removes the need to `Get` every instance and also allows us to reduce the search space by using `after` in our pagination.
    ```javascript
    CreateIndex({
    name: 'salesperson_commission',
    source: Class('sales'),
    terms: [{field: ['data', 'salesperson']}],
    values: [
    {field: ['data', 'date']},
    {field: ['data', 'total']}
    ]
    });
    ```
    ```javascript
    Map(
    Filter(
    Paginate(
    Match(Index('salesperson_commission'), Ref(Class('users'), '1234')),
    { size: 1000, after: ['2019-01-01'] },
    ),
    values => LTE(Select([0], values), '2019-04-21'),
    ),
    values => Select([1], values),
    )
    ```
    This query returns an array of sales totals. All that's left is to sum them together and multiply by 5%.


    # Final Solution
    Now we will pre-compute the commission for each sale using a `binding`
    ```javascript
    CreateIndex({
    name: 'salesperson_commission',
    source: {
    class: Class('sales'),
    fields: {
    commission: Query(
    Lambda(
    'instance',
    Multiply(Select(['data', 'total'], Var('instance'), 0.05)),
    ),
    ),
    },
    },
    terms: [{ field: ['data', 'salesperson'] }],
    values: [
    { field: ['data', 'date'] },
    { binding: 'commission' }
    ],
    });

    ```
    ```javascript
    Add(
    Map(
    Filter(
    Paginate(
    Match(Index('salesperson_commission'), Ref(Class('users'), '1234')),
    { size: 1000, after: ['2019-01-01'] },
    ),
    values => LTE(Select([0], values), '2019-04-21'),
    ),
    values => Select([1], values),
    )
    )
    ```
    We are now able to sum all of the pre-computed commissions and return the total commission _all using only a single `read` operation_.

    # Key Takeaways
    - Try to keep the number of `read` operations to a minimum by avoiding using `Get` when possible.
    - Use `terms` to sort indexes and reduce the search space
    - Pre-compute values using `bindings` which can be as complex as required. The only limitation is that they reads/writes cannot be performed in bindings.

    # Extra Credit
    How would you change the index definition if the commission structure changed to the following?

    - $5 for sales up to $500
    - $10 for sales up to $1,000
    - $20 for any sale over $1,000