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

Posted

技术标签:

【中文标题】使用 group by 子句从抽象实体上的提取请求中检索对象 ID【英文标题】:Retrieve Object ID from a fetch request on an abstract entity with a group by clause 【发布时间】:2015-01-07 09:37:00 【问题描述】:

我正在 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。这工作得很好,除了我得到每个CategoryTag 等的多个结果,因为与搜索词有一对多的关系(我不能使用一对一的关系,因为有些结果需要是高于其他人)。所以我想以某种方式将结果按与它们相关的实际实体分组。

首先 - 如果有更好的方法来实现我想要做的事情而不必进行分组,那么我会全力以赴。也许使用子查询的东西.. 否则,这就是它变得混乱的地方!

为了对结果进行分组,我显然需要一些东西来分组;我对每条记录都有一个唯一标识符(例如,来自我的远程 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 中的一个错误,或者当 fetch 请求具有 group by 子句时,该方法可能无效。没有办法回到具体的实体实例,我被困住了。由于我要查询至少数万条记录,我不想进行太多查询或将大量数据放入内存,所以直到现在我已经排除了将结果存储在一个数组并在代码中过滤它们(我更愿意继续使用 NSFetchedResultsController 和批处理结果)。如果有人对我可以从这里去哪里有任何建议,我将不胜感激。

【问题讨论】:

【参考方案1】:

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

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

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

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

Search <----->> Condition

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

【讨论】:

你说得对,如果数据实体不知道搜索实体,这是一个更好的设计。您是否介意验证我认为您的建议:我删除了搜索实体上的该关系和抽象/子关系 - 而是将实体类和标识符存储在搜索记录中,然后我可以将其用作后续获取请求中的条件?那么我的初始获取请求是针对搜索实体的,其谓词建立至少一个与搜索对象匹配相关的 Condition 对象?这对我来说很有意义,只是想在我开始重构之前检查一下:) 你所描述的似乎真的有点矫枉过正。如果我理解正确,您建议先在搜索中进行搜索,然后根据找到的搜索搜索数据——这很荒谬。我更多地考虑在某些应用程序中找到一个简单的“保存的搜索”功能 - 所以没有在搜索中进行搜索。您只需获取保存的搜索一次,然后根据用户选择的搜索获取数据。 不完全是——我正在存储一个要匹配的术语列表,例如 FTS 引擎如何创建 n-gram 等(这就是我想象的“条件”实体的方式,与“搜索”指向特定数据记录)。由于记录的数量,搜索必须是文本输入驱动的——你会使用全文搜索,但 Core Data 只支持 CONTAINS——所以要求用户选择结果是不可行的。我怀疑最好不要在 Core Data 中执行此操作,直接使用 SQLite 或其他方式,但目前这不是一个选项。 非常混乱。我不认为 Core Data 有你提到的限制。这似乎与原始数据模型设计问题无关。在我看来,Search 实体只是条件的集合,类似于可以串在一起创建搜索谓词的谓词。就这个子主题提出一个新的、更详细的问题也许是有意义的。 是的 - 如果我可以通过更改模型来实现我想要的,那么我不必担心对实体进行分组,那么我很高兴 - 从谓词方面我想我知道我知道什么正在做。根据您的建议,我将重构我的模型,看看它是否有效,谢谢。【参考方案2】:

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

fetchRequest.resultType = NSManagedObjectIDResultType

【讨论】:

谢谢 - 使用 group 子句的 fetch 请求必须是 NSDictionaryResultType 结果类型。

以上是关于使用 group by 子句从抽象实体上的提取请求中检索对象 ID的主要内容,如果未能解决你的问题,请参考以下文章

查询没有重复和聚合函数或 GROUP BY 子句问题。 - 重复

3-实体数据模型与LINQ-分组

oracle group by 性能优化

在 WHERE 或 GROUP BY 子句中使用列表别名

使用 Group By 作为 where 子句的一部分

only_full_group_by : "ORDER BY 子句不在 GROUP BY 子句中"