I'm working on an iOS flash-card style learning app that, on load, needs to grab a bunch of data from Core Data. But the data I need is a fairly specific subset of the entity, based on user settings, so there are multiple predicates involved testing equivalence. I'm finding these fetches are super slow and, based on research on SQLite, I think an index would be a good choice here.
我正在开发一个iOS闪存卡式学习应用程序,在加载时需要从Core Data中获取大量数据。但是我需要的数据是实体的一个相当特定的子集,基于用户设置,所以有多个谓词涉及测试等价。我发现这些提取速度非常慢,根据对SQLite的研究,我认为索引在这里是个不错的选择。
Now, I understand (largely from reading other * questions) that SQLite and Core Data are two different, basically orthogonal things that should not be confused. But it's also my understanding that you're supposed to work through Core Data to do any sort of database work and tweaking; you shouldn't try to bypass and work directly with SQLite when optimizing or designing object permanence in your app.
现在,我理解(主要是通过阅读其他*问题)SQLite和Core Data是两个不同的,基本上是正交的东西,不应该混淆。但是我的理解是你应该通过Core Data来完成任何类型的数据库工作和调整;在优化或设计应用程序中的对象持久性时,不应试图绕过并直接使用SQLite。
But the only thing I can find for indexes in Core Data is that one "indexed" checkbox for each attribute in a model. And that's just not doing the sort of optimization I'm looking for.
但我唯一可以找到的核心数据中的索引是模型中每个属性的一个“索引”复选框。而这只是没有做我正在寻找的那种优化。
Here's the fetch request, currently:
这是获取请求,目前:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"SKUserItem" inManagedObjectContext:context];
fetchRequest.entity = entity;
NSSortDescriptor *sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:@"next" ascending:YES] autorelease];
fetchRequest.sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSMutableArray *predicates = [NSMutableArray arrayWithCapacity:6];
[predicates addObject:[NSPredicate predicateWithFormat:@"next < %f", now() + (60.0*60.0*24.0)]];
[predicates addObject:[NSPredicate predicateWithFormat:@"next > %f", nextOffset]];
[predicates addObject:[NSPredicate predicateWithFormat:@"user == %@", user]];
[predicates addObject:[NSPredicate predicateWithFormat:@"langRaw == %d", lang]];
NSArray *stylePredicates = [NSArray arrayWithObjects:[NSPredicate predicateWithFormat:@"styleRaw == %d", SK_SIMP_AND_TRAD], [NSPredicate predicateWithFormat:@"styleRaw == %d", self.style], nil];
[predicates addObject:[NSCompoundPredicate orPredicateWithSubpredicates:stylePredicates]];
if([self.parts count] == 4 || (self.lang == SK_JA && [self.parts count] == 3))
; // don't have to filter by parts; they're studying all of them
else {
NSMutableArray *partPredicates = [NSMutableArray arrayWithCapacity:[self.parts count]];
for(NSString *part in self.parts)
[partPredicates addObject:[NSPredicate predicateWithFormat:@"partRaw == %d", partCode(part)]];
[predicates addObject:[NSCompoundPredicate orPredicateWithSubpredicates:partPredicates]];
}
NSPredicate *compoundPredicate = [NSCompoundPredicate andPredicateWithSubpredicates:predicates];
fetchRequest.predicate = compoundPredicate;
So essentially what this fetch does is sort by next (the time when the given item is due) and filter for username, language being studied, the style being studied (in Chinese there's simplified and traditional) and the parts being studied (writing, tone, reading, or definition), and only fetching within a "next" range. Here's a short list of things I've learned from tweaking and fiddling with this:
所以这个fetch所做的基本上是下一个(给定项目到期的时间)和用户名,正在研究的语言,正在研究的样式(中文有简化和传统)以及正在研究的部分(写作,音调)的过滤器,阅读或定义),并且仅在“下一个”范围内获取。这是我从调整和摆弄这个方面所学到的一些简短列表:
- It always scans the whole table, or seems to. Though next is indexed, even if I force it to search a range which I know will return nothing, it still takes several seconds for the fetch to complete.
- 它总是扫描整个表,或者似乎。虽然next是索引的,但即使我强制它搜索我知道不会返回任何内容的范围,仍然需要几秒钟才能完成提取。
- The predicates, any number of predicates, makes this slow. If I remove some but not all, it's about as slow. If I remove all predicates (thus breaking the app) then it's much faster.
- 谓词,任意数量的谓词,都会让这个变慢。如果我删除一些但不是全部,它的速度一样慢。如果我删除所有谓词(从而打破了应用程序),那么它会更快。
- The speed is heavily dependent on how many UserItems there are total in the table. The more items there are, the slower this is. Some people can have tens of thousands of items, and that's when this fetch can take as much as 10 seconds to complete. This is leading to awkward pauses in my app.
- 速度在很大程度上取决于表中总共有多少UserItem。物品越多,这个就越慢。有些人可能拥有成千上万的物品,而这种物品的取出时间可能需要10秒才能完成。这导致我的应用程序出现尴尬的停顿。
- The upper bound on the next value was added not because we need it, but because it speeds up the fetch a little bit.
- 添加下一个值的上限不是因为我们需要它,而是因为它加速了一点点提取。
- Having the query return a subset of the properties in a dictionary (rather than an entire managed object) and fetching the rest lazily is faster, but still not faster enough.
- 让查询返回字典中的属性的子集(而不是整个托管对象)并且懒惰地获取其余属性更快,但仍然不够快。
I'm coming from Google App Engine here, so I'm used to the indexes they provide there. Essentially I want that sort of index, but applied to SQLite through Core Data. I found information on adding indexes in SQLite, the kind I would want, but doing this sort of indexing through Core Data, I can't find any information on that.
我来自Google App Engine,所以我习惯了他们在那里提供的索引。基本上我想要那种索引,但是通过Core Data应用于SQLite。我找到了有关在SQLite中添加索引的信息,我想要的那种,但是通过Core Data进行这种索引,我找不到任何关于它的信息。
2 个解决方案
#1
18
What you want is a Compound Index which Core Data supports in iOS 5.0 and later.
你想要的是Core Data在iOS 5.0及更高版本中支持的复合索引。
You can set it up in Xcode: The Entity inspector has an Indexes section, or if you're creating the NSEntityDescription
in code, use -setCompoundIndexes:
.
您可以在Xcode中进行设置:实体检查器具有索引部分,或者如果您在代码中创建NSEntityDescription,请使用-setCompoundIndexes:。
If you use Xcode, you'd add a line in the Indexes section that says
如果你使用Xcode,你需要在索引部分添加一行说明
next,user,langRaw
That way SQL can use an index for your query.
这样SQL就可以为您的查询使用索引。
#2
2
Core Data has an SQL backend. The way you have done this you will have all the data in one table (part of one entity) and to find the objects you are looking for will require searching through all rows, as you say is happening.
Core Data有一个SQL后端。您完成此操作的方式将在一个表(一个实体的一部分)中包含所有数据,并找到您要查找的对象将需要搜索所有行,正如您所说的那样。
In your Data Model you need to break up some of the attributes you are searching for into other entities. Try and make it more object based and think about what you will be searching for.
在您的数据模型中,您需要将要搜索的一些属性分解为其他实体。尝试使其更基于对象并考虑您将要搜索的内容。
E.g. have an entity for User, Language and perhaps one for lessons or whatever time based thing it is that you are searching over.
例如。有一个用户,语言的实体,也许还有一个用于课程的实体或者你正在搜索的任何基于时间的东西。
The Lesson entity has a to many relationship for Language and a single relationship to user. (or to many if more than one user takes a class)
Lesson实体具有与语言的多对多关系以及与用户的单一关系。 (如果不止一个用户参加课程,则为多人)
Then to look for a user's data you fetch that user and investigate her Language or Lessons property to find out more.
然后,要查找用户的数据,您需要获取该用户并调查她的语言或课程属性以了解更多信息。
To look for a list of users studying a language fetch the language entity you are looking for and investigate the users property.
要查找学习语言的用户列表,请获取您要查找的语言实体并调查users属性。
#1
18
What you want is a Compound Index which Core Data supports in iOS 5.0 and later.
你想要的是Core Data在iOS 5.0及更高版本中支持的复合索引。
You can set it up in Xcode: The Entity inspector has an Indexes section, or if you're creating the NSEntityDescription
in code, use -setCompoundIndexes:
.
您可以在Xcode中进行设置:实体检查器具有索引部分,或者如果您在代码中创建NSEntityDescription,请使用-setCompoundIndexes:。
If you use Xcode, you'd add a line in the Indexes section that says
如果你使用Xcode,你需要在索引部分添加一行说明
next,user,langRaw
That way SQL can use an index for your query.
这样SQL就可以为您的查询使用索引。
#2
2
Core Data has an SQL backend. The way you have done this you will have all the data in one table (part of one entity) and to find the objects you are looking for will require searching through all rows, as you say is happening.
Core Data有一个SQL后端。您完成此操作的方式将在一个表(一个实体的一部分)中包含所有数据,并找到您要查找的对象将需要搜索所有行,正如您所说的那样。
In your Data Model you need to break up some of the attributes you are searching for into other entities. Try and make it more object based and think about what you will be searching for.
在您的数据模型中,您需要将要搜索的一些属性分解为其他实体。尝试使其更基于对象并考虑您将要搜索的内容。
E.g. have an entity for User, Language and perhaps one for lessons or whatever time based thing it is that you are searching over.
例如。有一个用户,语言的实体,也许还有一个用于课程的实体或者你正在搜索的任何基于时间的东西。
The Lesson entity has a to many relationship for Language and a single relationship to user. (or to many if more than one user takes a class)
Lesson实体具有与语言的多对多关系以及与用户的单一关系。 (如果不止一个用户参加课程,则为多人)
Then to look for a user's data you fetch that user and investigate her Language or Lessons property to find out more.
然后,要查找用户的数据,您需要获取该用户并调查她的语言或课程属性以了解更多信息。
To look for a list of users studying a language fetch the language entity you are looking for and investigate the users property.
要查找学习语言的用户列表,请获取您要查找的语言实体并调查users属性。