Indexes and Ordering with Data Aware cWebLists

Multi-line Row Support in Lists and Grids

Responsive Applications

 

When cWebLists are used in select style drilldown views, the use of indexes and column ordering of lists can be confusing. This section will help you understand this process.

This discussion applies primarily to working with lists where the list loads the data for you (pbDataAware is true, peDbGridType is gtAutomatic or gtAllData).

 

General Advice about cWebList Ordering

When working with drilldown view follow these guidelines:

  1. Use piSortColumn to control index order

  2. Always set an initial piSortColumn value

  3. If you need to further control index order, augment the IndexOrder function

  4. Do not set these properties:

 

piOrdering Property

piOrdering sets a design-time index order for a list. This is not a web-property. Once set it should not be changed.

Its default value is -1, which is usually the best choice for this property.

If the list is relates-to constrained, piOrdering is not used; instead -1 is used which tells the runtime to pick the most efficient index.

Avoid setting this property. Instead, set piSortColumn and augment IndexOrder.

 

piSortColumn Web Property

Set piSortColumn, to the column you want. It will find records by whatever index is deemed best for this column and display the data in that column order.

At application startup, each column in a cWebList object calculates the best index for sorting for that column and sets this index into piDefaultIndex. This never changes. Do not set or change piDefaultIndex yourself.

The logic for determining piDefaultIndex is fairly basic. It checks if the column’s data binding (file.field) has an index associated with it. If it does, it uses that. If it does not, it sets piDefaultIndex to -1. It does not take into account relates-to constraint or other filters you might have applied. This means that the index selected may not be a good index based on the constraint (i.e., it is not efficient).

If a relates-to constraint is in place, piSortColumn will not use the piDefaultIndex as its choice because this will most likely not be an efficient index (where efficient means it can jump-in, find the records and jump out). Instead it returns -1, instructing the runtime to pick the best index. This is usually the best choice. Often with constrained parent finds there is only one good index to use and the runtime will find it. With relates-to constraints, this means that the ordering you see for a column may not make sense (i.e., it will not appear sorted by that column). That’s because a single best index is used for all columns. By default, we err on the side of performance.

The above process is dynamic. With drilldown views the same view may be used with no filter or with a relates-to constraint filter. For example, the SelectOrder.wo in the sample can be accessed in three ways – no parent constraint, a customer parent constraint or a sales-person parent constraint. When there is no parent constraint (meaning you did not navigate into this from a parent view) you would want to use the column’s sort order (piDefaultIndex). When there are parent constraints you want to use an efficient finding index (-1). This is how this all works by default.

When a mobile/touch view is navigated to, it uses the design-time value piSortColumn as the initial value. This is the property set within your object declaration using the Set command. Once applied, this can be queried using WebGet and changed using WebSet.

IndexOrder is the function that takes all of the above information and determines the best index to use for a specific cWebList instance.

 

IndexOrder Function

IndexOrder is called for every find and specifies the index order to be used. It looks at all of the information about your view, your list and your sort column and determines which index will be used. The index can be a specific index number or -1, which lets the runtime pick the best index for you.

IndexOrder can be used for debugging and it can be augmented.

 

Using IndexOrder During Debugging

Placing a breakpoint in this function (it’s in cWebList) will let you see exactly what index is being used. If your index is not behaving as you want (e.g., the order seems wrong or it’s too slow) the first thing you want to do is to find out what index is being used. If you are ordering via piSortColumn you can use this to verify that the proper sort column is being used.

Keep in mind that IndexOrder is dynamic and that a different index order will be returned based on how a view is being used. In particular the end user’s choice of sort column (piSortColumn) and their navigation path into the view (which determines if relates-to constraints are applied) will determine what index order is used. This is why choosing an index cannot be controlled with a single property or even a property for each column.

Augmenting IndexOrder

If you can apply a better index order for a specific condition, you can augment IndexOrder and return an index of your choosing. To do this will want to look at the current sort-column (WebGet piSortColumn) and if a relates-to constrain is in effect (Get Constrain_File).

One advantage of augmenting IndexOrder is that this provides a single place where you can provide an index and you get to define all of the rules.

For example, assume you have a list with two columns and, for whatever reason, you want column 0’s index to be 12 and column 1’s index to be 10.

Function IndexOrder Returns integer

    Integer iCol

    WebGet piSortColumn to  iCol

    If (iCol=0) Begin

        Function_Return 12

    End

    Else If (iCol=1) Begin

        Function_Return 10

    End

End_Function

Note that we use piSortColumn to determine which column is controlling the ordering. This is a web-property and must be accessed using WebGet.

In most cases, the automatic assignment of indexes works fine with lists that are not parent constrained. When there is no parent constraint, about the only time you might augment IndexOrder is when you are applying other DDO constraints and you have determined that there is an efficient index that will provide you the order you want for the filters applied.

When your list is constrained to a parent, the default IndexOrder behavior is to return -1, which lets the runtime decide. The runtime will pick the most efficient index for the current constraints. Depending on your indexes there may be other good choices that are ordered in a different way but are still fast. Alternately, speed may not matter in this case (because the tables are small enough).  In such a case you might to provide a specific index that can provide the right mix of ordering and speed.

Depending on how a select view is navigated-to to, you may or may not have a parent constraint. This can all be tested and handled within IndexOrder. This is determined by looking at the DDO’s Constrain_File property. If zero, there is no parent constraint; if non-zero, the list is constrained to that parent.

In this example, we provide special indexes when the view is being constrained by a particular parent (Customer). This assumes that the child table has two good indexes that can be used when searching by Customer (perhaps Customer x Order_Number and Customer x Date x Order_Number) and you want a different index for each column. Note that we apply three tests here – 1) is there a parent constraint, 2) what is the parent constraining the list and 3) what is the current sort column.

Function IndexOrder Returns Integer

    Integer iCol iConstrain

    Handle hoDD

    Get Server to hoDD

    Get Constrain_File of hoDD to iConstrain

        // if no parent constraint, just do normal logic.

        If (iConstrain=0) Begin

            Forward Get IndexOrder to iCol

            Function_Return iCol

        End

        // if constrained, we will supply the indexes

        Else Begin

            // if constrained by customer we have special indexes we can use

            If (iConstrain=Customer.File_Number) Begin

                WebGet piSortColumn to  iCol

                If (iCol=0) Begin

                    Function_Return 8

                End

                Else If (iCol=1) Begin

                    Function_Return 11

                End

            End

            // some other parent constraint, tell the runtime to provide the best index

            Else Begin

                Function_Return -1

            End

        End

    End

End_Function

 

Setting Ordering in your DDOs

As with all DDOs, you can set the Ordering directly in the DDO. If you do that, it overrides all finds and uses that index for every find. Its default value, -1, allows the framework and the developer to specify their index of choice.

Avoid setting this. This advice is valid for all styles of DataFlex applications.

 

Reading in All of the Data at Once

An alternate approach to augmenting IndexOrder is using a single constrained index that reads all of the data in at one time. If you do that, the client can control the sorting and you will be able to sort by any column. This strategy only makes sense if your record set is small enough. Fortunately, the drill down nature of an application may make this viable. For example, if you always drill into your orders via a customer select view, your orders are limited to orders for that one customer. In such a case it might be easiest to read all of those customer orders in a single step. You would accomplish this by adding the following to your list:

Set peDbGridType to gtAllData

Using this can be a very effective technique and it always provides the most flexible column sort options. In fact, you may want to look at all of your select lists to see if this would be a good setting.

If you have a view that may or may not have a parent constraint, you will need to make this setting dynamic. During forward navigation we will test to see if there is a parent constraint. If there is, we will read all records for that parent at once. If there is no constraint, we will use normal dynamic record loading. This is done as follows:

Procedure OnNavigateForward tWebNavigateData NavigateData Handle hoInvokingView Handle hoInvokingObject

    Integer iConstrain

    Handle hoDDO

    :

    Get Server of oList to hoDDO

    Get Constrain_File of hoDDO to iConstrain

    If (iConstrain<>0) Begin

        WebSet peDbGridType of oList to gtAllData

    End

    Else Begin

        WebSet peDbGridType of oList to gtAutomatic

    End

    :

End_Procedure

Remember to use WebSet when changing peDbGridType dynamically. If you are planning on changing a list’s peDbGridType dynamically, make sure that the initial value of this property is gtAutomatic (this is “Set” at design-time in your code and is the default – so just don’t change the default).

The WebOrderMobile does exactly this with the SelectOrder view although it uses a slightly different mechanism to determine if the select view is constrained.

Sometimes this is the perfect compromise. When there are no parent constraints the list may be too large to load at once. Fortunately the default column sort behavior (of allowing the framework to pick the best index for a column) works well. When there are parent constraints, loading all child records may be viable and you get to use client sorting instead of having to deal with setting (and possible creating) the best index for each column.

Note: When using gtAllData, the piSortColumn no longer determines record finding order although it still determines the display order on the client which is where the sort will occur. This means that the data will be loaded using index -1, which tells the runtime to pick the best index based on the filters applied.  This is exactly what you want. You get fast finding and flexible client sorting. If for some reason you want to use a different finding index, augment IndexOrder.

 

Dynamic Parent Constraints in Drilldown Style Views

Select views assign parent constraints dynamically as that view is being forward navigated to. This is what allows a single select view to be used under different navigation conditions. This dynamic process can be disabled.

pbDrillDownDynamicConstraints can be used to stop the application of dynamic relates-to constraints to a view. By default its value is true so dynamic parent constraints are applied. When set false, relates-to constraints are not applied dynamically and whatever you’ve set as design-time values in your view will be used (just like desktop style views). Be careful using this.

Searching on Parent Columns

The framework, both windows and web, allows you to search lists based on a parent column. It can do this if the parent column is related to the child (which it should be since this is the definition of a parent) and if there is a child index that contains the parent key as its first index segment. This parent super finding works automatically with prompt lists (cDbCJPromptList and cWebPromptList). With cWebLists, which would be used for mobile/touch select views, you must set the cWebList peRequestFindMode to rfmFindMainSuper.

      Set peRequestFindMode to rfmFindMainSuper

This kind of parent find lookup should only be used with parent tables that are not relates-to constraining the list. Performing a search a constrained parent makes no sense.

 

Remember that You Control the Navigation Possibilities

In the examples discussed here and in our WebOrderMobile sample we assume that a mobile view may be accessed through a number of different navigation paths. We do this to demonstrate the flexibility of this framework style and show how the same view can be used in a variety of ways.

This is not necessarily the best way to write a mobile/touch application.

Unlike the framework’s desktop style, you get to completely define the navigation paths that a user will traverse. Use this power wisely. Providing multiple ways to do the same thing may not be doing an end user a service. Our advice would be to:

Remember that the mobile interface, both visual and input, is much more limited than a desktop. You need to find the best way to perform the task.   

If you follow these guidelines, views will tend to be used for a single purpose. When this happens the dynamic nature of a view becomes more static and much of the complexity being discussed disappears. Not only will this make your job much easier but it will probably result in a much better application.

(Clearly our WebOrderMobile sample does not follow this rule. That’s because we created the sample to show developers what’s possible and what the code looks like to do this.)

Single View for Multiple Purposes or Multiple Views for Single Purpose

The mobile touch style of application uses a drilldown style where many different navigation paths may lead to views that perform similar but not identical functions. As a developer you have to know how to go about creating those similar views. The conceptually simplest approach is to create a different, yet similar view, for each possible navigation path. While this requires that you create many almost identical views, it makes it much easier to control how each view behaves.

A conceptually more complicated, but more software elegant approach is to create as few views as possible and use them in as many navigation paths as possible. The difference in the view’s behavior must be controlled by the developer to customize its appearance and behavior based on how it is being used. The framework’s drilldown style provides you with all of the tools to perform these customizations. This is the approach that is demonstrated in our WebOrderMobile sample and we believe it is what most developers will prefer in the long run.

The use of these two strategies involve trade-offs. You don’t want to duplicate views if you don’t need to. However, there may be times when attempting to customize a view for multiple uses may become too complicated. When that happens, multiple views that are similar might be the best approach.

This is mentioned here as part of the discussion of cWebList ordering because this is an area where creating multiple single purpose views may make sense. If you find yourself writing lots of if statements, setting lots of properties to defeat some of the dynamic features, or writing a lot of code to radically change the appearance and behavior of a view , you might want to consider creating a new view. This assumes you have already gone through the exercise of reducing the number of navigation paths to a single view.

One of the biggest differences between desktop style and mobile/touch style applications is that relates-to parent constraints are applied dynamically. This works but it is an area where you may find yourself writing a lot of code to handle constrained and non-constrained conditions. You can always create multiple similar views. This lets you separate the requirements to handle constrained and non-constrained views. This simplification will make list behavior and code align more closely to the desktop style.   

 

Previous Topic: Multi-Line Row Support in Lists and Grids

Next Topic: Responsive Applications

 

See Also

Developing Web Applications