Просмотр исходного кода

refactoring, use index for searches in CD entities

polscm32 2 лет назад
Родитель
Сommit
d9b7ce278f

+ 37 - 29
FreeAPS/Sources/APS/Storage/GlucoseStorage.swift

@@ -49,42 +49,50 @@ final class BaseGlucoseStorage: GlucoseStorage, Injectable {
             debug(.deviceManager, "start storage glucose")
 
             self.coredataContext.perform {
-                for glucoseEntry in glucose {
-                    guard let glucoseValue = glucoseEntry.glucose else { continue }
+                // read
+                let datesToCheck: Set<Date> = Set(glucose.compactMap { $0.dateString as Date? })
+                let fetchRequest: NSFetchRequest<NSFetchRequestResult> = GlucoseStored.fetchRequest()
+                fetchRequest.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [
+                    NSPredicate(format: "date IN %@", datesToCheck),
+                    NSPredicate.predicateForOneDayAgo
+                ])
+                fetchRequest.propertiesToFetch = ["date"]
+                fetchRequest.resultType = .dictionaryResultType
+
+                var existingDates = Set<Date>()
+                do {
+                    let results = try self.coredataContext.fetch(fetchRequest) as? [NSDictionary]
+                    existingDates = Set(results?.compactMap({ $0["date"] as? Date }) ?? [])
+                } catch {
+                    debugPrint("Failed to fetch existing glucose dates: \(error)")
+                }
 
-                    let dateString = glucoseEntry.dateString
-                    let fetchRequest: NSFetchRequest<NSFetchRequestResult> = GlucoseStored.fetchRequest()
-                    fetchRequest.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [
-                        NSPredicate(format: "date == %@", dateString as NSDate),
-                        NSPredicate.predicateForOneDayAgo
-                    ])
-                    fetchRequest.fetchLimit = 1
-                    fetchRequest.propertiesToFetch = ["date"]
-                    fetchRequest.resultType = .dictionaryResultType
-
-                    let count = (try? self.coredataContext.count(for: fetchRequest)) ?? 0
-                    if count > 0 {
-                        debugPrint("duplicate glucose detected. Skipping saving...")
-                        continue
-                    }
+                // filtering before loop
+                let filteredGlucose = glucose.filter { glucoseEntry -> Bool in
+                    !existingDates.contains(glucoseEntry.dateString)
+                }
+
+                // save
+                for glucoseEntry in filteredGlucose {
+                    guard let glucoseValue = glucoseEntry.glucose else { continue }
 
                     let newItem = GlucoseStored(context: self.coredataContext)
                     newItem.id = UUID()
                     newItem.glucose = Int16(glucoseValue)
-                    newItem.date = dateString
+                    newItem.date = glucoseEntry.dateString
                     newItem.direction = glucoseEntry.direction?.symbol
+                }
 
-                    if self.coredataContext.hasChanges {
-                        do {
-                            try self.coredataContext.save()
-                            debugPrint(
-                                "Glucose Storage: \(CoreDataStack.identifier) \(DebuggingIdentifiers.succeeded) saved glucose to core data"
-                            )
-                        } catch {
-                            debugPrint(
-                                "Glucose Storage: \(CoreDataStack.identifier) \(DebuggingIdentifiers.failed) failed to save glucose to core data: \(error)"
-                            )
-                        }
+                if self.coredataContext.hasChanges {
+                    do {
+                        try self.coredataContext.save()
+                        debugPrint(
+                            "Glucose Storage: \(CoreDataStack.identifier) \(DebuggingIdentifiers.succeeded) saved glucose to core data"
+                        )
+                    } catch {
+                        debugPrint(
+                            "Glucose Storage: \(CoreDataStack.identifier) \(DebuggingIdentifiers.failed) failed to save glucose to core data: \(error)"
+                        )
                     }
                 }
             }

+ 37 - 1
Model/Core_Data.xcdatamodeld/Core_Data.xcdatamodel/contents

@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="22757" systemVersion="23E224" minimumToolsVersion="Automatic" sourceLanguage="Swift" usedWithSwiftData="YES" userDefinedModelVersionIdentifier="">
+<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="22222" systemVersion="22G120" minimumToolsVersion="Automatic" sourceLanguage="Swift" usedWithSwiftData="YES" userDefinedModelVersionIdentifier="">
     <entity name="Autosens_" representedClassName="Autosens_" syncable="YES">
         <attribute name="newisf" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
         <attribute name="ratio" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
@@ -33,6 +33,9 @@
         <attribute name="isFPU" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
         <attribute name="note" optional="YES" attributeType="String"/>
         <attribute name="protein" optional="YES" attributeType="Double" defaultValueString="0.0" usesScalarValueType="YES"/>
+        <fetchIndex name="byDate">
+            <fetchIndexElement property="date" type="Binary" order="ascending"/>
+        </fetchIndex>
     </entity>
     <entity name="Forecast" representedClassName="Forecast" syncable="YES">
         <attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
@@ -40,6 +43,9 @@
         <attribute name="type" optional="YES" attributeType="String"/>
         <relationship name="forecastValues" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="ForecastValue" inverseName="forecast" inverseEntity="ForecastValue"/>
         <relationship name="orefDetermination" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="OrefDetermination" inverseName="forecasts" inverseEntity="OrefDetermination"/>
+        <fetchIndex name="byDate">
+            <fetchIndexElement property="date" type="Binary" order="descending"/>
+        </fetchIndex>
     </entity>
     <entity name="ForecastValue" representedClassName="ForecastValue" syncable="YES">
         <attribute name="index" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
@@ -52,6 +58,9 @@
         <attribute name="glucose" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
         <attribute name="id" optional="YES" attributeType="UUID" usesScalarValueType="NO"/>
         <attribute name="isManual" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
+        <fetchIndex name="byDate">
+            <fetchIndexElement property="date" type="Binary" order="descending"/>
+        </fetchIndex>
     </entity>
     <entity name="HbA1c" representedClassName="HbA1c" syncable="YES">
         <attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
@@ -70,6 +79,9 @@
         <attribute name="scheduledBasal" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
         <attribute name="tempBasal" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
         <relationship name="insulin" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Oref0Suggestion" inverseName="computedInsulinDistribution" inverseEntity="Oref0Suggestion"/>
+        <fetchIndex name="byDate">
+            <fetchIndexElement property="date" type="Binary" order="ascending"/>
+        </fetchIndex>
     </entity>
     <entity name="InsulinStored" representedClassName="InsulinStored" syncable="YES">
         <attribute name="amount" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
@@ -77,11 +89,17 @@
         <attribute name="external" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
         <attribute name="id" optional="YES" attributeType="UUID" usesScalarValueType="NO"/>
         <attribute name="isSMB" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
+        <fetchIndex name="byDate">
+            <fetchIndexElement property="date" type="Binary" order="ascending"/>
+        </fetchIndex>
     </entity>
     <entity name="LastLoop" representedClassName="LastLoop" syncable="YES">
         <attribute name="cob" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
         <attribute name="iob" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
         <attribute name="timestamp" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
+        <fetchIndex name="byDate">
+            <fetchIndexElement property="timestamp" type="Binary" order="descending"/>
+        </fetchIndex>
     </entity>
     <entity name="LoopStatRecord" representedClassName="LoopStatRecord" syncable="YES">
         <attribute name="duration" optional="YES" attributeType="Double" defaultValueString="0.0" usesScalarValueType="YES"/>
@@ -97,6 +115,9 @@
         <attribute name="percent" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
         <attribute name="status" optional="YES" attributeType="String"/>
         <attribute name="voltage" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <fetchIndex name="byDate">
+            <fetchIndexElement property="date" type="Binary" order="descending"/>
+        </fetchIndex>
     </entity>
     <entity name="Oref0Suggestion" representedClassName="Oref0Suggestion" syncable="YES">
         <relationship name="computedInsulinDistribution" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="InsulinDistribution" inverseName="insulin" inverseEntity="InsulinDistribution"/>
@@ -135,6 +156,9 @@
         <attribute name="timestampEnacted" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
         <attribute name="totalDailyDose" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
         <relationship name="forecasts" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Forecast" inverseName="orefDetermination" inverseEntity="Forecast"/>
+        <fetchIndex name="byDate">
+            <fetchIndexElement property="deliverAt" type="Binary" order="descending"/>
+        </fetchIndex>
     </entity>
     <entity name="Override" representedClassName="Override" syncable="YES">
         <attribute name="advancedSettings" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
@@ -155,6 +179,9 @@
         <attribute name="start" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
         <attribute name="target" optional="YES" attributeType="Decimal" defaultValueString="100"/>
         <attribute name="uamMinutes" optional="YES" attributeType="Decimal" defaultValueString="30"/>
+        <fetchIndex name="byDate">
+            <fetchIndexElement property="date" type="Binary" order="descending"/>
+        </fetchIndex>
     </entity>
     <entity name="OverridePresets" representedClassName="OverridePresets" syncable="YES">
         <attribute name="advancedSettings" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
@@ -196,6 +223,9 @@
         <attribute name="tdd" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
         <attribute name="timestamp" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
         <relationship name="computed" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Oref0Suggestion" inverseName="computedTDD" inverseEntity="Oref0Suggestion"/>
+        <fetchIndex name="byDate">
+            <fetchIndexElement property="timestamp" type="Binary" order="ascending"/>
+        </fetchIndex>
     </entity>
     <entity name="TempTargets" representedClassName="TempTargets" syncable="YES">
         <attribute name="active" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
@@ -204,6 +234,9 @@
         <attribute name="hbt" optional="YES" attributeType="Double" defaultValueString="160" usesScalarValueType="YES"/>
         <attribute name="id" optional="YES" attributeType="String"/>
         <attribute name="startDate" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
+        <fetchIndex name="byDate">
+            <fetchIndexElement property="date" type="Binary" order="descending"/>
+        </fetchIndex>
     </entity>
     <entity name="TempTargetsSlider" representedClassName="TempTargetsSlider" syncable="YES">
         <attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
@@ -213,5 +246,8 @@
         <attribute name="hbt" optional="YES" attributeType="Double" defaultValueString="160" usesScalarValueType="YES"/>
         <attribute name="id" optional="YES" attributeType="String" defaultValueString="empy"/>
         <attribute name="isPreset" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
+        <fetchIndex name="byDate">
+            <fetchIndexElement property="date" type="Binary" order="descending"/>
+        </fetchIndex>
     </entity>
 </model>