Doubt/Issue creating new instances using model with accountRelation "set"

I am using Ceramic One, and created a model with accountRelation “set”, where I am setting two fields as it can be seen in the following code providing the model:

const DeploymentIndexModel: ModelDefinition = {
    version: "2.0",
    name: "DeploymentIndex development",
    description: "A unique anchor document for a specific application deployment development",
    // This model is controlled by an admin/system DID. 
    // Each deployment is a unique instance.
    accountRelation: { type: "set", fields: ["environment", "deployment_version"] }, 
    interface: false,
    implements: [],
    schema: {
      type: "object",
      properties: {
        // Human-readable name for the deployment
        environment: { type: "string" },
        // Optional version for this deployment
        deployment_version: { type: "string" },
      },
      required: ["environment", "deployment_version"],
      additionalProperties: false,
    },
  };

The thing is that, when I try to create different instances of this model under the same authenticateDID, I think I should receive the same StreamID, but I am getting different ones. Is this right?

For example in the following code, I should get the same streamIDs, right? Correct me if I am wrong:


const devDeploymentInstance = await modelInstanceClient.createInstance({
    model: deploymentIdModelStreamId,
    controller: authenticatedDID,
    content: {
        environment: "devDeploymentTest",
        deployment_version: "v_",
      },
    shouldIndex: true,
});

const devDeploymentInstance2 = await modelInstanceClient.createInstance({
  model: deploymentIdModelStreamId,
  controller: authenticatedDID,
  content: {
      environment: "devDeploymentTest",
      deployment_version: "v_",
    },
  shouldIndex: true,
});

await new Promise(resolve => setTimeout(resolve, 3000));


// To be used in 
console.log("instance stream", devDeploymentInstance.baseID.toString()); 
console.log("instance 2 stream", devDeploymentInstance2.baseID.toString());

Thanks a lot! @mohsin

Also, another thing that i realize is that when I create an instance using accountRelation “set”, I am not able to get the document state (getDocumentState) from the instance, while using “list” works for me. Not sure if I am doing something wrong.

That’s a great question. By default, ModelInstanceClient.createInstance will create a unique stream, even if the content across two SDK calls is the same.

There is a way to create what are called deterministic streams where you can get the same stream ID each time. Let me check and get back to you.

1 Like

Also, you’re right… ModelInstanceClient.getDocumentState doesn’t seem to be working for new MIDs with accountRelation “set”.

I’m checking.

1 Like

Perfect! Thanks a lot @mohsin, let me know when you figure out or when fixed to use it as it would be useful for my use case.

@mohsin I think I just realized about this error. “set” account relation actually works, the problem here is that I was using a model with “set” account relation and to create instances I was using createInstance method, which is wrong, because “single” and “set” account relation models should use createSingleton method instead.

If I use it this way, it works as expected. Correct me if I am wrong, but I think I am right.

Thanks a lot!

Forget previous message, still having the issue, but it is true that i was not using it properly.

1 Like

Ok, understood. I’ll keep investigating and keep you posted.

1 Like

Hi @Eloy, I finally figured it out! (Sorry, this was code I wasn’t super familiar with.)

The C1 SDK issue is happening because SET relations have a specific requirement for instance creation. The Init Event needs to have a “unique” value based on the fields that WILL be put in a subsequent Data Event. In order to do that, the call needs to have access to the Model definition.

In ComposeDB instance creation, the GraphQL layer read model.accountRelation.fields from the runtime Model definition and thus knew how to create the right unique value for SET relation.

The newer C1 SDK implementation doesn’t do it the same way. It doesn’t currently have the Model definition and doesn’t set the unique value in the Init Event correctly.

I’m chatting with the team right now to figure out the best way to fix this.

1 Like

Hi @Eloy, it took a while but the latest version of the SDK should have a fix for creating MIDs with account type SET!

1 Like

Hey @mohsin, thanks a lot! I just tried but it is still not working for me.

I mainly updated all the sdk packages to version 12:

    "@ceramic-sdk/events": "^0.12.0",
    "@ceramic-sdk/flight-sql-client": "^0.12.0",
    "@ceramic-sdk/http-client": "^0.12.0",
    "@ceramic-sdk/identifiers": "^0.12.0",
    "@ceramic-sdk/model-client": "^0.12.0",
    "@ceramic-sdk/model-instance-client": "^0.12.0",
    "@ceramic-sdk/model-protocol": "^0.12.0",

and also for the ceramic node I used the latest version of docker (doing docker pull before docker compose up to ensure the image is updated): public.ecr.aws/r5b3e0r5/3box/ceramic-one:latest

After that, I mainly followed the same process I explained at the beginning of this post, using same model for different instances of the model using the same field under accountRelation, so the instances should have the same stream, but when I try to update the new set instance I got an error updating it (if I use createInstance it will work properly).

model:

// 1. Create a model client
const modelClient = new ModelClient({
  ceramic,
  did: authenticatedDID,
});

// 2. Define the Model's Schema Definition
const DeploymentIndexModel: ModelDefinition = {
    version: "2.0",
    name: "DeploymentIndex development",
    description: "A unique anchor document for a specific application deployment development 2",
    // This model is controlled by an admin/system DID. 
    // Each deployment is a unique instance.
    accountRelation: { type: "set", fields: ["environment"], }, 
    interface: false,
    implements: [],
    schema: {
      type: "object",
      $schema: "https://json-schema.org/draft/2020-12/schema",
      properties: {
        // Human-readable name for the deployment
        environment: { type: "string", maxLength: 100 },
        // Optional version for this deployment
        deployment_version: { type: "string", maxLength: 100 },
      },
      required: ["environment", "deployment_version"],
      additionalProperties: false,
    },
  };

// 3. Create the Model Stream
const modelStream = await modelClient.createDefinition(DeploymentIndexModel);

await new Promise(resolve => setTimeout(resolve, 3000));

console.log("modelStream", modelStream);

// 4. Get the Stream's Model Definition
const modelDefinition = await modelClient.getModelDefinition(modelStream);

await new Promise(resolve => setTimeout(resolve, 3000));

console.log("modelDefinition", modelDefinition);

Logs from previous execution:

modelStream StreamID(kjzl6hvfrbw6cabymzratp29my20da270dioricr21kr8es4gd8hno1cjjm86nx)
modelDefinition {
  accountRelation: { fields: [ 'environment' ], type: 'set' },
  description: 'A unique anchor document for a specific application deployment development 2',
  implements: [],
  interface: false,
  name: 'DeploymentIndex development',
  schema: {
    '$schema': 'https://json-schema.org/draft/2020-12/schema',
    additionalProperties: false,
    properties: { deployment_version: [Object], environment: [Object] },
    required: [ 'environment', 'deployment_version' ],
    type: 'object'
  },
  version: '2.0'
}

Creation of new instances with same required fields (so they should return same streamid):

const deploymentIdModelStreamId = StreamID.fromString("kjzl6hvfrbw6cabymzratp29my20da270dioricr21kr8es4gd8hno1cjjm86nx");

// 1. Instantiate a ModelInstanceClient
const modelInstanceClient = new ModelInstanceClient({
  ceramic,
  did: authenticatedDID,
});

await new Promise(resolve => setTimeout(resolve, 3000));


const devDeploymentInstance = await modelInstanceClient.createSingleton({
  model: deploymentIdModelStreamId,
  controller: authenticatedDID,
});

await new Promise(resolve => setTimeout(resolve, 8000));

console.log("instance stream", devDeploymentInstance.baseID.toString()); 

await new Promise(resolve => setTimeout(resolve, 8000));

await modelInstanceClient.updateDocument({
  streamID: devDeploymentInstance.baseID.toString(),
  newContent: {
    environment: "devDeploymentTest",
    deployment_version: `v_${Date.now()}`,
  },
  shouldIndex: true,
});

await new Promise(resolve => setTimeout(resolve, 8000));

const currentStateAfterModify = await modelInstanceClient.getDocumentState(devDeploymentInstance.baseID);

await new Promise(resolve => setTimeout(resolve, 8000));

console.log("currentStateAfterModify", currentStateAfterModify, "baseId", devDeploymentInstance.baseID);

And these are the logs:

instance stream k2t6wzhkhabz4593z28rnykfvf91kscvh2axz3xvyk54uu4gnuu1q0dbgrjdy4
file:///
            throw new Error(`Failed to fetch stream state: ${error.message}`);
                  ^

Error: Failed to fetch stream state: undefined

Node.js v20.18.3

Looks like it is not able to retrieve the information from the new created set instance. If I just change the accountRelation to “single” and remove the “fields” array, it works properly and I am able to retrieve everything.

Am I doing something wrong?

Thanks in advance! :slight_smile:

Hi @mohsin,

Forget previous message, I made it work and set is working properly for me.

Main issue I had is that I was following documentation but I think it is not updated for the changes you made. I followed the code of the repo based on the tests and worked for me.

Main issues were:

  • I was using createSingleton to create set instances as suggested here, but after looking the tests in the repository and using createInstance instead, it worked properly for me. So documentation needs to be updated, but the last version fix the issues with set.
  • Another important thing is that, while in the repository states that createInstance function has modelDefinition parameter as optional, it is actually a required parameter, needed to create the instance accordingly. If this parameter is not used, it would not be possible to create the instance successfully and then it would not be possible to retrieve the information.

Thanks a lot!