【问题标题】:Core Data - How to fetch an entity with max value property核心数据 - 如何获取具有最大值属性的实体
【发布时间】:2012-05-10 23:58:25
【问题描述】:

我有一个实体Person,其属性为personId(personId 是唯一的)

如何获取具有最大 personId 的 Person?

(我想获取人本身而不是财产的价值)

【问题讨论】:

    标签: iphone objective-c ios core-data nsfetchedresultscontroller


    【解决方案1】:

    除了 Ryan 的回答,今天在 Swift 中,NSManagedObjectexecute(_:) 返回一个 NSPersistentStoreResult 对象,需要一些额外的代码来检索值:

    // Cast `NSPersistentStoreResult` to `NSAsynchronousFetchResult<NSDictionary>`
    let fetchResult = moc.execute(request) as! NSAsynchronousFetchResult<NSDictionary>
    
    // Retrieve array of dictionary result
    let dictsResult = fetchResult.finalResult
    
    // Retrieve and cast the real result
    let key = /* `expressionDescription.name` */
    let result = dictsResult.first!.object(forKey: key) as! /* Your type, depending on `expressionDescription.expressionResultType` */
    

    注意:上面使用强制不安全类型转换来简化代码,在实际情况下,您应该始终避免这种情况。

    【讨论】:

      【解决方案2】:

      SWIFT 4

      let request: NSFetchRequest<Person> = Person.fetchRequest()
      request.fetchLimit = 1
      
      let predicate = NSPredicate(format: "personId ==max(personId)")
      request.predicate = predicate
      
      var maxValue: Int64? = nil
      do {
          let result = try self.context.fetch(request).first
          maxValue = result?.personId
      } catch {
          print("Unresolved error in retrieving max personId value \(error)")
      }
      

      【讨论】:

        【解决方案3】:

        上面使用 NSExpression 给出的答案是正确的。这是 Swift 版本。

        private func getLastSyncTimestamp() -> Int64? {
        
        let request: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest()
        request.entity = NSEntityDescription.entity(forEntityName: "EntityName", in: self.moc)
        request.resultType = NSFetchRequestResultType.dictionaryResultType
        
        let keypathExpression = NSExpression(forKeyPath: "timestamp")
        let maxExpression = NSExpression(forFunction: "max:", arguments: [keypathExpression])
        
        let key = "maxTimestamp"
        
        let expressionDescription = NSExpressionDescription()
        expressionDescription.name = key
        expressionDescription.expression = maxExpression
        expressionDescription.expressionResultType = .integer64AttributeType
        
        request.propertiesToFetch = [expressionDescription]
        
        var maxTimestamp: Int64? = nil
        
        do {
        
            if let result = try self.moc.fetch(request) as? [[String: Int64]], let dict = result.first {
               maxTimestamp = dict[key]
            }
        
        } catch {
            assertionFailure("Failed to fetch max timestamp with error = \(error)")
            return nil
        }
        
        return maxTimestamp
        }
        

        moc 是一个 NSManagedObjectContext。

        【讨论】:

          【解决方案4】:

          斯威夫特 3

          let request:NSFetchRequest = Person.fetchRequest()
          
          let sortDescriptor1 = NSSortDescriptor(key: "personId", ascending: false)
          
          request.sortDescriptors = [sortDescriptor1]
          
          request.fetchLimit = 1
          
          do {
              let persons = try context.fetch(request)
              return persons.first?.personId
          } catch {
              print(error.localizedDescription)
          }
          

          【讨论】:

            【解决方案5】:

            推荐的方式是使用Apple Recommended MethodNSExpression。我希望这会比使用排序便宜。如果您考虑一下,使用排序您将不得不对所有记录进行排序并保留最大的记录。使用表达式,您只需通读列表并将最大值保留在内存中。

            这是我在 NSDate 中使用的示例

            - (NSDate *)lastSync:(PHAssetMediaType)mediaType {
                NSEntityDescription *entity = [NSEntityDescription  entityForName:kMediaItemEntity inManagedObjectContext:self.managedObjectContext];
            
                NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
                fetchRequest.entity = entity;
                fetchRequest.resultType = NSDictionaryResultType;
            
                NSMutableArray *predicates = [NSMutableArray array];
                [predicates addObject:[NSPredicate predicateWithFormat:@"%K=%d", kMediaType,mediaType]];
                [predicates addObject:[NSPredicate predicateWithFormat:@"%K=%d", kMediaProviderType,self.mediaProviderType]];
                NSPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates: predicates];
                fetchRequest.predicate = predicate;
            
                // Create an expression for the key path.
            
                NSExpression *keyPathExpression = [NSExpression expressionForKeyPath:kSyncTime];
                // Create an expression to represent the function you want to apply
            
                NSExpression *maxExpression = [NSExpression expressionForFunction:@"max:"
                                                                        arguments:@[keyPathExpression]];
            
                // Create an expression description using the maxExpression and returning a date.
                NSExpressionDescription *expressionDescription = [[NSExpressionDescription alloc] init];
                [expressionDescription setName:@"maxDate"];
                [expressionDescription setExpression:maxExpression];
                [expressionDescription setExpressionResultType:NSDateAttributeType];
            
                // Set the request's properties to fetch just the property represented by the expressions.
                fetchRequest.propertiesToFetch = @[expressionDescription] ; // @[kSyncTime];
            
                NSError *fetchError = nil;
                id requestedValue = nil;
            
                // fetch stored media
                NSArray *results = [self.managedObjectContext executeFetchRequest:fetchRequest error:&fetchError];
                if (fetchError || results == nil || results.count == 0) {
                    return [NSDate dateWithTimeIntervalSince1970:0];
                }
                requestedValue = [[results objectAtIndex:0] valueForKey:@"maxDate"];
                if (![requestedValue isKindOfClass:[NSDate class]]) {
                    return [NSDate dateWithTimeIntervalSince1970:0];
                }
                DDLogDebug(@"sync date %@",requestedValue);
                return (NSDate *)requestedValue;
            }
            

            【讨论】:

            • 看起来该方法确实是最好的方法,但它似乎不适用于内存存储:stackoverflow.com/questions/19301181/…
            • 它对我有用,直到我将项目迁移到 Swift 3。为了更大的利益,Apple 引入了强类型获取请求,这不适用于这种方法,导致 Could not cast value of type 'NSKnownKeysDictionary1' (0x106019870) to 'NSManagedObject' (0x106019b18). 错误。也许解决方法是使用MOC的execute方法,但它似乎太低级了,我将恢复为'排序'方法。
            • 我在获取 downcast to--> 后遇到了同样的问题! [NSDictionary] 解决了我的问题
            • 请注意,此解决方案仅适用于已保存到后备存储的数据。如果您在 MOC 中有未保存的对象,它们将不属于最大计算的一部分。
            【解决方案6】:

            您将fetchLimit 设置为1 并按personId 降序排列。例如:

            NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:@"Person"];
            
            fetchRequest.fetchLimit = 1;
            fetchRequest.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"personId" ascending:NO]];
            
            NSError *error = nil;
            
            id person = [managedObjectContext executeFetchRequest:fetchRequest error:&error].firstObject;
            

            【讨论】:

            • 如果你没有对要排序的属性的索引,那么这种技术比扫描一个最大值 O(n) 的列表更昂贵,O(n log n),如@Uilleann 的回答中所述。也就是说,如果您确实对要排序的属性有索引,那么这两种技术应该是相同的。
            • 是否值得将 fetchBatchSize 也设置为 1,还是隐含在 fetchLimit 为 1 中?
            • 不应该有所作为
            【解决方案7】:

            您需要使用带有 NSPredicate 的 NSFetchRequest 来指定您的查询...

            改编自 Apple 的谓词编程指南:

            NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
            NSEntityDescription *entity = [NSEntityDescription entityForName:@"Person"
                inManagedObjectContext:managedObjectContext];
            [request setEntity:entity];
            
            request.predicate = [NSPredicate predicateWithFormat:@"personId==max(personId)"];
            request.sortDescriptors = [NSArray array];
            
            NSError *error = nil;
            NSArray *array = [managedObjectContext executeFetchRequest:request error:&error];
            

            【讨论】:

            • 是否需要将sortDescriptors设置为空数组?
            • @AbhiBeckert 您通常可以添加 NSSortDescriptors 来指定您希望返回的对象如何排序。但在这种情况下,添加排序描述符没有意义,因为查询/谓词要求 personId 的最大值,所以只会返回一个对象。
            猜你喜欢
            • 2016-12-30
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多