DEPRECATED - Please see elm-slate/query
Query Engine for Slate that allows Queries to be defined in Elm. The Engine takes care of all of the complexity of building the SQL queries. This is the heart of Slate since most of the code written to use Slate will use the Engine for queries.
Since the Elm Package Manager doesn't allow for Native code and most everything we write at Panoramic Software has some native code in it, you have to install this library directly from GitHub, e.g. via elm-github-install or some equivalent mechanism. It's just not worth the hassle of putting libraries into the Elm package manager until it allows native code.
Entities
in Slate are records or objects that have Properties
(see below). Entities can have relationships with other Entites
. Those relationships can optionally be an ownership.
Properties
can be primitive types that can be represented as a String
or a Value Object
which is represented as a JSON string. Value Objects
are atomically mutated, i.e. you cannot just change a part of the Value Object
, e.g. imagine the following:
type alias EntirePerson =
{ name : Maybe Name
, age : Maybe Int
, address : Maybe EntityReference
}
type alias Name =
{ first : String
, middle : String
, last : String
}
Here Name
is a property of EntirePerson
. When changing the Name
, the whole record will be replaced. There is NO support for Events that only modify the middle
of Name
. The whole Name
will be replaced making it an atomic mutation.
If there were a case where the individual fields of Name
needed to be mutated independently, then this Value Object
should be implemented as an Entity
.
Queries
in Slate are defined as a tree. There is 1 root node Entity Type
. Each child node is another Entity Type
that has a relationships with its parent node. So for example, imagine 2 Entities
, a Person
and an Address
:
type alias EntirePerson =
{ name : Maybe Name
, age : Maybe Int
, address : Maybe EntityReference
}
type alias EntireAddress =
{ street : Maybe String
, city : Maybe String
, state : Maybe String
, zip : Maybe String
}
In this example, Person
has a relationship with Address
. (Actually, Person
owns
Address
, but that's defined in the Person Schema
which is not shown.)
So one could query the Slate DB in 2 different ways.
- Get all (or a select group of)
Addresses
and their correspondingPersons
- Get all (or a select group of)
Persons
and their correspondingAddresses
Depending on the structure of the Entity Design, one query may be more efficient than the other. For example, if Addresses
are owned by many different entities, then Query #1 would be inefficient since it would bring back far more Addresses
than Persons
.
Here is an example of how Query #2 could be defined in Elm as:
query : NodeQuery Msg
query =
Slate.Engine.Query.mtQuery UnspecifiedMutationInQuery
personQuery : Query Msg
personQuery =
Node { query | schema = personSchema, properties = Just [ "name" ], tagger = MutatePerson }
[ Leaf { query | schema = addressSchema, properties = Just [ "street" ], tagger = MutateAddress } ]
Here a Node
contains 1 or more children and Leaf
is a terminal node in the Query Tree
. Each node contains a Query
.
Parent nodes have relationships with child nodes.
personQuery
first queries for Persons
then the associated Addresses
.
The properties
field can be used to define which properties are returned. If omitted then all properties are returned. To return NO properties simple provide an empty list, i.e. [].
See Test.App
for how to implement Queries
.
Additional information can be found in slate-common.
Slate Engine configuration.
type alias Config msg =
{ logTagger : ( LogLevel, ( QueryStateId, String ) ) -> msg
, errorTagger : ( ErrorType, ( QueryStateId, String ) ) -> msg
, eventProcessingErrorTagger : ( String, String ) -> msg
, completionTagger : Int -> msg
, routeToMeTagger : Msg -> msg
, queryBatchSize : Int
}
logTagger
- Msg constructor for logging for the Engine's parent.errorTagger
- Msg constructor for errors for the Engine's parent.eventProcessingErrorTagger
- Msg constructor for event processing errors for the client of the Engine.completionTagger
- Msg constructor for query completion for the client of the Engine.routeToMeTagger
- Msg constructor to route a msg back to the Engine.queryBatchSize
- Number of events to return per retreival from the Slate Database.
The Engine's model has a Type Parameter msg
which is the Event Processor's Msg
.
The Engine's Msg type.
Initial model for the Engine.
initModel : Model msg
initModel
Engine's update function.
update : Config msg -> Msg -> Model msg -> ( ( Model msg, Cmd Msg ), List msg )
update config msg model
Usage
The following code snippet uses elm-parent-child-update:
type alias Model =
{ engineModel : Engine.Model Msg
, ...
}
type Msg
= SlateEngine Engine.Msg
...
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
let
updateEngine : Engine.Msg -> Model -> ( Model, Cmd Msg )
updateEngine =
ParentChildUpdate.updateChildApp (Engine.update engineConfig) update .engineModel SlateEngine (\model engineModel -> { model | engineModel = engineModel })
in
case msg of
...
See Test/App.elm
in this repo for usage in context.
Execute Slate Query.
executeQuery : Config msg -> DBConnectionInfo -> Model msg -> Maybe String -> Query msg -> List String -> Result (List String) ( Model msg, Cmd msg, Int )
executeQuery config dbInfo model additionalCriteria query rootIds
config
- The Engine's configuration.dbInfo
- DB Connection info.model
- The Engine's model.additionalCriteria
- [OPTIONAL] This is a criteria that will be added to EVERYEntity
in theQuery
. It should be written as a SQLWHERE
clause minus theWHERE
.query
- The SlateQuery
.rootIds
- TheEntity Ids
for the rootEntity
. If thisList
is empty then ALL Entities will be returned.
Returns
Result:
ERROR - errors
SUCCESS - (model, cmd, queryStateId)
queryStateId
- This is the id of theQueryState
that uniquely identifies thisQuery
from otherQueries
that theEngine
is maintaining.
Usage
See Test/App.elm
in this repo for usage in context.
Refresh an existing Slate Query, i.e. process events since the last executeQuery
or refreshQuery
.
refreshQuery : Config msg -> DbConnectionInfo -> Model msg -> QueryStateId -> Result String ( Model msg, Cmd msg )
refreshQuery config dbInfo model queryStateId
config
- The Engine's configuration.dbInfo
- DB Connection info.model
- The Engine's model.queryStateId
- The QueryState id, i.e. the Query to refresh.
Returns
Result:
ERROR - "Invalid QueryStateId"
SUCCESS - (model, cmd)
Stop managing specified Query
. Afterwards, the queryStateId
will no longer be valid.
disposeQuery : Model msg -> Int -> Model msg
disposeQuery model queryStateId
model
- The Engine's model.queryStateId
- The QueryState id, i.e. the Query to refresh.
Create a JSON String for saving the specified QueryState
.
This is useful for saving a query for a subsequent execution of your App.
exportQueryState : Model msg -> QueryStateId -> Result String String
exportQueryState model queryStateId
model
- The Engine's model.queryStateId
- The QueryState id, i.e. the Query to refresh.
Returns
Result:
ERROR - "Invalid QueryStateId"
SUCCESS - JSON
Recreate a previously saved QueryState
from the specified JSON String.
importQueryState : Query msg -> Model msg -> String -> Result String (Model msg)
importQueryState query model json
model
- The Engine's model.query
- The SlateQuery
.json
- A JSON string created fromexportQueryState
Convenience function to make defining queries easier.
This generates an empty query that can be mutated using the terse syntax in Elm.
type alias EventTagger msg =
EventRecord -> msg
mtQuery : EventTagger msg -> NodeQuery msg
mtQuery unhandledSpecifiedTagger =
{ properties = Nothing
, criteria = Nothing
, schema = mtEntitySchema
, tagger = unhandledSpecifiedTagger
}
properties
- [OPTIONAL]This defines which properties are to be returned. If omitted, then all will be returned. To return NO properties, set this to [].criteria
- [OPTIONAL] This criteria will be applied ONLY to quering this Entity. To apply a criteria to ALL Entities see Engine.executeQuery. It should be written as a SQLWHERE
clause minus theWHERE
.schema
- This is the schema of the Entity that's part of the query.tagger
- Msg constructor to create a Msg from anEventRecord
. When this message is received by the initiator of the query, it will typically do mutation operations on the Entity specified by theEventRecord
.unhandledSpecifiedTagger
- Msg constructor that handles queries that fail to supplytagger
. This should be considered a FATAL error.
Build Query Template from a query.
buildQueryTemplate : Query msg -> Result (List String) (List String)
buildQueryTemplate query
This function is used by the Engine and is not usually needed by clients of the Engine.
Parametric replacement of a template where the prefix
and suffix
define the delimiters of the parameters.
parametricReplace : String -> String -> List ( String, String ) -> String -> String
parametricReplace prefix suffix replacements template =
This function is used by the Engine and is not usually needed by clients of the Engine.
Build a Msg Dictionary that relates the Slate Event Name to the taggers in the query.
buildMsgDict : Query msg -> MsgDict msg
buildMsgDict query =
This function is used by the Engine and is not usually needed by clients of the Engine.