mst-gql icon indicating copy to clipboard operation
mst-gql copied to clipboard

Dealing with incomplete data - merging new data to a model

Open kidroca opened this issue 4 years ago • 8 comments

Hi I'm trying to use mst-gql - I've got some really large models that are rarely used in full.
They've scaffolded with nullable types so that partial data requests can be made as explained here:

Dealing with incomplete objects

GraphQL makes it possible to query a subset of the fields of any object. The upside of this is that data traffic can be minimized. The downside is that it cannot be guaranteed that any object is loaded in its 'complete' state. It means that fields might be missing in the client state, even though are defined as being mandatory in the original graphQL object type!...

When I make a query for partial data and commit it to the model I expected it would merge with the existing data on the model but instead the previous data gets deleted

Let's say we have a model like this:

{
  firstName: 'Mister',
  lastName: 'Freeze',
  nested: { isFavorite: false },
}

How to merge additional data coming in like

{
  nested: {
    age: 40,
    gender: 'male',
  }
}

So far I just use object spread to merge the additional data which results in the nested key getting overwritten

Is it even required to merge the new data - it seems to already be converted to a model from the query result but I expected it would somehow get merged with any previously fetched data for that model

Here's my actual model code:

import {applySnapshot, getSnapshot, Instance} from "mobx-state-tree"
import {PatientTypeModelBase, patientTypeModelPrimitives, selectFromPatientType} from "./PatientTypeModel.base"

/* The TypeScript type of an instance of PatientTypeModel */
export interface PatientTypeModelType extends Instance<typeof PatientTypeModel.Type> {}

/* A graphql query fragment builders for PatientTypeModel */
export {selectFromPatientType, patientTypeModelPrimitives, PatientTypeModelSelector} from "./PatientTypeModel.base"

/**
 * PatientTypeModel
 *
 * Patient data
 */
export const PatientTypeModel = PatientTypeModelBase
    .actions(self => ({
        /**
         * Prepare initial data for the dashboard
         */
        fetchInitialData() {
            const { clientId, store } = self;
            const query = store.queryPatient({ clientId }, PROFILE_DATA_FRAGMENT);

            // the patient param here seems to already be converted to a reactive object
            // but it does not contain the previously fetched info - just the one from the current query
            query.then(({ patient }) => {
                // First I tried Object.assign(self, patient);
                // this results in problems because mst thinks we're resetting the identifier fields

                // Then I tried doing nothing at all - maybe the query code and the type system 
                // already handles it automatically and I have some other problem 

                // Doing the bellow works but the previous data gets deleted not merged
                // const snapshot = getSnapshot(patient);
                // applySnapshot(self, snapshot);
                console.log('JSON.stringify(self): ', JSON.stringify(self));
            })

            return query;
        }
    }))
    .views(self => ({
        get statusPriority() {
            return self.status?.priority || Infinity;
        },
    }))

const PROFILE_DATA_FRAGMENT = selectFromPatientType()
    .client_id
    .first_name
    .last_name
    .gender
    .emr_data
    .dob
    .client(c => c
        .client_id
        .email
        .phone
        .created)
    .image(i => i.large)
    .toString();

I regularly use mobx and mobx-react, but have no actual experience with mobx-state-tree so I maybe missing something on that end

kidroca avatar Apr 20 '20 02:04 kidroca

MST should automatically merge the data for you. Note that if you look at the data on the query you might still only be seeing the specific fields asked for as part of PROFILE_DATA_FRAGMENT. You'll need to look in your store to see the full merged object.

What type is nested in this case? Is this a nested model? a map?

chrisdrackett avatar Apr 20 '20 15:04 chrisdrackett

What type is nested in this case? Is this a nested model? a map? It's a nested model from the scaffold

I noticed something else, that I think was the issue: The root types were not picked correctly. Now that I added a configuration with explicit array of root types - collections (maps) were correctly generated across the root models, but I'm dealing with another issue now - an exception when a query is fetched because it cannot be serialized to the model for some reason - I think it's because it holds an array of objects with no id identifier - it's just a wrapper for 2 other object that do have ids... I'm almost certain the issue is with our gql schema having though

I also seem to have other problems related to circular deps

I'll try to confirm whether the root models fixed this

I've created another root store model to separate some of the logic to a different store - this might be the cause too - having all the methods and props in the RootStore will just make it way too big for our app, while the sub models are mostly infrastructure and cannot serve to hold large portions of logic there

kidroca avatar Apr 20 '20 20:04 kidroca

@kidroca could your problems be related to the fact that mst-gql seems to expects a single id field to exist and be named id? https://github.com/mobxjs/mst-gql/issues/223, https://github.com/mobxjs/mst-gql/issues/215

barbalex avatar May 12 '20 12:05 barbalex

Just ran into the same issue here. It seems that merging doesn't happen properly for objects without an ID and instead it just replaces the whole object. I'll see if I can make a patch for this

RXminuS avatar Jul 06 '20 08:07 RXminuS

I had to solve the same problem in mst-query (which originally had the same merge logic as this library).

Maybe some parts of my solution can be helpful: https://github.com/ConrabOpto/mst-query/blob/61865be905cbf5cd98690155c5317636571f9784/src/merge.ts#L68

k-ode avatar Jun 20 '21 23:06 k-ode

I had to solve the same problem in mst-query (which originally had the same merge logic as this library).

Maybe some parts of my solution can be helpful: https://github.com/ConrabOpto/mst-query/blob/master/srcmerge.ts#L59

Hey @k-ode It seems the link is broken. Can you kindly update it? TIA.

venkateshpullaganti avatar Jul 19 '22 05:07 venkateshpullaganti

@venkateshpullaganti Fixed it

k-ode avatar Jul 19 '22 08:07 k-ode

Will take a look at this soon, thank you!

jesse-savary avatar Jul 19 '22 14:07 jesse-savary