fix: deep querying, handle getLocalizedPaths for blocks (#10187)

Fixes https://github.com/payloadcms/payload/issues/10126

Properly handles the `getLocalizedPaths` function for blocks.

Previously, using collection with this config:
```
{
  slug: 'deep-nested',
  fields: [
    {
      type: 'tabs',
      tabs: [
        {
          name: 'content',
          fields: [
            {
              type: 'blocks',
              name: 'blocks',
              blocks: [
                {
                  slug: 'testBlock',
                  fields: [
                    {
                      type: 'tabs',
                      tabs: [
                        {
                          name: 'meta',
                          fields: [
                            {
                              type: 'relationship',
                              relationTo: 'movies',
                              name: 'movie',
                            },
                          ],
                        },
                      ],
                    },
                  ],
                },
              ],
            },
          ],
        },
      ],
    },
  ],
},

```
The following query didn't work in MongoDB:
```ts
const result = await payload.find({
  collection: 'deep-nested',
  where: {
    'content.blocks.meta.movie': {
      equals: movie.id,
    },
  },
})
```
This commit is contained in:
Sasha
2024-12-27 16:46:09 +02:00
committed by GitHub
parent ebf3cee803
commit eff75f9613
4 changed files with 150 additions and 5 deletions

View File

@@ -45,13 +45,32 @@ export function getLocalizedPaths({
let fieldsToSearch: FlattenedField[] let fieldsToSearch: FlattenedField[]
let matchedField: FlattenedField
if (lastIncompletePath?.field?.type === 'blocks') {
if (segment === 'blockType') {
matchedField = {
name: 'blockType',
type: 'text',
}
} else {
for (const block of lastIncompletePath.field.blocks) {
matchedField = block.flattenedFields.find((field) => field.name === segment)
if (matchedField) {
break
}
}
}
} else {
if (lastIncompletePath?.field && 'flattenedFields' in lastIncompletePath.field) { if (lastIncompletePath?.field && 'flattenedFields' in lastIncompletePath.field) {
fieldsToSearch = lastIncompletePath.field.flattenedFields fieldsToSearch = lastIncompletePath.field.flattenedFields
} else { } else {
fieldsToSearch = lastIncompletePath.fields fieldsToSearch = lastIncompletePath.fields
} }
const matchedField = fieldsToSearch.find((field) => field.name === segment) matchedField = fieldsToSearch.find((field) => field.name === segment)
}
lastIncompletePath.field = matchedField lastIncompletePath.field = matchedField
if (currentPath === 'globalType' && globalSlug) { if (currentPath === 'globalType' && globalSlug) {
@@ -94,7 +113,6 @@ export function getLocalizedPaths({
} }
switch (matchedField.type) { switch (matchedField.type) {
case 'blocks':
case 'json': case 'json':
case 'richText': { case 'richText': {
const upcomingSegments = pathSegments.slice(i + 1).join('.') const upcomingSegments = pathSegments.slice(i + 1).join('.')

View File

@@ -387,6 +387,47 @@ export default buildConfigWithDefaults({
}, },
], ],
}, },
{
slug: 'deep-nested',
fields: [
{
type: 'tabs',
tabs: [
{
name: 'content',
fields: [
{
type: 'blocks',
name: 'blocks',
blocks: [
{
slug: 'testBlock',
fields: [
{
type: 'tabs',
tabs: [
{
name: 'meta',
fields: [
{
type: 'relationship',
relationTo: 'movies',
name: 'movie',
},
],
},
],
},
],
},
],
},
],
},
],
},
],
},
], ],
onInit: async (payload) => { onInit: async (payload) => {
await payload.create({ await payload.create({

View File

@@ -363,6 +363,38 @@ describe('Relationships', () => {
expect(docs[0].id).toBe(doc.id) expect(docs[0].id).toBe(doc.id)
}) })
it('should allow querying within tabs-blocks-tabs', async () => {
const movie = await payload.create({ collection: 'movies', data: { name: 'Pulp Fiction' } })
const { id } = await payload.create({
collection: 'deep-nested',
data: {
content: {
blocks: [
{
blockType: 'testBlock',
meta: {
movie: movie.id,
},
},
],
},
},
})
const result = await payload.find({
collection: 'deep-nested',
where: {
'content.blocks.meta.movie': {
equals: movie.id,
},
},
})
expect(result.totalDocs).toBe(1)
expect(result.docs[0].id).toBe(id)
})
describe('Custom ID', () => { describe('Custom ID', () => {
it('should query a custom id relation', async () => { it('should query a custom id relation', async () => {
const { customIdRelation } = await restClient const { customIdRelation } = await restClient

View File

@@ -28,6 +28,7 @@ export interface Config {
'rels-to-pages': RelsToPage; 'rels-to-pages': RelsToPage;
'rels-to-pages-and-custom-text-ids': RelsToPagesAndCustomTextId; 'rels-to-pages-and-custom-text-ids': RelsToPagesAndCustomTextId;
'object-writes': ObjectWrite; 'object-writes': ObjectWrite;
'deep-nested': DeepNested;
users: User; users: User;
'payload-locked-documents': PayloadLockedDocument; 'payload-locked-documents': PayloadLockedDocument;
'payload-preferences': PayloadPreference; 'payload-preferences': PayloadPreference;
@@ -52,6 +53,7 @@ export interface Config {
'rels-to-pages': RelsToPagesSelect<false> | RelsToPagesSelect<true>; 'rels-to-pages': RelsToPagesSelect<false> | RelsToPagesSelect<true>;
'rels-to-pages-and-custom-text-ids': RelsToPagesAndCustomTextIdsSelect<false> | RelsToPagesAndCustomTextIdsSelect<true>; 'rels-to-pages-and-custom-text-ids': RelsToPagesAndCustomTextIdsSelect<false> | RelsToPagesAndCustomTextIdsSelect<true>;
'object-writes': ObjectWritesSelect<false> | ObjectWritesSelect<true>; 'object-writes': ObjectWritesSelect<false> | ObjectWritesSelect<true>;
'deep-nested': DeepNestedSelect<false> | DeepNestedSelect<true>;
users: UsersSelect<false> | UsersSelect<true>; users: UsersSelect<false> | UsersSelect<true>;
'payload-locked-documents': PayloadLockedDocumentsSelect<false> | PayloadLockedDocumentsSelect<true>; 'payload-locked-documents': PayloadLockedDocumentsSelect<false> | PayloadLockedDocumentsSelect<true>;
'payload-preferences': PayloadPreferencesSelect<false> | PayloadPreferencesSelect<true>; 'payload-preferences': PayloadPreferencesSelect<false> | PayloadPreferencesSelect<true>;
@@ -350,6 +352,27 @@ export interface ObjectWrite {
updatedAt: string; updatedAt: string;
createdAt: string; createdAt: string;
} }
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "deep-nested".
*/
export interface DeepNested {
id: string;
content?: {
blocks?:
| {
meta?: {
movie?: (string | null) | Movie;
};
id?: string | null;
blockName?: string | null;
blockType: 'testBlock';
}[]
| null;
};
updatedAt: string;
createdAt: string;
}
/** /**
* This interface was referenced by `Config`'s JSON-Schema * This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-locked-documents". * via the `definition` "payload-locked-documents".
@@ -425,6 +448,10 @@ export interface PayloadLockedDocument {
relationTo: 'object-writes'; relationTo: 'object-writes';
value: string | ObjectWrite; value: string | ObjectWrite;
} | null) } | null)
| ({
relationTo: 'deep-nested';
value: string | DeepNested;
} | null)
| ({ | ({
relationTo: 'users'; relationTo: 'users';
value: string | User; value: string | User;
@@ -667,6 +694,33 @@ export interface ObjectWritesSelect<T extends boolean = true> {
updatedAt?: T; updatedAt?: T;
createdAt?: T; createdAt?: T;
} }
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "deep-nested_select".
*/
export interface DeepNestedSelect<T extends boolean = true> {
content?:
| T
| {
blocks?:
| T
| {
testBlock?:
| T
| {
meta?:
| T
| {
movie?: T;
};
id?: T;
blockName?: T;
};
};
};
updatedAt?: T;
createdAt?: T;
}
/** /**
* This interface was referenced by `Config`'s JSON-Schema * This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "users_select". * via the `definition` "users_select".