tigrisdata-archive / tigris

Tigris is an Open Source Serverless NoSQL Database and Search Platform.

Home Page:https://www.tigrisdata.com/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Improve the experience of working with nested arrays

garrensmith opened this issue · comments

commented

If I have a model like this:

export class Comment {
  @Field()
  content: string;

  @Field()
  name: string;

  @Field({ timestamp: "createdAt" })
  createdAt?: Date;
}

@TigrisCollection("post")
export class Post {
  @PrimaryKey(TigrisDataTypes.UUID, { order: 1, autoGenerate: true })
  id?: string;

  @Field({ timestamp: "createdAt" })
  createdAt?: Date;

  @Field({ timestamp: "updatedAt" })
  updatedAt?: Date;

  @Field()
  title: string;

  @Field()
  content?: string;

  @Field({ default: false })
  published?: boolean;

  @Field(TigrisDataTypes.ARRAY, { elements: Comment })
  comments: Array<Comment>;
}

If I want to add another comment to the post document, I have to read the whole document and write it back:

const post = await postCollection.findOne({
    filter: { title: "my blog post" },
  });

post.comments.push({
    content: "nice post",
    name: "bob",
  });

 postCollection.insertOrReplaceOne(post)

I think it would be much nicer to have something similar to MongoDB $push

postCollection.updateOne({
    filter: { id: post.id },
    fields: {$push: {comments: {content: "nice", name: "bob"}}},
  });
commented

Can I contribute to this?

  • As a starting point we can support inserting a single element and later add modifiers like $each, $sort and $slice on this $push field operator ( or should we do that along with this $push ? )

Proposed Solution:

  1. Validate and Mutate Payload: If the request contains a push field operator, then mutate and validate the payload with newInsertPayloadMutator in the UpdateQueryRunner.
  2. Add $push field operator: MergeAndGet in FieldOperatorFactory contains this push option as well, where we append this new element into the existing array and also check for primaryKeyMutation. If the field is absent, then add the array field with the value as its only element.

@akhill10 this would be a good start.

@akhill10 You only need to do (2) i.e.

  • In MergeAndGet, check if this operator is present in FieldOperators
  • if yes, then the factory will have a method called Push (factory.Push) where we will have a logic of appending to an array, and if the array is empty, then an array with this element only.

The syntax of the operator to start with would be,

  • { $push: { <field1>: <value1>, ... } } (similar to other Update operators)

About other modifiers, we can start with only supporting the above in the first iteration. The next important modifier I can think of is $each, which allows us to add batch, but we can support it later. We can also support accepting value as a slice then we can support appending a batch of values automatically. I'll leave this part to you. We can always build on this incrementally.

commented

@himank I think there is one scenario that we are missing out here while pushing an element to the array of objects that contains default fields such as createdAt (this field is optional ), the default field is not getting populated, since we aren't using any insert mutators when updating the array with push. This case needs to be handled right?

commented

Handled the above scenario as well. All the default values are set for the new payload.

@akhill10 We can simplify that for now as we don't support default attributes for an array of objects. This will make it simple.

Thanks for working on this @akhill10. I am closing this and this will be pushed as part of our next release.

@akhill10, do you mind adding support for it in the TypeScript and Go clients?

commented

Sure @ovaistariq will do that.