Persistence
The query API retrieves your data in the form of ordinary objects and arrays, and storing your data is no different. The use of objects instead of models makes persistence an extremely flexible proposition: you can write what you want when you want, without having to construct or query an entity instance (this is equally cause to be careful; woe betide the author of a destroy
who misses execution paths which produce incomplete criteria). The persistence methods operate only on the columns you specify, and leave all others with the existing or default value, as appropriate.
Persistence methods are ordinarily the province of tables, but you can also write data through "updatable" views which meet certain criteria.
save
save
stores a single object in the database. Massive determines whether or not the object contains primary key information, updating it if it does or inserting it otherwise. This means it is not a true upsert, and that some care must be taken with its use in certain situations! save
yields the created or modified record represented as an object.
Some restrictions apply:
save
may not be used with foreign tables or updatable views, since they cannot have primary keys. If you need to persist data to a foreign table or updatable view, useinsert
andupdate
.save
is only useful with tables having a primary key wholly or partially generated in the database, such as an auto-incrementing sequence or one of the UUID-creating functions. If you are trying to create a record and your table definition requires you to specify values for all primary key columns, you must useinsert
;save
is effectively anupdate
in this scenario.
Insert options, as well as the universal options, may be passed as a second argument. However, most of these are of limited utility.
let test = await db.tests.save({
version: 1,
name: 'homepage'
});
test.version = 2;
test.priority = 'high';
test = await db.tests.save(test);
insert
insert
writes a new record or records into your table. You do not have to include every single field of the target table; as long as you ensure that no NOT NULL
constraints will be violated, you can include as many or as few fields as you need. When you insert an object, the record will be returned as an object. If you have an autogenerated or serial primary key or default fields, they will have been calculated and added.
const test = await db.tests.insert({
name: 'homepage',
version: 1,
});
To insert multiple records, pass an array of records instead:
const tests = await db.tests.insert([{
name: 'homepage',
version: 1,
priority: 'low'
}, {
name: 'about us',
version: 1
}]);
Insert options, as well as the universal options, may be passed as a second argument.
const testOrNull = await db.tests.insert({
id: 1,
name: 'homepage',
version: 1
}, {
onConflictIgnore: true, // if the id exists, do nothing
});
insert
yields an object if an object record was passed, or an array for an array of records.
Deep Insert
This is automatically enabled with joins in Massive v6 and up.
Inserting into multiple related tables at once happens fairly frequently, especially when you're dealing with many-to-many relationships through a junction table. For these cases, you can pass arrays of related records into insert
as a property named for the related table. Related records must have the foreign key to the original record explicitly set to undefined
.
const onlyTheTest = await db.tests.insert({
name: 'homepage',
version: 1,
priority: 'low',
user_tests: [{
test_id: undefined,
user_id: 1,
role: 'primary'
}, {
test_id: undefined,
user_id: 2,
role: 'auxiliary'
}]
}, {
deepInsert: true
});
Deep insert is only supported when inserting single records. Attempts to deep insert an array of records will be rejected.
update
update
alters existing records in a table, given a criteria object (or unary primary key) and a map of column names to the new values. It yields an array containing the zero or more updated record(s) if passed a criteria object, or an object if passed a primary key.
The second "changes" argument can include a $set
key with a map of column names to raw SQL expressions. This allows arithmetic, column references, function calls, and other such dynamic changes to be made. However, raw SQL can introduce SQL injection vulnerabilities if used carelessly. Don't interpolate user input into $set
expressions!
const tests = await db.tests.update(1, {
priority: 'moderate',
$set: {
self_destruct_in: 'self_destruct_in - 1'
}
});
update
can use the universal query options:
const sqlAndParams = await db.tests.update({
priority: 'high'
}, {
priority: 'moderate'
}, {
build: true
});
destroy
destroy
removes records either by primary key or by matching a criteria object. It yields an array containing the zero or more deleted record(s) if passed a criteria object, or an object if passed a primary key.
destroy
can use the universal query options:
const deletedTest = await db.tests.destroy(1);
const deletedTests = await db.tests.destroy({
priority: 'high'
}, {
only: true
});