Skip to content

Commit

Permalink
Merge pull request #55 from owainhunt/datastore-aggregation-query
Browse files Browse the repository at this point in the history
Datastore aggregation query, composite filter and multiple database support
  • Loading branch information
Andrewangeta authored Aug 29, 2024
2 parents aba2423 + d968ed7 commit 927b20c
Show file tree
Hide file tree
Showing 15 changed files with 654 additions and 162 deletions.
408 changes: 307 additions & 101 deletions Datastore/Sources/Data API/API/ProjectAPI.swift

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
import Core

public struct AllocateIdsRequest: GoogleCloudModel {
public init(keys: [Key]? = nil) {
public init(
keys: [Key]? = nil,
databaseId: String? = nil
) {
self.databaseId = databaseId
self.keys = keys
}
/// A list of keys with incomppublic lete key paths for which to allocate IDs. No key may be reserved/read-only.
/// The ID of the database against which to make the request.
/// '(default)' is not allowed; please use empty string '' to refer the default database.
public let databaseId: String?
/// A list of keys with incomplete key paths for which to allocate IDs. No key may be reserved/read-only.
public let keys: [Key]?
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
import Core

public struct BeginTransactionRequest: GoogleCloudModel {
public init(transactionOptions: TransactionOptions? = nil) {
public init(
transactionOptions: TransactionOptions? = nil,
databaseId: String? = nil
) {
self.transactionOptions = transactionOptions
self.databaseId = databaseId
}
/// Options for a new transaction.
public let transactionOptions: TransactionOptions?
/// The ID of the database against which to make the request.
/// '(default)' is not allowed; please use empty string '' to refer the default database.
public let databaseId: String?
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,14 @@ import Core

public struct CommitRequest: GoogleCloudModel {

public init(mode: Mode = .nonTransactional, mutations: [Mutation]) {
public init(
mode: Mode = .nonTransactional,
mutations: [Mutation],
databaseId: String? = nil
) {
self.mode = mode
self.mutations = mutations
self.databaseId = databaseId
}

/// The type of commit to perform.
Expand All @@ -19,6 +24,9 @@ public struct CommitRequest: GoogleCloudModel {
public let mutations: [Mutation]
/// The identifier of the transaction associated with the commit.
private var transaction: String?
/// The ID of the database against which to make the request.
/// '(default)' is not allowed; please use empty string '' to refer the default database.
public let databaseId: String?

/// The modes available for commits.
public enum Mode: GoogleCloudModel {
Expand Down Expand Up @@ -105,11 +113,14 @@ public struct CommitRequest: GoogleCloudModel {
case mode
case mutations
case transaction
case databaseId
}

public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(mutations, forKey: .mutations)
try container.encodeIfPresent(databaseId, forKey: .databaseId)
try container.encodeIfPresent(transaction, forKey: .transaction)

switch mode {
case .transactional(let value):
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
import Core

public struct LookupRequest: GoogleCloudModel {
public init(keys: [Key]? = nil,
readOptions: ReadOptions? = nil) {
public init(
keys: [Key]? = nil,
readOptions: ReadOptions? = nil,
databaseId: String? = nil
) {
self.keys = keys
self.readOptions = readOptions
self.databaseId = databaseId
}
/// Keys of entities to look up.
public let keys: [Key]?
/// The options for this lookup request.
public let readOptions: ReadOptions?
/// The ID of the database against which to make the request.
/// '(default)' is not allowed; please use empty string '' to refer the default database.
public let databaseId: String?
}

Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import Core

public struct ReserveIdsRequest: GoogleCloudModel {
public init(databaseId: String? = nil,
keys: [Key]) {
public init(
databaseId: String? = nil,
keys: [Key]
) {
self.databaseId = databaseId
self.keys = keys
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
import Core

public struct RollbackRequest: GoogleCloudModel {

public init(
transaction: String,
databaseId: String? = nil
) {
self.transaction = transaction
self.databaseId = databaseId
}

/// The transaction identifier,
public let transaction: String
/// The ID of the database against which to make the request.
/// '(default)' is not allowed; please use empty string '' to refer the default database.
public let databaseId: String?
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import Core

struct RunAggregationQueryRequest: GoogleCloudModel {

init(
gqlQuery: GqlQuery,
partitionId: PartitionId? = nil,
readOptions: ReadOptions? = nil,
databaseId: String? = nil
) {
self.gqlQuery = gqlQuery
self.aggregationQuery = nil
self.partitionId = partitionId
self.readOptions = readOptions
self.databaseId = databaseId
}

init(
query: Query,
aggregations: [Aggregation],
partitionId: PartitionId? = nil,
readOptions: ReadOptions? = nil,
databaseId: String? = nil
) {
self.aggregationQuery = AggregationQuery(aggregations: aggregations, nestedQuery: query)
self.gqlQuery = nil
self.partitionId = partitionId
self.readOptions = readOptions
self.databaseId = databaseId
}

/// The GQL query to run.
let gqlQuery: GqlQuery?
/// The aggregation query to run.
let aggregationQuery: AggregationQuery?
/// The (optional) namespace and partition against which to run the query
let partitionId: PartitionId?
/// The options for this query.
let readOptions: ReadOptions?
/// The ID of the database against which to make the request.
/// '(default)' is not allowed; please use empty string '' to refer the default database.
let databaseId: String?
}

Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
import Core

public struct RunQueryRequest: GoogleCloudModel {
public init(gqlQuery: GqlQuery? = nil,
partitionId: PartitionId? = nil,
query: Query? = nil,
readOptions: ReadOptions? = nil) {

public init(
gqlQuery: GqlQuery? = nil,
partitionId: PartitionId? = nil,
query: Query? = nil,
readOptions: ReadOptions? = nil,
databaseId: String? = nil
) {
self.gqlQuery = gqlQuery
self.partitionId = partitionId
self.query = query
self.readOptions = readOptions
self.databaseId = databaseId
}

/// The GQL query to run.
public let gqlQuery: GqlQuery?
/// Entities are partitioned into subsets, identified by a partition ID. Queries are scoped to a single partition. This partition ID is normalized with the standard default context partition ID.
Expand All @@ -18,5 +24,8 @@ public struct RunQueryRequest: GoogleCloudModel {
public let query: Query?
/// The options for this query.
public let readOptions: ReadOptions?
/// The ID of the database against which to make the request.
/// '(default)' is not allowed; please use empty string '' to refer the default database.
public let databaseId: String?
}

Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import Core

public struct LookupResponse: GoogleCloudModel {
public init(deferred: [Key]? = nil, found: [EntityResult]? = nil, missing: [EntityResult]? = nil) {
public init(
deferred: [Key]? = nil,
found: [EntityResult]? = nil,
missing: [MissingEntityResult]? = nil
) {
self.deferred = deferred
self.found = found
self.missing = missing
Expand All @@ -11,5 +15,16 @@ public struct LookupResponse: GoogleCloudModel {
/// Entities found as ResultType.FULL entities. The order of results in this field is undefined and has no relation to the order of the keys in the input.
public let found: [EntityResult]?
/// Entities not found as ResultType.KEY_ONLY entities. The order of results in this field is undefined and has no relation to the order of the keys in the input.
public let missing: [EntityResult]?
public let missing: [MissingEntityResult]?
}

public struct MissingEntityResult: GoogleCloudModel {
/// A KEY_ONLY entity
public let entity: MissingEntity
/// the version of the snapshot that was used to look up the entity
public let version: String

public struct MissingEntity: GoogleCloudModel {
public let key: Key
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import Core

public struct RunAggregationQueryResponse: GoogleCloudModel {
/// A batch of query results (always present).
public let batch: AggregationResultBatch
/// The parsed form of the GqlQuery from the request, if it was set.
public let query: AggregationQuery?
public let transaction: String?
}

public struct AggregationResultBatch: Codable {
public let aggregationResults: [AggregationResult]
}

public struct AggregationResult: Codable {
public let aggregateProperties: AggregateProperties
}

public typealias AggregateProperties = [String: Value]

Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import Core

public struct RunQueryResponse: GoogleCloudModel {
public init(batch: QueryResultBatch? = nil,
query: Query? = nil) {
public init(
batch: QueryResultBatch? = nil,
query: Query? = nil
) {
self.batch = batch
self.query = query
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
public struct AggregationQuery: Codable {

public let aggregations: [AggregationReference]
public let nestedQuery: Query

init(
aggregations: [Aggregation],
nestedQuery: Query
) {
self.aggregations = aggregations.map(AggregationReference.init)
self.nestedQuery = nestedQuery
}

public struct AggregationReference: Codable {

public let alias: String?
public let count: Count?
public let sum: Sum?
public let avg: Average?

init(
alias: String? = nil,
count: Count? = nil,
sum: Sum? = nil,
avg: Average? = nil
) {
self.alias = alias
self.count = count
self.sum = sum
self.avg = avg
}

init(_ aggregration: Aggregation) {
switch aggregration {
case .count(alias: let alias, upTo: let upTo):
self.init(alias: alias, count: Count(upTo: upTo)); return
case .sum(alias: let alias, property: let property):
self.init(alias: alias, sum: Sum(property: property)); return
case .avg(alias: let alias, property: let property):
self.init(alias: alias, avg: Average(property: property)); return
}
}

public struct Count: Codable {
public let upTo: String?
}

public struct Sum: Codable {
public let property: PropertyReference
}

public struct Average: Codable {
public let property: PropertyReference
}
}
}

public enum Aggregation: Codable {
case count(alias: String? = nil, upTo: String? = nil)
case sum(alias: String? = nil, property: PropertyReference)
case avg(alias: String? = nil, property: PropertyReference)
}
24 changes: 17 additions & 7 deletions Datastore/Sources/Data API/Models/Project API/Types/Query.swift
Original file line number Diff line number Diff line change
Expand Up @@ -82,23 +82,33 @@ public struct CompositeFilter: GoogleCloudModel {
/// The list of filters to combine. Must contain at least one filter.
public let filters: [Filter]
/// The operator for combining multiple filters.
public let op: Operator?
public let op: Operator

/// A composite filter operator.
public enum Operator: String, RawRepresentable, GoogleCloudModel {
/// The results are required to satisfy each of the combined filters.
case and = "AND"
/// Documents are required to satisfy at least one of the combined filters.
case or = "OR"
}

public init(_ filters: [Filter]) {
self.init(filters: filters, op: .and)
}

init(filters: [Filter],
op: Operator? = nil) {
public init(
filters: [Filter],
op: Operator = .and
) {
self.filters = filters
self.op = op
}

public init(
filters: [Filter.TypedFilter],
op: Operator = .and
) {
self.init(
filters: filters.map(Filter.init),
op: op
)
}
}

/// A filter on a specific property.
Expand Down
Loading

0 comments on commit 927b20c

Please sign in to comment.