Exclude posts already displayed

I want to display a list of max 3 posts from a certain tag on the homepage (index.hbs) but exclude any posts previously displayed in the main stream of posts.

I am trying to replicate what is described here:

No posts are being displayed when using the exact code above with index.bhs. It works as expected in post.hbs. Have there been any changes with Ghost 2 that prevent the use of this like described?

1 Like

Nothing has changed in 2.0 that breaks this behaviour and it is still working as expected for me.

I would suggest that where it has gone wrong is to do with ensuring that the path used to access IDs is not correct relative to where you’re calling the code in your theme. {{posts[*].id}} and {{post.id}} are relative paths which are navigating the JSON data associated with the template, so if you’re inside either {{#foreach posts}} or {{#post}} or inside any other block, you’d need to adjust the path to work correctly.

If you share some theme code we can help you adjust the paths.

1 Like

When I implement the code referenced on a single post, it seems to work fine. It is just on the front page of the blog that I do not get the expected results. I first get all featured posts from category X, next I get all posts from category X but skipping the previously loaded post from the first loop.

Here is the code itself:

{{!-- Featured --}}
{{#get "posts" filter="featured:true+tag:[frontpage]" include="authors,tags" limit="1"}}
    {{#foreach posts}}

        <section class="first-story relative flex">
          <section class="featured flex">

            {{#if feature_image}}
                <figure class="featured-image full-width">
                  <img src="{{feature_image}}" />
                </figure>
            {{/if}}

            <section class="fluid-container flex">
                <div class="headline center">
                    {{#if primary_tag}}
                        <span class="post-card-tags">{{primary_tag.name}}</span>
                    {{/if}}
                    <span class="reading-time">{{reading_time}}</span>
                    <h1><a href="{{url}}">{{title}}</a></h1>
                    <p>{{excerpt}}</p>
                </div>
            </section>

          </section>
        </section>

    {{/foreach}}
{{/get}}

<aside class="second-story">
    <section class="fluid-container grid flex">
        
        {{#get "posts" filter="tag:[frontpage]+id:-[{{posts[*].id}}]" include="authors,tags" limit="3"}}
        {{#foreach posts}}
            <section class="grid-box">
              <div class="ratio-container"><a href="{{url}}">
                {{#if feature_image}}
                    <figure class="secondary-image">
                        <img src="{{feature_image}}" />
                    </figure>
                {{/if}}
                <div class="ratio-item flex">
                    <h1>{{title}}</h1>
                </div> 
              </a></div>
            </section>
        {{/foreach}}
        {{/get}}

    </section>
</aside>
1 Like

Every template you work with in Ghost, e.g. post.hbs or index.hbs has an associated JSON “blob” - a bunch of data that we prefetch for you.

There is a context table in the docs that shows you for each potential URL what template is used by default and what the structure of the associated JSON blob looks like.

In addition to that blob, you can fetch any data you like using the get helper, as you are doing.

You still need to be aware of the existing JSON blob so that you can understand namespaces of data, and how to access it.

Inside {{#get "posts"...}}...{{/get}}, the results from that data fetch are referred to by default with the key posts, unless you explicitly provide it with a different name.

On index.hbs, that naming clashes with the default posts list.

In the code you’ve provided, posts[*] in the second {{#get}} block is referring to the default list of posts, not the list of posts fetched by the first request.

1 Like

Thanks for the reply!

After reading your comment I assumed then that if I defined a new name, like “featured”, in the first {{#get}} block:

{{#get "posts" filter="featured:true+tag:[frontpage]" include="authors,tags" limit="1" as |featuredpost|}}

I would be able to exclude the post from that query with featured[*].

{{#get "posts" filter="tag:[frontpage]+id:-[{{featuredpost[*].id}}]" include="authors,tags" limit="3"}}

But I am clearly still misunderstanding… :confused:

The blocks are scope, the new “featured” object won’t exist outside of the {{/get}} tag at the end of the block.

1 Like

Yes, right, I should have remembered that!

This works fine now as long as the second {{#get}} is nested within the first {{#get}}.

{{#get "posts" filter="featured:true+tag:[frontpage]" limit="1"}}
{{#foreach posts}}
	*Do stuff*
{{/foreach}}

	{{#get "posts" filter="tag:[frontpage]+id:-[{{posts[*].id}}]" limit="3"}}
	{{#foreach posts}}
		*Do more stuff*
	{{/foreach}}
	{{/get}}

{{/get}}

Thank you for the help and patience :smiley:

1 Like

Can two id filters be combined - for example if I wanted to do id:-[{{posts[*].id}}] and id:-{{id}} both at once? I tried all obvious combinations and none seems to work.

Is this still possible in v5? I seem to get an “Error parsing filter” message.