SQL's IN clause equivalent in Swift - swift

I wanted to know if there is any equivalent command in Core Data / Swift of MySQL's IN clause:
"SELECT details FROM tbl_details WHERE ticket IN (1,2,3)

This should work:
let list = [1, 2, 3]
let inPred = NSPredicate(format: "ticket IN %#", list)
var request = NSFetchRequest(entityName: "details")
request.returnsObjectsAsFaults = false
request.predicate = inPred
After that, call context.executeFetchRequest the usual way to retrieve the results.

Related

Swift - Core Data - using the result of fetch with GroupBy

I couldn't find answer to this question, I checked all questions here.
Summary:
I don't know how to use the result(that I successfully get back) of .fetch() method with GroupBy. The fetch method result must have .dictionaryResultType because I am using GroupBy, otherwise the GroupBy would not work. I can see that fetch returns [Any], so array of Any. However I try to cast/use/access this [Any] it fails.
My code:
//Create new entity
let timeLogEntity = NSEntityDescription.entity(forEntityName: "TimeLog", in: managedContext)!
//sum up the duration column
let keypathDuration = NSExpression(forKeyPath: "duration")
let expression = NSExpression(forFunction: "sum:", arguments: [keypathDuration])
let sumDesc = NSExpressionDescription()
sumDesc.expression = expression
sumDesc.name = "DurationSum"
sumDesc.expressionResultType = .integer64AttributeType
//Fetch request
var timeLogFetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "TimeLog")
//group by region
timeLogFetchRequest.returnsObjectsAsFaults = false
timeLogFetchRequest.propertiesToGroupBy = ["region"]
//sum of duration column
timeLogFetchRequest.propertiesToFetch = [sumDesc, "region"]
timeLogFetchRequest.resultType = .dictionaryResultType
//it works only like this with [Any[\]
var dailyFetchResult: [Any]?
do{
dailyFetchResult = try managedContext.fetch(timeLogFetchRequest)
}catch...
So the fetch works and returns values, I just don't know how to use them, or convert them to anything usable dictionary, tuple, String and Int...
The result looks like this: {DurationSum = 235; region = RegionName1}{DurationSum = 256; region = RegionName2} etc
Thanks
(dailyFetchResult[0] as! [String:Any]).keys , and (dailyFetchResult[0] as! [String:Any]).values did it. Thank you!

CoreData Fetch: Getting the objects with the maximum of one atttribute and having another attribute in common

I have a CoreData ManagedObject type Event with the properties name:Sting and date:Date.
I want to fetch all EventObject, with the name containing a filter. If more than one object with the same name matches the filter, only the object with the latest date should be returned.
Just to clarify what I want. In a table based approach I would query in SQL like:
SELECT name, max(date) FROM Event
WHERE name contains 'filter'
GROUP BY name
In CoreData I tried like this, but got:
Could not cast value of type 'NSKnownKeysDictionary1' to 'xx12.Event'.
let fetchRequest : NSFetchRequest<Event> = Event.fetchRequest( fetchRequest.predicate = NSPredicate (format: "name contains %#", filter)
fetchRequest.sortDescriptors = [NSSortDescriptor (key: "name", ascending: true)]
// We want Event not Dictionary back
// fetchRequest.resultType = NSFetchRequestResultType.dictionaryResultType
let keypathExpression = NSExpression(forKeyPath: "date")
let maxExpression = NSExpression(forFunction: "max:", arguments: [keypathExpression])
let key = "maxDate"
let expressionDescription = NSExpressionDescription()
expressionDescription.name = key
expressionDescription.expression = maxExpression
expressionDescription.expressionResultType = .dateAttributeType
fetchRequest.propertiesToFetch = [expressionDescription]
let resultEvents = try moc.fetch(fetchRequest)
Example:
if I have these objects
meeting1, 1.1.2020
meeting1, 1.4.2020
meeting2, 1.1.2020
meeting2, 1.4.2020
party, 2.3.2020
and the filter: meeting
I want to get the objects:
meeting1, 1.4.2020
meeting2, 1.4.2020
I would use a basic fetch request and then do the grouping part in swift code instead of working with NSExpression
let fetchRequest : NSFetchRequest<Event> = Event.fetchRequest()
fetchRequest.predicate = NSPredicate(format: "name contains %#", filter)
let result = try moc.fetch(fetchRequest)
return Dictionary(grouping: result, by: \.name).values
.compactMap { $0.max {$0.date < $1.date } }

Check object with CoreData predicate

I use Core Data to find CatalogItem with predicate "parentId == 3". And I want to check in this predicate also if is exist item with CatalogItem.parentId == id". This "id" is from result item of first query part. Is it possible to use id, which i don't know yet?
I hesitate to provide this answer, because (as #JoakimDanielson says in comments) a better solution is to implement a reflexive relationship from CatalogItem to itself. However, if that's not an option, I think the following variation on this answer to a similar question should achieve what you need:
let parentFetch = CatalogItem.fetchRequest()
parentFetch.predicate = NSPredicate(format:"parentId == 3")
parentFetch.propertiesToFetch = ["id"]
let ctxtExp = NSExpression(forConstantValue: managedObjectContext)
let fetchExp = NSExpression(forConstantValue: parentFetch)
let fre = NSFetchRequestExpression.expression(forFetch: fetchExp, context: ctxtExp, countOnly: false)
let childFetch = CatalogItem.fetchRequest()
childFetch.predicate = NSPredicate(format:"parentId IN %#",fre)
... proceed to execute the fetch against the relevant context

How to write an NSPredicate to search date attribute of to-many relationship?

I have a "Patient" object with a to-many relationship to an "Act" object (Patient.acts)
An Act object has a startDate attribute of type Date
I am trying to write the NSPredicate to get all patients who have at least one act whose startDate is today
I am on XCode 10, using swift 4.2
I cannot figure out the correct left expression in:
let start = NSExpression(forConstantValue: Calendar.current.startOfDay(for: Date()))
let end = NSExpression(forConstantValue: Calendar.current.endOfDay(for: Date()))
let todayRange = NSExpression(forAggregate: [start, end])
let actToday = NSComparisonPredicate(leftExpression: ????, rightExpression: todayRange, modifier: .any, type: .between, options: [])
for the leftExpression, I have tried several options but nothing works:
let left = NSExpression(forConstantValue: "acts.startDate")
I have tried a subpredicate approach:
let actDate = NSPredicate(format: "$x.startDate")
let left = NSExpression(forSubquery: NSExpression(forKeyPath: "acts"), usingIteratorVariable: "x", predicate: actDate2)
UPDATE and answer
I have come up with the following expression:
let seenToday = NSPredicate(format: "SUBQUERY(acts, $v, $v.actStartDate => %# && $v.actStartDate < %#).#count!=0", argumentArray:[startToday, endToday] )
Thanks to vadian for his answer. The question I wonder now is, is it more efficient to do it the way vadian proposes or is my subquery predicate adequate.
I'd recommend to fetch all acts for the given date and map them to their patient attribute (assuming there is an appropriate reverse relationship in Act)
let interval = Calendar.current.dateInterval(of: .day, for: Date())!
let fetchRequest : NSFetchRequest<Act> = Act.fetchRequest()
fetchRequest.predicate = NSPredicate(format: "startDate BETWEEN %#", [interval.start, interval.end] as [CVarArg])
Then fetch the acts and map the result to a set of patients
let acts = try context.fetch(fetchRequest)
let patients = Set(acts.map{$0.patient})

NSPredicate with Swift and Core Data

i have a Core Data Object and i have 2 Fieds (one String(GUID) and one Int which i want to use as Filter)
So in SQL it would be "SELECT * FROM Answers WHERE qIndex = 1 AND GUID = '88bfd206-82fb-4dd0-b65d-096f8902855c'
Ive tried it with Core Data but i am not able to Filter with the String Value.
Here is my Code
var request = NSFetchRequest(entityName: "Answers")
request.returnsObjectsAsFaults = false;
let resultPredicate1 = NSPredicate(format: "qIndex = %i", qIndex)
let resultPredicate2 = NSPredicate(format: "formUUID = %s", formUUID)
var compound = NSCompoundPredicate.andPredicateWithSubpredicates([resultPredicate1, resultPredicate2])
request.predicate = compound
var results:NSArray = context.executeFetchRequest(request, error: nil)
Any ideas what i am doing Wrong? With the Same Code and Filter for 2 Integer Values it works fine.
Thanks in Advance
If formUUID is an NSString or a Swift String then you have to use the
%# placeholder:
let resultPredicate2 = NSPredicate(format: "formUUID = %#", formUUID)
This is not the exact response to your question, but a problem people might now encouter with your code now:
In the latest version of XCode, you must now unwrap the predicate, like this:
var compound = NSCompoundPredicate.andPredicateWithSubpredicates([predicate1!, predicate2!])
because NSPredicate initializer now return NSPredicate? type.
Instead of worrying about %# conversions and then composing AND predicates, you can use the PredicatePal framework:
let compound = *(Key("qIndex") == qIndex && Key("formUUID") == formUUID)
Assuming that qIndex and formUUID are the correct type, Swift will automatically deduce the correct types for the Key objects.