New release: GRDB 3.6.0


#1

Hello Swift Community,

Version 3.6.0 of GRDB, the toolkit for SQLite databases, is out!

This release comes with:

  • A nice sugar for updating records (my personal favorite of this release):

    // Does not hit the database if player is not modified
    try player.update(db) {
        $0.score = 1000
        $0.hasAward = true
    }
    
  • Enhanced database observation with new methods on ValueObservation, such as compactMap, combine, ...

  • Observation of specific transactions, with the new DatabaseRegionObservation.

  • Various other improvements listed in the Release Notes.

Happy GRDB!


#2

I was looking for a library exactly like this the other day!

Is there support for running parent/child/sibling queries? I couldn't find it in the docs.

Thanks!


#3

I was looking for a library exactly like this the other day!

Thanks @twof! I was in the exact same position three years ago :sweat_smile:

Is there support for running parent/child/sibling queries?

Would you please mind tell me what you mean by parent, child, and sibling queries? I'm not familiar with this vocabulary.


#4

What I mean by "parent/child" is a one to many relationship and "siblings" would be describing a many to many relationship. It's extremely common to want to get all the objects owned by another object (get all posts made by a user), or the parent of an object (get the user who made this post), so ORMs will often have features to make that sort of operation easier.

Vapor's ORM, Fluent, for example https://docs.vapor.codes/3.0/fluent/relations/

extension Galaxy {
    // this galaxy's related planets
    var planets: Children<Galaxy, Planet> { 
        return children(\.galaxyID)
    }
}

// to retrieve all Planets in a Galaxy

let planets = galaxy.planets.query(on: ...).all()

#5

All right, @twof.

GRDB has currently three Associations: hasOne, hasMany, and belongsTo (inspired by ActiveRecord).

To get all planets in a Galaxy, you'd write:

extension Galaxy {
    /// An association (backed by database foreign keys)
    static let planets = hasMany(Planet.self)

    /// The request for the planets of the galaxy
    var planets: QueryInterfaceRequest<Planet> {
        return request(for: Galaxy.planets)
    }
}

let galaxy: Galaxy = ...
let planets: [Planet] = try galaxy.planets.fetchAll(db)

Associations can be filtered, aggregated, observed...

let inhabitablePlanets: [Planet] = try galaxy
    .planets
    .filter(...)
    .fetchAll(db)

let bigGalaxies: [Galaxy] = try Galaxy
    .having(Galaxy.planets.count > 1e10) // it's a big SQLite database ;-)
    .fetchAll(db)

let observer = try ValueObservation
    .trackingAll(galaxy.planets)
    .start(in: ...) { planets: [Planet] in
        print("planets have changed")
    }

You can also join tables and build requests like "give me all galaxies with their number of planets", or "give me all planets with their galaxies", or "give me all planets whose galaxy is closer than N light years".

There are features which are still to be implemented:

  • Many-to-many associations. You currently need to model them out of two one-to-many associations.
  • Indirect associations, such as "one galaxy has many planets through its stars". You currently need to provide the full path to deeply nested records.
  • Requests of trees, such as "give me all galaxies with their planets". See this issue for more information and a workaround.

Please have a look at the doc. And come back if you have any question!