diff --git a/Fetching-and-querying.md b/Fetching-and-querying.md index fd962a3..8e5e446 100644 --- a/Fetching-and-querying.md +++ b/Fetching-and-querying.md @@ -4,10 +4,11 @@ Before we dive in, be aware that CoreStore distinguishes between *fetching* and - unsaved objects should be included in the search (though fetches can be configured to exclude unsaved ones) - A *query* pulls data straight from the persistent store. This means faster searches when computing aggregates such as *count*, *min*, *max*, etc. Use queries when: - you need to compute aggregate functions (see below for a list of supported functions) - - results can be raw values like `NSString`'s, `NSNumber`'s, `Int`'s, `NSDate`'s, an `NSDictionary` of key-values, etc. - - only specific attribute keys need to be included in the results + - results can be raw values like `NSString`s, `NSNumber`s, `Int`s, `NSDate`s, an `NSDictionary` of key-values, etc. + - only values for specified attribute keys need to be included in the results - unsaved objects should be ignored +#### `From` clause The search conditions for fetches and queries are specified using *clauses*. All fetches and queries require a `From` clause that indicates the target entity type: ```swift let people = CoreStore.fetchAll(From(MyPersonEntity)) @@ -15,7 +16,7 @@ let people = CoreStore.fetchAll(From(MyPersonEntity)) ``` `people` in the example above will be of type `[MyPersonEntity]`. The `From(MyPersonEntity)` clause indicates a fetch to all persistent stores that `MyPersonEntity` belong to. -If the entity exists in multiple configurations and you need to only search from a particular configuration, provide the `From` clause the configuration name for the destination persistent store: +If the entity exists in multiple configurations and you need to only search from a particular configuration, indicate in the `From` clause the configuration name for the destination persistent store: ```swift let people = CoreStore.fetchAll(From("Config1")) // ignore objects in persistent stores other than the "Config1" configuration ``` @@ -25,19 +26,19 @@ let person = CoreStore.fetchAll(From(nil)) ``` Now we know how to use a `From` clause, let's move on to fetching and querying. -#### Fetching +### Fetching There are currently 5 fetch methods you can call from `CoreStore`, from a `DataStack` instance, or from a `BaseDataTransaction` instance. All of the methods below accept the same parameters: a required `From` clause, and an optional series of `Where`, `OrderBy`, and/or `Tweak` clauses. -- `fetchAll(_:_:)` - returns an array of all objects that match the criteria. -- `fetchOne(_:_:)` - returns the first object that match the criteria. -- `fetchCount(_:_:)` - returns the number of objects that match the criteria. -- `fetchObjectIDs(_:_:)`` - returns an array of `NSManagedObjectID`'s for all objects that match the criteria. -- `fetchObjectID(_:_:)` - returns the `NSManagedObjectID`'s for the first objects that match the criteria. +- `fetchAll(...)` - returns an array of all objects that match the criteria. +- `fetchOne(...)` - returns the first object that match the criteria. +- `fetchCount(...)` - returns the number of objects that match the criteria. +- `fetchObjectIDs(...)` - returns an array of `NSManagedObjectID`s for all objects that match the criteria. +- `fetchObjectID(...)` - returns the `NSManagedObjectID`s for the first objects that match the criteria. Each method's purpose is straightforward, but we need to understand how to set the clauses for the fetch. -**`Where` clause** +#### `Where` clause The `Where` clause is CoreStore's `NSPredicate` wrapper. It specifies the search filter to use when fetching (or querying). It implements all initializers that `NSPredicate` does (except for `-predicateWithBlock:`, which Core Data does not support): ```swift @@ -58,7 +59,7 @@ var people = CoreStore.fetchAll( Where(predicate) // predicate initializer ) ``` -`Where` clauses also implement the `&&`, `||`, and `!` logic operators, so you can provide logical conditions without writing too much `AND`, `OR`, and `NOT` strings in the conditions: +`Where` clauses also implement the `&&`, `||`, and `!` logic operators, so you can provide logical conditions without writing too much `AND`, `OR`, and `NOT` strings: ```swift var people = CoreStore.fetchAll( From(MyPersonEntity), @@ -67,7 +68,7 @@ var people = CoreStore.fetchAll( ``` If you do not provide a `Where` clause, all objects that belong to the specified `From` will be returned. -**`OrderBy` clause** +#### `OrderBy` clause The `OrderBy` clause is CoreStore's `NSSortDescriptor` wrapper. Use it to specify attribute keys in which to sort the fetch (or query) results with. ```swift @@ -76,9 +77,9 @@ var mostValuablePeople = CoreStore.fetchAll( OrderBy(.Descending("rating"), .Ascending("surname")) ) ``` -As seen above, `OrderBy` accepts a list of `SortKey` enumeration values, which can be either `.Ascending` or `.Descending`. The associated value for the `SortKey` enumeration is the attribute key string. +As seen above, `OrderBy` accepts a list of `SortKey` enumeration values, which can be either `.Ascending` or `.Descending`. -You can use the `+` and `+=` operator to append `OrderBy`'s together. This is useful when sorting conditionally: +You can use the `+` and `+=` operator to append `OrderBy`s together. This is useful when sorting conditionally: ```swift var orderBy = OrderBy(.Descending("rating")) if sortFromYoungest { @@ -90,9 +91,9 @@ var mostValuablePeople = CoreStore.fetchAll( ) ``` -**`Tweak` clause** +#### `Tweak` clause -The `Tweak` clause lets you, well, *tweak* the fetch (or query). `Tweak` exposes the `NSFetchRequest` in a closure where you can make changes to its properties: +The `Tweak` clause lets you, uh, *tweak* the fetch (or query). `Tweak` exposes the `NSFetchRequest` in a closure where you can make changes to its properties: ```swift var people = CoreStore.fetchAll( From(MyPersonEntity), @@ -108,21 +109,22 @@ var people = CoreStore.fetchAll( The clauses are evaluated the order they appear in the fetch/query, so you typically need to set `Tweak` as the last clause. `Tweak`'s closure is executed only just before the fetch occurs, so make sure that any values captured by the closure is not prone to race conditions. -Do note that while `Tweak` lets you micro-configure its `NSFetchRequest`, don't forget that CoreStore already preconfigured that `NSFetchRequest` to suitable defaults. Only use `Tweak` when you know what you are doing! +While `Tweak` lets you micro-configure the `NSFetchRequest`, note that CoreStore already preconfigured that `NSFetchRequest` to suitable defaults. Only use `Tweak` when you know what you are doing! -#### Querying -One of the functionalities overlooked by other Core Data wrapper libraries is raw properties fetching. If you are familiar with `NSDictionaryResultType` and `-[NSFetchedRequest propertiesToFetch]`, you probably know how painful it is to setup a query for raw values and aggregate values. CoreStore makes querying easy by exposing the 2 methods below: +### Querying -- `queryValue(_:_:_:)` - returns a single raw value for an attribute or for an aggregate value. If there are multiple results, `queryValue(...)` only returns the first item. -- `queryAttributes(_:_:_:)` - returns an array of dictionaries containing attribute keys with their corresponding values. +One of the functionalities overlooked by other Core Data wrapper libraries is raw properties fetching. If you are familiar with `NSDictionaryResultType` and `-[NSFetchedRequest propertiesToFetch]`, you probably know how painful it is to setup a query for raw values and aggregate values. CoreStore makes this easy by exposing the 2 methods below: + +- `queryValue(...)` - returns a single raw value for an attribute or for an aggregate value. If there are multiple results, `queryValue(...)` only returns the first item. +- `queryAttributes(...)` - returns an array of dictionaries containing attribute keys with their corresponding values. Both methods above accept the same parameters: a required `From` clause, a required `Select` clause, and an optional series of `Where`, `OrderBy`, `GroupBy`, and/or `Tweak` clauses. Setting up the `From`, `Where`, `OrderBy`, and `Tweak` clauses is similar to how you would when fetching. For querying, you also need to know how to use the `Select` and `GroupBy` clauses. -**`Select` clause** +#### `Select` clause -The `Select` clause specifies the target attribute/aggregate key and the return type: +The `Select` clause specifies the target attribute/aggregate key, as well as the expected return type: ```swift let johnsAge = CoreStore.queryValue( From(MyPersonEntity), @@ -130,7 +132,7 @@ let johnsAge = CoreStore.queryValue( Where("name == %@", "John Smith") ) ``` -The example above queries the "age" property for the first object that matches the `Where` condition. `johnsAge` will be bound to type `Int?`, as indicated by the `Select` generic type. For `queryValue(...)`, the following are allowed as the return type (and as the generic type for `Select`): +The example above queries the "age" property for the first object that matches the `Where` condition. `johnsAge` will be bound to type `Int?`, as indicated by the `Select` generic type. For `queryValue(...)`, the following are allowed as the return type (and therefore as the generic type for `Select`): - `Bool` - `Int8` - `Int16` @@ -147,7 +149,7 @@ The example above queries the "age" property for the first object that matches t - `NSManagedObjectID` - `NSString` -For `queryAttributes(...)`, only `NSDictionary` is valid for `Select`, thus you are allowed omit the generic type: +For `queryAttributes(...)`, only `NSDictionary` is valid for `Select`, thus you are allowed to omit the generic type: ```swift let allAges = CoreStore.queryAttributes( From(MyPersonEntity), @@ -159,9 +161,7 @@ If you only need a value for a particular attribute, you can just specify the ke - `.Average(...)` - `.Count(...)` - `.Maximum(...)` -- `.Median(...)` - `.Minimum(...)` -- `.StandardDeviation(...)` - `.Sum(...)` ```swift @@ -179,7 +179,7 @@ let personJSON = CoreStore.queryAttributes( ) ``` `personJSON` will then have the value: -```json +```swift [ [ "name": "John Smith", @@ -232,9 +232,9 @@ which now returns: ] ``` -**`GroupBy` clause** +#### `GroupBy` clause -The `GroupBy` clause lets you group results by a specified attribute/aggregate. This is only useful only for `queryAttributes(...)` since `queryValue(...)` just returns the first value anyway. +The `GroupBy` clause lets you group results by a specified attribute/aggregate. This is useful only for `queryAttributes(...)` since `queryValue(...)` just returns the first value. ```swift let personJSON = CoreStore.queryAttributes( From(MyPersonEntity),