mirror of https://github.com/dapr/docs.git
Added documentation for StateStore Query API (#1874)
Reworked this overview to make this more consistent and include recent updates from releases.
This commit is contained in:
parent
232d9cfec4
commit
91fe55b023
|
@ -0,0 +1,421 @@
|
|||
---
|
||||
type: docs
|
||||
title: "How-To: Query state"
|
||||
linkTitle: "How-To: Query state"
|
||||
weight: 250
|
||||
description: "API for querying state stores"
|
||||
---
|
||||
|
||||
{{% alert title="alpha" color="warning" %}}
|
||||
The state query API is in **alpha** stage.
|
||||
{{% /alert %}}
|
||||
|
||||
## Introduction
|
||||
|
||||
The state query API provides a way of querying the key/value data stored in state store components. This query API is not a replacement for a complete query language, and is focused on retrieving, filtering and sorting key/value data that you have saved through the state management APIs.
|
||||
|
||||
Even though the state store is a key/value store, the `value` might be a JSON document with its own hierarchy, keys, and values.
|
||||
The query API allows you to use those keys and values to retrive corresponding documents.
|
||||
|
||||
This query API does not support querying of actor state stored in a state store. For that you need to use the query API for the specific database.
|
||||
See [querying actor state]({{< ref "state-management-overview.md#querying-actor-state" >}}).
|
||||
|
||||
You can find additional information in the [related links]({{< ref "#related-links" >}}) section.
|
||||
|
||||
## Querying the state
|
||||
|
||||
You submit query requests via HTTP POST/PUT or gRPC.
|
||||
The body of the request is the JSON map with 3 entries: `filter`, `sort`, and `pagination`.
|
||||
|
||||
The `filter` is an optional section. It specifies the query conditions in the form of a tree of key/value operations, where the key is the operator and the value is the operands.
|
||||
|
||||
The following operations are supported:
|
||||
| Operator | Operands | Description |
|
||||
|----------|-------------|--------------|
|
||||
| `EQ` | key:value | key == value |
|
||||
| `IN` | key:[]value | key == value[0] OR key == value[1] OR ... OR key == value[n] |
|
||||
| `AND` | []operation | operation[0] AND operation[1] AND ... AND operation[n] |
|
||||
| `OR` | []operation | operation[0] OR operation[1] OR ... OR operation[n] |
|
||||
|
||||
If `filter` section is omitted, the query returns all entries.
|
||||
|
||||
The `sort` is an optional section and is an ordered array of `key:order` pairs, where `key` is a key in the state store, and the `order` is an optional string indicating sorting order: `"ASC"` for ascending and `"DESC"` for descending. If omitted, ascending order is the default.
|
||||
|
||||
The `pagination` is an optional section containing `limit` and `token` parameters. `limit` sets the page size. `token` is an iteration token returned by the component, and is used in subsequent queries.
|
||||
|
||||
For some background understanding, this query request is translated into the native query language and executed by the state store component.
|
||||
|
||||
## Example data and query
|
||||
|
||||
Let's look at some real examples, starting with simple and progressing towards more complex ones.
|
||||
|
||||
As a dataset, let's consider a [collection of with employee records](../query-api-examples/dataset.json) containing employee ID, organization, state, and city.
|
||||
Notice that this dataset is an array of key/value pairs where `key` is the unique ID, and the `value` is the JSON object with employee record.
|
||||
To better illustrate functionality, let's have organization name (org) and employee ID (id) as a nested JSON person object.
|
||||
|
||||
First, you need to create an instance of MongoDB, which is your state store.
|
||||
```bash
|
||||
docker run -d --rm -p 27017:27017 --name mongodb mongo:5
|
||||
```
|
||||
|
||||
Next is to start a Dapr application. Refer to this [component configuration file](../query-api-examples/components/mongodb.yml), which instructs Dapr to use MongoDB as its state store.
|
||||
```bash
|
||||
dapr run --app-id demo --dapr-http-port 3500 --components-path query-api-examples/components
|
||||
```
|
||||
|
||||
Now populate the state store with the employee dataset, so you can then query it later.
|
||||
```bash
|
||||
curl -X POST -H "Content-Type: application/json" -d @query-api-examples/dataset.json http://localhost:3500/v1.0/state/statestore
|
||||
```
|
||||
|
||||
Once populated, you can examine the data in the state store. The image below a section of the MongoDB UI displaying employee records.
|
||||
<table><tr><td>
|
||||
<img src="/images/state-management-query-mongodb-dataset.png" width=500 alt="Sample dataset" class="center">
|
||||
</td></tr></table>
|
||||
|
||||
Each entry has the `_id` member as a concatenated object key, and the `value` member containing the JSON record.
|
||||
|
||||
The query API allows you to select records from this JSON structure.
|
||||
|
||||
Now you can run the queries.
|
||||
|
||||
### Example 1.
|
||||
|
||||
First, let's find all employees in the state of California and sort them by their employee ID in descending order.
|
||||
|
||||
This is the [query](../query-api-examples/query1.json):
|
||||
```json
|
||||
{
|
||||
"query": {
|
||||
"filter": {
|
||||
"EQ": { "value.state": "CA" }
|
||||
},
|
||||
"sort": [
|
||||
{
|
||||
"key": "value.person.id",
|
||||
"order": "DESC"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
An equivalent of this query in SQL is:
|
||||
```sql
|
||||
SELECT * FROM c WHERE
|
||||
value.state = "CA"
|
||||
ORDER BY
|
||||
value.person.id DESC
|
||||
```
|
||||
|
||||
Execute the query with the following command:
|
||||
{{< tabs "HTTP API (Bash)" "HTTP API (PowerShell)" >}}
|
||||
{{% codetab %}}
|
||||
```bash
|
||||
curl -s -X POST -H "Content-Type: application/json" -d @query-api-examples/query1.json http://localhost:3500/v1.0-alpha1/state/statestore/query | jq .
|
||||
```
|
||||
{{% /codetab %}}
|
||||
{{% codetab %}}
|
||||
```powershell
|
||||
Invoke-RestMethod -Method Post -ContentType 'application/json' -InFile query-api-examples/query1.json -Uri 'http://localhost:3500/v1.0-alpha1/state/statestore/query'
|
||||
```
|
||||
{{% /codetab %}}
|
||||
{{< /tabs >}}
|
||||
|
||||
The query result is an array of matching key/value pairs in the requested order:
|
||||
```json
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"key": "3",
|
||||
"data": {
|
||||
"person": {
|
||||
"org": "Finance",
|
||||
"id": 1071
|
||||
},
|
||||
"city": "Sacramento",
|
||||
"state": "CA"
|
||||
},
|
||||
"etag": "44723d41-deb1-4c23-940e-3e6896c3b6f7"
|
||||
},
|
||||
{
|
||||
"key": "7",
|
||||
"data": {
|
||||
"city": "San Francisco",
|
||||
"state": "CA",
|
||||
"person": {
|
||||
"id": 1015,
|
||||
"org": "Dev Ops"
|
||||
}
|
||||
},
|
||||
"etag": "0e69e69f-3dbc-423a-9db8-26767fcd2220"
|
||||
},
|
||||
{
|
||||
"key": "5",
|
||||
"data": {
|
||||
"state": "CA",
|
||||
"person": {
|
||||
"org": "Hardware",
|
||||
"id": 1007
|
||||
},
|
||||
"city": "Los Angeles"
|
||||
},
|
||||
"etag": "f87478fa-e5c5-4be0-afa5-f9f9d75713d8"
|
||||
},
|
||||
{
|
||||
"key": "9",
|
||||
"data": {
|
||||
"person": {
|
||||
"org": "Finance",
|
||||
"id": 1002
|
||||
},
|
||||
"city": "San Diego",
|
||||
"state": "CA"
|
||||
},
|
||||
"etag": "f5cf05cd-fb43-4154-a2ec-445c66d5f2f8"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Example 2.
|
||||
|
||||
Let's now find all employees from the "Dev Ops" and "Hardware" organizations.
|
||||
|
||||
This is the [query](../query-api-examples/query2.json):
|
||||
```json
|
||||
{
|
||||
"query": {
|
||||
"filter": {
|
||||
"IN": { "value.person.org": [ "Dev Ops", "Hardware" ] }
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
An equivalent of this query in SQL is:
|
||||
```sql
|
||||
SELECT * FROM c WHERE
|
||||
value.person.org IN ("Dev Ops", "Hardware")
|
||||
```
|
||||
|
||||
Execute the query with the following command:
|
||||
{{< tabs "HTTP API (Bash)" "HTTP API (PowerShell)" >}}
|
||||
{{% codetab %}}
|
||||
```bash
|
||||
curl -s -X POST -H "Content-Type: application/json" -d @query-api-examples/query2.json http://localhost:3500/v1.0-alpha1/state/statestore/query | jq .
|
||||
```
|
||||
{{% /codetab %}}
|
||||
{{% codetab %}}
|
||||
```powershell
|
||||
Invoke-RestMethod -Method Post -ContentType 'application/json' -InFile query-api-examples/query2.json -Uri 'http://localhost:3500/v1.0-alpha1/state/statestore/query'
|
||||
```
|
||||
{{% /codetab %}}
|
||||
{{< /tabs >}}
|
||||
|
||||
Similar to the previous example, the result is an array of matching key/value pairs.
|
||||
|
||||
### Example 3.
|
||||
|
||||
In this example let's find all employees from the "Dev Ops" department
|
||||
and those employees from the "Finance" departing residing in the states of Washington and California.
|
||||
|
||||
In addition, let's sort the results first by state in descending alphabetical order, and then by employee ID in ascending order.
|
||||
Also, let's process up to 3 records at a time.
|
||||
|
||||
This is the [query](../query-api-examples/query3.json):
|
||||
|
||||
```json
|
||||
{
|
||||
"query": {
|
||||
"filter": {
|
||||
"OR": [
|
||||
{
|
||||
"EQ": { "value.person.org": "Dev Ops" }
|
||||
},
|
||||
{
|
||||
"AND": [
|
||||
{
|
||||
"EQ": { "value.person.org": "Finance" }
|
||||
},
|
||||
{
|
||||
"IN": { "value.state": [ "CA", "WA" ] }
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"sort": [
|
||||
{
|
||||
"key": "value.state",
|
||||
"order": "DESC"
|
||||
},
|
||||
{
|
||||
"key": "value.person.id"
|
||||
}
|
||||
],
|
||||
"pagination": {
|
||||
"limit": 3
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
An equivalent of this query in SQL is:
|
||||
```sql
|
||||
SELECT * FROM c WHERE
|
||||
value.person.org = "Dev Ops" OR
|
||||
(value.person.org = "Finance" AND value.state IN ("CA", "WA"))
|
||||
ORDER BY
|
||||
value.state DESC,
|
||||
value.person.id ASC
|
||||
LIMIT 3
|
||||
```
|
||||
|
||||
Execute the query with the following command:
|
||||
{{< tabs "HTTP API (Bash)" "HTTP API (PowerShell)" >}}
|
||||
{{% codetab %}}
|
||||
```bash
|
||||
curl -s -X POST -H "Content-Type: application/json" -d @query-api-examples/query3.json http://localhost:3500/v1.0-alpha1/state/statestore/query | jq .
|
||||
```
|
||||
{{% /codetab %}}
|
||||
{{% codetab %}}
|
||||
```powershell
|
||||
Invoke-RestMethod -Method Post -ContentType 'application/json' -InFile query-api-examples/query3.json -Uri 'http://localhost:3500/v1.0-alpha1/state/statestore/query'
|
||||
```
|
||||
{{% /codetab %}}
|
||||
{{< /tabs >}}
|
||||
|
||||
Upon successful execution, the state store returns a JSON object with a list of matching records and the pagination token:
|
||||
```json
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"key": "1",
|
||||
"data": {
|
||||
"person": {
|
||||
"org": "Dev Ops",
|
||||
"id": 1036
|
||||
},
|
||||
"city": "Seattle",
|
||||
"state": "WA"
|
||||
},
|
||||
"etag": "6f54ad94-dfb9-46f0-a371-e42d550adb7d"
|
||||
},
|
||||
{
|
||||
"key": "4",
|
||||
"data": {
|
||||
"person": {
|
||||
"org": "Dev Ops",
|
||||
"id": 1042
|
||||
},
|
||||
"city": "Spokane",
|
||||
"state": "WA"
|
||||
},
|
||||
"etag": "7415707b-82ce-44d0-bf15-6dc6305af3b1"
|
||||
},
|
||||
{
|
||||
"key": "10",
|
||||
"data": {
|
||||
"person": {
|
||||
"org": "Dev Ops",
|
||||
"id": 1054
|
||||
},
|
||||
"city": "New York",
|
||||
"state": "NY"
|
||||
},
|
||||
"etag": "26bbba88-9461-48d1-8a35-db07c374e5aa"
|
||||
}
|
||||
],
|
||||
"token": "3"
|
||||
}
|
||||
```
|
||||
|
||||
The pagination token is used "as is" in the [subsequent query](../query-api-examples/query3-token.json) to get the next batch of records:
|
||||
|
||||
```json
|
||||
{
|
||||
"query": {
|
||||
"filter": {
|
||||
"OR": [
|
||||
{
|
||||
"EQ": { "value.person.org": "Dev Ops" }
|
||||
},
|
||||
{
|
||||
"AND": [
|
||||
{
|
||||
"EQ": { "value.person.org": "Finance" }
|
||||
},
|
||||
{
|
||||
"IN": { "value.state": [ "CA", "WA" ] }
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"sort": [
|
||||
{
|
||||
"key": "value.state",
|
||||
"order": "DESC"
|
||||
},
|
||||
{
|
||||
"key": "value.person.id"
|
||||
}
|
||||
],
|
||||
"pagination": {
|
||||
"limit": 3,
|
||||
"token": "3"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
And the result of this query is:
|
||||
```json
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"key": "9",
|
||||
"data": {
|
||||
"person": {
|
||||
"org": "Finance",
|
||||
"id": 1002
|
||||
},
|
||||
"city": "San Diego",
|
||||
"state": "CA"
|
||||
},
|
||||
"etag": "f5cf05cd-fb43-4154-a2ec-445c66d5f2f8"
|
||||
},
|
||||
{
|
||||
"key": "7",
|
||||
"data": {
|
||||
"city": "San Francisco",
|
||||
"state": "CA",
|
||||
"person": {
|
||||
"id": 1015,
|
||||
"org": "Dev Ops"
|
||||
}
|
||||
},
|
||||
"etag": "0e69e69f-3dbc-423a-9db8-26767fcd2220"
|
||||
},
|
||||
{
|
||||
"key": "3",
|
||||
"data": {
|
||||
"person": {
|
||||
"org": "Finance",
|
||||
"id": 1071
|
||||
},
|
||||
"city": "Sacramento",
|
||||
"state": "CA"
|
||||
},
|
||||
"etag": "44723d41-deb1-4c23-940e-3e6896c3b6f7"
|
||||
}
|
||||
],
|
||||
"token": "6"
|
||||
}
|
||||
```
|
||||
That way you can update the pagination token in the query and iterate through the results until no more records are returned.
|
||||
|
||||
## Related links
|
||||
- [Query API reference ]({{< ref "state_api.md#state-query" >}}),
|
||||
- [State store components with those that implement query support]({{< ref supported-state-stores.md >}}),
|
||||
- [State store query API implementation guide](https://github.com/dapr/components-contrib/blob/master/state/Readme.md#implementing-state-query-api).
|
|
@ -0,0 +1,10 @@
|
|||
apiVersion: dapr.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: statestore
|
||||
spec:
|
||||
type: state.mongodb
|
||||
version: v1
|
||||
metadata:
|
||||
- name: host
|
||||
value: localhost:27017
|
|
@ -0,0 +1,112 @@
|
|||
[
|
||||
{
|
||||
"key": "1",
|
||||
"value": {
|
||||
"person": {
|
||||
"org": "Dev Ops",
|
||||
"id": 1036
|
||||
},
|
||||
"city": "Seattle",
|
||||
"state": "WA"
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "2",
|
||||
"value": {
|
||||
"person": {
|
||||
"org": "Hardware",
|
||||
"id": 1028
|
||||
},
|
||||
"city": "Portland",
|
||||
"state": "OR"
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "3",
|
||||
"value": {
|
||||
"person": {
|
||||
"org": "Finance",
|
||||
"id": 1071
|
||||
},
|
||||
"city": "Sacramento",
|
||||
"state": "CA"
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "4",
|
||||
"value": {
|
||||
"person": {
|
||||
"org": "Dev Ops",
|
||||
"id": 1042
|
||||
},
|
||||
"city": "Spokane",
|
||||
"state": "WA"
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "5",
|
||||
"value": {
|
||||
"person": {
|
||||
"org": "Hardware",
|
||||
"id": 1007
|
||||
},
|
||||
"city": "Los Angeles",
|
||||
"state": "CA"
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "6",
|
||||
"value": {
|
||||
"person": {
|
||||
"org": "Finance",
|
||||
"id": 1094
|
||||
},
|
||||
"city": "Eugene",
|
||||
"state": "OR"
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "7",
|
||||
"value": {
|
||||
"person": {
|
||||
"org": "Dev Ops",
|
||||
"id": 1015
|
||||
},
|
||||
"city": "San Francisco",
|
||||
"state": "CA"
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "8",
|
||||
"value": {
|
||||
"person": {
|
||||
"org": "Hardware",
|
||||
"id": 1077
|
||||
},
|
||||
"city": "Redmond",
|
||||
"state": "WA"
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "9",
|
||||
"value": {
|
||||
"person": {
|
||||
"org": "Finance",
|
||||
"id": 1002
|
||||
},
|
||||
"city": "San Diego",
|
||||
"state": "CA"
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "10",
|
||||
"value": {
|
||||
"person": {
|
||||
"org": "Dev Ops",
|
||||
"id": 1054
|
||||
},
|
||||
"city": "New York",
|
||||
"state": "NY"
|
||||
}
|
||||
}
|
||||
]
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"query": {
|
||||
"filter": {
|
||||
"EQ": { "value.state": "CA" }
|
||||
},
|
||||
"sort": [
|
||||
{
|
||||
"key": "value.person.id",
|
||||
"order": "DESC"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"query": {
|
||||
"filter": {
|
||||
"IN": { "value.person.org": [ "Dev Ops", "Hardware" ] }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"query": {
|
||||
"filter": {
|
||||
"OR": [
|
||||
{
|
||||
"EQ": { "value.person.org": "Dev Ops" }
|
||||
},
|
||||
{
|
||||
"AND": [
|
||||
{
|
||||
"EQ": { "value.person.org": "Finance" }
|
||||
},
|
||||
{
|
||||
"IN": { "value.state": [ "CA", "WA" ] }
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"sort": [
|
||||
{
|
||||
"key": "value.state",
|
||||
"order": "DESC"
|
||||
},
|
||||
{
|
||||
"key": "value.person.id"
|
||||
}
|
||||
],
|
||||
"pagination": {
|
||||
"limit": 3,
|
||||
"token": "3"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"query": {
|
||||
"filter": {
|
||||
"OR": [
|
||||
{
|
||||
"EQ": { "value.person.org": "Dev Ops" }
|
||||
},
|
||||
{
|
||||
"AND": [
|
||||
{
|
||||
"EQ": { "value.person.org": "Finance" }
|
||||
},
|
||||
{
|
||||
"IN": { "value.state": [ "CA", "WA" ] }
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"sort": [
|
||||
{
|
||||
"key": "value.state",
|
||||
"order": "DESC"
|
||||
},
|
||||
{
|
||||
"key": "value.person.id"
|
||||
}
|
||||
],
|
||||
"pagination": {
|
||||
"limit": 3
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,39 +8,37 @@ description: "Overview of the state management building block"
|
|||
|
||||
## Introduction
|
||||
|
||||
Using state management, your application can store data as key/value pairs in the [supported state stores]({{< ref supported-state-stores.md >}}).
|
||||
Using state management, your application can store and query data as key/value pairs in the [supported state stores]({{< ref supported-state-stores.md >}}). This enables you to build stateful, long running applications that can save and retrieve their state, for example a shopping cart or a game's session state.
|
||||
|
||||
When using state management your application can leverage features that would otherwise be complicated and error-prone to build yourself such as:
|
||||
When using state management, your application can leverage features that would otherwise be complicated and error-prone to build yourself such as:
|
||||
|
||||
- Distributed concurrency and data consistency
|
||||
- Bulk [CRUD](https://en.wikipedia.org/wiki/Create,_read,_update_and_delete) operations
|
||||
- Setting the choices on concurrency control and data consistency.
|
||||
- Performing bulk update operations [CRUD](https://en.wikipedia.org/wiki/Create,_read,_update_and_delete) including multiple transactional operations.
|
||||
- Querying and filtering the key/value data.
|
||||
|
||||
Your application can use Dapr's state management API to save and read key/value pairs using a state store component, as shown in the diagram below. For example, by using HTTP POST you can save key/value pairs and by using HTTP GET you can read a key and have its value returned.
|
||||
Your application can use Dapr's state management API to save, read and query key/value pairs using a state store component, as shown in the diagram below. For example, by using HTTP POST you can save or query key/value pairs and by using HTTP GET you can read a specific key and have its value returned.
|
||||
|
||||
<img src="/images/state-management-overview.png" width=900>
|
||||
|
||||
|
||||
## Features
|
||||
These are the features available as part of the state management API:
|
||||
|
||||
### Pluggable state stores
|
||||
|
||||
Dapr data stores are modeled as components, which can be swapped out without any changes to your service code. See [supported state stores]({{< ref supported-state-stores >}}) to see the list.
|
||||
|
||||
### Configurable state store behavior
|
||||
|
||||
Dapr allows developers to attach additional metadata to a state operation request that describes how the request is expected to be handled. You can attach:
|
||||
### Configurable state store behaviors
|
||||
Dapr allows you to include additional metadata in a state operation request that describes how the request is expected to be handled. You can attach:
|
||||
- Concurrency requirements
|
||||
- Consistency requirements
|
||||
|
||||
By default, your application should assume a data store is **eventually consistent** and uses a **last-write-wins** concurrency pattern.
|
||||
|
||||
[Not all stores are created equal]({{< ref supported-state-stores.md >}}). To ensure portability of your application you can query the capabilities of the store and make your code adaptive to different store capabilities.
|
||||
[Not all stores are created equal]({{< ref supported-state-stores.md >}}). To ensure portability of your application you can query the metadata capabilities of the store and make your code adaptive to different store capabilities.
|
||||
|
||||
### Concurrency
|
||||
Dapr supports Optimistic Concurrency Control (OCC) using ETags. When a state value is requested, Dapr always attaches an ETag property to the returned state. When the user code tries to update or delete a state, it’s expected to attach the ETag either through the request body for updates or the `If-Match` header for deletes. The write operation can succeed only when the provided ETag matches with the ETag in the state store.
|
||||
|
||||
Dapr supports optimistic concurrency control (OCC) using ETags. When a state is requested, Dapr always attaches an ETag property to the returned state. When the user code tries to update or delete a state, it’s expected to attach the ETag either through the request body for updates or the `If-Match` header for deletes. The write operation can succeed only when the provided ETag matches with the ETag in the state store.
|
||||
|
||||
Dapr chooses OCC because in many applications, data update conflicts are rare because clients are naturally partitioned by business contexts to operate on different data. However, if your application chooses to use ETags, a request may get rejected because of mismatched ETags. It's recommended that you use a retry policy to compensate for such conflicts when using ETags.
|
||||
Dapr chooses OCC because in many applications, data update conflicts are rare because clients are naturally partitioned by business contexts to operate on different data. However, if your application chooses to use ETags, a request may get rejected because of mismatched ETags. It's recommended that you use a retry policy in your code to compensate for such conflicts when using ETags.
|
||||
|
||||
If your application omits ETags in writing requests, Dapr skips ETag checks while handling the requests. This essentially enables the **last-write-wins** pattern, compared to the **first-write-wins** pattern with ETags.
|
||||
|
||||
|
@ -50,14 +48,7 @@ For stores that don't natively support ETags, it's expected that the correspondi
|
|||
|
||||
Read the [API reference]({{< ref state_api.md >}}) to learn how to set concurrency options.
|
||||
|
||||
### Automatic encryption
|
||||
|
||||
Dapr supports automatic client encryption of application state with support for key rotations. This is a preview feature and it is supported on all Dapr state stores.
|
||||
|
||||
For more info, read the [How-To: Encrypt application state]({{< ref howto-encrypt-state.md >}}) section.
|
||||
|
||||
### Consistency
|
||||
|
||||
Dapr supports both **strong consistency** and **eventual consistency**, with eventual consistency as the default behavior.
|
||||
|
||||
When strong consistency is used, Dapr waits for all replicas (or designated quorums) to acknowledge before it acknowledges a write request. When eventual consistency is used, Dapr returns as soon as the write request is accepted by the underlying data store, even if this is a single replica.
|
||||
|
@ -66,25 +57,40 @@ Read the [API reference]({{< ref state_api.md >}}) to learn how to set consisten
|
|||
|
||||
### Bulk operations
|
||||
|
||||
Dapr supports two types of bulk operations - **bulk** or **multi**. You can group several requests of the same type into a bulk (or a batch). Dapr submits requests in the bulk as individual requests to the underlying data store. In other words, bulk operations are not transactional. On the other hand, you can group requests of different types into a multi-operation, which is handled as an atomic transaction.
|
||||
Dapr supports two types of bulk operations: **bulk** or **multi**. You can group several requests of the same type into a bulk (or a batch). Dapr submits requests in bulk operations as individual requests to the underlying data store. In other words, bulk operations are not transactional. On the other hand, you can group requests of different types into a multi-operation, which is then handled as an atomic transaction.
|
||||
|
||||
Read the [API reference]({{< ref state_api.md >}}) to learn how use bulk and multi options.
|
||||
|
||||
### State encryption
|
||||
Dapr supports automatic client encryption of application state with support for key rotations. This is supported on all Dapr state stores. For more info, read the [How-To: Encrypt application state]({{< ref howto-encrypt-state.md >}}) topic.
|
||||
|
||||
### Shared state between applications
|
||||
Different applications might have different needs when it comes to sharing state. For example, in one scenario you may want to encapsulate all state within a given application and have Dapr manage the access for you. In a different scenario, you may need to have two applications working on the same state be able to get and save the same keys. Dapr enable states to be isolated to an application, shared in a state store between applications or have multiple applications share state across different state stores. For more details read [How-To: Share state between applications]({{< ref howto-share-state.md >}}),
|
||||
|
||||
### Actor state
|
||||
Transactional state stores can be used to store actor state. To specify which state store to be used for actors, specify value of property `actorStateStore` as `true` in the metadata section of the state store component. Actors state is stored with a specific scheme in transactional state stores, which allows for consistent querying. Only a single state store component can be used as the statestore for all actors. Read the [API reference]({{< ref state_api.md >}}) to learn more about state stores for actors and the [actors API reference]({{< ref actors_api.md >}})
|
||||
Transactional state stores can be used to store actor state. To specify which state store to be used for actors, specify value of property `actorStateStore` as `true` in the metadata section of the state store component. Actors state is stored with a specific scheme in transactional state stores, which allows for consistent querying. Only a single state store component can be used as the state store for all actors. Read the [API reference]({{< ref state_api.md >}}) to learn more about state stores for actors and the [actors API reference]({{< ref actors_api.md >}})
|
||||
|
||||
### Query state store directly
|
||||
### Querying state
|
||||
There are two ways to query the state:
|
||||
* Using the [state management query API]({{< ref "#state-query-api" >}}) provided in Dapr runtime.
|
||||
* Querying state store [directly]({{< ref "#query-state-store-directly" >}}) with the store's native SDK.
|
||||
|
||||
#### Query API
|
||||
The query API provides a way of querying the key/value data saved using state management in state stores regardless of underlying database or storage technology. It is an optional state management API. Using the state management query API you can filter, sort and paginate the key/value data. For more details read [How-To: Query state]({{< ref howto-state-query-api.md >}}).
|
||||
|
||||
#### Querying state store directly
|
||||
Dapr saves and retrieves state values without any transformation. You can query and aggregate state directly from the [underlying state store]({{< ref query-state-store >}}).
|
||||
|
||||
For example, to get all state keys associated with an application ID "myApp" in Redis, use:
|
||||
|
||||
```bash
|
||||
KEYS "myApp*"
|
||||
```
|
||||
|
||||
#### Querying actor state
|
||||
{{% alert title="Note on direct queries" color="primary" %}}
|
||||
Direct queries of the state store are not governed by Dapr concurrency control, since you are not calling through the Dapr runtime. What you see are snapshots of committed data which are acceptable for read-only queries across multiple actors, however writes should be done via the Dapr state management or actors APIs.
|
||||
{{% /alert %}}
|
||||
|
||||
##### Querying actor state
|
||||
If the data store supports SQL queries, you can query an actor's state using SQL queries. For example use:
|
||||
|
||||
```sql
|
||||
|
@ -97,20 +103,20 @@ You can also perform aggregate queries across actor instances, avoiding the comm
|
|||
SELECT AVG(value) FROM StateTable WHERE Id LIKE '<app-id>||<thermometer>||*||temperature'
|
||||
```
|
||||
|
||||
{{% alert title="Note on direct queries" color="primary" %}}
|
||||
Direct queries of the state store are not governed by Dapr concurrency control, since you are not calling through the Dapr runtime. What you see are snapshots of committed data which are acceptable for read-only queries across multiple actors, however writes should be done via the Dapr state management or actors APIs.
|
||||
{{% /alert %}}
|
||||
### State Time-to-Live (TTL)
|
||||
Dapr enables per state set request time-to-live (TTL). This means that applications can set time-to-live per state stored, and these states cannot be retrieved after expiration.
|
||||
|
||||
### State management API
|
||||
|
||||
The API for state management can be found in the [state management API reference]({{< ref state_api.md >}}) which describes how to retrieve, save and delete state values by providing keys.
|
||||
The state management API can be found in the [state management API reference]({{< ref state_api.md >}}) which describes how to retrieve, save, delete and query state values by providing keys.
|
||||
|
||||
## Next steps
|
||||
* Follow these guides on:
|
||||
* [How-To: Save and get state]({{< ref howto-get-save-state.md >}})
|
||||
* [How-To: Build a stateful service]({{< ref howto-stateful-service.md >}})
|
||||
* [How-To: Share state between applications]({{< ref howto-share-state.md >}})
|
||||
* [How-To: Query state]({{< ref howto-state-query-api.md >}})
|
||||
* [How-To: Encrypt application state]({{< ref howto-encrypt-state.md >}})
|
||||
* [State Time-to-Live]({{< ref state-store-ttl.md >}})
|
||||
* Try out the [hello world quickstart](https://github.com/dapr/quickstarts/blob/master/hello-world/README.md) which shows how to use state management or try the samples in the [Dapr SDKs]({{< ref sdks >}})
|
||||
* List of [state store components]({{< ref supported-state-stores.md >}})
|
||||
* Read the [state management API reference]({{< ref state_api.md >}})
|
||||
|
|
|
@ -292,6 +292,132 @@ None.
|
|||
curl -X "DELETE" http://localhost:3500/v1.0/state/starwars/planet -H "If-Match: xxxxxxx"
|
||||
```
|
||||
|
||||
## Query state
|
||||
|
||||
This endpoint lets you query the key/value state.
|
||||
|
||||
{{% alert title="alpha" color="warning" %}}
|
||||
This API is in alpha stage.
|
||||
{{% /alert %}}
|
||||
|
||||
### HTTP Request
|
||||
|
||||
```
|
||||
POST/PUT http://localhost:<daprPort>/v1.0-alpha/state/<storename>/query
|
||||
```
|
||||
|
||||
#### URL Parameters
|
||||
|
||||
Parameter | Description
|
||||
--------- | -----------
|
||||
daprPort | the Dapr port
|
||||
storename | ```metadata.name``` field in the user configured state store component yaml. Refer to the Dapr state store configuration structure mentioned above.
|
||||
metadata | (optional) metadata as query parameters to the state store
|
||||
|
||||
> Note, all URL parameters are case-sensitive.
|
||||
|
||||
#### Response Codes
|
||||
|
||||
Code | Description
|
||||
---- | -----------
|
||||
200 | State query successful
|
||||
400 | State store is missing or misconfigured
|
||||
500 | State query failed
|
||||
|
||||
#### Response Body
|
||||
An array of JSON-encoded values
|
||||
|
||||
### Example
|
||||
|
||||
```shell
|
||||
curl http://localhost:3500/v1.0-alpha/state/myStore/query \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"query": {
|
||||
"filter": {
|
||||
"OR": [
|
||||
{
|
||||
"EQ": { "value.person.org": "Dev Ops" }
|
||||
},
|
||||
{
|
||||
"AND": [
|
||||
{
|
||||
"EQ": { "value.person.org": "Finance" }
|
||||
},
|
||||
{
|
||||
"IN": { "value.state": [ "CA", "WA" ] }
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"sort": [
|
||||
{
|
||||
"key": "value.state",
|
||||
"order": "DESC"
|
||||
},
|
||||
{
|
||||
"key": "value.person.id"
|
||||
}
|
||||
],
|
||||
"pagination": {
|
||||
"limit": 3
|
||||
}
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
> The above command returns an array of objects along with a token:
|
||||
|
||||
```json
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"key": "1",
|
||||
"data": {
|
||||
"person": {
|
||||
"org": "Dev Ops",
|
||||
"id": 1036
|
||||
},
|
||||
"city": "Seattle",
|
||||
"state": "WA"
|
||||
},
|
||||
"etag": "6f54ad94-dfb9-46f0-a371-e42d550adb7d"
|
||||
},
|
||||
{
|
||||
"key": "4",
|
||||
"data": {
|
||||
"person": {
|
||||
"org": "Dev Ops",
|
||||
"id": 1042
|
||||
},
|
||||
"city": "Spokane",
|
||||
"state": "WA"
|
||||
},
|
||||
"etag": "7415707b-82ce-44d0-bf15-6dc6305af3b1"
|
||||
},
|
||||
{
|
||||
"key": "10",
|
||||
"data": {
|
||||
"person": {
|
||||
"org": "Dev Ops",
|
||||
"id": 1054
|
||||
},
|
||||
"city": "New York",
|
||||
"state": "NY"
|
||||
},
|
||||
"etag": "26bbba88-9461-48d1-8a35-db07c374e5aa"
|
||||
}
|
||||
],
|
||||
"token": "3"
|
||||
}
|
||||
```
|
||||
To pass metadata as query parammeter:
|
||||
|
||||
```
|
||||
POST http://localhost:3500/v1.0-alpha/state/myStore/query?metadata.partitionKey=mypartitionKey
|
||||
```
|
||||
|
||||
## State transactions
|
||||
|
||||
Persists the changes to the state store as a multi-item transaction.
|
||||
|
|
|
@ -26,38 +26,38 @@ The following stores are supported, at various levels, by the Dapr state managem
|
|||
|
||||
### Generic
|
||||
|
||||
| Name | CRUD | Transactional | ETag | [TTL]({{< ref state-store-ttl.md >}}) | [Actors]({{< ref howto-actors.md >}}) | Status | Component version | Since |
|
||||
|----------------------------------------------------------------|------|---------------------|------|-----|------|--------| -------|------|
|
||||
| [Aerospike]({{< ref setup-aerospike.md >}}) | ✅ | ❌ | ✅ | ❌ | ❌ | Alpha | v1 | 1.0 |
|
||||
| [Apache Cassandra]({{< ref setup-cassandra.md >}}) | ✅ | ❌ | ❌ | ✅ | ❌ | Alpha | v1 | 1.0 |
|
||||
| [Cloudstate]({{< ref setup-cloudstate.md >}}) | ✅ | ❌ | ✅ | ❌ | ❌ | Alpha | v1 | 1.0 |
|
||||
| [Couchbase]({{< ref setup-couchbase.md >}}) | ✅ | ❌ | ✅ | ❌ | ❌ | Alpha | v1 | 1.0 |
|
||||
| [Hashicorp Consul]({{< ref setup-consul.md >}}) | ✅ | ❌ | ❌ | ❌ | ❌ | Alpha | v1 | 1.0 |
|
||||
| [Hazelcast]({{< ref setup-hazelcast.md >}}) | ✅ | ❌ | ❌ | ❌ | ❌ | Alpha | v1 | 1.0 |
|
||||
| [Memcached]({{< ref setup-memcached.md >}}) | ✅ | ❌ | ❌ | ✅ | ❌ | Alpha | v1 | 1.0 |
|
||||
| [MongoDB]({{< ref setup-mongodb.md >}}) | ✅ | ✅ | ✅ | ❌ | ✅ | Stable | v1 | 1.0 |
|
||||
| [MySQL]({{< ref setup-mysql.md >}}) | ✅ | ✅ | ✅ | ❌ | ✅ | Alpha | v1 | 1.0 |
|
||||
| [PostgreSQL]({{< ref setup-postgresql.md >}}) | ✅ | ✅ | ✅ | ❌ | ✅ | Alpha | v1 | 1.0 |
|
||||
| [Redis]({{< ref setup-redis.md >}}) | ✅ | ✅ | ✅ | ✅ | ✅ | Stable | v1 | 1.0 |
|
||||
| [RethinkDB]({{< ref setup-rethinkdb.md >}}) | ✅ | ✅ | ✅ | ❌ | ✅ | Alpha | v1 | 1.0 |
|
||||
| [Zookeeper]({{< ref setup-zookeeper.md >}}) | ✅ | ❌ | ✅ | ❌ | ❌ | Alpha | v1 | 1.0 |
|
||||
| Name |CRUD|Transactional|ETag| [TTL]({{< ref state-store-ttl.md >}}) | [Actors]({{< ref howto-actors.md >}}) | [Query]({{< ref howto-state-query-api.md >}}) | Status | Component version | Since |
|
||||
|----------------------------------------------------|----|-------------|----|----|----|----|-------|----|-----|
|
||||
| [Aerospike]({{< ref setup-aerospike.md >}}) | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | Alpha | v1 | 1.0 |
|
||||
| [Apache Cassandra]({{< ref setup-cassandra.md >}}) | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | Alpha | v1 | 1.0 |
|
||||
| [Cloudstate]({{< ref setup-cloudstate.md >}}) | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | Alpha | v1 | 1.0 |
|
||||
| [Couchbase]({{< ref setup-couchbase.md >}}) | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | Alpha | v1 | 1.0 |
|
||||
| [Hashicorp Consul]({{< ref setup-consul.md >}}) | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | Alpha | v1 | 1.0 |
|
||||
| [Hazelcast]({{< ref setup-hazelcast.md >}}) | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | Alpha | v1 | 1.0 |
|
||||
| [Memcached]({{< ref setup-memcached.md >}}) | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | Alpha | v1 | 1.0 |
|
||||
| [MongoDB]({{< ref setup-mongodb.md >}}) | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | Stable | v1 | 1.0 |
|
||||
| [MySQL]({{< ref setup-mysql.md >}}) | ✅ | ✅ | ✅ | ❌ | ✅ | ❌ | Alpha | v1 | 1.0 |
|
||||
| [PostgreSQL]({{< ref setup-postgresql.md >}}) | ✅ | ✅ | ✅ | ❌ | ✅ | ❌ | Alpha | v1 | 1.0 |
|
||||
| [Redis]({{< ref setup-redis.md >}}) | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | Stable | v1 | 1.0 |
|
||||
| [RethinkDB]({{< ref setup-rethinkdb.md >}}) | ✅ | ✅ | ✅ | ❌ | ✅ | ❌ | Alpha | v1 | 1.0 |
|
||||
| [Zookeeper]({{< ref setup-zookeeper.md >}}) | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | Alpha | v1 | 1.0 |
|
||||
|
||||
|
||||
### Amazon Web Services (AWS)
|
||||
| Name | CRUD | Transactional | ETag | [TTL]({{< ref state-store-ttl.md >}}) | [Actors]({{< ref howto-actors.md >}}) | Status | Component version | Since |
|
||||
|------------------------------------------------------------------|------|---------------------|------|-----|--------|-----|-----|-------|
|
||||
| [AWS DynamoDB]({{< ref setup-dynamodb.md>}}) | ✅ | ❌ | ❌ | ❌ | ❌ | Alpha | v1 | 1.0 |
|
||||
| Name |CRUD|Transactional|ETag| [TTL]({{< ref state-store-ttl.md >}}) | [Actors]({{< ref howto-actors.md >}}) | [Query]({{< ref howto-state-query-api.md >}}) | Status | Component version | Since |
|
||||
|----------------------------------------------------|----|-------------|----|----|----|----|------|----|-----|
|
||||
| [AWS DynamoDB]({{< ref setup-dynamodb.md>}}) | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | Alpha | v1 | 1.0 |
|
||||
|
||||
### Google Cloud Platform (GCP)
|
||||
| Name | CRUD | Transactional | ETag | [TTL]({{< ref state-store-ttl.md >}}) | [Actors]({{< ref howto-actors.md >}}) | Status | Component version | Since |
|
||||
|------------------------------------------------------------------|------|---------------------|------|-----|--------|-----|-----|-------|
|
||||
| [GCP Firestore]({{< ref setup-firestore.md >}}) | ✅ | ❌ | ❌ | ❌ | ❌ | Alpha | v1 | 1.0 |
|
||||
| Name |CRUD|Transactional|ETag| [TTL]({{< ref state-store-ttl.md >}}) | [Actors]({{< ref howto-actors.md >}}) | [Query]({{< ref howto-state-query-api.md >}}) | Status | Component version | Since |
|
||||
|----------------------------------------------------|------|---------------|----|----|----|----|------|----|-----|
|
||||
| [GCP Firestore]({{< ref setup-firestore.md >}}) | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | Alpha | v1 | 1.0 |
|
||||
|
||||
### Microsoft Azure
|
||||
|
||||
| Name | CRUD | Transactional | ETag | [TTL]({{< ref state-store-ttl.md >}}) | [Actors]({{< ref howto-actors.md >}}) | Status | Component version | Since |
|
||||
|------------------------------------------------------------------|------|---------------------|------|-----|--------|-----|-----|-------|
|
||||
| [Azure Blob Storage]({{< ref setup-azure-blobstorage.md >}}) | ✅ | ❌ | ✅ | ❌ | ❌ | Stable | v1 | 1.0 |
|
||||
| [Azure CosmosDB]({{< ref setup-azure-cosmosdb.md >}}) | ✅ | ✅ | ✅ | ✅ | ✅ | Stable | v1 | 1.0 |
|
||||
| [Azure SQL Server]({{< ref setup-sqlserver.md >}}) | ✅ | ✅ | ✅ | ❌ | ✅ | Stable | v1 | 1.5 |
|
||||
| [Azure Table Storage]({{< ref setup-azure-tablestorage.md >}}) | ✅ | ❌ | ✅ | ❌ | ❌ | Alpha | v1 | 1.0 |
|
||||
| Name |CRUD|Transactional|ETag| [TTL]({{< ref state-store-ttl.md >}}) | [Actors]({{< ref howto-actors.md >}}) | [Query]({{< ref howto-state-query-api.md >}}) | Status | Component version | Since |
|
||||
|------------------------------------------------------------------|----|-------------|----|----|----|----|-------|----|-----|
|
||||
| [Azure Blob Storage]({{< ref setup-azure-blobstorage.md >}}) | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | Stable | v1 | 1.0 |
|
||||
| [Azure CosmosDB]({{< ref setup-azure-cosmosdb.md >}}) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | Stable | v1 | 1.0 |
|
||||
| [Azure SQL Server]({{< ref setup-sqlserver.md >}}) | ✅ | ✅ | ✅ | ❌ | ✅ | ❌ | Stable | v1 | 1.5 |
|
||||
| [Azure Table Storage]({{< ref setup-azure-tablestorage.md >}}) | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | Alpha | v1 | 1.0 |
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 88 KiB After Width: | Height: | Size: 190 KiB |
Binary file not shown.
After Width: | Height: | Size: 205 KiB |
Loading…
Reference in New Issue