Initial commit
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
---
|
||||
title: Use Field Modifiers for Incremental Updates
|
||||
impact: HIGH
|
||||
impactDescription: Atomic updates, prevents race conditions, cleaner code
|
||||
tags: sdk, modifiers, relations, files, numbers, atomic
|
||||
---
|
||||
|
||||
## Use Field Modifiers for Incremental Updates
|
||||
|
||||
PocketBase supports `+` and `-` modifiers for incrementing numbers, appending/removing relation IDs, and managing file arrays without replacing the entire value.
|
||||
|
||||
**Incorrect (read-modify-write pattern):**
|
||||
|
||||
```javascript
|
||||
// Race condition: two users adding tags simultaneously
|
||||
async function addTag(postId, newTagId) {
|
||||
const post = await pb.collection('posts').getOne(postId);
|
||||
const currentTags = post.tags || [];
|
||||
|
||||
// Another user might have added a tag in between!
|
||||
await pb.collection('posts').update(postId, {
|
||||
tags: [...currentTags, newTagId] // Might overwrite the other user's tag
|
||||
});
|
||||
}
|
||||
|
||||
// Inefficient for incrementing counters
|
||||
async function incrementViews(postId) {
|
||||
const post = await pb.collection('posts').getOne(postId);
|
||||
await pb.collection('posts').update(postId, {
|
||||
views: post.views + 1 // Extra read, race condition
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
**Correct (using field modifiers):**
|
||||
|
||||
```javascript
|
||||
// Atomic relation append with + modifier
|
||||
async function addTag(postId, newTagId) {
|
||||
await pb.collection('posts').update(postId, {
|
||||
'tags+': newTagId // Appends to existing tags atomically
|
||||
});
|
||||
}
|
||||
|
||||
// Append multiple relations
|
||||
async function addTags(postId, tagIds) {
|
||||
await pb.collection('posts').update(postId, {
|
||||
'tags+': tagIds // Appends array of IDs
|
||||
});
|
||||
}
|
||||
|
||||
// Prepend relations (+ prefix)
|
||||
async function prependTag(postId, tagId) {
|
||||
await pb.collection('posts').update(postId, {
|
||||
'+tags': tagId // Prepends to start of array
|
||||
});
|
||||
}
|
||||
|
||||
// Remove relations with - modifier
|
||||
async function removeTag(postId, tagId) {
|
||||
await pb.collection('posts').update(postId, {
|
||||
'tags-': tagId // Removes specific tag
|
||||
});
|
||||
}
|
||||
|
||||
// Remove multiple relations
|
||||
async function removeTags(postId, tagIds) {
|
||||
await pb.collection('posts').update(postId, {
|
||||
'tags-': tagIds // Removes all specified tags
|
||||
});
|
||||
}
|
||||
|
||||
// Atomic number increment
|
||||
async function incrementViews(postId) {
|
||||
await pb.collection('posts').update(postId, {
|
||||
'views+': 1 // Atomic increment, no race condition
|
||||
});
|
||||
}
|
||||
|
||||
// Atomic number decrement
|
||||
async function decrementStock(productId, quantity) {
|
||||
await pb.collection('products').update(productId, {
|
||||
'stock-': quantity // Atomic decrement
|
||||
});
|
||||
}
|
||||
|
||||
// File append (for multi-file fields)
|
||||
async function addImage(albumId, newImage) {
|
||||
await pb.collection('albums').update(albumId, {
|
||||
'images+': newImage // Appends new file to existing
|
||||
});
|
||||
}
|
||||
|
||||
// File removal
|
||||
async function removeImage(albumId, filename) {
|
||||
await pb.collection('albums').update(albumId, {
|
||||
'images-': filename // Removes specific file by name
|
||||
});
|
||||
}
|
||||
|
||||
// Combined modifiers in single update
|
||||
async function updatePost(postId, data) {
|
||||
await pb.collection('posts').update(postId, {
|
||||
title: data.title, // Replace field
|
||||
'views+': 1, // Increment number
|
||||
'tags+': data.newTagId, // Append relation
|
||||
'tags-': data.oldTagId, // Remove relation
|
||||
'images+': data.newImage // Append file
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
**Modifier reference:**
|
||||
|
||||
| Modifier | Field Types | Description |
|
||||
|----------|-------------|-------------|
|
||||
| `field+` or `+field` | relation, file | Append/prepend to array |
|
||||
| `field-` | relation, file | Remove from array |
|
||||
| `field+` | number | Increment by value |
|
||||
| `field-` | number | Decrement by value |
|
||||
|
||||
**Benefits:**
|
||||
- **Atomic**: No read-modify-write race conditions
|
||||
- **Efficient**: Single request, no extra read needed
|
||||
- **Clean**: Expresses intent clearly
|
||||
|
||||
**Note:** Modifiers only work with `update()`, not `create()`.
|
||||
|
||||
Reference: [PocketBase Relations](https://pocketbase.io/docs/working-with-relations/)
|
||||
Reference in New Issue
Block a user