Content API error filtering by excerpt or meta_description

Hello,

I am using Ghost (v5.89.0) as a headless CMS on a ReactJS/NextJS website.
Everything is working fine, and I am now developing a search view on the website, but I can’t seem to be able to make the browse posts function to filter by ‘excerpt’ or ‘meta_description’ fields.
It works if I filter for the ‘title’ field but it doesn’t work for the other two.

What are the supported fields on the Content API’s filter function? According to the docs it should be able to filter by any field, right?

Here is my example (anonymized of course)

request:
https://mysite.com/ghost/api/content/posts/?key=xxxxxxxx&fields=slug%2C%20id%2C%20title%2C%20meta_description%2C%20excerpt%2C%20uuid%2C%20feature_image&limit=60&include=tags&page=1&filter=excerpt%3A~'house'

returns

{
	"errors": [
		{
			"message": "Request not understood error, cannot list posts.",
			"context": "Could not understand request.",
			"type": "BadRequestError",
			"details": null,
			"property": null,
			"help": null,
			"code": "ER_BAD_FIELD_ERROR",
			"id": "de632520-5c85-11ef-87ef-857d4a641cd6",
			"ghostErrorCode": null
		}
	]
}

Although I don’t think it’s that important here is my code

export async function searchPosts(query: string) {
  
  return await api.posts
    .browse({
      fields: 'slug, id, title, excerpt, uuid, feature_image',
      limit: config.api.ghost.settings.items_per_page * 2,
      include: 'tags',
      page: 1,
      filter: `excerpt:~'${query}'`
    })
    .catch(err => {
      console.error(err);
    });
}

Can you please help? Am I missing something?

Thank you. Best regards

You can only filter custom_excerpt.
This is because Ghost only stores custom excerpts, while the default excerpt is generated from the original post content and Ghost doesn’t store it.

Thank you for the answer Raki. Although I am not using custom_excerpt to put any searchable content.

What are the other fields that are filterable? I have also tried meta_description but it doesn’t look like it’s filterable.

If you want to search post content, you can filter the plaintext field, which contains the post’s text content.

It looks like there might be a bug when filtering on certain fields. Internally, Ghost has a posts table, and a posts_meta table. The posts_meta table was added a few years ago because of column width limitations on the posts table.

When filtering a native column, the filter is properly translated (e.g. %ghost%). but when filtering a joined column, the filter is not translated, (e.g. /this/i).

# Filter: filter=html:~%27Ghost%27
# Result: 7 posts

knex:query select id,uuid,title,slug,html,comment_id,plaintext,feature_image,featured,type,status,locale,visibility,email_recipient_filter,created_at,created_by,updated_at,updated_by,published_at,published_by,custom_excerpt,codeinjection_head,codeinjection_foot,custom_template,canonical_url,newsletter_id,show_title_and_feature_image from `posts` where (`posts`.`status` = ? and (lower(`posts`.`html`) like ? ESCAPE ? and `posts`.`type` = ?)) order by `posts`.`published_at` DESC limit ? undefined
knex:bindings [ 'published', '%ghost%', '*', 'post', 15 ] undefined

# Filter: filter=meta_description:~%27this%27
# Result: 0 posts
# Expected: 1 post

knex:query select id,uuid,title,slug,html,comment_id,plaintext,feature_image,featured,type,status,locale,visibility,email_recipient_filter,created_at,created_by,updated_at,updated_by,published_at,published_by,custom_excerpt,codeinjection_head,codeinjection_foot,custom_template,canonical_url,newsletter_id,show_title_and_feature_image from `posts` where (`posts`.`status` = ? and (`posts`.`type` = ? and `posts`.`id` in (select `posts`.`id` from `posts` left join `posts_meta` on `posts_meta`.`post_id` = `posts`.`id` where `posts_meta`.`meta_description` like ?))) order by `posts`.`published_at` DESC limit ? undefined
knex:bindings [ 'published', 'post', /this/i, 15 ] undefined
2 Likes

Thank you both for the help.

I went with the plaintext option. It’s working now.

Best regards!

FYI I created a pull request to fix this:

2 Likes