CoreData fetch 基于谓词添加属性
Posted
技术标签:
【中文标题】CoreData fetch 基于谓词添加属性【英文标题】:CoreData fetch adding an attribute based on a predicate 【发布时间】:2014-09-24 09:04:04 【问题描述】:我有以下数据模型(简化):
WordEntity ListItemEntity ListEntity
---------- -------------- ----------
text name
---------- -------------- ----------
listItems <------>> word
list <<---------------> items
以及以下基本查询:
let fetchRequest = NSFetchRequest(entityName: "WordEntity")
let controller = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.managedObjectContext, sectionNameKeyPath: nil, cacheName: nil)
let list = <ListEntity instance>
... do something clever here ...
controller.performFetch(nil)
我需要获取的结果为每个对象包含一个属性inList
,如果该WordEntity 对象附加到ListItemEntity,而ListItemEntity 又附加到list
,这将是正确的。类似于以下谓词,除了用于创建新属性而不是过滤获取请求:
NSPredicate(format: "ANY listItems.list == %@", list)
我查看了 NSExpressions,但它们似乎只用于聚合,不能做谓词。 SUBQUERY 可以做谓词,但仅用于过滤结果集。并且计算出的瞬态属性可以进行任何我想要的查找,但是它们无法对外部值进行操作...
我希望这很清楚......在此先感谢。
更新:
正如 pbasdf 建议的那样,我能够使用获取的属性来做到这一点。我在WordEntity
上有一个带有谓词的属性listItemsInList
:
(word == $FETCH_SOURCE) AND (list == $FETCHED_PROPERTY.userInfo.list)
然后在代码中:
let request = NSFetchRequest()
let entity = NSEntityDescription.entityForName("WordEntity", inManagedObjectContext: context)!
request.entity = entity
for property in entity.properties
if property.name == "listItemsInList"
let list = <ListEntity instance>
(property as NSFetchedPropertyDescription).userInfo!["list"] = list
最后:
if word.listItemsInList.count > 0
... this is what I was looking for ...
这行得通。不幸的是,它的效率很低。获取的属性总是返回获取对象的数组,而不是计算值。他们总是获取整个对象。最糟糕的是,它们不能被预取,因此检查表格单元格中的属性意味着每行都会命中数据库。所以我仍然希望有更聪明的方法来做到这一点。
【问题讨论】:
您查看过获取的属性吗? 谢谢。我不认为它对获取的属性是可行的,但我再次尝试了你的建议,我让它工作了。我已经用详细信息更新了问题。 如果您只是想检查给定单词是否在给定列表中,请尝试if ([word.listItems intersectsSet:list.items]) ...
(抱歉,Objective-C)。
谢谢。没关系,我可以阅读Objective-C。起初我是这样做的,只是检查一下设置,但我认为应该有更好的方法来做到这一点。我认为,如果我可以将它放入获取单词列表的同一个 CoreData 查询中,那将是最有效的。
【参考方案1】:
经过一番反复试验,我找到了一个解决方案,只要您对“按值”返回的实体属性感到满意(即它们将是基础属性)在字典数组中。一个节省的优点是字典可以包含相关对象的 CoreData objectID,因此检索该对象相对简单。
诀窍是使用一个 NSExpression,其中包括一个 SUBQUERY(用相关的列表名称替换)和@count。这是我使用的(抱歉,又是 Obj-C!):
// First, get an NSAttribute description for each of the attributes we want in our query
NSEntityDescription* entity = [NSEntityDescription entityForName:@"WordEntity" inManagedObjectContext:self.context];
NSAttributeDescription *wordDesc = [entity.attributesByName objectForKey:@"word"];
// Also get an NSExpression description for the object itself
NSExpression *objIDExpression = [NSExpression expressionForEvaluatedObject];
NSExpressionDescription *objIDDescription = [[NSExpressionDescription alloc] init];
[objIDDescription setName: @"cdObjectID"];
[objIDDescription setExpression: objIDExpression];
[objIDDescription setExpressionResultType: NSObjectIDAttributeType];
// Define an expression to count a subquery
NSExpression *countExpression = [NSExpression expressionWithFormat:@"SUBQUERY(listItems,$x,$x.list.listName like %@).@count",@"Animals",nil];
// Note, in the above, replace @"Animals" with list.listName for the list of interest
NSExpressionDescription *expressionDescription = [[NSExpressionDescription alloc] init];
[expressionDescription setName: @"count"];
[expressionDescription setExpression: countExpression];
[expressionDescription setExpressionResultType: NSInteger32AttributeType];
NSFetchRequest* fetch = [NSFetchRequest fetchRequestWithEntityName:@"WordEntity"];
[fetch setPropertiesToFetch:@[wordDesc, objIDDescription, expressionDescription]];
[fetch setResultType:NSDictionaryResultType];
NSArray *myResults = [self.context executeFetchRequest:fetch error:&error];
NSLog(@"myResults: %@",myResults);
// Recover the actual WordEntity object for the first item in myResults:
WordEntity *myWord = (WordEntity *)[self.context objectWithID:[[myResults firstObject] valueForKey:@"cdObjectID"]];
myResults 的输出如下所示。我的测试数据包括我在名为 Animals、Food 等的列表中包含的各种单词。下面的输出是针对 Animals 的;注意 count=1 for Dog!
cdObjectID = "0xd000000000040004 <x-coredata://4B6BC796-CAEF-4B0C-9B27-BC6CCDA572C7/WordEntity/p1>";
count = 0;
word = Orange;
,
cdObjectID = "0xd000000000080004 <x-coredata://4B6BC796-CAEF-4B0C-9B27-BC6CCDA572C7/WordEntity/p2>";
count = 0;
word = Geranium;
,
cdObjectID = "0xd0000000000c0004 <x-coredata://4B6BC796-CAEF-4B0C-9B27-BC6CCDA572C7/WordEntity/p3>";
count = 0;
word = Banana;
,
cdObjectID = "0xd000000000100004 <x-coredata://4B6BC796-CAEF-4B0C-9B27-BC6CCDA572C7/WordEntity/p4>";
count = 1;
word = Dog;
,
cdObjectID = "0xd000000000140004 <x-coredata://4B6BC796-CAEF-4B0C-9B27-BC6CCDA572C7/WordEntity/p5>";
count = 0;
word = Apple;
,
cdObjectID = "0xd000000000180004 <x-coredata://4B6BC796-CAEF-4B0C-9B27-BC6CCDA572C7/WordEntity/p6>";
count = 1;
word = Elephant;
, ....
我不知道在性能方面如何与其他解决方案进行比较!
【讨论】:
谢谢!这正是我希望能够做到的事情。以上是关于CoreData fetch 基于谓词添加属性的主要内容,如果未能解决你的问题,请参考以下文章