NickLightbody

Designing a good Portal Filter

Discussion created by NickLightbody on Oct 29, 2014
Latest reply on Nov 10, 2014 by NickLightbody

Portal filters are great and provide an effective method of giving the user easy access to subsets of records from all those that would otherwise be displayed through the relationship powering the portal.

 

These days Filemaker is ever more efficient in delivering records to portals so you can safely display quite larger numbers of records in portals quickly - but on mobile devices you should consider a method of preventing too many records being displayed - for example not permitting “all” records to be shown - only those found through the filter, or only those to which the user has access.

 

When designing for mobile devices with limited screen space it is essential to make your Ui as simple as possible, which means a limited number of items on each screen and that means only one field available for use in portal filtering - or “finding” as many users will likely think of it.

 

So how can you give your users a little more refinement through this single entry field?

 

For example in order to find records which cover a variety of use cases such as:

 

(a) John or Smith

(b) John and Smith

(c) John but excluding Smith

(d) John and Smith but excluding UK

(e) John excluding Smith and excluding UK

 

And how efficient will such a method be?

 

Well - here is one method which works efficiently and surprising quickly even on large datasets - I call it "Multifiltering" - it uses Patterncount() as the key operator which means of course that you are searching on individual words irrespective of their order in the search string.

 

I will post some statistics on this thread in the future on the performance I am seeing using this method in various forms of deployment.

 

Multifiltering a Portal - an effective method - here’s how to do it

 

This method is proven and can of course be extended to increase both its scope and its efficiency - do please suggest how you believe that this can be improved - for example could it be written as a recursive custom function and how efficient would that be?

 

Step 1: define a text field in your data table which we will call a3summary which is set to auto enter and to replace existing with a List() of the fields that you wish to index and search/filter - i.e don't check the don't change box.

 

Step 2: define a global numeric field which we will call zb3allornothing which acts as a switch to control whether the user can see all records or no records when the portal filter field is empty - potentially this field can be manually set by the user as a preference or auto set based on the total number of records which the user would be able to display through the relationship.

 

Step 3: define a global field in your Ui file for the entry of the filter criteria we will call za3search

 

Step 4: write a script which is called when a search/filter is instigated to reduce the number of words in za3search to the number you are accommodating - in this case 3 - and remember to tell the user why this has been done.

 

Step 4a: define custom function xTrim() as follows:

 

// xTrim()

// v1: NL: 060319

//Trim4 (" ¶¶Your text here ¶ ")

//Ray Cologon

//=============

Let([

C1 = PatternCount ( " ¶"; Left ( text; 1 ));

Cn = PatternCount ( " ¶"; Right ( text; 1 ));

Ca = C1 + Cn ];

If ( Ca;

xTrim ( Middle ( text; 1 + C1; Length ( text ) - Ca ) );

Text )

)

 

You will note that as a matter of good practice every custom function I use includes a version history crediting where/who it originally came from and - as appropriate - how I have changed it - in this case I think I did nothing other than rename it to my convention which is that all custom functions commence with "x".

 

Step 5: define a custom function we will call xPFilter3w as follows:

 

/*

xPFilter3w v3

Nick Lightbody

December 2012 & October 2014

To provide filtering on up to three words - in any order

and to exclude records containing search words commencing -

and to inc those commencing + together with the first word

and to default to an OR search

comprising use cases:

(a) John or Smith - notated as john smith

(b) John and Smith - notated as john +smith

(c) John but excluding Smith - notated as john -smith

(d) John and Smith but excluding UK - notated as john +smith -uk

(e) John excluding Smith and excluding UK - notated as john -smith -uk

 

input:

Fn2search [fieldname to search]

Fnsearch [search/filter fieldname]

allornothing [switch to control whether the portal will display all when the the search/filter field is empty)

--------------------------------------------*/

 

Let([

k=Fn2search;

// string of up to 3 words - converted to values

x=Substitute( FnSearch ; " "; ¶ );

// symbols used for exc and inc - so you define this to use whatever characters you wish

exc="-";

inc="+";

 

// word 1

w1=xTrim(LeftValues(x;1));

L1=Length(w1)-1;

x1=Case(Left(w1;1)=exc ;Right(w1;L1);"");

y1=Case(Left(w1;1)=inc ;Right(w1;L1);"");

px1=Case(PatternCount(k;x1)>0;1;0);

pw1=Case(PatternCount(k;w1)>0;1;0);

py1=Case(PatternCount(k;y1)>0;1;0);

//summary output - exc o

p1=Case(px1;0 ;pw1 or py1;1;0);

 

//word 2

w2=xTrim(MiddleValues(x;2;1));

L2=Length(w2)-1;

x2=Case(Left(w2;1)=exc ;Right(w2;L2);"");

y2=Case(Left(w2;1)=inc ;Right(w2;L2);"");

px2=Case(PatternCount(k;x2)>0;1;0);

pw2=Case(PatternCount(k;w2)>0;1;0);

py2=Case(PatternCount(k;y2)>0;1;0);

//summary output - exc o

p2=Case(px2;0 ;pw2 or py2;1;0);

 

//word 3

w3=xTrim(MiddleValues(x;3;1));

L3=Length(w3)-1;

x3=Case(Left(w3;1)=exc ;Right(w3;L3);"");

y3=Case(Left(w3;1)=inc ;Right(w3;L3);"");

px3=Case(PatternCount(k;x3)>0;1;0);

pw3=Case(PatternCount(k;w3)>0;1;0);

py3=Case(PatternCount(k;y3)>0;1;0);

//summary output - exc o

p3=Case(px3;0 ;pw3 or py3;1;0);

 

// controllers

$n=ValueCount(x);

$nx=PatternCount(x;exc);

$ny=PatternCount(x;inc);

 

// No of includes in string

$ny12=Case($ny=1 or $ny=2;1;0);

$ny123=Case($ny=1 or $ny=2 or $ny=3;1;0);

 

// No of excludes in string

$nx12=Case($nx=1 or $nx=2;1;0);

$nx123=Case($nx=1 or $nx=2 or $nx=3;1;0);

 

// No of matching excludes

$px12=Case(px1 or px2;1;0);

$px123=Case(px1 or px2 or px3;1;0)

];

 

Case ($n=0 ; allornothing

;

$n=1 ; Case ($nx=1 and px1; 0

;$nx=$n; 1

;$nx=0 and p1;1

;0

)

;

$n=2 ; Case ($nx12 and $px12; 0

;$nx=$n; 1

;$nx=1; p1

;$ny12 and p1 and py2;1

;$ny12;0

;pw1 or pw2;1

;0

)

;

$n=3 ; Case($nx123 and $px123; 0

;$nx=$n;1

;$nx=2; p1

;$nx=1 and $ny12 and p1 and py2;1

;$nx=1 and $ny12;0

;$ny123 and p1 and py2 and py3;1

;$ny123;0

;pw1 or pw2 or pw3;1

;0

)

)

)

//[END of function]

 

Step 6: add something like the following into your portal:

 

xPFilter3w (

data_al::a3summary ;

Ui_al::za3search;

zb3allornothing

)

 

I hope that you find this a useful technique and will choose to share your improvements on it with the rest of us,

 

Cheers, Nick

Outcomes