Skip to content

Commit

Permalink
Update AdvancedSql documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
e-tobi authored and jeremydmiller committed May 28, 2024
1 parent 8b79c2a commit 308e22a
Showing 1 changed file with 48 additions and 18 deletions.
66 changes: 48 additions & 18 deletions docs/documents/querying/advanced-sql.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# Advanced querying with Postgresql SQL

Besides Linq queries or simple raw SQL queries via `session.Query<T>("where...")`, it is also possible to do even more complex SQL queries via `session.AdvancedSqlQueryAsync<T>()`.
Besides Linq queries or simple raw SQL queries via `session.Query<T>("where...")`, it is also possible to do even more complex SQL queries via `session.AdvancedSql.QueryAsync<T>()`.
With this method Marten does not try to add any missing parts to the SQL query, instead you have to provide the whole query string yourself.

Marten just makes some assumptions on how the schema of the SQl query result must look like, in order to be able to map the query result to documents, scalars or other JSON serializable types.
With `AdvancedSqlQueryAsync` / `AdvancedSqlQuery` it is even possible to return multiple documents, objects and scalars as a tuple. Currently up to three result types can be queried for.
With `AdvancedSql.QueryAsync` / `AdvancedSql.Query` it is even possible to return multiple documents, objects and scalars as a tuple. Currently up to three result types can be queried for.

The following rules must be followed when doing queries with `AdvancedSqlQueryAsync` / `AdvancedSqlQuery`:
The following rules must be followed when doing queries with `AdvancedSql.QueryAsync` / `AdvancedSql.Query`:

- If a document should be returned, the SQL `SELECT` statement must contain all the columns required by Marten to build
the document in the correct order. Which columns are needed depends on the session type and if any meta data are
Expand Down Expand Up @@ -36,7 +36,7 @@ Querying for a simple scalar value can be done like this:
<a id='snippet-sample_advanced_sql_query_single_scalar'></a>
```cs
var schema = session.DocumentStore.Options.Schema;
var name = (await session.AdvancedSqlQueryAsync<string>(
var name = (await session.AdvancedSql.QueryAsync<string>(
$"select data ->> 'Name' from {schema.For<DocWithMeta>()} limit 1",
CancellationToken.None)).First();
```
Expand All @@ -48,7 +48,7 @@ Or for multiple scalars returned as a tuple:
<!-- snippet: sample_advanced_sql_query_multiple_scalars -->
<a id='snippet-sample_advanced_sql_query_multiple_scalars'></a>
```cs
var (number,text, boolean) = (await session.AdvancedSqlQueryAsync<int, string, bool>(
var (number,text, boolean) = (await session.AdvancedSql.QueryAsync<int, string, bool>(
"select row(5), row('foo'), row(true) from (values(1)) as dummy",
CancellationToken.None)).First();
```
Expand All @@ -60,7 +60,7 @@ You can also query for any arbitrary JSON that will get deserialized:
<!-- snippet: sample_advanced_sql_query_json_object -->
<a id='snippet-sample_advanced_sql_query_json_object'></a>
```cs
var result = (await session.AdvancedSqlQueryAsync<Foo, Bar>(
var result = (await session.AdvancedSql.QueryAsync<Foo, Bar>(
"select row(json_build_object('Name', 'foo')), row(json_build_object('Name', 'bar')) from (values(1)) as dummy",
CancellationToken.None)).First();
```
Expand All @@ -73,7 +73,7 @@ Querying for documents requires to return the correct columns:
<a id='snippet-sample_advanced_sql_query_documents'></a>
```cs
var schema = session.DocumentStore.Options.Schema;
var docs = await session.AdvancedSqlQueryAsync<DocWithoutMeta>(
var docs = await session.AdvancedSql.QueryAsync<DocWithoutMeta>(
$"select id, data from {schema.For<DocWithoutMeta>()} order by data ->> 'Name'",
CancellationToken.None);
```
Expand All @@ -87,7 +87,7 @@ important!:
<a id='snippet-sample_advanced_sql_query_documents_with_metadata'></a>
```cs
var schema = session.DocumentStore.Options.Schema;
var doc = (await session.AdvancedSqlQueryAsync<DocWithMeta>(
var doc = (await session.AdvancedSql.QueryAsync<DocWithMeta>(
$"select id, data, mt_version from {schema.For<DocWithMeta>()} where data ->> 'Name' = 'Max'",
CancellationToken.None)).First();
```
Expand All @@ -111,7 +111,7 @@ await session.SaveChangesAsync();

var schema = session.DocumentStore.Options.Schema;
IReadOnlyList<(DocWithMeta doc, DocDetailsWithMeta detail, long totalResults)> results =
await session.AdvancedSqlQueryAsync<DocWithMeta, DocDetailsWithMeta, long>(
await session.AdvancedSql.QueryAsync<DocWithMeta, DocDetailsWithMeta, long>(
$"""
select
row(a.id, a.data, a.mt_version),
Expand Down Expand Up @@ -139,20 +139,50 @@ results[1].detail.Detail.ShouldBe("Likes to cook");
<sup><a href='https://github.com/JasperFx/marten/blob/master/src/DocumentDbTests/Reading/advanced_sql_query.cs#L100-L137' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_advanced_sql_query_related_documents_and_scalar' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

For sync queries you can use the `AdvancedSqlQuery<T>(...)` overloads.
For sync queries you can use the `AdvancedSql.Query<T>(...)` overloads.

## Getting document and schema names
When you need to query for large datasets, the `AdvancedSql.StreamAsync<>(...)` methods can be used. They will return
an IAsyncEnumerable<>, which you can use to iterate over the result set. See:

Tables can be referenced with raw sql, but it is also possible to have marten tell you names of documents and aggregates.
<!-- snippet: sample_advanced_sql_stream_related_documents_and_scalar -->
<a id='snippet-sample_advanced_sql_stream_related_documents_and_scalar'></a>
```cs
session.Store(new DocWithMeta { Id = 1, Name = "Max" });
session.Store(new DocDetailsWithMeta { Id = 1, Detail = "Likes bees" });
session.Store(new DocWithMeta { Id = 2, Name = "Michael" });
session.Store(new DocDetailsWithMeta { Id = 2, Detail = "Is a good chess player" });
session.Store(new DocWithMeta { Id = 3, Name = "Anne" });
session.Store(new DocDetailsWithMeta { Id = 3, Detail = "Hates soap operas" });
session.Store(new DocWithMeta { Id = 4, Name = "Beatrix" });
session.Store(new DocDetailsWithMeta { Id = 4, Detail = "Likes to cook" });
await session.SaveChangesAsync();

These names can be found on the `Schema` property of the StoreOptions:
var schema = session.DocumentStore.Options.Schema;

<!-- snippet: sample_document_schema_resolver_options -->
<a id='snippet-sample_document_schema_resolver_options'></a>
```cs
var schema = theSession.DocumentStore.Options.Schema;
var asyncEnumerable = session.AdvancedSql.StreamAsync<DocWithMeta, DocDetailsWithMeta, long>(
$"""
select
row(a.id, a.data, a.mt_version),
row(b.id, b.data, b.mt_version),
row(count(*) over())
from
{schema.For<DocWithMeta>()} a
left join
{schema.For<DocDetailsWithMeta>()} b on a.id = b.id
where
(a.data ->> 'Id')::int > 1
order by
a.data ->> 'Name'
""",
CancellationToken.None);

var collectedResults = new List<(DocWithMeta doc, DocDetailsWithMeta detail, long totalResults)>();
await foreach (var result in asyncEnumerable)
{
collectedResults.Add(result);
}
```
<sup><a href='https://github.com/JasperFx/marten/blob/master/src/CoreTests/DocumentSchemaResolverTests.cs#L40-L42' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_document_schema_resolver_options' title='Start of snippet'>anchor</a></sup>
<sup><a href='https://github.com/JasperFx/marten/blob/master/src/DocumentDbTests/Reading/advanced_sql_query.cs#L144-L179' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_advanced_sql_stream_related_documents_and_scalar' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

Using this you can resolve schemas:
Expand Down

0 comments on commit 308e22a

Please sign in to comment.