Thank you Jesse and Michael, 

I’ll share the solution we came up with :-)

We have a “user search” tool were we can dynamically fill out and query for 
users in the system with various checks such as obvious things like first and 
last name but also by their roles, whether they are expired or not, and also 
their email address. 

We wanted a new checkbox to look for “non-standard” email addresses. Weird 
characters, or at least ones we think are suspicious like % and * (percent and 
asterisk). Things we don’t like shouldn’t be let in but there is also very old 
data and a desire to clean it up. 

If we want a qualifier to search for asterisk, we might naively attempt using 
Wonder ERXKeys like so:
===
EOQualifier qualifier = User.PERSON.dot(Person.EMAIL).contains("*")

The “contains” method guts are the following:
===
public static ERXKeyValueQualifier contains(String key, String value) {
        value = "*" + value + "*";
        return ERXQ.likeInsensitive(key, value);
}

So we are effectively doing a case-insensitive-like on triple-asterisk which 
yields all records. The asterisk in WO is like percent for regular SQL, it gets 
translated so * becomes % when it hits the DB. 

As far as I know, there is no way to escape an asterisk. Can’t be done! But 
hope is not lost, when you meet a brick wall, walk around it. 

I dug the raw SQL qualifier out of the basement. NeXT/Apple provides it, it is 
deprecated, but useful when you are in a pickle. 

You would do something like this:
===
EOEntity userEntity = EOUtilities.entityNamed(editingContext(), 
User.ENTITY_NAME);
EOQualifier qualifier = new EOSQLQualifier(userEntity, 
User.PERSON.dot(Person.EMAIL).key() + " like '%%*%%'", null)

WARNING: you might think there should be a single percent (%) but that would 
lead to errors because that is a qualifierFormat string and you typically 
provide variables for replacement with %@ or if you want to be clear you would 
do %s for a string, %d for an int, and %f for a float/double. Then where I 
passed in “null” you would instead pass in an array of values to use for 
replacement. You have to escape the percent and to do that you double it as %%

In context the code could be crafted like so:
===
EOEntity userEntity = EOUtilities.entityNamed(editingContext(), 
User.ENTITY_NAME);
EOQualifier otherThanStandardCharactersQualifier = ERXQ.or(
        User.PERSON.dot(Person.EMAIL).contains("#"),
        User.PERSON.dot(Person.EMAIL).contains("$"),
        User.PERSON.dot(Person.EMAIL).contains("%"), 
        … other similar checks
        new EOSQLQualifier(userEntity, User.PERSON.dot(Person.EMAIL).key() + " 
like '%%*%%'", null), 
        new EOSQLQualifier(userEntity, User.PERSON.dot(Person.EMAIL).key() + " 
like '%%?%%'", null)
);
… do a fetch using EOF.

What gets generated by EOF into SQL looks something like:
===
SELECT *
FROM 
  user t0 
  INNER JOIN person T1 ON t0.person_id = T1.id 
WHERE 
  UPPER(T1.email) LIKE UPPER('%#%' :: varchar(255)) ESCAPE '|' 
  OR 
  UPPER(T1.email) LIKE UPPER('%$%' :: varchar(255)) ESCAPE '|' 
  OR 
  UPPER(T1.email) LIKE UPPER('%|%%' :: varchar(255)) ESCAPE '|' 
  OR 
  T1.email like '%*%' 
  OR 
  T1.email like '%?%' 
;

Note #1: look at how regular qualifiers always produce something that is 
escaped with a pipe symbol "|" even when they have nothing to escape. 

Note #2: when doing a contains("%") it does escape it using a pipe in the 
generated output and makes sense. 

Note #3: The last two lines are from our raw SQL qualifier but it was cool that 
it figured out how to follow the key path and use the join appropriately then 
only using T1.email where its needed. Pretty smart. I was surprised it was able 
to do that. 

Note #4: We could use the raw SQL qualifier to do things that EOF has no way of 
supporting like regular expressions. 
But now we’d have two problems: 
https://blog.codinghorror.com/regular-expressions-now-you-have-two-problems/
How to do regex in Postgres:
https://www.sqlshack.com/working-with-regular-expressions-in-postgresql/
https://www.postgresql.org/docs/current/functions-matching.html

Note #5: I also added the question mark because there is an undocumented 
feature of EOF that would otherwise treat it like a wildcard courtesy of “he 
who will not be named” (a NeXT engineer). 

Cheers,
— Aaron

> On Oct 12, 2023, at 12:52 PM, Michael Kondratov <mich...@aspireauctions.com> 
> wrote:
> 
> I think you can pass it as a variable to a fetch specification. 
> 
> 
> Sent from my iPhone
> 
>> On Oct 12, 2023, at 10:45, Aaron Rosenzweig via Webobjects-dev 
>> <webobjects-dev@lists.apple.com> wrote:
>> 
>> Hi all,
>> 
>> We can do a case insensitive like with EOF for *blue* on a text field (to 
>> find “blue” at the beginning, middle, or end of a field)
>> 
>> But what if we want to find an asterisk anywhere in a text field? How do we 
>> do a case insensitive like looking for an asterisk anywhere in the text? Is 
>> this possible with WO or do we need to resort to a raw SQL qualifier?
>> 
>> Thanks in advance,
>> — Aaron
>> 
>> _______________________________________________
>> Do not post admin requests to the list. They will be ignored.
>> Webobjects-dev mailing list      (Webobjects-dev@lists.apple.com)
>> Help/Unsubscribe/Update your Subscription:
>> https://lists.apple.com/mailman/options/webobjects-dev/michael%40aspireauctions.com
>> 
>> This email sent to mich...@aspireauctions.com

 _______________________________________________
Do not post admin requests to the list. They will be ignored.
Webobjects-dev mailing list      (Webobjects-dev@lists.apple.com)
Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/webobjects-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Reply via email to