Using predicates to search within results - core-data

I have a Core Data model of cars created and I'd like select one of vehicle type PLUS any of several different colors Here's what I envision to do this:
Selection ViewController
1) Select the type of car (sedan, coupe, SUV, hybrid) (I envision a segmented control to set this as a predicate)
2) Once the type of vehicle is selected, I'd like to select from a list of colors and display only cars that satisfy the type of vehicle I selected in the first step. For example, I'd like to display only SUVs that are (red, green, and blue) or only hybrids that are black and white.
Results TableViewController
3) Once those are selected, I'd like to dump them on another TVC.
This post helps a bit, but person asking the question trying to find disparate items (ex: A, B, & C). I'm trying to find X, Y & Z within only A.
Can I apply multiple predicates to an NSFetchRequest? Would it be better to manually parse my results?
I'm running into trouble when I try retrieve one type of vehicle plus a list of selected colors
let vehiclePredicate = NSPredicate(format: "type == %#", "(one of sedan/coupe/SUV/hybrid)")
AND any of the following criteria
let colorRedPredicate = NSPredicate(format: "color == %#", "red")
let colorGreenPredicate = NSPredicate(format: "color == %#", "green")
let colorBluePredicate = NSPredicate(format: "color == %#", "red")
let colorBlackPredicate = NSPredicate(format: "color == %#", "black")
let colorWhitePredicate = NSPredicate(format: "color == %#", "white")
let colorPredicates: NSArray = [**add colors here when they're selected**]
I'm trying to figure out the cleanest way to do this, but the examples I've found only retrieve disparate items.
The other roadblock I've run into is how to get the results from my SelectionVC to the ResultsTVC. I know I'm going to need a delegate, but what will I need? Any assistance on this would be greatly appreciated.

You want to end up with a predicate like:
type == %# AND color IN %#
And then supply your type as a string and the colours as a collection. You can do this with a compound predicate created from 2 others or just change what your UI selection means and generate the two parameters as the segment name and an array of colours.
If you want to combine multiple options of the oboe then you need to create each and then use a compound predicate to combine them.

Your question is not clear. Your predicates have wrong syntax - are unintelligible. That being said, I think I guess what you want, so here it is.
You could start with a fully loaded table view without any filters. You could implement a filter menu (or buttons) and present the appropriate control (segmented control, table view with multiple selection, table view with single selection, slider etc) modally.
In your original table view you use a NSFetchedResultsController, and you keep a NSPredicate as a property. Whenever the predicate changes, you nil out the fetched results controller and reload your table.
The modal control views can feed back to your master view controller which then modifies the predicate. You can combine all predicates with the NSCompoundPredicate API. Maybe it is most convenient to keep them in a mutable array, so you have them handy.
var predicateArray = [NSPredicate]()
E.g. start with this
predicate = NSPredicate(value: "true")
predicateArray.append(predicate)
Now the color controller sends back ["red", "green", "blue"]. You could construct or predicates by constructing the right predicate string, or by using orPredicateWithSubpredicates, but it might be easier to just use collection operators:
let colorPredicate = NSPredicate(format: "color in %#", colorStrings)!
predicateArray.append(colorPredicate)
In your fetched results controller getter you reconstruct the predicate based on your predicate array:
fetchRequest.predicate =
NSCompoundPredicate.andPredicateWithSubpredicates(predicateArray)

Related

Core Data - User Input selection multiple predicates - View results / Later save it to Favorite searches

I am trying to build an app that let users to select from a series of values to compose the final filter criteria. And later save it to "User favorite searches".
example
In my example the user should chose to filter Houses with 1 Room, 2 Bathrooms, Area 100 > 200mp, etc. and see all results with matching criteria.
In my app i use the standart FetchRequest entity. I would like to have something like this:
#FetchRequest(entity: Listing.entity(),sortDescriptors:[NSSortDescriptor(keyPath: \Listing.publishdate, ascending: false),],predicate: NSPredicate(format: "category == %# AND rooms == %#" AND ==%# bathrooms, "House", "User Input Nr of rooms", "User input nr of Bathrooms"))
How can i achieve this?

Core Data - NSFetchRequestResult: count of items in one-to-many relationship from result?

Consider a Core Data model with two Entities: TermDictionary and Term. The TermDictionary has a "name" property, and a one-to-many relationship called "terms" which points to a set of Term objects, each of which consists of two properties: "name" and "score".
I've got an NSFetchRequest which I'm using as a data source for a UITableView which displays all of the TermDictionaries in the database. The idea is that the table will, for each cell, display the name of the dictionary, along with a count of the number of terms in that dictionary.
In the following code snippet, item contains an NSFetchRequestResult for the "TermDictionary" entity:
let thisDict = item as! TermDictionary
cell.textLabel?.text = thisDict.name
cell.detailTextLabel?.text = "\(thisDict.terms?.count ?? 0) terms"
...The table cells are correctly displaying the names of the Term Dictionaries, however it looks like thisDict.terms is always coming up nil, so the number-of-terms label always shows zero.
Do I need do do something special with item rather than just casting it to my TermDictionary managed object subclass?
You do not need to do anything special. If thisDict.terms prints as nil, it really is nil. Check your data store.

Sort Core data entity by String (Swift3)

This is my code:
let fetche:NSFetchRequest<Location> Location.fetchRequest()
let sort = NSSortDescriptor(key:#keyPath(Location.locationName), ascending: true)
fetche.sortDescriptors = [sort]
let predicate1 = NSPredicate(format: "countryId CONTAINS %# ",results.countryId!)
fetche.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [predicate1])
I need to sort data by the string "Cho" in the location name.
If I understand your comments correctly, you have location names that contain strings naming countries ("USA", "KSA", "Palestine", etc) and also one that says "Choose country...", and you want to know how to sort "Choose country..." to the top of the list.
Ideally your data model would only include, well, data. Since "Choose country" is not a location name, it shouldn't be included in the list of countries. If you want to show this text above a list of countries, two better options are:
Don't include that text in the picker. Instead, put it above or next to the picker in a UILabel.
If you want to include that text in the picker, write the picker delegate so that it returns "Choose country" for the first entry, and then uses your country list for all other rows.
There are ways to sort the list as you suggest, but there are no good ways.

how to create compoundpredicate within nsFetchRequest that filters by two parameters of different entity

Some background info on my datamodel:
manufacturer <-->> item <<-->> tag
I currently generate a list of items by a fetchrequest:
- (NSFetchRequest*) rankingRequestForItem:(Item*)item {
NSFetchRequest* r = [NSFetchRequest fetchRequestWithEntityName:#"Item"];
NSPredicate* p = [NSPredicate predicateWithFormat:#"SELF != %#",item.objectID];
r.resultType = NSDictionaryResultType;
r.resultType = NSDictionaryResultType;
r.propertiesToFetch = #[[self objectIDExpressionDescription],#"itemName",
[self rankingExpressionDescriptionForTags:[item mutableSetValueForKey:#"itemToTag"]]];
r.predicate = p;
r.sortDescriptors = #[[NSSortDescriptor sortDescriptorWithKey:#"itemName" ascending:YES]];
return r;
}
This generates a list of all items. I want to filter it for items that have a relationship to a specific manufacturer. So I'm adding a predicate after the listing of all items and it sorts by selectedManufacturer.
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"itemToMa = %#", selectedManufacturer];
This works, but is grabbing a lot of items that will be filtered out. With large data sets I'm assuming will become slower and slower as it searches all items rather than just the ones associated with one manufacturer. I want to filter for items within the initial 'rankingRequestForItem' method.
Is it possible to move the above predicate with the top predicate and create a compoundpredicate?
I would not worry about performance. Core Data manages that pretty well under the hood. Sometimes the order of the predicates matters, so maybe put the manufacturer filter first.
You can combine the predicates in one as suggested in the comment to your question, or use compound predicates -- the result is pretty much the same.

Sorting NSFetchedResultsController using a to-many relationship property

SCENARIO
I have two entities: Item and ListDetail (which contains prices for different lists for every item). This is absolutely needed and I can't provide a price attribute for the Item entity because every item can have more prices for different dynamic lists (retail, b2b ecc.).
The relationship is:
Item (lists) <------->> (item) ListDetail
The current active list in my app change dinamically, so let's say I have an integer variable with the current active list: _ACTIVE_LIST_CODE_. When I need a price for an item object I use an helper method on the Item class:
-(NSNumber*) getPrice {
NSSet *lists=[self.lists filteredSetUsingPredicate: [NSPredicate predicateWithFormat:#"listId == %d",_ACTIVE_LIST_CODE_]];
ListDetail *activeList=[[lists allObjects] objectAtIndex:0];
return activeList.price;
}
THE PROBLEM
I use a UITableView with NSFetchedResultController in order to select and show some items for different sections. Nothing special. I would like to order the fetchedObjects using the items price for the active list. If price was an attribute of Item I would added simply a sort descriptor to the fetch request like so:
[NSSortDescriptor sortDescriptorWithKey:#"price" ascending:YES];
But as said before this is not possible, price is a dynamic attribute.
If using transient properties was possible for sort descriptors, I would set a price transient properties calculated on fly using my helper method. Nothing to do.
Using a keypath in the descriptor like "lists.price" is not possible (or maybe I don't know how to do that), just because it's a to-many relationship and it's modeled with a NSSet.
I tried some workaround, without success:
1) observing _ACTIVE_LIST_CODE_ changes to set items price in a non-transient attribute.
2) after the fetch request, before presenting the table view, reorder a brand new array with fetched objects using the transient "price" property, iterate the orderdered array following an ascending integer index "i" and assigning this value to a non-transient property "order" for the Item entity. Using "order" for sort descriptor in the fetch request. (This approach is described here: Re-ordering NSFetchedResultsController)
Both of them works, but they slow down performance because I have thousands of items in the fetch results... Any idea?
How about fetching ListDetail instead? You could restrict and sort with the appropriate predicates and sort descriptors, exactly as you propose.
fetchRequest.predicate =
[NSPredicate predicateWithFormat:#"listID = %#", activeListCode];
fetchRequest.sortDescriptors =
#[[NSSortDescriptor sortDescriptorWithKey:#"price" ascending:YES]];
Now, to group by some attribute of item should be simple and efficient because it is a to-one relationship. Your fetched results controller's sectionNameKeyPath can be something like
#"item.category"

Resources