使用 group by 子句从抽象实体的提取请求中检索对象 ID

Retrieve Object ID from a fetch request on an abstract entity with a group by clause

我正在 iOS 应用程序中执行搜索(针对 iOS 8 及以上),并且有一组实体存储其他对象的搜索词。搜索词实体有一个抽象的父实体。每个具体的搜索词实体与其相关的对象都有关系(一个对象可以有多个搜索词)。这样我就可以 运行 针对抽象父搜索词实体的提取请求,并通过每个具体结果到达匹配的对象。例如:

然后我发出这样的请求以查找给定字符串的匹配项:

NSString *text = @"query";
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"AbstractSearchTerm"];

fetchRequest.predicate = [NSPredicate predicateWithFormat:@"term CONTAINS[cd] %@", text];

NSSortDescriptor *boostSortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"boost" ascending:NO];
fetchRequest.sortDescriptors = @[boostSortDescriptor];

NSError *error = nil;
[self.managedObjectContext executeFetchRequest:fetchRequest error:&error];

然后将获取请求提供给 NSFetchedResultsController,后者驱动 table 搜索结果视图。这很好用,除了我为每个 CategoryTag 等得到多个结果,因为 to-many 与搜索词的关系(我不能使用 to-one关系,因为有些结果需要优于其他结果)。所以我想以某种方式根据它们相关的实际实体对结果进行分组。

首先 - 如果有更好的方法来实现我正在尝试做的事情而不必进入分组,那么我会洗耳恭听。也许使用子查询的东西..否则,这里会变得混乱!

为了对结果进行分组,我显然需要一些东西来分组;我为每条记录(例如,来自我的远程 API 的记录)设置了一个唯一标识符,我可以将其非规范化为 AbstractSearchTerm 上的 属性,因此我可以使用它。我像这样修改了我的获取请求:

NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"AbstractSearchTerm" inManagedObjectContext:self.managedObjectContext];
NSDictionary *entityProperties = [entityDescription propertiesByName];

NSString *text = @"query";
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"AbstractSearchTerm"];

// ...

fetchRequest.propertiesToFetch = @[entityProperties[@"uniqueIdentifier"], entityProperties[@"term"], entityProperties[@"boost"]];
fetchRequest.propertiesToGroupBy = @[entityProperties[@"uniqueIdentifier"], entityProperties[@"term"], entityProperties[@"boost"]];

fetchRequest.resultType = NSDictionaryResultType;

NSError *error = nil;
[self.managedObjectContext executeFetchRequest:fetchRequest error:&error];

我得到了结果,但是我没有 objectID 可以返回到实际的搜索对象,从而返回到匹配的关系。所以我想也检索 objectID。根据 this answer,有一种方法可以做到这一点,但是当我这样尝试时:

NSExpressionDescription* objectIdDesc = [[NSExpressionDescription alloc] init];
objectIdDesc.name = @"objectID";
objectIdDesc.expression = [NSExpression expressionForEvaluatedObject];
objectIdDesc.expressionResultType = NSObjectIDAttributeType;

// ...

fetchRequest.propertiesToFetch = @[objectIdDesc, entityProperties[@"uniqueIdentifier"], entityProperties[@"term"], entityProperties[@"boost"]];
fetchRequest.propertiesToGroupBy = @[objectIdDesc, entityProperties[@"uniqueIdentifier"], entityProperties[@"term"], entityProperties[@"boost"]];

我收到这个错误:

Invalid keypath expression ((<NSExpressionDescription: 0x7f9e89454e50>), name objectID, isOptional 1, isTransient 0, entity (null), renamingIdentifier objectID, validation predicates ( ), warnings ( ), versionHashModifier (null) userInfo { }) passed to setPropertiesToFetch:

我无法绕过。这可能是 Core Data 中的错误,或者当获取请求具有 group by 子句时,该方法可能无效。没有办法回到具体的实体实例,我被困住了。因为我要查询至少数万条记录,所以我不想进行太多查询或将大量数据放入内存,所以直到现在我都排除了将结果存储在一个数组并在代码中过滤它们(我更愿意继续使用 NSFetchedResultsController 和批处理结果)。如果有人对我可以从这里去哪里有任何建议,我将不胜感激。

您需要添加以下内容以获取 ObjectID

fetchRequest.resultType = NSManagedObjectIDResultType

我假设您想存储用户可以编写的搜索并将它们保存在您的 Core Data 模型中。创建描述此搜索信息的实体是一种有效的方法。

但是,我认为您的想法 link 这些具有实际数据对象的搜索对象从设计的角度来看并不是很好。数据对象不应负担搜索功能(尤其是在数据模型级别)。因此,您的搜索建模应该独立于您的数据,即这两组实体之间没有直接关系。

直接关系真的没有什么优势。构建和执行获取请求以及拉取关系中的实体都具有相同的持久存储开销,因此只会增加不必要的复杂性。

相反,让与搜索相关的实体描述搜索。我认为您可以在 没有 父实体和子实体的情况下非常简单地实现这一目标。我认为所有需要的可能是子谓词的对多关系,但甚至可能不是。所有搜索相关信息都可以在字符串属性中编码。整个搜索模型可以像这样简单:

Search <----->> Condition

我认为您可以通过这种非常简化的设置实现您想要的一切(包括分组、计数等)。此外,您不必再担心结果类型,只需获取 NSManagedObejcts 即可。