mentat/sdks/swift/Mentat/Mentat/Query/Query.swift

341 lines
13 KiB
Swift

/* Copyright 2018 Mozilla
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
* this file except in compliance with the License. You may obtain a copy of the
* License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License. */
import Foundation
import MentatStore
/**
This class allows you to construct a query, bind values to variables and run those queries against a mentat DB.
This class cannot be created directly, but must be created through `Mentat.query(String:)`.
The types of values you can bind are
- `Int64`
- `Entid`
- `Keyword`
- `Bool`
- `Double`
- `Date`
- `String`
- `UUID`.
Each bound variable must have a corresponding value in the query string used to create this query.
```
let query = """
[:find ?name ?cat
:in ?type
:where
[?c :community/name ?name]
[?c :community/type ?type]
[?c :community/category ?cat]]
"""
mentat.query(query: query)
.bind(varName: "?type", toKeyword: ":community.type/website")
.run { result in
...
}
```
Queries can be run and the results returned in a number of different formats. Individual result values are returned as `TypedValues` and
the format differences relate to the number and structure of those values. The result format is related to the format provided in the query string.
- `Rel` - This is the default `run` function and returns a list of rows of values. Queries that wish to have `Rel` results should format their query strings:
```
let query = """
[: find ?a ?b ?c
: where ... ]
"""
mentat.query(query: query)
.run { result in
...
}
```
- `Scalar` - This returns a single value as a result. This can be optional, as the value may not be present. Queries that wish to have `Scalar` results should format their query strings:
```
let query = """
[: find ?a .
: where ... ]
"""
mentat.query(query: query)
.runScalar { result in
...
}
```
- `Coll` - This returns a list of single values as a result. Queries that wish to have `Coll` results should format their query strings:
```
let query = """
[: find [?a ...]
: where ... ]
"""
mentat.query(query: query)
.runColl { result in
...
}
```
- `Tuple` - This returns a single row of values. Queries that wish to have `Tuple` results should format their query strings:
```
let query = """
[: find [?a ?b ?c]
: where ... ]
"""
mentat.query(query: query)
.runTuple { result in
...
}
```
*/
open class Query: OptionalRustObject {
/**
Binds a `Int64` value to the provided variable name.
- Parameter varName: The name of the variable in the format `?name`.
- Parameter value: The value to be bound
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the query has already been executed.
- Returns: This `Query` such that further function can be called.
*/
open func bind(varName: String, toLong value: Int64) throws -> Query {
query_builder_bind_long(try! self.validPointer(), varName, value)
return self
}
/**
Binds a `Entid` value to the provided variable name.
- Parameter varName: The name of the variable in the format `?name`.
- Parameter value: The value to be bound
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the query has already been executed.
- Returns: This `Query` such that further function can be called.
*/
open func bind(varName: String, toReference value: Entid) throws -> Query {
query_builder_bind_ref(try! self.validPointer(), varName, value)
return self
}
/**
Binds a `String` value representing a keyword for an attribute to the provided variable name.
Keywords take the format `:namespace/name`.
- Parameter varName: The name of the variable in the format `?name`.
- Parameter value: The value to be bound
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the query has already been executed.
- Returns: This `Query` such that further function can be called.
*/
open func bind(varName: String, toReference value: String) throws -> Query {
query_builder_bind_ref_kw(try! self.validPointer(), varName, value)
return self
}
/**
Binds a keyword `String` value to the provided variable name.
Keywords take the format `:namespace/name`.
- Parameter varName: The name of the variable in the format `?name`.
- Parameter value: The value to be bound
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the query has already been executed.
- Returns: This `Query` such that further function can be called.
*/
open func bind(varName: String, toKeyword value: String) throws -> Query {
query_builder_bind_kw(try! self.validPointer(), varName, value)
return self
}
/**
Binds a `Bool` value to the provided variable name.
- Parameter varName: The name of the variable in the format `?name`.
- Parameter value: The value to be bound
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the query has already been executed.
- Returns: This `Query` such that further function can be called.
*/
open func bind(varName: String, toBoolean value: Bool) throws -> Query {
query_builder_bind_boolean(try! self.validPointer(), varName, value ? 1 : 0)
return self
}
/**
Binds a `Double` value to the provided variable name.
- Parameter varName: The name of the variable in the format `?name`.
- Parameter value: The value to be bound
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the query has already been executed.
- Returns: This `Query` such that further function can be called.
*/
open func bind(varName: String, toDouble value: Double) throws -> Query {
query_builder_bind_double(try! self.validPointer(), varName, value)
return self
}
/**
Binds a `Date` value to the provided variable name.
- Parameter varName: The name of the variable in the format `?name`.
- Parameter value: The value to be bound
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the query has already been executed.
- Returns: This `Query` such that further function can be called.
*/
open func bind(varName: String, toDate value: Date) throws -> Query {
query_builder_bind_timestamp(try! self.validPointer(), varName, value.toMicroseconds())
return self
}
/**
Binds a `String` value to the provided variable name.
- Parameter varName: The name of the variable in the format `?name`.
- Parameter value: The value to be bound
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the query has already been executed.
- Returns: This `Query` such that further function can be called.
*/
open func bind(varName: String, toString value: String) throws -> Query {
query_builder_bind_string(try! self.validPointer(), varName, value)
return self
}
/**
Binds a `UUID` value to the provided variable name.
- Parameter varName: The name of the variable in the format `?name`.
- Parameter value: The value to be bound
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the query has already been executed.
- Returns: This `Query` such that further function can be called.
*/
open func bind(varName: String, toUuid value: UUID) throws -> Query {
var rawUuid = value.uuid
withUnsafePointer(to: &rawUuid) { uuidPtr in
query_builder_bind_uuid(try! self.validPointer(), varName, uuidPtr)
}
return self
}
/**
Execute the query with the values bound associated with this `Query` and call the provided callback function with the results as a list of rows of `TypedValues`.
- Parameter callback: the function to call with the results of this query
- Throws: `QueryError.executionFailed` if the query fails to execute. This could be because the provided query did not parse, or that
variable we incorrectly bound, or that the query provided was not `Rel`.
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the query has previously been executed.
*/
open func run(callback: @escaping (RelResult?) -> Void) throws {
var error = RustError(message: nil)
let result = query_builder_execute(try! self.validPointer(), &error);
self.raw = nil
if let err = error.message {
let message = String(destroyingRustString: err)
throw QueryError.executionFailed(message: message)
}
guard let results = result else {
callback(nil)
return
}
callback(RelResult(raw: results))
}
/**
Execute the query with the values bound associated with this `Query` and call the provided callback function with the result as a single `TypedValue`.
- Parameter callback: the function to call with the results of this query
- Throws: `QueryError.executionFailed` if the query fails to execute. This could be because the provided query did not parse, that
variable we incorrectly bound, or that the query provided was not `Scalar`.
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the query has previously been executed.
*/
open func runScalar(callback: @escaping (TypedValue?) -> Void) throws {
var error = RustError(message: nil)
let result = query_builder_execute_scalar(try! self.validPointer(), &error)
self.raw = nil
if let err = error.message {
let message = String(destroyingRustString: err)
throw QueryError.executionFailed(message: message)
}
guard let results = result else {
callback(nil)
return
}
callback(TypedValue(raw: results))
}
/**
Execute the query with the values bound associated with this `Query` and call the provided callback function with the result as a list of single `TypedValues`.
- Parameter callback: the function to call with the results of this query
- Throws: `QueryError.executionFailed` if the query fails to execute. This could be because the provided query did not parse, that
variable we incorrectly bound, or that the query provided was not `Coll`.
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the query has previously been executed.
*/
open func runColl(callback: @escaping (ColResult?) -> Void) throws {
var error = RustError(message: nil)
let result = query_builder_execute_coll(try! self.validPointer(), &error)
self.raw = nil
if let err = error.message {
let message = String(destroyingRustString: err)
throw QueryError.executionFailed(message: message)
}
guard let results = result else {
callback(nil)
return
}
callback(ColResult(raw: results))
}
/**
Execute the query with the values bound associated with this `Query` and call the provided callback function with the result as a list of single `TypedValues`.
- Parameter callback: the function to call with the results of this query
- Throws: `QueryError.executionFailed` if the query fails to execute. This could be because the provided query did not parse, that
variable we incorrectly bound, or that the query provided was not `Tuple`.
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the query has previously been executed.
*/
open func runTuple(callback: @escaping (TupleResult?) -> Void) throws {
var error = RustError(message: nil)
let result = query_builder_execute_tuple(try! self.validPointer(), &error)
self.raw = nil
if let err = error.message {
let message = String(destroyingRustString: err)
throw QueryError.executionFailed(message: message)
}
guard let results = result else {
callback(nil)
return
}
callback(TupleResult(raw: results))
}
override open func cleanup(pointer: OpaquePointer) {
query_builder_destroy(pointer)
}
}