<p>Currently, it can't be done out of the box, since it's currently not possible to search the related entries like this.</p>
<p>However, you can extend GraphQL to pull this off. Ideally, it would be possible to add arguments to existing queries, but that won't be possible until Craft 4.0, so you'll have to add an entirely new query.</p>
<p>So, add a custom query and register it. It should in essence be an almost facsimile of <code>craft\gql\queries\Entry</code>, with the addition of an additional argument. Let's pretend you named it <code>searchRelated</code>.</p>
<p>Now also create an argument handler and register it. You would set the <code>$argumentName</code> value to match the name you picked above and your <code>handleArgument</code> method would do the search and return the matching element ids in the form of <code>[[$ids]]</code>. The double-array is required because the method is expected to return a list of id sets for more complex matches (for example, <code>relatedAssets: [{criteriaSet1}, {criteriaSet2}]</code>). This is not relevant to your specific case, however, it does enforce a certain syntax that has to be adhered to.</p>
<p>Anyway, if all is done correctly, you should be able to use your new query to fetch the results you want.</p>
<p>Some relevant reading on extending GraphQL:</p>
<ul>
<li><a href="https://craftcms.com/docs/3.x/extend/graphql.html#queries" rel="nofollow noreferrer">https://craftcms.com/docs/3.x/extend/graphql.html#queries</a></li>
<li><a href="https://craftcms.com/docs/3.x/extend/graphql.html#example-argument-handler-class" rel="nofollow noreferrer">https://craftcms.com/docs/3.x/extend/graphql.html#example-argument-handler-class</a></li>
</ul>

CC BY-SA 4.0
2021-08-25T18:31:08.250Z
Thank you so much - this will be very helpful! Question: So I don't actually need a custom resolver for this? Just a custom argument handler?
Eric
CC BY-SA 4.0
2021-08-26T06:31:02.903Z
Just a custom argument handler. The ids you return (provided you extend the `RelationArgumentHandler` class) are going to get included in the `relatedTo` argument.

So, run your search based on the argument value. That will get you entry ids. Since Craft doesn't have the concept of reverse relations, you'll have to do another search to find the Matrix Blocks that are related to the entries with the ids you have.

After that, you should return the ids of all the Matrix Blocks and, as they get rolled in the `relatedTo` argument, you get a list of the entries related to the Matrix Blocks.
Andris Sevcenko
CC BY-SA 4.0
2021-08-26T18:11:01.553Z
That makes sense, I feel like I am close. I have the entry IDs being returned based on my search:

nestedEntries(searchRelated: [{search: "*out*"}]) { ... }

I'm not sure where to look to find the function for the subsequent query though. Can you point me in the direction of where I can find the query that will allow me to "find the Matrix Blocks that are related to the entries with the ids you have"?
Eric
CC BY-SA 4.0
2021-08-27T11:43:02.350Z
Something like `$relatedIds = MatrixBlock::find()->select('ownerId')->column();` should return what you're after. (`MatrixBlock` should be an alias of `\craft\elements\MatrixBlock`).
Andris Sevcenko
CC BY-SA 4.0
2021-08-27T18:43:13.383Z
Thank you so much for your help thus far Andris, I added the line you mentioned and I am getting back another set of IDs, however, no results appear as a result of the query. I added the relevant code to the original question for posterity and also for a complete look at how to accomplish this. Something still isn't right and I'm not sure what it is. One thing I had to add that you didn't mention is a custom argument class, not sure if that's causing the issue although I can't imagine it would. Thoughts?
Eric
CC BY-SA 4.0
2021-08-30T08:59:06.047Z
My apologies. I had both over-analyzed and under-analyzed what the argument handler should accomplish here. 

It still extends the base relational argument handler class, since we want the built-in capability of resolving nested relational arguments there, but the `handleArgument` method already returns the specific element `ids` we're after.

I rewrote the class to fit the purpose and tested it. You can find it here: I rewrote that one file here: https://gist.github.com/andris-sevcenko/2245ca93e8adc91a3752a82d5b345050
Andris Sevcenko
CC BY-SA 4.0
2021-08-31T14:14:37.060Z
No worries at all - those code as you provided works perfectly. I cannot thank you enough! I plan on publishing this as a plugin on the Craft CMS store so others can make use of it as well!
Eric
CC BY-SA 4.0
2021-08-25T13:58:07.403Z
2021-08-25T13:58:07.403Z
Andris Sevcenko
<p>So I have a fairly complex section structure that (on an Entry Type) has a matrix field that contains entries as fields(sub-entry) within a blocktype.</p>
<p>I'd like to write a GraphQL query such that when text is matched on a field in the sub-entry on the matrix blocktype to return the entire section entry. I have thus far only been able to return matches on the title of the sub-entry. When I search for text on a custom field within the sub-entry nothing is returned. I <em>do</em> have the <code>Use this field’s values as search keywords</code> option checked on the matrix field of the main entry, and the custom fields on the sub-entry checked as well. I also went poking around the database in the <code>searchindex</code> table and I can see the field values/keywords in there for the sub-entry I created.</p>
<p>Here is the query I've been playing around with:</p>
<pre><code>fragment tour on tours_tour_Entry {
  title
  ... on tours_tour_Entry {
    title
    tourVariety
    duration
    complexity
    thumbnail {
      url
    }
    backgroundImage {
      url
    }
    introHeading
    introSubheading
    introContentBlocks {
      ... on introContentBlocks_introBlock_BlockType {
        body
      }
    }
    factsHeading
    factsContentBlocks {
      ... on factsContentBlocks_factsContentBlock_BlockType {
        body
      }
    }
    tourPois {
      ... on tourPois_tourPoi_BlockType {
        title
        description
        poi {
          title
          ... on pois_poi_Entry {
            fov
            description
            astroObjects {
              ... on astroObjects_astroObject_Entry {
                ra
                dec
                image {
                  url
                }
                characteristics
              }
            }
          }
        }
      }
    }
  }
}

fragment poi on pois_poi_Entry {
  fov
  description
  astroObjects {
    ... on astroObjects_astroObject_Entry {
      ra
      dec
      image {
        url
      }
      characteristics
    }
  }
}

fragment astroObject on astroObjects_astroObject_Entry {
  ra
  dec
  image {
    url
  }
  characteristics
}

{
  entries(search: &quot;*desc* OR *char*&quot;) {
    ...tour
    ...poi
    ...astroObject
  }
}
</code></pre>
<p>When I perform this search, the fragments for &quot;poi&quot; and &quot;astroObject&quot; return results, but not &quot;tour&quot;. What am I doing wrong in the query or in the &quot;tour&quot; fragment&quot;? Is this even possible?</p>
<p><strong>Edit based on Andris's answer:</strong></p>
<p>(Including all the relevant code for posterity)</p>
<p>Okay, so I've created a custom plugin that extends the relevant classes and I'm still not able to get entries as results - which is odd considering that I am getting IDs back from my custom query.</p>
<p>Plugin.php</p>
<pre class="lang-php prettyprint-override"><code>&lt;?php
namespace edr;

use Craft;
use edr\queries\EntryAsField;
use edr\arguments\EntryAsFieldArgumentHandler;
use craft\events\RegisterGqlQueriesEvent;
use craft\services\Gql;
use yii\base\Event;
use craft\gql\ArgumentManager;
use craft\events\RegisterGqlArgumentHandlersEvent;

class Plugin extends \craft\base\Plugin {
    public static $plugin;

    public $hasCpSettings = true;

    public function init() {
        parent::init();
        self::$plugin = $this;

        $this-&gt;registerArgumentHandler();
        $this-&gt;registerQuery();
        
    }

    protected function createSettingsModel() {
        return new \edr\models\Settings();
    }

    protected function registerArgumentHandler() {
        Event::on(
            ArgumentManager::class,
            ArgumentManager::EVENT_DEFINE_GQL_ARGUMENT_HANDLERS,
            function(RegisterGqlArgumentHandlersEvent $event) {
                $event-&gt;handlers[&quot;searchRelated&quot;] = EntryAsFieldArgumentHandler::class;
            }
        );
    }

    protected function settingsHtml() {
        return \Craft::$app-&gt;getView()-&gt;renderTemplate(
            'edr-nested-entry-query/settings',
            [ 'settings' =&gt; $this-&gt;getSettings() ]
        );
    }

    public function registerQuery() {
        Event::on(
            Gql::class,
            Gql::EVENT_REGISTER_GQL_QUERIES,
            function(RegisterGqlQueriesEvent $event) {                
                $event-&gt;queries['nestedEntries'] = EntryAsField::getQueries();
            }
        );
    }
}
</code></pre>
<p>EntryAsField.php</p>
<pre class="lang-php prettyprint-override"><code>&lt;?php

namespace edr\queries;

use edr\arguments\EntryAsFieldArguments;
use craft\gql\arguments\elements\Entry as EntryArguments;
use craft\gql\base\Query;
use craft\gql\interfaces\elements\Entry as EntryInterface;
use craft\gql\resolvers\elements\Entry as EntryResolver;
use craft\helpers\Gql as GqlHelper;
use GraphQL\Type\Definition\Type;

class EntryAsField extends \craft\gql\base\Query
{
    public static function getQueries($checkToken = true): array
    {
        if ($checkToken &amp;&amp; !GqlHelper::canQueryEntries()) {
            Craft::info(&quot;platypus&quot;);
            return [];
        }

        return [
                'type' =&gt; Type::listOf(EntryInterface::getType()),
                'args' =&gt; EntryAsFieldArguments::getArguments(),
                'resolve' =&gt; EntryResolver::class . '::resolve',
                'description' =&gt; 'This query is used to query for entries.',
                'complexity' =&gt; GqlHelper::relatedArgumentComplexity(),
        ];
    }
}
</code></pre>
<p>EntryAsFieldArguments.php</p>
<pre class="lang-php prettyprint-override"><code>&lt;?php

namespace edr\arguments;

use GraphQL\Type\Definition\Type;
use craft\gql\types\QueryArgument;
use craft\gql\types\input\criteria\Entry;

class EntryAsFieldArguments extends \craft\gql\base\ElementArguments
{
    public static function getArguments(): array
    {
        return array_merge(parent::getArguments(), self::getContentArguments(), [
            'searchRelated' =&gt; [
                'name' =&gt; 'searchRelated',
                'type' =&gt; Type::listOf(Entry::getType()),
                'description' =&gt; 'Matches search on entries-as-fields and returns the whole entry.'
            ],
        ]);
    }
}
</code></pre>
<p>EntryAsFieldArgumentHandler.php</p>
<pre class="lang-php prettyprint-override"><code>&lt;?php

namespace edr\arguments;

use Craft;
use craft\elements\Entry;
use craft\gql\base\RelationArgumentHandler;
use craft\elements\MatrixBlock;

class EntryAsFieldArgumentHandler extends RelationArgumentHandler
{
    protected $argumentName = 'searchRelated';

    protected function handleArgument($argumentValue)
    {
        Craft::info(&quot;bobcat&quot;);
        $argumentValue = parent::handleArgument($argumentValue);
        $ids = $this-&gt;getIds(Entry::class, $argumentValue);
        $relatedIds = MatrixBlock::find()-&gt;select('ownerId')-&gt;column();
        if(count($relatedIds) &gt; 0) { // dedupe and stuff in 2d array
            $relatedIds = [array_unique($relatedIds)];
        }
        Craft::info(&quot;nested-entries-graphql-queries - DEBUG - Returned IDs from 1st query&quot;);
        Craft::info($ids);
        Craft::info(&quot;nested-entries-graphql-queries - DEBUG - Returned IDs from 2nd queryz&quot;);
        Craft::info($relatedIds);
        return $relatedIds;
    }
}
</code></pre>
<p>In the argument handler above, the first query to getIDs() returns IDs fine. The MatrixBlock query to get the ownerIDs also returns IDs fine and I return these IDs as a 2d array. I get no results from this query though - am I missing something?</p>
<p>Query:</p>
<pre><code>{
  nestedEntries(searchRelated: [{search: &quot;*out*&quot;}]) {
    id
    title
  }
}
</code></pre>

<entries><entries-field><graphql><entry-query>
How do I write a GraphQL query to search through sub-entries/entries as fields and return the whole entry?
# How do I write a GraphQL query to search through sub-entries/entries as fields and return the whole entry?

(Asked by: username_0 on 2021-07-14)

So I have a fairly complex section structure that (on an Entry Type) has a matrix field that contains entries as fields(sub-entry) within a blocktype.
I'd like to write a GraphQL query such that when text is matched on a field in the sub-entry on the matrix blocktype to return the entire section entry. I have thus far only been able to return matches on the title of the sub-entry. When I search for text on a custom field within the sub-entry nothing is returned. I do have the Use this field’s values as search keywords option checked on the matrix field of the main entry, and the custom fields on the sub-entry checked as well. I also went poking around the database in the searchindex table and I can see the field values/keywords in there for the sub-entry I created.
Here is the query I've been playing around with:
fragment tour on tours_tour_Entry {
  title
  ... on tours_tour_Entry {
    title
    tourVariety
    duration
    complexity
    thumbnail {
      url
    }
    backgroundImage {
      url
    }
    introHeading
    introSubheading
    introContentBlocks {
      ... on introContentBlocks_introBlock_BlockType {
        body
      }
    }
    factsHeading
    factsContentBlocks {
      ... on factsContentBlocks_factsContentBlock_BlockType {
        body
      }
    }
    tourPois {
      ... on tourPois_tourPoi_BlockType {
        title
        description
        poi {
          title
          ... on pois_poi_Entry {
            fov
            description
            astroObjects {
              ... on astroObjects_astroObject_Entry {
                ra
                dec
                image {
                  url
                }
                characteristics
              }
            }
          }
        }
      }
    }
  }
}

fragment poi on pois_poi_Entry {
  fov
  description
  astroObjects {
    ... on astroObjects_astroObject_Entry {
      ra
      dec
      image {
        url
      }
      characteristics
    }
  }
}

fragment astroObject on astroObjects_astroObject_Entry {
  ra
  dec
  image {
    url
  }
  characteristics
}

{
  entries(search: "*desc* OR *char*") {
    ...tour
    ...poi
    ...astroObject
  }
}

When I perform this search, the fragments for "poi" and "astroObject" return results, but not "tour". What am I doing wrong in the query or in the "tour" fragment"? Is this even possible?
Edit based on Andris's answer:
(Including all the relevant code for posterity)
Okay, so I've created a custom plugin that extends the relevant classes and I'm still not able to get entries as results - which is odd considering that I am getting IDs back from my custom query.
Plugin.php
registerArgumentHandler();
        $this->registerQuery();
        
    }

    protected function createSettingsModel() {
        return new \edr\models\Settings();
    }

    protected function registerArgumentHandler() {
        Event::on(
            ArgumentManager::class,
            ArgumentManager::EVENT_DEFINE_GQL_ARGUMENT_HANDLERS,
            function(RegisterGqlArgumentHandlersEvent $event) {
                $event->handlers["searchRelated"] = EntryAsFieldArgumentHandler::class;
            }
        );
    }

    protected function settingsHtml() {
        return \Craft::$app->getView()->renderTemplate(
            'edr-nested-entry-query/settings',
            [ 'settings' => $this->getSettings() ]
        );
    }

    public function registerQuery() {
        Event::on(
            Gql::class,
            Gql::EVENT_REGISTER_GQL_QUERIES,
            function(RegisterGqlQueriesEvent $event) {                
                $event->queries['nestedEntries'] = EntryAsField::getQueries();
            }
        );
    }
}

EntryAsField.php
 Type::listOf(EntryInterface::getType()),
                'args' => EntryAsFieldArguments::getArguments(),
                'resolve' => EntryResolver::class . '::resolve',
                'description' => 'This query is used to query for entries.',
                'complexity' => GqlHelper::relatedArgumentComplexity(),
        ];
    }
}

EntryAsFieldArguments.php
 [
                'name' => 'searchRelated',
                'type' => Type::listOf(Entry::getType()),
                'description' => 'Matches search on entries-as-fields and returns the whole entry.'
            ],
        ]);
    }
}

EntryAsFieldArgumentHandler.php
getIds(Entry::class, $argumentValue);
        $relatedIds = MatrixBlock::find()->select('ownerId')->column();
        if(count($relatedIds) > 0) { // dedupe and stuff in 2d array
            $relatedIds = [array_unique($relatedIds)];
        }
        Craft::info("nested-entries-graphql-queries - DEBUG - Returned IDs from 1st query");
        Craft::info($ids);
        Craft::info("nested-entries-graphql-queries - DEBUG - Returned IDs from 2nd queryz");
        Craft::info($relatedIds);
        return $relatedIds;
    }
}

In the argument handler above, the first query to getIDs() returns IDs fine. The MatrixBlock query to get the ownerIDs also returns IDs fine and I return these IDs as a 2d array. I get no results from this query though - am I missing something?
Query:
{
  nestedEntries(searchRelated: [{search: "*out*"}]) {
    id
    title
  }
}

**Tags:** entries, entries-field, graphql, entry-query

---

## 1 answers

### Answer by username_1 on 2021-08-25. Score: 2 | Accepted answer

Currently, it can't be done out of the box, since it's currently not possible to search the related entries like this.

However, you can extend GraphQL to pull this off. Ideally, it would be possible to add arguments to existing queries, but that won't be possible until Craft 4.0, so you'll have to add an entirely new query.

So, add a custom query and register it. It should in essence be an almost facsimile of `craft\gql\queries\Entry`, with the addition of an additional argument. Let's pretend you named it `searchRelated`.

Now also create an argument handler and register it. You would set the `$argumentName` value to match the name you picked above and your `handleArgument` method would do the search and return the matching element ids in the form of `[[$ids]]`. The double-array is required because the method is expected to return a list of id sets for more complex matches (for example, `relatedAssets: [{criteriaSet1}, {criteriaSet2}]`). This is not relevant to your specific case, however, it does enforce a certain syntax that has to be adhered to.

Anyway, if all is done correctly, you should be able to use your new query to fetch the results you want.

Some relevant reading on extending GraphQL:

* <https://craftcms.com/docs/3.x/extend/graphql.html#queries>
* <https://craftcms.com/docs/3.x/extend/graphql.html#example-argument-handler-class>

> **Comments:**
> **username_0**: Thank you so much - this will be very helpful! Question: So I don't actually need a custom resolver for this? Just a custom argument handler?  
> **username_1**: Just a custom argument handler. The ids you return (provided you extend the `RelationArgumentHandler` class) are going to get included in the `relatedTo` argument.  So, run your search based on the argument value. That will get you entry ids. Since Craft doesn't have the concept of reverse relations, you'll have to do another search to find the Matrix Blocks that are related to the entries with the ids you have.  After that, you should return the ids of all the Matrix Blocks and, as they get rolled in the `relatedTo` argument, you get a list of the entries related to the Matrix Blocks.  
> **username_0**: That makes sense, I feel like I am close. I have the entry IDs being returned based on my search:  nestedEntries(searchRelated: [{search: "*out*"}]) { ... }  I'm not sure where to look to find the function for the subsequent query though. Can you point me in the direction of where I can find the query that will allow me to "find the Matrix Blocks that are related to the entries with the ids you have"?  
> **username_1**: Something like `$relatedIds = MatrixBlock::find()->select('ownerId')->column();` should return what you're after. (`MatrixBlock` should be an alias of `\craft\elements\MatrixBlock`).  
> **username_0**: Thank you so much for your help thus far Andris, I added the line you mentioned and I am getting back another set of IDs, however, no results appear as a result of the query. I added the relevant code to the original question for posterity and also for a complete look at how to accomplish this. Something still isn't right and I'm not sure what it is. One thing I had to add that you didn't mention is a custom argument class, not sure if that's causing the issue although I can't imagine it would. Thoughts?  
> **username_1**: My apologies. I had both over-analyzed and under-analyzed what the argument handler should accomplish here.   It still extends the base relational argument handler class, since we want the built-in capability of resolving nested relational arguments there, but the `handleArgument` method already returns the specific element `ids` we're after.  I rewrote the class to fit the purpose and tested it. You can find it here: I rewrote that one file here: https://gist.github.com/andris-sevcenko/2245ca93e8adc91a3752a82d5b345050  
> **username_0**: No worries at all - those code as you provided works perfectly. I cannot thank you enough! I plan on publishing this as a plugin on the Craft CMS store so others can make use of it as well!  

---

