Potential reasons for receiving duplicate records in a query and how to debug the issue.

6 replies
Last updated: May 7, 2021
For what reason could I be potentially receiving duplicate records in my query like so
0:Object
_createdAt:"2021-04-21T16:35:41Z"
_type:"page"
_updatedAt:"2021-05-07T20:10:51Z"
pageId:"4b4a7270-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
routeLabel:"testing dynamic"
slug:"sub/directory"
1:Object
_createdAt:"2021-04-21T16:35:41Z"
_type:"page"
_updatedAt:"2021-05-07T20:10:51Z"
pageId:"4b4a7270--xxxx-xxxx-xxxx-xxxxxxxxxxxx"
routeLabel:"testing dynamic"
slug:"sub/directory"
If my query looks like this.

*[_type == "route" && slug_custom_format.current != "index"]{
    routeLabel,
    "slug": slug_custom_format.current,
    ...page->{
      _createdAt,
      "pageId": _id,
      _type,
      _updatedAt,
    } 
  } | order(_updatedAt desc)
A bit stumped as to why this is happening. There are no drafts of the document.
AI Update

Based on your query structure, you're likely seeing duplicate records because of how the spread operator (...page->) works with references and the raw perspective.

The issue is that when you query *[_type == "route"] and then spread properties from a referenced page document, if that page has both a published version and a draft version (even if you don't see drafts in the Studio), your query might be returning the same route multiple times - once for each state of the referenced page document.

Here's what's happening:

  1. Your query fetches all route documents
  2. For each route, it follows the reference to page and spreads its properties
  3. If the referenced page exists in multiple states (draft and published), the query may be evaluating differently for each state

Solutions:

Option 1: Use the published perspective (recommended for production)

Add a perspective parameter to your API request to only return published documents:

// In your client configuration or query
const query = `*[_type == "route" && slug_custom_format.current != "index"]{
    routeLabel,
    "slug": slug_custom_format.current,
    ...page->{
      _createdAt,
      "pageId": _id,
      _type,
      _updatedAt,
    } 
  } | order(_updatedAt desc)`

// Fetch with published perspective
client.fetch(query, {}, { perspective: 'published' })

Option 2: Filter out draft references in your GROQ query

Explicitly exclude drafts in your query:

*[_type == "route" && slug_custom_format.current != "index" && !(_id in path("drafts.**"))]{
    routeLabel,
    "slug": slug_custom_format.current,
    ...page->{
      _createdAt,
      "pageId": _id,
      _type,
      _updatedAt,
    } 
  } | order(_updatedAt desc)

Option 3: Use defined() to ensure valid references

Sometimes references can cause duplication if they're undefined:

*[_type == "route" && slug_custom_format.current != "index" && defined(page)]{
    routeLabel,
    "slug": slug_custom_format.current,
    ...page->{
      _createdAt,
      "pageId": _id,
      _type,
      _updatedAt,
    } 
  } | order(_updatedAt desc)

The perspectives documentation explains how different perspectives affect query results. Even if you don't see drafts in the Studio, they may exist in your Content Lake, and the default raw perspective returns both states.

For production environments, always use the published perspective to avoid these kinds of duplicate results.

*[_type == "route" && slug_custom_format.current != "index" && !(_id in path('drafts.**'))] { ... }
If that still returns two results, then you must have two routes that are matching and reference the same page. For debugging, it’s probably best that you return the
_id
of the route.
*[_type == "route" && slug_custom_format.current != "index" && !(_id in path('drafts.**'))] { ... }
If that still returns two results, then you must have two routes that are matching and must reference the same page. For debugging, it’s probably best that you return the
_id
of the route.
Ahh! Thanks for the perspective Geoff,I hid that route _id because I didn't need it, and turns out there is a draft _id there.
This leads me to some interesting questions. Mostly about the structuring I am using.

For context I am messing around with a previewing in Sanity with a non-react front-end. So I do in fact want access to the draft documents.

I think I am running into a circular references issue here though as my larger issue.
Ran into some weird things when trying to preview
1. 1-2 ->This is where the draft is, but I don't remember changing it so attempt to revert to published
3. Says it's successful restoring the document
4. Publish and get this message.
5. Culprit
Great! Glad you found that.
There were a few days when
v2.8.0
was the latest version where drafts could be referenced. It was quickly fixed , but unfortunately any references created during that time that pointed to a draft would need to be corrected manually. Based on the error you’re getting, I suspect that’s what’s happening.
Yep that's definitely what it was. Fixing it manually seemed to do the trick.Just a matter of some quick clicks and publishes to get it back.
Thanks again for the insight.
Yep that's definitely what it was. Fixing it manually seemed to do the trick.Just a matter of some quick clicks and publishes to get it back.
Thanks again for the insight.

Sanity – Build the way you think, not the way your CMS thinks

Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.

Was this answer helpful?