Enhancing User Interactivity in Looker with Liquid - GSP934

Enhancing User Interactivity in Looker with Liquid - GSP934

Overview

Looker is a modern data platform in Google Cloud that lets you analyze and visualize your data interactively. You can use Looker to do in-depth data analysis, integrate insights across different data sources, build actionable data-driven workflows, and create custom data applications.

In this lab, you will explain how Liquid parameters and templated filters can be used to enhance interactivity by users in Looker and use Liquid parameters and templated filters to create dynamic dimensions and measures.

Objectives

In this lab, you will learn how to:

  • Create a dynamic dimension using a Liquid parameter

  • Create a dynamic dimension using templated filters

  • Create a dynamic measure using templated filters

Prerequisites:

Familiarity with LookML and Liquid are necessary. Completing the Understanding LookML in Looker skill badge quest, as well as the Getting Started with Liquid to Customize the Looker User Experience lab is recommended lab before beginning this lab.

Setup

Before you click the Start Lab button

Read these instructions. Labs are timed and you cannot pause them. The timer, which starts when you click Start Lab, shows how long Google Cloud resources will be made available to you.

This hands-on lab lets you do the lab activities yourself in a real cloud environment, not in a simulation or demo environment. It does so by giving you new, temporary credentials that you use to sign in and access Google Cloud for the duration of the lab.

To complete this lab, you need:

  • Access to a standard internet browser (Chrome browser recommended).

Note: Use an Incognito or private browser window to run this lab. This prevents any conflicts between your personal account and the Student account, which may cause extra charges incurred to your personal account.

  • Time to complete the lab---remember, once you start, you cannot pause a lab.

Note: If you already have your own personal Google Cloud account or project, do not use it for this lab to avoid extra charges to your account.

How to start your lab and sign in to Looker

  1. When ready, click .

    A new panel will appear with the temporary credentials that you must use for this lab.

    If you need to pay for the lab, a pop-up will open for you to select your payment method.

  2. Note your lab credentials in the left pane. You will use them to sign in to the Looker instance for this lab.

    Note: If you use other credentials, you will get errors or incur charges.

  3. Click Open Looker.

  4. Enter the provided Username and Password in the Email and Password fields.

    Important: You must use the credentials from the Connection Details panel on this page. Do not use your Google Cloud Skills Boost credentials. If you have your own Looker account, do not use it for this lab.

  5. Click Log In.

    After a successful login, you will see the Looker instance for this lab.

Task 1. Using Liquid to enhance interactivity

In LookML, we call lots of things parameters, like a dimension or measure type, sql, and drill_fields. There is also an object itself called a parameter, which we can call a Liquid parameter for clarity.

Parameters and templated filters use Liquid to increase interactivity in Explores, Looks, and dashboards. The use case for this is: sometimes you want more flexibility in influencing the generated SQL. When you use a dimension filter, it only ever updates the WHERE clause of the outer query. When you use a measure filter, it updates the HAVING clause of the outer query. Either option might be filtering your results set “too much.”

Parameters & templated filters

“Parameters and templated filters” is often said together in one phrase because they basically achieve the same purpose when using Liquid. The main difference is that a parameter only allows one specific, fixed value, whereas a templated filter leverages the full range of filter operators for a given data type. For a string, that would be “is equal to,” “is not equal to,” “contains,” “starts with,” etc. The differences are outlined as follows:

  • Parameters: Specific, fixed values that can be entered by users and then passed directly into a SQL query using Liquid

  • Templated Filters: user-entered values that are passed into SQL queries using intelligently written conditional logic

Using parameters and templated filters provides greater flexibility in how user inputs can influence the SQL queries written. You can use parameters and templated filters to create:

  • Dynamic dimensions and measures, which not only consolidate code but can also provide a smoother user experience

  • Dynamic derived tables, in terms of data granularity and filtering

  • Conditionally displayed values in the results set and labels

So this all sounds great, but what does this cycle of behavior look like in Looker?

Steps one to four of the behavior cycle

  • Step 1: It starts with the developer setting up the back end logic, which has two parts:

    • A parameter or templated filter, which appears in the UI for the user to interact with

    • A place in the sql parameter that summons the parameter or filter value and does something with it

  • Step 2: The end user then inputs a value into the parameter or templated filter, which resembles a “normal” dimension or measure filter in an Explore, Look, or dashboard.

  • Step 3: That value is then inserted into the SQL, and a query is formed with the changed SQL.

  • Step 4: That query runs and returns an Explore that reflects the value the user put in. This cycle applies to both templated filters and parameters

Parameters are little easier to understand conceptually, so that’s where we’ll start.

Task 2. Create a dynamic dimension using a Liquid parameter

Liquid parameters are defined to receive specific, hard-coded values selected by users, then these values are then passed onto the generated SQL query.

In this section, you will create a parameter and dynamic dimension within the order_items view that together enable users to choose between different order creation date fields in the Order Items Explore. Users should be able to choose between Date, Week, and Month and see that the query results change depending on which is selected.

  1. First, on the bottom left of the Looker User Interface, click the toggle button to enter Development mode.

Development mode toggle

  1. Click the Develop tab and then select the qwiklabs-ecommerce LookML project. Navigate to the order_items view file.

To start, you need to give the user something to interact with in the UI. This would be the parameter object. Give it a name as you would a dimension or measure: select_timeframe. The type should be unquoted, because you don’t want Looker to generate single quotes around the value for us.

Next, hard-code one or more allowed_values. The value sub-parameter is what actually gets plugged into the SQL query, and the label sub-parameter determines how the value is displayed in the UI. You can also choose to set one of these allowed_value values as the default_value. For this, you can select the month.

You will now create the Liquid parameter that can receive the user input; only three values are hard-coded and thus available for the user to select: created_date, created_week, and created_month.

  1. In the order_items view file, above all dimensions, add the following syntax for the new parameter (~line 6 under drill_fields):
parameter: select_timeframe {
    type: unquoted
    default_value: "created_month"
    allowed_value: {
      value: "created_date"
      label: "Date"
    }
    allowed_value: {
      value: "created_week"
      label: "Week"
    }
    allowed_value: {
      value: "created_month"
      label: "Month"
    }
  }

To recap, the sub-parameters are defined as follows:

  • label: what the user will see in the filter options

  • value: the value that will be inserted into the SQL query

  • default_value: the value that will be inserted automatically if a user has not yet made a selection

Your file should now resemble the following:

order_items.view page

Next, you may want the fields displayed in a dashboard or Look to be dynamic, based on a user's selection of the metrics or data granularity they wish to see. Now you can use the LookML parameter you just created to apply this parameter to a dimension that ties the parameter's filter values to your different timeframe fields.

  1. Next, you will define the dynamic dimension in the same view file. In the order_items view file, after the parameter definition, add the following syntax for the new dynamic dimension (~line 23):
dimension: dynamic_timeframe {
    label_from_parameter: select_timeframe
    type: string
    sql:
    {% if select_timeframe._parameter_value == 'created_date' %}
    ${created_date}
    {% elsif select_timeframe._parameter_value == 'created_week' %}
    ${created_week}
    {% else %}
    ${created_month}
    {% endif %} ;;
  }

The specified timeframe field will now be returned based on whichever parameter value the user selects on the dashboard. Note that you are using label_from_parameter to pass the selected value label to your tile.

Because the ${created_week} and ${created_month} timeframes—and possibly ${created_date}, depending on your database dialect—are cast by Looker to strings, the overall dynamic_timeframe dimension needs to be a string to accommodate.

This means, when business users are using this dimension in an Explore, they’ll need to remember to double-check the sort order. Looker’s default sort behavior is: check if there is a date or time dimension; if there isn’t, then sort by the first measure. So since this is technically a string type, Looker will sort by the measure first, which will likely mess up the chronological order.

You might wonder, well, what is the point then? Isn’t this introducing unnecessary complexity? Two things to keep in mind: Most business users of Looker are viewers, not explorers. That means they are looking at prepared dashboards and Looks, and they aren’t able to pick and choose different fields such as Created Date vs. Created Week in an Explore. Filters are the only way they can change what they’re seeing.

Since viewer users would be looking at content created by someone else, the fact that dynamic_timeframe is technically a string and needs to be manually sorted has zero impact on them.

Your file should now resemble the following:

order_items.view file page

Note: Notice that the parameter created in the previous step is referenced as the label_from_parameter and in the sql parameter. In the visualization tab, the timeframe option will show up as Month, Week, Date, rather than the name of the dimension (Dynamic Timeframe).

Now you can test the dynamic dimension in the Order Items Explore.

  1. Click Save Changes. Next, navigate to the Order Items Explore.

  2. Under the Order Items view, select the new Dynamic Timeframe dimension and the Order Count measure.

  3. Click on the filter icon next to the new Filter-Only Field called Select Timeframe

    Note: This is the Liquid parameter and is listed under the Order Items view above the Dimensions list.

  4. For the filter option at the top of the UI, leave “is” selected. Select Month from the drop-down menu.

Filters(1) dialog box with order items select timeframe set to is month

  1. Click Run to see the results. You can also now click on the SQL tab to review the SQL.

Results page displaying ten rows of information under the Oder items: Dynamic timeframe and Oder items: Order count columns

  1. Next, select the Week and Date filters. Click Run to see the updated results for each of them.

  2. For each run, click the SQL tab to review how the parameter is changing and is inserted into the SQL query.

  3. Navigate back to the Order Items view.

Commit changes and deploy to production

  1. Click Validate LookML and then click Commit Changes & Push.

  2. Add a commit message and click Commit.

  3. Lastly, click Deploy to Production.

Click Check my progress to verify that you've performed the above task.

Create a dynamic dimension using a Liquid parameter

Check my progress

Task 3. Create a dynamic dimension using templated filters

Templated filters follow the same logical pattern as parameters. Again, the major difference is that templated filters allow end users to choose from a number of filter operators. For the number data type, that could be “is equal to,” “is greater than,” “is between,” and so on.

In filters, values are not hard-coded; they are entered by users and then passed onto the generated SQL query. However, you can display a drop-down menu of options by specifying an explore and dimension in the filter definition.

In this section, you will create a dynamic dimension that takes an input value for product category and creates two groups in the results: the original category selected and all other categories.

  1. Back in the Looker IDE, navigate to the products view file.

Same as with Liquid parameters, first you need to create something in the UI for the end user to interact with. For a templated filter, you need a filter object.

Note: For optimal performance, you can create a small hidden Explore (such as explore: products {} ) that only queries the base view required for the suggested values.

  1. You will now create the filter object that can receive the user input. In the products view file, above all dimensions, add the following syntax for the new filter (~line 6 under drill_fields).

As you can see, you cannot hard-code allowed_values for templated filters as you can for parameters.

  1. The recommended approach is to use suggest_explore and suggest_dimension to provide a drop-down menu of filter suggestions to the end users:
filter: select_category {
    type: string
    suggest_explore: order_items
    suggest_dimension: products.category
  }

To recap, the sub-parameters are defined as follows:

  • suggest_explore: the Explore that will be queried in order to pull a list of suggested filter values

  • suggest_dimension: the dimension that should be used within the suggest Explore for providing a list of suggested filter value

Your file should now resemble the following:

products.view file page

Next, implement the user’s filter input somewhere. You will now define the dynamic dimension in the same view file.

Templated filters are referenced inside of a Liquid block using the syntax {% condition filter_name %}. This prepares the Liquid to apply a templated filter. Then give it the name of the field you want to apply that filter to, and finish the Liquid block with an {% endcondition %} tag. Notice how this is a little different from how you referenced a parameter; here you need to designate a field to apply the templated filter to, and an endconditon.

  1. In the products view file, after the filter definition, add the following syntax for the new dynamic dimension (~line 12). Notice that the filter created in the previous step is referenced in the sql parameter:
dimension: category_comparison {
    type: string
    sql:
      CASE
      WHEN {% condition select_category %}
        ${category}
        {% endcondition %}
      THEN ${category}
      ELSE 'All Other Categories'
      END
      ;;
}

Here you are taking the user's filter criteria from select_category, and are applying it to the ${category} dimension. If a category value does indeed meet the criteria, it should be displayed in the category_comparison dimension. If a category value does not meet the criteria, it should be lumped in with all the others that don’t match as ‘All Other Categories’.

Your file should now resemble the following:

products.view file page

Now you can test the dynamic dimension in the Order Items Explore.

  1. Click Save Changes. Next, navigate to the Order Items Explore.

  2. Under the Products view, select the dimension called Category Comparison.

  3. Click on the filter icon next to the new Filter-Only Field called Select Category (note: this is the templated filter and is listed under the Products view above the Dimensions list.)

  4. Under the Order Items view, select the Order Count measure.

  5. For the filter option at the top of the UI, leave “is equal to” selected.

  6. Click in the empty text box to see the drop-down menu, or type Jeans (you will also see other possible values in a drop-down menu).

Filters(1) dialog box with Products select category set to is equal to Jeans

  1. Click Run to see the results. You should see 19,243 counts for Jeans and 145,402 counts for All Other Categories.

Results page

  1. Templated filter logic adapts automatically as the user updates the filter. See this for yourself:

    • Try adding multiple values into the filter

    • Try changing the filter from “is equal to” to “contains” or “starts with”. What does that look like?

  2. Click on the SQL tab to review the SQL after each run.

Note: While looking at the SQL tab is useful for inspecting how the query is formatted from LookML, the process of analyzing the SQL promotes the appropriate process for troubleshooting any errors you may have.

  1. Navigate back to the Products view.

Commit changes and deploy to production

  1. Click Validate LookML and then click Commit Changes & Push.

  2. Add a commit message and click Commit.

  3. Lastly, click Deploy to Production.

Click Check my progress to verify that you've performed the above task.

Create a dynamic dimension using templated filters

Check my progress

Task 4. Create a dynamic measure using templated filters

You can combine templated filters with a hidden dimension to adjust the filter criteria of a filtered measure, resulting in dynamic measure values.

For example, a common use case for a marketing team is the need to analyze the share of users coming from each traffic source. In this section, you will be able to address this use case by creating a measure that allows a user to choose any available Traffic Source and see a dynamic count of users (by dimension, such as country) for the selected traffic source.

  1. Back in the Looker IDE, navigate to the users view file.

Again, you first need to create something in the UI for the end user to interact with. For a templated filter, you need the filter object.

  1. In the users view file, above all dimensions, add the following syntax for the new filter (~line 6 under drill_fields):
filter: select_traffic_source {
  type: string
  suggest_explore: order_items
  suggest_dimension: users.traffic_source
 }

No values are hard-coded, but there are suggested values for an explore and dimension, which will be used to populate a drop-down menu available to users. Users can still input other values.

Note: This is very similar to the filter you made in the previous section. However, here you are setting the Explore to order_items and the dimension to users.traffic_source.

Your file should now resemble the following:

users.view page

  1. Next, you will define the hidden dimension in the same view file. In the users view file, after the filter definition, add the following syntax for the new hidden dimension (~line 12). Notice that the filter created in the previous step is referenced in the sql parameter:
dimension: hidden_traffic_source_filter {
    hidden: yes
    type: yesno
    sql: {% condition select_traffic_source %} ${traffic_source} {% endcondition %} ;;
}

Your file should now resemble the following:

users.view page

  1. Lastly, you will define the dynamic measure in the same view file. In the users view file, after all the dimension definitions, add the following syntax for the new dynamic measure (~line 105):

Note: Notice that the hidden dimension created in the previous step is referenced in the filter parameter.

measure: dynamic_count {
  type: count_distinct
  sql: ${id} ;;
  filters: [ hidden_traffic_source_filter: "Yes" ]
}

Your file should now resemble the following:

users.file page

Now you can test the dynamic measure in the Order Items Explore.

  1. Click Save Changes. Next, navigate to the Order Items Explore.

  2. Under the Users view, select the Country dimension (or some other dimension to get a count by attribute).

  3. Select the new Dynamic Count measure under the Users view.

  4. Click on the filter icon next to the new Filter-Only Field called Select Traffic Source (note: this is the templated filter and is listed in the left menu of the Explore above the Dimension.

  5. For the filter value at the top of the UI, leave “is equal to” selected.

  6. Click in the empty text box to see the drop-down menu, or type Email (you will also see other possible values in a drop-down menu).

Filters(1) dialog box with Users select traffic source set to is equal to email

  1. Click Run to see the results. You will see the count for each country for all users with the traffic source equal to Email.

Results page

Click Check my progress to verify that you've performed the above task.

Create a dynamic measure using templated filters

Check my progress

  1. Try playing around with some other filter values, and click the SQL tab to review how the parameter is changing and is inserted into the SQL query for each run.

  2. Navigate back to the Users view.

Commit changes and deploy to production

  1. Click Validate LookML and then click Commit Changes & Push.

  2. Add a commit message and click Commit.

  3. Lastly, click Deploy to Production.


Solution of Lab

Step - 1: Turn on Development mode

Step - 2: Replace below code in users.view file

view: users {
  sql_table_name: `cloud-training-demos.looker_ecomm.users`
    ;;
  drill_fields: [id]

  filter: select_traffic_source {
    type: string
    suggest_explore: order_items
    suggest_dimension: users.traffic_source
  }

  dimension: hidden_traffic_source_filter {
    hidden: yes
    type: yesno
    sql: {% condition select_traffic_source %} ${traffic_source} {% endcondition %} ;;
  }

  dimension: id {
    primary_key: yes
    type: number
    sql: ${TABLE}.id ;;
  }

  dimension: age {
    type: number
    sql: ${TABLE}.age ;;
  }

  dimension: city {
    type: string
    sql: ${TABLE}.city ;;
  }

  dimension: country {
    type: string
    map_layer_name: countries
    sql: ${TABLE}.country ;;
  }

  dimension_group: created {
    type: time
    timeframes: [
      raw,
      time,
      date,
      week,
      month,
      quarter,
      year
    ]
    sql: ${TABLE}.created_at ;;
  }

  dimension: email {
    type: string
    sql: ${TABLE}.email ;;
  }

  dimension: first_name {
    type: string
    sql: ${TABLE}.first_name ;;
  }

  dimension: gender {
    type: string
    sql: ${TABLE}.gender ;;
  }

  dimension: last_name {
    type: string
    sql: ${TABLE}.last_name ;;
  }

  dimension: latitude {
    type: number
    sql: ${TABLE}.latitude ;;
  }

  dimension: longitude {
    type: number
    sql: ${TABLE}.longitude ;;
  }

  dimension: state {
    type: string
    sql: ${TABLE}.state ;;
    map_layer_name: us_states
  }

  dimension: traffic_source {
    type: string
    sql: ${TABLE}.traffic_source ;;
  }

  dimension: zip {
    type: zipcode
    sql: ${TABLE}.zip ;;
  }

  measure: count {
    type: count
    drill_fields: [id, last_name, first_name, events.count, order_items.count]
  }

  measure: dynamic_count {
    type: count_distinct
    sql: ${id} ;;
    filters: [ hidden_traffic_source_filter: "Yes" ]
  }
}

Step - 3: Replace below code in training_ecommerce.model file

connection: "bigquery_public_data_looker"

# include all the views
include: "/views/*.view"
include: "/z_tests/*.lkml"
include: "/**/*.dashboard"

datagroup: training_ecommerce_default_datagroup {
  # sql_trigger: SELECT MAX(id) FROM etl_log;;
  max_cache_age: "1 hour"
}

persist_with: training_ecommerce_default_datagroup

label: "E-Commerce Training"

explore: order_items {
  join: users {
    type: left_outer
    sql_on: ${order_items.user_id} = ${users.id} ;;
    relationship: many_to_one
  }

  join: inventory_items {
    type: left_outer
    sql_on: ${order_items.inventory_item_id} = ${inventory_items.id} ;;
    relationship: many_to_one
  }

  join: products {
    type: left_outer
    sql_on: ${inventory_items.product_id} = ${products.id} ;;
    relationship: many_to_one
  }

  join: distribution_centers {
    type: left_outer
    sql_on: ${products.distribution_center_id} = ${distribution_centers.id} ;;
    relationship: many_to_one
  }
}

explore: events {
  join: event_session_facts {
    type: left_outer
    sql_on: ${events.session_id} = ${event_session_facts.session_id} ;;
    relationship: many_to_one
  }
  join: event_session_funnel {
    type: left_outer
    sql_on: ${events.session_id} = ${event_session_funnel.session_id} ;;
    relationship: many_to_one
  }
  join: users {
    type: left_outer
    sql_on: ${events.user_id} = ${users.id} ;;
    relationship: many_to_one
  }
}

explore: +order_items {
  query: Task1 {
    dimensions: [dynamic_timeframe]
    measures: [order_count]
    filters: [order_items.select_timeframe: "created^_month"]
  }
}

explore: +order_items {
  query: Task2 {
    dimensions: [products.category_comparison]
    measures: [order_count]
    filters: [products.select_category: "Jeans"]
  }
}

explore: +order_items {
  query: Task3 {
    dimensions: [users.country]
    measures: [users.dynamic_count]
    filters: [users.select_traffic_source: "Email"]
  }
}

Step - 4: Replace below code in products.view file

view: products {
  sql_table_name: `cloud-training-demos.looker_ecomm.products`
    ;;
  drill_fields: [id]

  filter: select_category {
    type: string
    suggest_explore: order_items
    suggest_dimension: products.category
  }

  dimension: category_comparison {
    type: string
    sql:
      CASE
      WHEN {% condition select_category %}
        ${category}
        {% endcondition %}
      THEN ${category}
      ELSE 'All Other Categories'
      END
      ;;
  }

  dimension: id {
    primary_key: yes
    type: number
    sql: ${TABLE}.id ;;
  }

  dimension: brand {
    type: string
    sql: ${TABLE}.brand ;;
  }

  dimension: category {
    type: string
    sql: ${TABLE}.category ;;
  }

  dimension: cost {
    type: number
    sql: ${TABLE}.cost ;;
  }

  dimension: department {
    type: string
    sql: ${TABLE}.department ;;
  }

  dimension: distribution_center_id {
    type: string
    # hidden: yes
    sql: ${TABLE}.distribution_center_id ;;
  }

  dimension: name {
    type: string
    sql: ${TABLE}.name ;;
  }

  dimension: retail_price {
    type: number
    sql: ${TABLE}.retail_price ;;
  }

  dimension: sku {
    type: string
    sql: ${TABLE}.sku ;;
  }

  measure: count {
    type: count
    drill_fields: [id, name, distribution_centers.name, distribution_centers.id, inventory_items.count]
  }
}

Step - 5: Replace below code in order_items.view file

view: order_items {
  sql_table_name: `cloud-training-demos.looker_ecomm.order_items`
    ;;
  drill_fields: [order_item_id]

  parameter: select_timeframe {
    type: unquoted
    default_value: "created_month"
    allowed_value: {
      value: "created_date"
      label: "Date"
    }
    allowed_value: {
      value: "created_week"
      label: "Week"
    }
    allowed_value: {
      value: "created_month"
      label: "Month"
    }
  }

  dimension: dynamic_timeframe {
    label_from_parameter: select_timeframe
    type: string
    sql:
    {% if select_timeframe._parameter_value == 'created_date' %}
    ${created_date}
    {% elsif select_timeframe._parameter_value == 'created_week' %}
    ${created_week}
    {% else %}
    ${created_month}
    {% endif %} ;;
  }

  dimension: order_item_id {
    primary_key: yes
    type: number
    sql: ${TABLE}.id ;;
  }

  dimension_group: created {
    type: time
    timeframes: [
      raw,
      time,
      date,
      week,
      month,
      quarter,
      year
    ]
    sql: ${TABLE}.created_at ;;
  }

  dimension_group: delivered {
    type: time
    timeframes: [
      raw,
      date,
      week,
      month,
      quarter,
      year
    ]
    convert_tz: no
    datatype: date
    sql: ${TABLE}.delivered_at ;;
  }

  dimension: inventory_item_id {
    type: number
    # hidden: yes
    sql: ${TABLE}.inventory_item_id ;;
  }

  dimension: order_id {
    type: number
    sql: ${TABLE}.order_id ;;
  }

  dimension_group: returned {
    type: time
    timeframes: [
      raw,
      time,
      date,
      week,
      month,
      quarter,
      year
    ]
    sql: ${TABLE}.returned_at ;;
  }

  dimension: sale_price {
    type: number
    sql: ${TABLE}.sale_price ;;
  }

  dimension_group: shipped {
    type: time
    timeframes: [
      raw,
      date,
      week,
      month,
      quarter,
      year
    ]
    convert_tz: no
    datatype: date
    sql: ${TABLE}.shipped_at ;;
  }

  dimension: status {
    type: string
    sql: ${TABLE}.status ;;
  }

  dimension: user_id {
    type: number
    # hidden: yes
    sql: ${TABLE}.user_id ;;
  }


  measure: average_sale_price {
    type: average
    sql: ${sale_price} ;;
    drill_fields: [detail*]
    value_format_name: usd_0
  }

  measure: order_item_count {
    type: count
    drill_fields: [detail*]
  }

  measure: order_count {
    type: count_distinct
    sql: ${order_id} ;;
  }

  measure: total_revenue {
    type: sum
    sql: ${sale_price} ;;
    value_format_name: usd
  }

  measure: total_revenue_from_completed_orders {
    type: sum
    sql: ${sale_price} ;;
    filters: [status: "Complete"]
    value_format_name: usd
  }


  # ----- Sets of fields for drilling ------
  set: detail {
    fields: [
      order_item_id,
      users.last_name,
      users.id,
      users.first_name,
      inventory_items.id,
      inventory_items.product_name
    ]
  }
}

Step - 6: Open each task and run from looker