Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions packages/esix/src/base-model.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -683,6 +683,89 @@ describe('BaseModel', () => {
})
})

describe('update', () => {
it('updates an existing model', async () => {
vi.useFakeTimers()
vi.setSystemTime(new Date('2023-01-01T10:00:00Z'))

const book = new Book()

book.id = '5f347707fdec6e388b5c1d33'
book.title = 'Emma'
book.isbn = '9780141439600'
book.pages = 448
book.authorId = 'author-1'
book.createdAt = new Date('2022-12-01T10:00:00Z').getTime()

await book.update({
pages: 512,
title: 'Emma (Second Edition)'
})

expect(book.pages).toEqual(512)
expect(book.title).toEqual('Emma (Second Edition)')

expect(collection.updateOne).toHaveBeenCalledWith(
{
_id: '5f347707fdec6e388b5c1d33'
},
{
$set: {
_id: '5f347707fdec6e388b5c1d33',
authorId: 'author-1',
createdAt: new Date('2022-12-01T10:00:00Z').getTime(),
isAvailable: true,
isbn: '9780141439600',
pages: 512,
title: 'Emma (Second Edition)',
updatedAt: new Date('2023-01-01T10:00:00Z').getTime()
}
},
{
upsert: true
}
)
})

it('inserts a new model when it has no id', async () => {
vi.useFakeTimers()
vi.setSystemTime(new Date('2023-01-01T10:00:00Z'))

vi.mocked(ObjectId.prototype.toHexString).mockReturnValue(
'5f347707fdec6e388b5c1d33'
)

const book = new Book()

await book.update({
isbn: '9780141439600',
title: 'Emma'
})

expect(book.wasRecentlyCreated).toEqual(true)

expect(collection.updateOne).toHaveBeenCalledWith(
{
_id: '5f347707fdec6e388b5c1d33'
},
{
$set: {
_id: '5f347707fdec6e388b5c1d33',
createdAt: new Date('2023-01-01T10:00:00Z').getTime(),
isAvailable: true,
isbn: '9780141439600',
pages: 0,
title: 'Emma',
updatedAt: null
}
},
{
upsert: true
}
)
})
})

describe('where', () => {
it('finds all documets that matches a query', async () => {
const cursor = createCursor([
Expand Down
22 changes: 22 additions & 0 deletions packages/esix/src/base-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,28 @@ export default class BaseModel {
this.id = id
}
}

/**
* Assigns the given attributes to the model and persists it to the
* database. It's equivalent to assigning each attribute individually and
* then calling `save()`, so `updatedAt` is stamped automatically on
* existing models. Note that assigning a new `id` upserts a new document
* rather than renaming the existing one.
*
* Example
* ```
* const product = await Product.find(id);
*
* await product.update({ name: 'Chair 1', price: 42.0 });
* ```
*
* @param attributes
*/
async update(attributes: Partial<this>): Promise<void> {
Object.assign(this, attributes)

await this.save()
}
}

function getDefaultValues<T extends BaseModel>(
Expand Down
35 changes: 35 additions & 0 deletions packages/esix/src/integration-test.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,41 @@ describe('Integration', () => {
})
})

it('updates multiple attributes at once', async () => {
const dateSpy = vi.spyOn(Date, 'now')
dateSpy.mockReturnValue(new Date('2023-01-01T10:00:00Z').getTime())

const product = await Product.create({
name: 'Chair',
price: 20.0
})

dateSpy.mockReturnValue(new Date('2023-01-01T10:30:00Z').getTime())

const existingProduct = await Product.find(product.id)

expect(existingProduct).not.toBeNull()

if (!existingProduct) {
return
}

await existingProduct.update({
name: 'Chair 1',
price: 42.0
})

const updatedProduct = await Product.find(product.id)

expect(updatedProduct).toEqual({
createdAt: new Date('2023-01-01T10:00:00Z').getTime(),
id: product.id,
name: 'Chair 1',
price: 42.0,
updatedAt: new Date('2023-01-01T10:30:00Z').getTime()
})
})

it('persists a new model', async () => {
const dateSpy = vi.spyOn(Date, 'now')
dateSpy.mockReturnValue(new Date('2023-01-01T10:00:00Z').getTime())
Expand Down
31 changes: 29 additions & 2 deletions packages/website/docs/inserting-and-updating-models.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
---
title: Inserting & Updating Models
description: Learn how to create, update, and modify records in your MongoDB database using Esix's intuitive model methods and mass assignment features.
description:
Learn how to create, update, and modify records in your MongoDB database using
Esix's intuitive model methods and mass assignment features.
---

When it comes to adding new models to the database, there are two different ways to go about it. You can either use the `create` method and pass it the attributes you want the model to have, or you can create a new instance of the model and call its `save` method.
When it comes to adding new models to the database, there are two different ways
to go about it. You can either use the `create` method and pass it the
attributes you want the model to have, or you can create a new instance of the
model and call its `save` method.

```ts
// Using the create method.
Expand Down Expand Up @@ -38,6 +43,28 @@ await product.save()
When you call `save` on an already existing model, the `updatedAt` field will be
filled with the current timestamp.

## Updating multiple attributes

Instead of assigning each attribute individually and calling `save`, you can
pass all the changes to the `update` method in one go.

```ts
const product = await Product.find('5f5a474b32fa462a5724ff7d')

// Instead of this...
product.name = 'Chair 1'
product.price = 42.0

await product.save()

// ...you can do this.
await product.update({ name: 'Chair 1', price: 42.0 })
```

The `update` method assigns the given attributes to the model and saves it, so
the `updatedAt` field is filled with the current timestamp just like when you
call `save` yourself.

Both the timestamp properties contain the current time in milliseconds since
January 1st, 1970, using JavaScript's
[Date.now](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/now)
Expand Down
Loading