cozy / cozy-client

Document store and React components for the Cozy platform
MIT License
13 stars 12 forks source link

Fetch data by the ID of relationships #811

Open Merkur39 opened 3 years ago

Merkur39 commented 3 years ago

Hi! It would be good to be able to retrieve data that only has this or that ID from the same relationship

In these examples I imagine a solution which modifies the parameter of the .include() method of QueryDefinition And to go to the end of this idea, I propose an example of the Included variable

// Exemple for the Contacts
const { data, included } = await client.query(
  client.find('io.cozy.contacts').include([{ relName: 'groups', reIds: ['groupId01', 'groupId02'] }])
)
// data => All contacts with one or more specific groups
// included => [{ groups: [{ groupId01 }, { groupId02 }] }]

// Exemple for the Tasks
const { data, included } = await client.query(
  client.find('io.cozy.todos.item').include([{ relName: 'files', reIds: ['fileId01', 'fileId02'] }, { relName: 'contacts', reIds: ['contactId01', 'contactId02'] }])
)
// data => All tasks with one or more specific files and one or more specific contacts
// included => [{ files: [{ fileId01 }, { fileId02 }], contacts: [{ contactId01 }, { contactId02 }] }]

I don't believe it's currently possible to recover my data with this kind of filter, and I don't know if that would be the best way to implement it, but the idea is there :)

Today I do it in 2 steps

const { data: contacts } = await client.query(
  client.find(CONTACTS_DOCTYPE, {}).sortBy([{ 'name.givenName': 'asc' }])
)

let contactEntityList = []
contacts.forEach(contact => {
  if (!contact.me && hasRelationships(contact, 'groups', groupId)) {
    contactEntityList = [...contactEntityList, contact]
  }
})

export const hasRelationships = (doc, relName, relItemId) => {
  return !!HasMany.getHasManyItem(doc, relName, relItemId)
}
ptbrowne commented 3 years ago

.include is used to query for the other documents, to fetch their contents. Here you do not need to query the groups, since you are not filtering on any of their attributes. I think what you want is:

const queryDefinition = Q(CONTACTS_DOCTYPE).where({
  'relationships.groups.data': { $elemMatch: { _id: groupId } }
})
const { data: contacts } = await client.query(queryDefinition)

BY the way, the second part of the code, building the contactEntityList could be simplified with the use of filter. Here, a new array is built at each iteration, which is inefficient.

Crash-- commented 3 years ago

Since we provide a few static helpers to manipulate relationships, we're hiding our internal structure. By internal structure, I mean relationships.group.data for instance.

I think it can be easier to provide something like :

Q(CONTACTS_DOCTYPE).whereRelationships({ relName : {  $elemMatch: { _id: groupId } )

or something similar, that hides this relationships.relName.data

ptbrowne commented 3 years ago

Yes I agree that we should provide a utility since we hide the data.

I think it's the relationship class that should provide the utility, as this is it that knows the internal structure.

Q(CONTACTS_DOCTYPE).where(HasMany.queryForItem({ _id: groupId }))