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:

Previous
Writing queries
Next
Streaming data