Core Data: case insensitive "equals" predicate strings!?

  • Dear group,

    Sorry, but this is yet another lame Core Data question: when using a
    predicate string to perform a fetch, e.g.

    NSPredicate* predicate = [NSPredicate predicateWithFormat: @"%K ==
    %@", @"fullDestinationPath", newFullPath];

    The equals (==) is case insensitive; I need a case sensitive equals.
    The operations supported on strings all take modifiers, so you can
    write:

    NSPredicate* predicate = [NSPredicate predicateWithFormat: @"%K
    CONTAINS[c] %@", @"fullDestinationPath", newFullPath];

    where the [c] part means "case insensitive". Works like a charm, but
    there's no equals operation specifically for strings.. the obvious
    solution:

    NSPredicate* predicate = [NSPredicate predicateWithFormat: @"%K ==[c]
    %@", @"fullDestinationPath", newFullPath];

    throws an invalid argument exception.

    I've had to resort to the very lame:

    NSPredicate* predicate = [NSPredicate predicateWithFormat: @"%K
    BEGINSWITH[c] %@ AND %K ENDSWITH[c] %@", @"fullDestinationPath",
    newFullPath];

    to get things to work properly.. surely this is not the recommended
    way of doing case insensitive matching for core data?

    The "newFullPath" variable obviously is a string that may contain any
    number of special characters so wild-card matching is out. What's more
    this is a probably the most performance critical part of the program..

    Any thoughts?

    Best regards,

    Frank
  • On Jan 7, 2008, at 12:29 PM, Frank Reiff wrote:

    > Any thoughts?
    >
    It would probably be worth looking at the documentation for string
    comparisons...
    <http://developer.apple.com/documentation/Cocoa/Conceptual/Predicates/Articl
    es/pSyntax.html
    >

    mmalc
  • On Jan 7, 2008, at 2:29 PM, Frank Reiff wrote:

    > I've had to resort to the very lame:
    >
    > NSPredicate* predicate = [NSPredicate predicateWithFormat: @"%K
    > BEGINSWITH[c] %@ AND %K ENDSWITH[c] %@", @"fullDestinationPath",
    > newFullPath];
    >
    > to get things to work properly.. surely this is not the recommended
    > way of doing case insensitive matching for core data?

    I don't see an obvious case-insensitive equals operator.  LIKE or
    MATCHES are tempting, but get you in trouble if either of the
    arguments include characters that are interpreted as meta-characters.

    However, I would not use the BEGINSWITH AND ENDSWITH construction
    that you're using.  That would match if fullDestinationPath happened
    to contain the concatenation of newFullPath with itself.

    I think that something like A CONTAINS[c] B AND B CONTAINS[c] A would
    be safer.

    Still, you're right, it's far from elegant.

    -Ken
  • Hi Ken,

    Thanks. You're of course right: my construction is not even safe; the
    A contains B and B contains A would better.

    I naturally do look through the documentation before asking silly
    questions on this group, but there simply does not seem to exist any
    way of performing a case insensitive "equals" using the predicate
    string format. After having googled my way through the usual places of
    wisdom (CocoaDev, CocoaBuilder, etc.), I came up empty except for the
    very same question having been asked before, but not answered..

    For the permanent record then, the code below seems to work:

    NSPredicate* predicate = [NSComparisonPredicate
    predicateWithLeftExpression: [NSExpression expressionForKeyPath:
    @"fullDestinationPath"]
            rightExpression: [NSExpression expressionForConstantValue:
    newFullPath]
                                        modifier: NSDirectPredicateModifier type:
    NSEqualToPredicateOperatorType options:
    NSCaseInsensitivePredicateOption];

    Since there does not appear to be any way of expressing this as a
    predicate string, I will log a bug report with Apple asking them to
    add ==[c]  or  ==[cd] or something similar to the formatting string
    syntax.

    Best regards,

    Frank

    On 7 Jan 2008, at 23:22, Ken Thomases wrote:

    > On Jan 7, 2008, at 2:29 PM, Frank Reiff wrote:
    >
    >> I've had to resort to the very lame:
    >>
    >> NSPredicate* predicate = [NSPredicate predicateWithFormat: @"%K
    >> BEGINSWITH[c] %@ AND %K ENDSWITH[c] %@", @"fullDestinationPath",
    >> newFullPath];
    >>
    >> to get things to work properly.. surely this is not the recommended
    >> way of doing case insensitive matching for core data?
    >
    > I don't see an obvious case-insensitive equals operator.  LIKE or
    > MATCHES are tempting, but get you in trouble if either of the
    > arguments include characters that are interpreted as meta-characters.
    >
    > However, I would not use the BEGINSWITH AND ENDSWITH construction
    > that you're using.  That would match if fullDestinationPath happened
    > to contain the concatenation of newFullPath with itself.
    >
    > I think that something like A CONTAINS[c] B AND B CONTAINS[c] A
    > would be safer.
    >
    > Still, you're right, it's far from elegant.
    >
    > -Ken
  • On Jan 8, 2008, at 8:03 AM, Frank Reiff wrote:

    > Since there does not appear to be any way of expressing this as a
    > predicate string, I will log a bug report with Apple asking them to
    > add ==[c]  or  ==[cd] or something similar to the formatting string
    > syntax.
    >
    It's not clear what you're trying to achieve.
    In what way does the LIKE operator not do what you want?

    mmalc
  • On Jan 8, 2008, at 10:33 AM, mmalc crawford wrote:

    > On Jan 8, 2008, at 8:03 AM, Frank Reiff wrote:
    >
    >> Since there does not appear to be any way of expressing this as a
    >> predicate string, I will log a bug report with Apple asking them to
    >> add ==[c]  or  ==[cd] or something similar to the formatting string
    >> syntax.
    >>
    > It's not clear what you're trying to achieve.
    > In what way does the LIKE operator not do what you want?

    The concern I would have would be if his "newFullPath" variable
    contains characters which would be interpreted by the LIKE operator as
    wildcards. It's not as uncommon as one might guess that a file path
    contains "*" or "?".

    -Ken
  • On Jan 8, 2008, at 4:22 PM, Ken Thomases wrote:

    >> It's not clear what you're trying to achieve.
    >> In what way does the LIKE operator not do what you want?
    >
    > The concern I would have would be if his "newFullPath" variable
    > contains characters which would be interpreted by the LIKE operator
    > as wildcards. It's not as uncommon as one might guess that a file
    > path contains "*" or "?".
    >
    You can escape the wildcards...

    If you want to be really lazy:

    string = [[string componentsSeparatedByString:@"*"]
                 componentsJoinedByString:@"\\*"];
    string = [[string componentsSeparatedByString:@"?"]
                 componentsJoinedByString:@"\\?"];

    Better (*) is to create a mutable copy of the string and modify it in
    place:

    NSInteger i = [string length];
    NSMutableString *string = [target mutableCopy];

    for ( ; i >= 0 ; i-- )
    {
        // ...

    mmalc

    (*) With thanks to MT.
  • On Jan 8, 2008, at 7:53 PM, mmalc crawford wrote:

    > On Jan 8, 2008, at 4:22 PM, Ken Thomases wrote:
    >
    >>> It's not clear what you're trying to achieve.
    >>> In what way does the LIKE operator not do what you want?
    >>
    >> The concern I would have would be if his "newFullPath" variable
    >> contains characters which would be interpreted by the LIKE
    >> operator as wildcards. It's not as uncommon as one might guess
    >> that a file path contains "*" or "?".
    >>
    > You can escape the wildcards...

    That's not actually documented on the Predicate Format String Syntax
    or BNF Definition of Cocoa Predicates pages.  There's something about
    escape sequences for numbers, but not about string contents.  I
    suppose it's likely that escapes are supported for meta-characters.

    I suppose some trial and error would reveal if it works.  I'm not
    that invested. ;)

    -Ken
  • Convert both strings to uppercase and stick with ==.

    That said, I don't see why like[c] wouldn't do the job. The variable
    is being quoted by the parser so wildcards don't count, no?

    On Jan 11, 2008 1:55 AM, Ken Thomases <ken...> wrote:
    > On Jan 8, 2008, at 7:53 PM, mmalc crawford wrote:
    >
    >> On Jan 8, 2008, at 4:22 PM, Ken Thomases wrote:
    >>
    >>>> It's not clear what you're trying to achieve.
    >>>> In what way does the LIKE operator not do what you want?
    >>>
    >>> The concern I would have would be if his "newFullPath" variable
    >>> contains characters which would be interpreted by the LIKE
    >>> operator as wildcards. It's not as uncommon as one might guess
    >>> that a file path contains "*" or "?".
    >>>
    >> You can escape the wildcards...
    >
    > That's not actually documented on the Predicate Format String Syntax
    > or BNF Definition of Cocoa Predicates pages.  There's something about
    > escape sequences for numbers, but not about string contents.  I
    > suppose it's likely that escapes are supported for meta-characters.
    >
    > I suppose some trial and error would reveal if it works.  I'm not
    > that invested. ;)
    >
    > -Ken
    >
  • Sorry,

    I've been off the list for a few days.. using the non-predicate string
    syntax:

    NSPredicate* predicate = [NSComparisonPredicate
    predicateWithLeftExpression: [NSExpression expressionForKeyPath:
    @"fullDestinationPath"]
            rightExpression: [NSExpression expressionForConstantValue:
    newFullPath]
                                        modifier: NSDirectPredicateModifier type:
    NSEqualToPredicateOperatorType options:
    NSCaseInsensitivePredicateOption];

    works and it uses a proper case insensitive (no fooling around with
    uppercase, lowercase or escape sequences) equals; this syntax is
    unfortunately not available if you use predicate string format, which
    in my humble opinion is not a good thing; I have submitted the
    improvement request to Apple.

    Keeping in mind that this is not for in-memory use but for a CoreData
    query which goes to an sqlite backend, using the uppercase/ lowercase
    idea or escaping all characters and using LIKE, will very likely yield
    a less than perfectly optimized SQL query that is pretty much
    guaranteed to be slow. I don't know how well Core Data optimizes
    queries, so using the "proper" query rather than tricking Core Data
    into doing this might make no difference at all, but at least it gives
    it a shot at producing an optimized query.

    Using the uppercase (or lowercase) option would yield good performance
    results if the original path is stored in all uppercase (or
    lowercase); probably faster than the "proper" case insensitive equals
    predicate.

    HFS+ file paths are however case preserving (if unfortunately not case
    sensitive) so I can't just drop the case information. Keeping a
    separate field with an all uppercase version of the string would be
    possible but is probably a waste of hard disk and CPU time.

    Anyway, problem solved.

    Best regards,

    Frank

    On 11 Jan 2008, at 11:46, <slasktrattenator...> wrote:

    > Convert both strings to uppercase and stick with ==.
    >
    > That said, I don't see why like[c] wouldn't do the job. The variable
    > is being quoted by the parser so wildcards don't count, no?
    >
    > On Jan 11, 2008 1:55 AM, Ken Thomases <ken...> wrote:
    >> On Jan 8, 2008, at 7:53 PM, mmalc crawford wrote:
    >>
    >>> On Jan 8, 2008, at 4:22 PM, Ken Thomases wrote:
    >>>
    >>>>> It's not clear what you're trying to achieve.
    >>>>> In what way does the LIKE operator not do what you want?
    >>>>
    >>>> The concern I would have would be if his "newFullPath" variable
    >>>> contains characters which would be interpreted by the LIKE
    >>>> operator as wildcards. It's not as uncommon as one might guess
    >>>> that a file path contains "*" or "?".
    >>>>
    >>> You can escape the wildcards...
    >>
    >> That's not actually documented on the Predicate Format String Syntax
    >> or BNF Definition of Cocoa Predicates pages.  There's something about
    >> escape sequences for numbers, but not about string contents.  I
    >> suppose it's likely that escapes are supported for meta-characters.
    >>
    >> I suppose some trial and error would reveal if it works.  I'm not
    >> that invested. ;)
    >>
    >> -Ken
    >>

previous month january 2008 next month
MTWTFSS
  1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31      
Go to today