Querying
Performing mutations
Mutation queries make changes somewhere - ie., perform a task, or update a record.
Mutating operations are defined in Taxi using the write
modifier:
service FlightsService {
write operation bookFlight(BookingRequest):BookingConfirmation
write operation sendFlightManifest(Manifest):ManifestConfirmation
}
Mutating operations are excluded from being invoked during a query (ie., anything with a find {}
or stream {}
directive.)
Instead, they are invoked using a call
directive.
find { Passengers[] }
call FlightsService::sendFlightManifest
In this example, a list of Passengers
is fetched, and converted into a Manifest
to call the sendFlightManifest
operation
of the FlightsService
.
Many different data sources support mutations, such as databases, Kafka, MongoDB, and Hazelcast.
Consult the docs for the relevant data source to learn more.
Projecting and mutating
Orbital will automatically convert from the source type to whatever is required in the input of the mutation operation.
For example:
// The data we're writing to our database
@Table(connection = "films-database", schema = "public" , table = "films" )
closed parameter model Film {
@Id
filmId : FilmId inherits Int
title : Title inherits String
reviewScore : ReviewScore inherits Int
}
@DatabaseService(connection = "films-database")
sevice FilmsDatabase {
@UpsertOperation
write operation saveFilm(Film):Film
}
// Our source format
closed model Movie {
// doesn't have reviews
title : FilmId
}
// Another source containing reviews
closed model Review {
id : FilmId
score : ReviewScore
}
Given the above services and models, we can write a mutating query to find films, enrich them, and write to our database:
find { Film[] }
call FilmsDatabase::saveFilm
In the above example, note that our source format - Movie
lacks review data, but out target format - Film
needs it.
Orbital detects that the source and target formats don’t align, and so automatically triggers a projection to enrich the source data with reviews:
Manually projecting
If you want more control (eg., for performing calculations or derived fields), you can manually define a projection:
find { Film[] } as {
id : FilmId
title : Title = upperCase(Title) // make all the titles uppercase in the db
}
call FilmsDatabase::saveFilm
Here, Orbital still performs a series of projections:
- First, projecting from
Film
to the inlined type (defined in the projection) - Then from the inlined type to the input type of the mutating operation (
FilmsDatabase::saveFilm
)
Writing single item vs batch
Generally, Orbital tries to run projections and mutations in parallel, and - where possible - using micro-batching to batch writes.
However, sometimes you want everything written in a single write - such as writing a file to S3 (where incremental updates aren’t supported by S3)
To support this, simply declare the input into your mutation as an array.
For example, to update our earlier example to write to S3 instead of a database - let’s add the S3 sink:
@S3Service(connectionName = "MyAwsConnection")
service AwsBucketService {
@S3Operation(bucket = "MyBucket")
write operation writeToS3(@RequestBody films:Film[], filename:FilenamePattern = "films.csv"):Film[]
}
By updating our query:
find { Film[] }
call AwsBucketService::writeToS3
This time, the enrichments happen in parallel, but are held until the all enrichments are finished, then written to S3 in a batch at the end: