Birden fazla queries eş zamanlı çalıştırma
Birden fazla queries bir araya getirilebilir ve durumlarını ve verilerini yeniden kullanarak tek bir operasyon olarak çalıştırılabilir.
Bu, GraphQL sunucusunun da tek bir istekte birden fazla queries çalıştırdığı query batching'den farklıdır; ancak bu queries yalnızca birbirinden bağımsız olarak sırayla çalıştırılır.
Bu özellik performansı artırır. Queries'i farklı isteklerde bağımsız olarak çalıştırmak yerine (önce GraphQL sunucusuna karşı bir operasyon çalıştırır, ardından yanıtını bekler ve bu sonucu kullanarak başka bir operasyon gerçekleştiririz), bunları birlikte çalıştırabilir ve böylece birden fazla istekten kaynaklanan gecikmeyi önleyebiliriz.
Multiple Query Execution aynı zamanda GraphQL queries'imizi daha iyi organize etmemize de olanak tanır; birbirine bağımlı mantıksal birimlere bölerek ve bir önceki operasyonun sonucuna göre koşullu olarak çalıştırılmalarını sağlar.
Birden fazla queries çalıştırma nasıl kullanılır
Diyelim ki oturum açmış kullanıcının adından bahseden tüm gönderileri aramak istiyoruz. Normalde bunu gerçekleştirmek için iki queries'e ihtiyaç duyarız:
Önce kullanıcının name bilgisini alırız:
query GetLoggedInUserName {
me {
name
}
}...ve ardından, birinci queries çalıştırıldıktan sonra, alınan kullanıcının name bilgisini ikinci bir queries'te arama yapmak için $search değişkeni olarak geçirebiliriz:
query GetPostsContainingString($search: String!) {
posts(filter: { search: $search }) {
id
title
}
}Multiple Query Execution bu süreci basitleştirerek tüm verileri almamıza ve gerekli tüm mantığı tek bir istekte çalıştırmamıza olanak tanır:
query GetLoggedInUserName {
me {
name @export(as: "search")
}
}
query GetPostsContainingString @depends(on: "GetLoggedInUserName") {
posts(filter: { search: $search }) {
id
title
}
}Multiple Query Execution bu özel direktifler kullanılarak sağlanır:
@depends(operasyon direktifi): bir operasyonun (queryveyamutationolsun) hangi diğer operasyonların önce çalıştırılması gerektiğini belirtmesini sağlar@export(alan direktifi): bir operasyondaki alan değerini dışa aktararak başka bir operasyondaki bir alana girdi olarak enjekte eder@deferredExport(alan direktifi):@export'a benzer ancak Multi-Field Directives ile kullanılır.
Ayrıca @include ve @skip direktifleri de operasyon direktifleri olarak kullanılabilir (normalde yalnızca alan direktifleridir) ve bunlar bir operasyonu belirli bir koşulu sağlıyorsa koşullu olarak çalıştırmak için kullanılabilir.
GraphQL sunucusu, her @depends(on: ...) direktifinden alınan operasyonların yüklenip çalıştırılacağı listeyi oluşturur ve @export içeren herhangi bir alandan değerleri dinamik değişken olarak (as argümanı altında tanımlanan adla) sonraki operasyonlara girdi olarak aktarır.
Bu direktifleri bir araya getirerek karmaşık işlevselliği ara adımlara bölebilir, query ve mutation operasyonlarını dönüşümlü olarak kullanabilir, bağımlılıklarını gerekli sıraya göre ekleyebilir ve en dıştaki operasyonu ?operationName=... parametresiyle tanımlayarak hepsini tek bir istekte çalıştırabiliriz (yukarıdaki örnekte bu ?operationName=GetPostsContainingString olacaktır).
@depends aracılığıyla yüklenecek ve çalıştırılacak operasyonları tanımlama
GraphQL belgesi birden fazla operasyon içerdiğinde, sunucuya hangisini çalıştıracağını ?operationName=... URL parametresi aracılığıyla belirtiriz; aksi takdirde son operasyon çalıştırılır.
Bu başlangıç operasyonundan itibaren sunucu, depends(on: [...]) direktifi eklenerek tanımlanan tüm çalıştırılacak operasyonları toplar ve bağımlılıklara saygı göstererek bunları karşılık gelen sırada çalıştırır.
Direktifin operations argümanı bir operasyon adları dizisi ([String]) alır ya da tek bir operasyon adı (String) da sağlayabiliriz.
Bu queries'te ?operationName=Four geçiririz ve çalıştırılan operasyonlar (query veya mutation) şunlar olur: ["One", "Two", "Three", "Four"]:
mutation One {
# Do something ...
}
mutation Two {
# Do something ...
}
query Three @depends(on: ["One", "Two"]) {
# Do something ...
}
query Four @depends(on: "Three") {
# Do something ...
}@export aracılığıyla queries arasında veri paylaşımı
@export direktifi, bir alanın (veya alan kümesinin) değerini dinamik bir değişkene aktarır; bu değişken başka bir queries'teki bir alana girdi olarak kullanılır.
Örneğin, bu queries'te oturum açmış kullanıcının adını dışa aktarır ve bu değeri söz konusu dizeyi içeren gönderileri aramak için kullanırız ($loggedInUserName değişkeninin dinamik olduğundan FindPosts operasyonunda tanımlanmasına gerek olmadığına dikkat edin):
query GetLoggedInUserName {
me {
name @export(as: "loggedInUserName")
}
}
query FindPosts @depends(on: "GetLoggedInUserName") {
posts(filter: { search: $loggedInUserName }) {
id
}
}Dinamik değişken çıktıları
@export şunların bir kombinasyonuna dayalı olarak 6 farklı çıktı üretebilir:
typeargümanının değeri (SINGLE,LISTveyaDICTIONARY)- Direktifin tek bir alana mı yoksa birden fazla alana mı uygulandığı (Multi-Field Directives modülü aracılığıyla)
6 olası çıktı şunlardır:
SINGLEtipi:- Tek alan
- Çok alan
LISTtipi:- Tek alan
- Çok alan
DICTIONARYtipi:- Tek alan
- Çok alan
SINGLE tipi / Tek alan
type: SINGLE parametresi geçirildiğinde çıktı tek bir değerdir (varsayılan değer olarak ayarlanmıştır).
Bu queries'te:
query {
post(by: { id: 1 }) {
title @export(as: "postTitle", type: SINGLE)
}
}...dinamik değişken $postTitle şu değere sahip olacaktır:
"Hello world!"SINGLE'ın bir varlık dizisine uygulandığında son varlığın değerinin dışa aktarıldığına dikkat edin.
Bu queries'te:
query {
posts(filter: { ids: [1, 5] }) {
title @export(as: "postTitle", type: SINGLE)
}
}...dinamik değişken $postTitle, ID'si 5 olan gönderinin değerine sahip olacaktır:
"Everything good?"SINGLE tipi / Çok alan
@export birden fazla alana uygulandığında (Multi-Field Directives modülünün sağladığı affectAdditionalFieldsUnderPos parametresi eklenerek), dinamik değişkende ayarlanan değer { key: alan takma adı, value: alan değeri } biçiminde bir sözlük olur (JSONObject türünde).
Bu queries:
query {
post(by: { id: 1 }) {
title
content
@export(
as: "postData",
type: SINGLE,
affectAdditionalFieldsUnderPos: [1]
)
}
}...$postData dinamik değişkenini şu değerle dışa aktarır:
{
"title": "Hello world!",
"content": "Lorem ipsum."
}LIST tipi / Tek alan
type: LIST parametresi geçirilerek dinamik değişken, sorgulanan tüm varlıklardan (çevreleyen alandan) alan değerini içeren bir dizi içerecektir.
Bu queries çalıştırıldığında (sorgulanan varlıkların ID'si 1 ve 5 olan gönderiler olduğu):
query {
posts(filter: { ids: [1, 5] }) {
title @export(as: "postTitles", type: LIST)
}
}...dinamik değişken $postTitles şu değere sahip olacaktır:
[
"Hello world!",
"Everything good?"
]LIST tipi / Çok alan
Direktifin uygulandığı alanların değerlerini içeren her biri bir sözlük olan bir dizi (JSONObject türünde) elde ederiz.
Bu queries:
query {
posts(filter: { ids: [1, 5] }) {
title
content
@export(
as: "postsData",
type: LIST,
affectAdditionalFieldsUnderPos: [1]
)
}
}...$postsData dinamik değişkenini şu değerle dışa aktarır:
[
{
"title": "Hello world!",
"content": "Lorem ipsum."
},
{
"title": "Everything good?",
"content": "Quisque convallis libero in sapien pharetra tincidunt."
}
]DICTIONARY tipi / Tek alan
type: DICTIONARY parametresi geçirilerek dinamik değişken, sorgulanan varlığın ID'sini anahtar ve alan değerlerini değer olarak içeren bir sözlük (JSONObject türünde) içerecektir.
Bu queries:
query {
posts(filter: { ids: [1, 5] }) {
title @export(as: "postIDTitles", type: DICTIONARY)
}
}...$postIDTitles dinamik değişkenini şu değerle dışa aktarır:
{
"1": "Hello world!",
"5": "Everything good?"
}DICTIONARY tipi / Çok alan
Bu kombinasyonda bir sözlük sözlüğü dışa aktarırız: { key: varlık ID'si, value: { key: alan takma adı, value: alan değeri } } (JSONObject türünde girişler içeren JSONObject türü kullanılarak).
Bu queries:
query {
posts(filter: { ids: [1, 5] }) {
title
content
@export(
as: "postsIDProperties",
type: DICTIONARY,
affectAdditionalFieldsUnderPos: [1]
)
}
}...$postsIDProperties dinamik değişkenini şu değerle dışa aktarır:
{
"1": {
"title": "Hello world!",
"content": "Lorem ipsum."
},
"5": {
"title": "Everything good?",
"content": "Quisque convallis libero in sapien pharetra tincidunt."
}
}Operasyonların koşullu çalıştırılması
Multiple Query Execution etkinleştirildiğinde @include ve @skip direktifleri operasyon direktifleri olarak da kullanılabilir ve bunlar bir operasyonu belirli bir koşulu sağlıyorsa koşullu olarak çalıştırmak için kullanılabilir.
Örneğin, bu queries'te CheckIfPostExists operasyonu dinamik bir değişken olan $postExists'i dışa aktarır ve yalnızca değeri true ise ExecuteOnlyIfPostExists mutation'ı çalıştırılır:
query CheckIfPostExists($id: ID!) {
# Initialize the dynamic variable to `false`
postExists: _echo(value: false) @export(as: "postExists")
post(by: { id: $id }) {
# Found the Post => Set dynamic variable to `true`
postExists: _echo(value: true) @export(as: "postExists")
}
}
mutation ExecuteOnlyIfPostExists
@depends(on: "CheckIfPostExists")
@include(if: $postExists)
{
# Do something...
}Bir dizi veya JSON nesnesi üzerinde yineleme yaparken değerleri dışa aktarma
@export, çevreleyen herhangi bir meta-direktifin kardinalitesine saygı gösterir.
Özellikle, @export dizi öğeleri veya JSON nesnesi özellikleri üzerinde yineleme yapan bir meta-direktifin altına yerleştirildiğinde (yani @underEachArrayItem ve @underEachJSONObjectProperty), dışa aktarılan değer bir dizi olacaktır.
Bu queries:
{
post(by: { id: 19 }) {
coreContentAttributeBlocks: blockFlattenedDataItems(
filterBy: { include: "core/heading" }
)
@underEachArrayItem
@underJSONObjectProperty(
by: { path: "attributes.content" },
)
@export(
as: "contentAttributes",
)
}
}...$contentAttributes'u şu değerle üretir:
[
"List Block",
"Columns Block",
"Columns inside Columns (nested inner blocks)",
"Life is so rich",
"Life is so dynamic"
]Buna karşın, tüm öğeler üzerinde yineleme yapmak yerine dizideki belirli bir öğeye erişen aynı queries (@underEachArrayItem'ı @underArrayItem(index: 0) ile değiştirerek) tek bir değer dışa aktarır.
Bu queries:
{
post(by: { id: 19 }) {
coreContentAttributeBlocks: blockFlattenedDataItems(
filterBy: { include: "core/heading" }
)
@underArrayItem(index: 0)
@underJSONObjectProperty(
by: { path: "attributes.content" },
)
@export(
as: "contentAttributes",
)
}
}...$contentAttributes'u şu değerle üretir:
"List Block"Direktif çalıştırma sırası
@export'tan önce başka direktifler varsa, dışa aktarılan değer bu önceki direktiflerin değişikliklerini yansıtacaktır.
Örneğin, bu queries'te @export'un @strUpperCase'den önce mi sonra mı gerçekleştiğine bağlı olarak sonuç farklı olacaktır:
query One {
id
# First export "root", only then will be converted to "ROOT"
@export(as: "id")
@strUpperCase
again: id
# First convert to "ROOT" and then export this value
@strUpperCase
@export(as: "again")
}
query Two @depends(on: "One") {
mirrorID: _echo(value: $id)
mirrorAgain: _echo(value: $again)
}Şunu üretir:
{
"data": {
"id": "ROOT",
"again": "ROOT",
"mirrorID": "root",
"mirrorAgain": "ROOT"
}
}Multi-Field Directives
Multi-Field Directives özelliği etkinleştirildiğinde ve birden fazla alanın değerini bir sözlüğe aktardığımızda, alan değerini dışa aktarmadan önce ilgili tüm alanlardan tüm direktiflerin çalıştırıldığından emin olmak için @export yerine @deferredExport kullanın.
Örneğin, bu queries'te birinci alana @strUpperCase direktifi uygulanmış, ikinciye ise @titleCase uygulanmıştır. @deferredExport çalıştırıldığında dışa aktarılan değer bu direktiflerin uygulanmış halini içerecektir:
query One {
id @strUpperCase # Will be exported as "ROOT"
again: id @titleCase # Will be exported as "Root"
@deferredExport(as: "props", affectAdditionalFieldsUnderPos: [1])
}
query Two @depends(on: "One") {
mirrorProps: _echo(value: $props)
}Şunu üretir:
{
"data": {
"id": "ROOT",
"again": "Root",
"mirrorProps": {
"id": "ROOT",
"again": "Root"
}
}
}GraphQL spec
Bu işlevsellik şu anda GraphQL spec'in bir parçası değildir, ancak talep edilmiştir: