CoreData predicate using relationship - swift

I have two entities - Quotes and Customers. One customer can have many quotes. The relationships are quotes and customers.
I want to get a quote object based on the customer name and email address, sorted by date but I'm stuck trying to format the predicate...
func getMostRecentQuote(name: String, email: String) -> Quotes? {
var predicateList = [NSPredicate]()
let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "Quotes")
let predicate1 = NSPredicate(format: "name CONTAINS[c] %#", name)
let predicate2 = NSPredicate(format: "email CONTAINS[c] %#", email)
let orCompoundPredicate = NSCompoundPredicate(type: .or, subpredicates: [predicate1, predicate2])
predicateList.append(orCompoundPredicate)
fetchRequest.predicate = NSCompoundPredicate(type: .and, subpredicates: predicateList)
fetchRequest.fetchLimit = 1

Probably you have a to-one relationship from Quotes to Customers, if not, establish one and name the property customer
Then use this single predicate
let predicate = NSPredicate(format: "customer.name CONTAINS[c] %# OR customer.email CONTAINS[c] %#", name, email)
If you want to filter the full string caseinsensitive CONTAINS is actually the wrong operator, better use LIKE
let predicate = NSPredicate(format: "customer.name LIKE[c] %# OR customer.email LIKE[c] %#", name, email)
 
Note: Please name entities in singular form, semantically your method is going to return one Quote, not one Quotes

Related

Sort filtered results in Realm by matching

I have a dictionary with ~100k entries and provide a search function where the user can search for a string in english/chinese and all entries that contain this string will be displayed. An entry has the structure:
class DictionaryEntry: Object, Identifiable {
#objc dynamic var id: String = NSUUID().uuidString
#objc dynamic var character: String = ""
#objc dynamic var pinyin: String = ""
#objc dynamic var definition: String = ""
}
Let's say we have the following two entries:
entry1 = "宝山 / Bǎoshān / Bǎoshān district of Shanghai"
entry2 = "上海 / Shànghǎi / Shanghai"
and the search string is "shanghai". I filter the entries by this way:
let realm = try! Realm()
realm.objects(DictionaryEntry.self)
.filter("pinyin CONTAINS[cd] %# OR definition CONTAINS[cd] %# OR character CONTAINS[cd] %#", searchString, searchString, searchString)
//.sorted(by: ???)
Since both entries contain the search string both entries will appear, but they are not sorted by any logic yet. I would like to sort the results by the highest "coverage/matching". So first should "Shanghai" appear, then "Shanghai university", ..., and at the end entries like "Old canal between Suzhou and Shanghai". Is there any built-in realm solution?
Realm's Results have built-in methods for sorting, see Sorting in the docs.
If you want to sort based on a specific property, you can use sorted(byKeyPath:) which takes a single String representing the property name that you want to sort by:
let realm = try! Realm()
realm.objects(DictionaryEntry.self)
.filter("pinyin CONTAINS[cd] %# OR definition CONTAINS[cd] %# OR character CONTAINS[cd] %#", searchString, searchString, searchString)
.sorted(byKeyPath: "pinyin")
Or if you want to sort based on all 3 properties that you are filtering by, you can use sorted(by:), which takes a Sequence of SortDescriptor objects, which can be created based on key paths as well:
let realm = try! Realm()
realm.objects(DictionaryEntry.self)
.filter("pinyin CONTAINS[cd] %# OR definition CONTAINS[cd] %# OR character CONTAINS[cd] %#", searchString, searchString, searchString)
.sorted(by: [SortDescriptor(keyPath: "pinyin"), SortDescriptor(keyPath: "definition"), SortDescriptor(keyPath: "character")])

Searching 2 Attributes at the same time CoreData

I'm trying to use a predicate to search 2 attributes at the same time. I initially tried a compound predicate but it would only return results if both predicates matched the string.
Basically I'm looking for something similar to this:
let predicate = NSPredicate(format: "title CONTAINS[cd] %#" || "plainTextBody CONTAINS[cd] %#", searchString, searchString)
So it seems I was close with my original post but it's important to keep the search terms in quotation marks and not separate them like I did in my original question. Simply using the following works perfectly:
let predicate = NSPredicate(format: "title CONTAINS[cd] %# || plainTextBody CONTAINS[cd] %#", searchString, searchString)

How to sort ckrecords by userID?

How should I go about creating an nspredicate that checks the userID of a record in swift?
let userID = "__defaultOwner__"
let predicate = NSPredicate(format: "keyToUseHere == %#", userID)
What should I put in place of the 'keyToUseHere' in the nspredicate to sort by the creator's id?
You need to use the key which can be property of your model class something like this:-
let predicate = NSPredicate(format: "userID" == %#", userIDValue)

Combining Two Conditions in NSPredicate

How do you combine two conditions in NSPredicate? I am using the following statement and I would like to add another condition that compares the the password with the contents of a textfield using AND:
request.predicate = NSPredicate(format: "username = %#", txtUserName.text!)
As already said, you can use logical operators like "AND", "OR"
in predicates. Details can be found in
Predicate Format String Syntax in the "Predicate Programming Guide".
As an alternative, use "compound predicates":
let p1 = NSPredicate(format: "username = %#", "user")
let p2 = NSPredicate(format: "password = %#", "password")
let predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [p1, p2])
This is useful for more complex expressions, or if you want to build
a predicate dynamically at runtime.
Try this
request.predicate = NSPredicate(format: "username = %# AND password = %#", txtUserName.text!, txtPassword.text!)
AND is exactly what you need
request.predicate = NSPredicate(format: "username = %# AND password = %#", txtUserName.text!, txtPassWord.text!)

CKQuery where predicate has reference and not field?

I need to create a CKQuery where the predicate contains a reference of a record, and not a field of the record.
Like this
let query = CKQuery(recordType: "OUP", predicate: NSPredicate(format: "o = %#", "FF4FB4A9-271A-4AF4-B02C-722ABF25BF44")
How do I set o is a CKReference, not field!
I get this error:
Field value type mismatch in query predicate for field 'o'
You can use a CKRecord or CKRecordID, with or without a CKReference, to match relationships.
CKRecord:
let predicate = NSPredicate(format: "artist == %#", artist)
CKRecordID:
let predicate = NSPredicate(format: "artist == %#", artistID)
CKReference with CKRecord:
let recordToMatch = CKReference(record: artist, action: CKReferenceAction.None)
let predicate = NSPredicate(format: "artist == %#", recordToMatch)
CKReference with CKRecordID:
let recordToMatch = CKReference(recordID: artistID, action: CKReferenceAction.None)
let predicate = NSPredicate(format: "artist == %#", recordToMatch)
I found in CKQuery class reference the answer, or at least an example how to use CKReference in CKQuery:
CKReference* recordToMatch = [[CKReference alloc] initWithRecordID:employeeID action:CKReferenceActionNone];
NSPredicate* predicate = [NSPredicate predicateWithFormat:#"employee == %#", recordToMatch];
To match records that link to a different record whose ID you know, create a predicate that matches a field containing a reference object as shown in Listing 1. In the example, the employee field of the record contains a CKReference object that points to another record. When the query executes, a match occurs when the ID in the locally created CKReference object is the same ID found in the specified field of the record.