diff --git a/packages/store/src/drivers/base.test.js b/packages/store/src/drivers/base.test.js index 5fd7ab53..fd0d1b64 100644 --- a/packages/store/src/drivers/base.test.js +++ b/packages/store/src/drivers/base.test.js @@ -1,10 +1,16 @@ import { primary, string, object, u8, boolean, i64, date } from "@primate/types"; const w = (document, id) => ({ ...document, id }); +const defaults = { + mode: "loose", + readonly: false, + ambiguous: false, +}; const stores = [ ["User", { name: "User", + defaults, schema: { id: primary, name: string, @@ -19,7 +25,10 @@ const stores = [ }], ["StrictUser", { name: "StrictUser", - mode: "strict", + defaults: { + ...defaults, + mode: "strict", + }, schema: { id: primary, name: string, @@ -34,6 +43,7 @@ const stores = [ }], ["Comment", { name: "Comment", + defaults, schema: { id: primary, }, @@ -170,8 +180,29 @@ export default async (test, driver, lifecycle) => { assert(users4.find(({ id }) => id === user2$.id)).defined(); // embedded - const { id } = await User.insert({ ...traits }); - assert(await User.find({ id })).equals([{ id, ...traits }]); + { + const { id } = await User.insert({ ...traits }); + assert(await User.find({ id })).equals([{ id, ...traits }]); + } + + const criteria = { name: "Donald", age: 20 }; + // 0-field projection + { + // empty + const [users5] = await User.find(criteria); + const { id } = users5; + assert(users5).equals({ ...criteria, sex: "M", id }); + } + // 1-field projection + { + const [user] = await User.find(criteria, ["name"]); + assert(user).equals({ name: "Donald" }); + } + // n-field projection + { + const [user] = await User.find(criteria, ["name", "age"]); + assert(user).equals(criteria); + } }); }); diff --git a/packages/store/src/drivers/memory/Facade.js b/packages/store/src/drivers/memory/Facade.js index 84f2a922..6a70012d 100644 --- a/packages/store/src/drivers/memory/Facade.js +++ b/packages/store/src/drivers/memory/Facade.js @@ -65,8 +65,12 @@ export default class Connection { return collection.length > 0; } - async find(name, criteria) { - return this.#filter(name, criteria); + async find(name, criteria, projection = []) { + const documents = await this.#filter(name, criteria); + return projection.length === 0 + ? documents + : documents.map(document => o.filter(document, + ([key]) => projection.includes(key))); } async count(...args) { diff --git a/packages/store/src/drivers/mongodb/Facade.js b/packages/store/src/drivers/mongodb/Facade.js index c0a70766..97f624fc 100644 --- a/packages/store/src/drivers/mongodb/Facade.js +++ b/packages/store/src/drivers/mongodb/Facade.js @@ -34,8 +34,20 @@ export default class Facade { return { session: this.session }; } - async find(name, criteria = {}) { - return (await this.#by(name).find(cid(criteria), this.#options).toArray()) + async find(name, criteria = {}, projection = []) { + const options = { + ...this.#options, + ...projection.length === 0 + ? {} + : { + projection: { + // erase _id unless explicit in projection + _id: 0, + ...Object.fromEntries(projection.map(field => [field, 1])), + }, + }, + }; + return (await this.#by(name).find(cid(criteria), options).toArray()) .map(document => toid(document)); } diff --git a/packages/store/src/drivers/mysql/Facade.js b/packages/store/src/drivers/mysql/Facade.js index 876401ea..0ca68708 100644 --- a/packages/store/src/drivers/mysql/Facade.js +++ b/packages/store/src/drivers/mysql/Facade.js @@ -44,9 +44,10 @@ export default class Connection { this.connection = connection; } - async find(collection, criteria = {}) { + async find(collection, criteria = {}, projection = []) { const { where, bindings } = predicate(criteria); - const query = `select * from ${collection} ${where}`; + const select = projection.length === 0 ? "*" : projection.join(", "); + const query = `select ${select} from ${collection} ${where}`; const [result] = await this.connection.query(query, bindings); return filter_nulls(result); diff --git a/packages/store/src/drivers/postgresql/Facade.js b/packages/store/src/drivers/postgresql/Facade.js index 98e7e5e7..0660c993 100644 --- a/packages/store/src/drivers/postgresql/Facade.js +++ b/packages/store/src/drivers/postgresql/Facade.js @@ -26,10 +26,11 @@ export default class Connection { this.connection = connection; } - async find(collection, criteria = {}) { + async find(collection, criteria = {}, projection = []) { const { connection } = this; + const select = projection.length === 0 ? "*" : projection.join(", "); return filter_nulls(await connection` - select * + select ${connection.unsafe(select)} from ${connection(collection)} where ${Object.entries(criteria).reduce((acc, [key, value]) => connection`${acc} and ${connection(key)} = ${value}`, connection`true`)} diff --git a/packages/store/src/drivers/sqlite/Facade.js b/packages/store/src/drivers/sqlite/Facade.js index 277b8313..092cf3dd 100644 --- a/packages/store/src/drivers/sqlite/Facade.js +++ b/packages/store/src/drivers/sqlite/Facade.js @@ -46,9 +46,10 @@ export default class Connection { this.connection = connection; } - find(collection, criteria = {}) { + find(collection, criteria = {}, projection = []) { const { where, bindings } = predicate(criteria); - const query = `select * from ${collection} ${where}`; + const select = projection.length === 0 ? "*" : projection.join(", "); + const query = `select ${select} from ${collection} ${where}`; const statement = this.connection.prepare(query); if (!is_bun) { statement.safeIntegers(true); diff --git a/packages/store/src/drivers/surrealdb/Facade.js b/packages/store/src/drivers/surrealdb/Facade.js index 727e0041..5a3f4774 100644 --- a/packages/store/src/drivers/surrealdb/Facade.js +++ b/packages/store/src/drivers/surrealdb/Facade.js @@ -53,9 +53,10 @@ export default class Connection { return this.connection.query_raw(...args); } - async find(name, criteria = {}) { + async find(name, criteria = {}, projection = []) { const { where, bindings } = predicate(criteria); - const query = `select * from ${name} ${where}`; + const select = projection.length === 0 ? "*" : projection.join(", "); + const query = `select ${select} from ${name} ${where}`; const [{ result }] = await this.#query(query, bindings); return result; } diff --git a/packages/store/src/wrap.js b/packages/store/src/wrap.js index 82bd3b45..77ab830a 100644 --- a/packages/store/src/wrap.js +++ b/packages/store/src/wrap.js @@ -67,9 +67,12 @@ export default (config, facade, types) => { maybe(criteria).object(); return facade.count(name, criteria); }, - async find(criteria) { + async find(criteria, projection) { maybe(criteria).object(); - const documents = await facade.find(name, pack(criteria)); + maybe(projection).array(); + + const documents = await facade.find(name, pack(criteria), projection); + return documents.map(document => unpack(document)); }, async exists(criteria) {