Updated Fetching and querying (markdown)

John Estropia
2015-07-18 23:21:26 +09:00
parent f8f4a4d115
commit 5531aa2ca9

@@ -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<MyPersonEntity>("Config1")) // ignore objects in persistent stores other than the "Config1" configuration
```
@@ -25,19 +26,19 @@ let person = CoreStore.fetchAll(From<MyPersonEntity>(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<T>` 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<T>` and `GroupBy` clauses.
**`Select<T>` clause**
#### `Select<T>` clause
The `Select<T>` clause specifies the target attribute/aggregate key and the return type:
The `Select<T>` 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<Int>` generic type. For `queryValue(...)`, the following are allowed as the return type (and as the generic type for `Select<T>`):
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<Int>` generic type. For `queryValue(...)`, the following are allowed as the return type (and therefore as the generic type for `Select<T>`):
- `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),