NTJsonStore 1.00

NTJsonStore 1.00

测试已测试
Lang语言 Obj-CObjective C
许可 MIT
发布最后发布2015年3月

Ethan Nagel维护。



  • Ethan Nagel

NTJsonStore 是一个无模式的文档型数据存储,如果你使用过 MongoDB 或类似系统将显得非常熟悉。关键特性包括

  • 文档型 JSON 存储。值按 JSON 规范的 NSDictionaries 存储。(支持任何可以被 NSJSONSerialization 返回的内容 - NSNull, NSString, NSNumber, NSArrayNSDictionary。)
  • 完整的索引支持。数据最终存储在 SQLITE 中,因此你可以获得 SQLITE 索引的所有性能和灵活性。包括唯一索引和非唯一索引,支持多键以及 JSON 中的嵌套键。
  • 灵活的查询。查询可以包含 JSON 文档中出现的任何值,包括使用点记法表示的嵌套值。只要坚持使用一个集合(TABLE),任何允许在 SQLITE WHERE 子句中的内容都可以用于查询。
  • 无升级头痛。因为数据本质上是无模式的,所以你不需要在应用程序更新中对数据存储进行“升级”。当然,这可能会给使用数据的代码带来额外的负担,因为你可能会遇到旧数据或新数据,但这通常很容易解决。
  • 简单的多线程支持。任何调用都可以同步或异步执行。系统将确保每个集合的操作按相同的顺序执行。没有处理多个上下文的概念。

API 概览


NTJsonStore 是一组 NTJsonCollections 的容器。它拥有底层的 SQLITE 存储并具有协助在各个集合之间同步操作的方法。每个存储都有一个全局键值元数据集合,可以用来存储关于存储或集合的额外数据。集合在第一次访问时创建,因此没有必要显式创建集合。

每个集合由一个 NTJsonCollection 对象表示,该对象负责对单个集合的所有访问。集合在第一次访问时创建,并且是无模式的。

简单示例

NTJsonStore *store = [[NTJsonStore alloc] initWithName:@"sample.db"];
NSJsonCollection *collection = [store collectionWithName:@"users"];

// these are optional but improve performance...
[collection addIndex:@"[last_name], [first_name]"];
[collection addQueryableFields:@"[address.country]"];

NSString *country = @"US";
NSArray *users = [collection findWhere:@"[address.country] = ?" args:@[country] orderBy:@"[last_name], [first_name]"];

for(NSDictionary *user in users)
    NSLog(@"%@, %@", user[@"last_name"], user[@"first_name"]);

每个查询方法都有同步和异步版本。此外,还有一些异步调用,传递不同参数的默认值。最重要的工作方式是 -find,以下是可以调用它的所有方式:

-(void)beginFindWhere:(NSString *)where args:(NSArray *)args orderBy:(NSString *)orderBy limit:(int)limit completionQueue:(dispatch_queue_t)completionQueue completionHandler:(void (^)(NSArray *items, NSError *error))completionHandler;
-(void)beginFindWhere:(NSString *)where args:(NSArray *)args orderBy:(NSString *)orderBy limit:(int)limit completionHandler:(void (^)(NSArray *items, NSError *error))completionHandler;
-(NSArray *)findWhere:(NSString *)where args:(NSArray *)args orderBy:(NSString *)orderBy limit:(int)limit error:(NSError **)error;
-(NSArray *)findWhere:(NSString *)where args:(NSArray *)args orderBy:(NSString *)orderBy limit:(int)limit;
-(void)beginFindWhere:(NSString *)where args:(NSArray *)args orderBy:(NSString *)orderBy completionQueue:(dispatch_queue_t)completionQueue completionHandler:(void (^)(NSArray *items, NSError *error))completionHandler;
-(void)beginFindWhere:(NSString *)where args:(NSArray *)args orderBy:(NSString *)orderBy completionHandler:(void (^)(NSArray *items, NSError *error))completionHandler;
-(NSArray *)findWhere:(NSString *)where args:(NSArray *)args orderBy:(NSString *)orderBy error:(NSError **)error;
-(NSArray *)findWhere:(NSString *)where args:(NSArray *)args orderBy:(NSString *)orderBy;

对于每种方法,如果该方法在主队列上调用,则 completionQueue 的默认为主队列,否则为后台队列。

除了 find 之外,剩余的方法不应该让您感到惊讶。

  • findOne - 对 find 的封装,当找不到时返回单个对象或 nil。
  • count 返回具有可选 where 子句的项目数。
  • insert - 将传入的 JSON 插入到集合中。返回新的 rowid。注意原始 JSON 不会被修改,但当你读取它时,__rowid__ 键总会被填充。
  • insertBatch - 在单个事务中插入多个项目。如果任何插入失败,则不会进行任何更改。
  • update - 更新现有的 JSON 文档。传入的 JSON 必须 填充 __rowid__ 键。
    (系统返回的所有 JSON 值都将预先填充此键。)
  • remove - 从集合中删除单个项目。传入的 JSON 必须 填充 __rowid__ 键。
  • removeWhere - 从集合中删除多个项目。

此外,还有用于 配置 每个集合和 同步 排队的其他方法。

配置


Store 封装了数据库并允许访问集合数组。默认情况下,storePath 是缓存目录,storeName 的默认值是 'NTJsonStore.db'。可以在访问存储之前随时更改这些属性。

每个集合都有一组配置设置

  • 索引。 系统支持唯一和非唯一索引。使用 -addUniqueIndexWithKeys 添加唯一索引,或使用 -addIndexWithKeys: 添加非唯一索引。在这两种情况下,"keys" 是一个由逗号分隔的字段列表,这些字段将被索引。每个字段 必须 用方括号括起来。此外,您还可以为任何字段追加 DESCASC 以定义排序顺序。

  • 可查询字段。 可查询字段告诉系统您计划使用的字段。如果当集合为空时调用此方法,成本非常低。(一旦有记录,系统将提取每个 JSON 记录的字段并为您创建列。)-addQueryableFields: 消息接受一个字段名列表,每个字段 必须 用方括号括起来。此调用完全是可选的,用于提高性能——如果使用尚未物化的字段,系统将透明地为您处理。

  • 默认 JSON。 默认 JSON 定义了查询时字段的默认值。

  • 缓存大小。 系统为您缓存 JSON 结果以减少将 JSON 从数据存储中解析的开销,以及减少内存占用(每次请求时都返回相同的 NSDictionary)。默认情况下,系统将跟踪应用程序使用中的对象(使用一些引用计数魔法),并将缓存最多 0 项附加项目。setCacheSize: 用于更改默认值,将其设置为 0 将仅跟踪使用中的项目,而 -1 将禁用所有缓存,每次请求时都将返回新对象。任何其他值表示缓存大小。您还可以通过调用 -flushCache 来刷新缓存。

  • 别名。 别名实际上是为每个集合维护的宏。它们是映射模型对象属性名到查询中 JSON 字段的好方法。例如,您可能有一个如 [user.first_name] 的 JSON 字段,它最终映射到模型对象属性 firstName

这些值在应用程序启动之间持续存在(除了缓存大小,应在启动时设置)。建议您在每次启动应用程序时设置这些值,以便任何更改(例如升级导致的更改)都能立即反映出来。在它们已经生效时设置这些值没有任何效果。

查询字符串


查询字符串是 SQLITE WHERE 子句的一个子集,其中 JSON 字段被方括号包围。可以通过在查询字符串中插入一个 ? 并在 args 数组中添加值来使用值。(参数化 SQL。)需要注意的主要限制是,与大多数面向文档的系统一样,NTJsonStore 并不是一个关系存储,因此 查询限制为单个集合

  • 别名(作用类似于每个集合的宏)会立即展开,且 不在 方括号中。常见做法是添加将高级模型对象属性名称映射到 JSON 字段的别名。
  • 所有 JSON 字段都必须用方括号包围。可以使用 "." 表示法允许嵌套 JSON 字段。
  • 不支持跨表查询。
  • 存储会自动为您维护 SQL 中的列以执行查询。首次使用新字段时,必须将列“材料化”- 如果集合很大,这可能会造成性能影响。
  • 您可以通过为每个集合设置“QueryableFields”来告诉系统您打算访问哪些列,使用 -addQueryableFields:。这将立即将任何缺失的列“材料化”。
  • 在引用列的过程中,如排序子句、定义索引或可查询字段时,必须使用方括号包围字段名称(在这种情况下总是处理别名)。
  • 如果 JSON 中不存在值,则处理查询时会使用 defaultValues NSDictionary 中相应的值。如果您有一个布尔值等,当它不存在时想将其视为 false,这非常有用。

NTJsonRowId


从 NTJsonStore 返回的每个记录都有一个保证在每个集合中唯一的行 ID。(这个 ID 对于每个新记录都会递增,并且不会被重新使用。)这个 ID 在 JSON 中以 __rowid__NTjsonRowIdKey)返回。

线程和同步


NTJsonStore 使用 libdispatch 进行线程处理。每个集合维护自己的序列队列,用于所有操作。操作可以以同步方式或异步方式与调用线程一起执行。对于异步调用,您可以为运行其上的特定队列定义。您也可以通过传递 NTJsonStoreSerialQueue 来强制完成处理器在集合并发队列上运行,这可以在协调多个动作时非常有用。

每个集合都有允许您同步集合队列与自己的方法。当您开始多个异步调用并希望在它们全部完成时执行某些操作时,这非常有用。

此外,NTJsonStore 有同步方法可以允许您在不同的集合之间同步队列。

缓存


默认情况下,系统为每个集合维护两个缓存。

  • 使用中的项目。 在后台使用一些魔法来确定 JSON 文档是否仍在应用程序中使用(维护引用计数)。对于这些项目,总是会返回相同的值。
  • 已缓存的项目。 当项目从“使用中”缓存中移除后,它们将被收集到 LRU 缓存中。默认情况下,在缓存中维持 50 个条目。

cacheSize 设置为正值,以设置 LRU 缓存的大小,或将其设置为 0 来禁用。将 cacheSize 设置为 -1 来禁用所有缓存,包括使用中的项目缓存。

元数据存储


NTJsonStore用来维护一个键值集合,存储商店的元数据项。这用于内部存储每个集合的配置信息,但也可能用于您的其他目的。-metadataWithKey:返回与关联键相关的元数据(查询将在全局线程上运行,但将同步返回。)您可以使用-saveMetadataWithKey:value来设置元数据。