3

I'm trying to search a Parse.com field which is an array for a partial string.

When the field is in String format I can do the following:

    // Update the filtered array based on the search text and scope.
// Remove all objects from the filtered search array
[self.searchResults removeAllObjects];
// Filter the array using NSPredicate
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF.busnumber contains[c] %@", searchText];
self.searchResults = [NSMutableArray arrayWithArray:[self.objects filteredArrayUsingPredicate:predicate]];

This works, however the new field I want to search in is an Array. It works when I change the it to the following:

    PFQuery * query = [PFQuery queryWithClassName:@"Bus"];
[query whereKey:@"route" equalTo:[NSString stringWithFormat:@"%@", searchText]];

[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {

    NSLog(@"Objects: %@", objects);

    if (error)
      {
        NSLog(@"ERROR: %@", error.localizedDescription);
      }
    else
      {
        [self.searchResults removeAllObjects];
        [self.searchResults addObjectsFromArray:objects];
        [self.searchDisplayController.searchResultsTableView reloadData];
      }}];

However I need the exact String for this.

I want to be able to search for parts of a string though, but when I change it to:

    [query whereKey:@"route" containsString:[NSString stringWithFormat:@"%@", searchText]];

I get:

[Error]: $regex only works on string fields (Code: 102, Version: 1.7.4)

Any ideas? Thanks :)

1 Answer 1

2

What you've attempted is rational, but the string qualifiers on PFQuery work only on strings.

I've seen this theme frequently on SO: PFQuery provides only basic comparisons for simple attributes. To do anything more, one must query for a superset and do app level computation to reduce the superset to the desired set. Doing so is expensive for two reasons: app-level compute speed/space, and network transmission of the superset.

The first expense is mitigated and the second expense is eliminated by using a cloud function to do the app level reduction of the superset. Unless you need the superset records on the client anyway, consider moving this query to the cloud.

Specific to this question, here's what I think the cloud function would resemble:

// very handy to have underscore available
var _ = require('underscore');

// return Bus objects whose route array contains strings which contain
// the passed routeSubstring (request.params.routeSubstring)
Parse.Cloud.define("busWithRouteContaining", function(request, response) {
    // for now, don't deal with counts > 1k
    // there's a simple adjustment (using step recursively) to get > 1k results
    var query = new Parse.Query("Bus");
    query.find().then(function(buses) {
        var matching = _.select(buses, function(bus) {
            var route = bus.get("route");
            var routeSubstring = request.params.routeSubstring;
            return _.contains(route, function(routeString) {
                return routeString.includes(routeSubstring);
            }); 
        });
        response.success(matching);
    }, function(error) {
        response.error(error);
    });
});

If you do decide to perform the reduction on the client and need help with the code, I can edit that in. It will be a pretty simple switch to predicateWithBlock: with a block that iterates the array attribute and checks rangeOfString: on each.

Sign up to request clarification or add additional context in comments.

4 Comments

I'm not entirely sure where and how to implement this ... I've added the Cloud code and called it in -(void)filterContentForSearchText by using PFCloud callFunctionInBackground, but the result is empty ...
Maybe nothing matched? Did you remember to parse deploy? A good debug step would be to just return the result of the query (i.e. comment out everything in the find result function and just say response.success(buses)). If that fails, try hardcoding a result like response.success("hello world"); and see how that does. I'm pretty confident that this kind of thing works (and has no better alternative approach).
Okay, so changing to response.success(buses) gives back the result with all the buses. However, I'm unable to get any data back when searching, even though I'm 1000% sure the string (or part of it) exists in the array. Example of the "route" array: ["Pak Thang Khlong Kwang","Mooban Sethakit","Mall Bang Khae"]
My code: [PFCloud callFunctionInBackground:@"busWithRouteContaining" withParameters:@{@"routeSubstring": searchText} block:^(NSArray *result, NSError *error) { if (!error) { // result is @"Hello world!" NSLog(@"RESULT: %@", result); } } }];

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.