The ability to retrieve and search data plays an important role in many apps. We’ve seen the basic search capabilities that the auto-generated apps offer. But what if we want to provide more sophisticated search capabilities, such as the ability to filter records by date or drop-down values? In this chapter, we’ll find out how to add these features to our apps.
How to build a typical search screen that includes the ability to enter multiple search criteria and how to initiate the search through the click of a button.
How to join tables of data. Taking the analogy of an order processing app with two tables, customers and orders, how could we join these tables to show the customer detail that is associated with an order? How can we show customers who have not made orders or show the distinct customers who have made orders? We’ll find out how in this chapter.
The search scenarios that are most problematic, including a bug with datetime searches that can catch us out. We will also cover how we can overcome some of these issues by using features outside of Power Apps, including SQL Server views and SharePoint lists.
Basic Behavior
Data row retrieval limits and the term “delegation” can be a headache for many app builders. They can prevent us from retrieving our desired data, particularly in cases where we want to sum or to count fields from a data source. However, these limits exist for good reason. They prevent us from building features or writing formulas that can disproportionately slow down our apps. But with proper understanding of how data retrieval works in Power Apps, we can overcome many of the typical challenges that app builders face, and we can end up building more performant systems. Let’s find out more.
Understanding Delegation
Query delegation is a feature that speeds up data retrieval. Not all data sources support delegation, but SharePoint, SQL Server, and Dataverse are the popular data sources that do support delegation.
Equals operator (=) – We can apply the equals operator against string, numeric, and Boolean data types.
Comparison operators (=, <>, >=, <=, >, <) – We can apply comparison operators, such as greater than or less than, against numeric fields.
StartsWith – We can utilize the StartsWith function to test for text fields that start with a string that we specify.
SQL Server and Dataverse provide delegation support for a wider range of operations. For example, they both support the EndsWith and the Search functions. The Search function enables us to search multiple columns for values that match an input string. With SharePoint, there is no support to carry out this type of “contains” search in a delegable way. Another benefit of SQL Server and Dataverse is that there is delegation support for aggregate functions that include Sum, Average, Min, and Max.
With this expression, the designer will not show a warning.
Increasing the Data Row Limit
In cases where it isn’t possible to rewrite a query using non-delegable operators, we can retrieve more accurate results by increasing the limit to the maximum value of 2,000. Of course, this isn’t a complete fix, and the problem will still exist if our source data exceeds 2,000 rows.
It is useful to note that delegation support is a feature that Microsoft improves continually. It is likely that in the future, delegation support will extend to support a wider range of operators.
We can reduce the “Data row limit for non-delegable queries” to a low value (e.g., 1). This can help us more easily detect parts of our app that are affected by delegation limits during the development and test phases of an app.
How Do Controls Manage Large Quantities of Data?
Let’s suppose we add a gallery control to our screen and we attempt to display all records with a country that matches England or United States. In total, there are 10,000 records. This is a large quantity of data. What do we think will happen when we run our screen? Will there be a delay as Power Apps loads the data, or will this amount of data be too much for Power Apps to handle?
The actual experience feels very smooth and quick. The gallery control populates almost instantly with data. The reason for this is because Power Apps optimizes this process by retrieving only the first 100 records. If we scroll to the end of the gallery control, there will be a slight delay while Power Apps retrieves the next set of 100 records and appends those to the end of the list. This method of retrieving records in bite-size pieces provides a more responsive and fluid experience for the end user.
Although the content of this trace may look obscure, the important point is that it verifies that Power Apps retrieves data in 100 record chunks, and it also demonstrates how we can use this tool to diagnose and optimize data retrieval.
Will this function succeed in retrieving all 10,000 records? Unfortunately, the answer is no. The ClearCollect function will collect only up to the maximum number of records that is specified in the “Data row limit for non-delegable queries” setting, even if we specify a data source and query condition that supports delegation.
Searching Data
Developing these types of screen isn’t as easy as it first appears. There are several complexities that we need to cover. The first is to configure the search feature to not filter by date if the user leaves the date field blank. Additionally, there is also a difficulty that can arise when we attempt to apply date filters. If the underlying data field contains both date and time components and we want to filter just by the date component, we need to customize our search formula to take this into account.
The user can also choose not to filter by tenants by selecting the “Show all tenants” option from the drop-down; therefore, we need to build the formula that applies a wildcard search if the user chooses this option. The final challenge is to initiate the search operation from a search button.
If you don’t have access to SQL Server, you can easily substitute this with any other data source of your choice. The benefit of using SQL Server in this chapter is that it enables us to explore some of the performance optimization techniques that are available with SQL Server.
Basic Search Functions
Search – This function matches input text against the data in one or more columns.
Filter – This function matches records based on a formula that we provide.
LookUp – This function matches records based on a formula that we provide and returns the first record only.
In the sections that follow, we’ll examine how to use these functions in greater detail.
Filtering by Drop-Down Values
There are two steps required to build our basic search screen. The first step is to add a combo box and to configure the data source for this control. The second step is to configure a gallery control so that it filters by the selected item in the combo box.
This formula shows how the Selected property of the combo box enables us to retrieve additional fields, in this example, the TenantID field . This example demonstrates the common practice of showing a friendly description in a combo box and the formula that we would use to retrieve the numeric key value that is associated with the selected item.
Providing a “Show All”/Wildcard Option
To explain this formula in more detail, the Filter function takes two arguments. The first argument defines the data source, and the second argument defines the expression for inclusion into the result. The Filter function evaluates this expression for each record in the data source and includes the record if the expression resolves to true.
The expression in this example evaluates the number of selected items in the combo box. If no items are selected, the expression resolves to true, and the item will appear in the result.
If the combo box contains a selected item, the second part of the “or” operator will be evaluated (the || keyword denotes the logical “or” operator). In this case, the record will be included if the tenant ID value matches the selected tenant ID in the combo box.
Just for some background, the combo box control did not exist during the first edition of this book. In the initial release, we needed to manually append a blank row to the top of the combo box control to provide a wildcard search. The combo box control provides a great improvement because it includes a blank entry by default.
Searching by Date Criteria
Let’s now extend our search screen to enable users to additionally filter by create data. In doing so, we will also investigate a bug that often catches out app builders.
On our search screen, we’ll add a date picker control called dteSearchIssue . To default this control so that it shows a blank value, we can do so by clearing the DefaultDate property .
This introduces a non-delegable clause into the filter operation that forces Power Apps to carry out the query locally. Although this amendment will return some rows compared to none, the unfortunate effect is that we’ll experience the problems of a non-delegable query, which include slower performance and inaccurate results if the source data exceeds the maximum 2000-row limit.
The preferred way to fix this problem is to create a SQL Server view that casts to a DATETIMEOFFSET data type. We’ll cover this technique later in this chapter.
The date filtering bug applies to SQL Server and SharePoint data sources. You can find out more about this problem here:
Searching by Numeric Criteria
With text input controls, a useful tip is that we can set the Format property to “Number.” This provides a simple way to validate numeric input.
Searching by Text Criteria
The Search function accepts three or more arguments. The first argument defines the data source. The second argument defines the search string, and the following arguments define the target columns to search against. As shown in this example, a nice feature of this function is that it’s possible to specify multiple columns to search against.
The Search function matches records that contain the exact search phrase that we provide. That is, if the search phrase were to contain multiple words, the Search function would not return records that contain only one of the input words.
By default, the Search function carries out case-insensitive searches. With SQL Server, however, the behavior depends on the collation sequence of the database. If we search against a database with a case-sensitive collation sequence, any searches that we perform with the Search function will be case sensitive.
The main limitation of the Search function is that it works only against string column types. Also, it cannot search against SharePoint lookup fields. Although this function works fine against SQL Server data sources, a major limitation is that with SharePoint data sources, the Search function is not delegable.
Adding a Search Button
The implementation of our current search screen connects the gallery control directly to the data entry controls. Because of this, the gallery control refreshes as soon as a user enters any data into any of the controls. This behavior is not ideal because the gallery control will refresh more than it needs to, particularly if the user wants to enter multiple search criteria. To address this issue, here’s how to add a search button to the screen to trigger the search operation.
To build a search screen where users can enter multiple pieces of search criteria, we can provide a better user experience by triggering the search from a button.
Sorting Data by Multiple Columns
In addition to the SortByColumns function , there is also a function called Sort that provides similar functionality. The difference between SortByColumns and Sort is that with Sort, we provide a formula to sort by rather than a string column name. Why is this useful? One reason is to resolve a problem that some app builders encounter when they attempt to sort by numbers that are stored in a text field.
Joining Data
With apps that are based on a relational database structure or apps that are based on multiple SharePoint lists, a common requirement is to join and display data from multiple tables. There is no specific join command with Power Apps, so we’ll now explore some techniques that we can use to display related data.
Joining Records: Showing Related Records
This formula also illustrates an important learning point. It demonstrates how to fully qualify field names when we nest data with functions such as AddColumns, Filter, and LookUp.
The name for the @ operator is the disambiguation operator. Another scenario where this operator applies is in situations where a variable, collection, or data source matches a field name. In this case, we would specify the variable, collection, or data source in the format [@ObjectName] to distinguish it from the field name.
Aliasing Record Items
Rather than use the @ syntax, a friendlier way to resolve ambiguity is to use the As function. This function creates an alias for the current record and enables us to write a formula that is more intuitive to understand.
We can use the As function against record-scoped functions such as ForAll and Filter. We can also use it to alias expressions directly in the Items property of a gallery control.
Here, we call the As function to alias each record in the issue table with the name IssueRecord. From within the nested LookUp function, we can then refer to the TenantID field from the issue table with the identifier IssueRecord.TenantID .
The As function provides a great way to help resolve ambiguity in field names when we nest record-scoped functions.
Search Procedures That Can Cause Difficulties
Although Power Apps provides some powerful search capabilities, there are some areas where Power Apps doesn’t perform so well, especially in areas where there is a lack of delegable query support.
In this section, we’ll identify the areas where we need to apply caution. The purpose of this is to enable us to predetermine scenarios that can cause us difficulties at an earlier stage in the design process.
Fortunately, we can resolve many of these limitations through the use of SQL Server views. In the first release of Power Apps, there was no support for SQL Server views. We are very fortunate that Power Apps now supports views, because this enables us to query data in ways that were once impossible to accomplish in Power Apps.
Matching Against Lists of Data
The function that we use here is the in function. This function can be particularly confusing because there are two versions of this function which carry out different tasks. We can call the in function to check if a string contains an input value, or we can call it to check if a table contains one or more values that we provide as an input.
If we know that our source data will never exceed 2,000 rows, this will not be a problem. But in situations where it can, we need to find an alternate way to carry out the search.
One work-around is to loop through the items in the list box with the ForAll function, search the data source for each item row by row, and collect the results in a collection. This technique enables us to retrieve up to 2,000 issue records per tenant, rather than 2,000 records in total.
This process requires us to nest two record-scoped functions – ForAll and Filter. To disambiguate the TenantID field from the source list box and the TenantID field from the issue table, we call the RenameColumns function to rename the TenantID column in the source list box to SourceTenantID.
The ForAll function loops over this data source, and for each selected tenant, we collect the issue records that match the tenant ID into a collection called SearchResults.
This example highlights another useful disambiguation technique that relies on renaming columns to remove any ambiguity.
At the time of writing, there is a bug that prevents the correct resolution of field values when we call the As function to alias a source and include a nested call to the Filter function to filter a SQL Server data source. Hopefully, this problem will be fixed in a future release; but until then, the rename technique provides an effective alternative.
We can rename columns to help resolve ambiguous field names when we nest record-scoped functions.
Using in to Match Substring Text
This example returns all records where the description field includes the characters “ing.” Although this formula works fine with SQL Server, a caveat is that with SharePoint, this formula is not delegable.
A closely related function is the exactin function . From a Power Apps perspective, the exactin function works the same way as the in function, except that the matches are case sensitive. Unfortunately, the usefulness of the exactin function is limited because it is not delegable for both SharePoint and SQL Server data sources.
When we call the in function with SQL Server to match a substring, Power Apps will delegate the query to SQL Server as we would expect.
The case sensitivity of queries with SQL Server depends on the collation sequence. If the collation sequence of the target database columns is set to a case-sensitive collation, any searches that we make with the in function will be case sensitive, as opposed to the standard behavior in Power Apps which is case insensitive.
Here’s an interesting way that we can take advantage of this behavior. Let’s take the example of an app that connects to a SQL Server instance with a case-insensitive collation sequence. If we want to carry out case-sensitive searches, we can create a SQL Server view that casts our target columns to a case-sensitive collation. Any queries that we carry out with functions such as Filter or Search will now be case sensitive. The section “SQL Server Views” later in this chapter provides the example SQL syntax that we can use.
Examples of how to call the in and exactin functions
Function | Description |
---|---|
Filter('[dbo].[Tenant]', Postcode in ["BA3 7KD" ," LN2 1IV"] ) | Usage type – belonging to a table (case insensitive). This formula returns all records with a postcode that exactly matches the postcode “BA3 7KD” or “LN2 1IV”. The search is not case sensitive. Note that we cannot use this technique to find a partial match from within the Postcode column. This query is not delegable with SQL Server and SharePoint. |
Filter('[dbo].[Issue]', "smith" in Firstname ) | Usage type – substring test (case insensitive). This formula returns all records that contain the string “smith” in the Firstname column. This query is delegable with SQL Server, but not delegable with SharePoint. With SQL Server, this search might be case sensitive, depending on the database collation sequence. |
Filter('[dbo].[Tenant]', Postcode exactin ["BA3 7KD" ," LN2 1IV"] ) | Usage type – belonging to a table (case sensitive). This formula returns all records with a postcode that exactly matches “BA3 7KD” or “LN2 1IV”. This search is case sensitive. This query is not delegable with SQL Server and SharePoint. |
Filter('[dbo].[Issue]', "Smith" exactin Firstname ). | Substring test (case sensitive). This formula returns all records that contain the string “Smith” in the Firstname column. This search is case sensitive and will not return records that contain the lowercase value “smith”. This query is not delegable with SQL Server and SharePoint. |
Checking for Nonexistence
An important task that we often need to carry out is to check for the nonexistence of a group of data within another dataset. As an example, traditional order processing systems will use this technique to find customers who have not placed any orders. To highlight this technique, here’s how to show the names of tenants that are not associated with any issue records.
This formula filters the tenant table to show records where a matching TenantID doesn’t exist in the issue table. Just like the previous example, the syntax we use here is not delegable; and therefore, the code here may not return all expected records.
Matching Blank Fields
With this type of data structure, a typical requirement could be to return all records with a null CloseDateTime value . This would enable users to see all records that are in an open status.
The function that tests for null or empty values is called IsBlank. This function accepts an input value and returns true if the input is empty string or null.
This function would return all records with a null or empty date. However, a big limitation is that the IsBlank function is not delegable. If there are more than 2000 records in the issue table, the results will not include all matching records.
This expression returns records with the IssueID values 1 and 2. In other words, the IsBlank function matches both null and empty string values.
This formula would return the record for IssueID 2 only. Power Apps can delegate the operation of matching against an empty string, so we can use this method to help overcome delegation limitations.
Returning Distinct Records
Sometimes, there may be the requirement to return a list of distinct records. To give an example, let’s suppose we want to return a list of distinct tenants who have raised issues.
However, with SQL Server and SharePoint data sources, the date filter bug prevents this formula from returning correct data. As we saw earlier in this chapter, filtering data sources by date will incorrectly return zero rows.
One way to overcome this problem is to add the criteria && Day(StartDate) >0 to force the filter operation to run on the local device. The caveat of this approach is that this expression is non-delegable and will not return accurate results with data sources that exceed 2000 records.
Resolving Delegation Issues
These delegation issues can be a headache because they prevent us from retrieving the accurate results that we want. What approaches can we use to work around these delegation issues?
A popular approach is to load all the contents of a data source into a local collection. We would use a delegable query to retrieve records in batches of 2000 records, and we would populate our local collection with the results.
Although this technique can help us retrieve more accurate results, it is not efficient. Where possible, it is far better to try to resolve delegation problems through the use of views in SQL Server and calculated columns in SharePoint.
SQL Server Views
With SQL Server, a great way to overcome delegation issues is to build a view. Because SQL Server is a large separate topic, we won’t cover this in too much detail. However, the important thing to take away from this section is that there is an effective technique that we can apply to overcome query delegation problems.
Cheat sheet of SQL statements to use in views
The <, <=, >, and >= operators are not delegable against date fields in SQL Server. By using a view to convert the day, month, and year components to numbers, we can filter dates from Power Apps using these comparison operators.
SharePoint Columns
With SharePoint, the options to work around delegation issues are more limited. SharePoint calculated columns do not help because these are also not delegable.
By storing this date value in a number column, we can then filter the value using operators that include the greater than and less than operators.
If we were adding these new fields to an existing list, we could use a Power Automate flow to carry out the initial population.
Summary
This chapter focused on how to retrieve data and how to provide search capabilities for users. We covered one of the most challenging and widely discussed areas of Power Apps – delegation.
When we want to retrieve a filtered set of data, the ideal outcome is that the data source filters the records and returns the results to the app. This is the definition of a delegable query – Power Apps delegates the search query to the data source.
For more complex query conditions, Power Apps may not be able to delegate the search query to the data source. In this situation, the data source returns a maximum of 2000 records, and Power Apps executes the query on the local device. A non-delegable query misses records that fall outside this maximum 2000-record limit, and because of this, it may not return all results. This behavior presents a challenge for many app builders.
Data sources provide varying support for query delegation. Dataverse and SQL Server provide the best support, whereas SharePoint offers more limited support.
Areas where we are most likely to encounter problems with query delegation are queries where we use the IsBlank function to search for empty or null values, queries that call the in function to test that an input set of records belongs to a table or collection, and – by extension – queries that call the not and in functions to find records that do not match an input set of records. Another problem to be aware of is that there is a bug that prevents SQL Server and SharePoint data sources from filtering date columns using the greater than and less than operators.
With SQL Server as a data source, we can solve the majority of delegation problems by creating SQL Server views. The chapter provides a summary of the most common SQL statements that we can use to overcome delegation problems.
The three most common functions to query data are Search, Filter, and LookUp. The Search function matches input text against one or more text columns, the Filter function matches records based on a conditional formula, and the LookUp function returns the first record based on a conditional formula. The LookUp function is particularly useful because we use it to join or to show the details of a related record.
Another common user requirement is to return distinct records. We can return distinct records by grouping by the column where we want to show distinct values.
In the main part of the chapter, we walked through the process of building a custom search screen. This included the ability for users to filter records by a combo box control and a date picker. To build this type of search screen, we use the Filter function to filter our source data against the values that the user enters into the data entry controls. We also learned how to initiate a search from the click of a button by attaching a formula to the button to store the criteria values into variables and setting the Filter function to filter the data source by the variables.