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
15 changes: 8 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ export interface JoinDataParam {
local: LocalParam;

/**
* Object(s) or an asynchronous callback function that returns the data from the source.
* Objects or an asynchronous callback function that returns the data from the source.
*/
from: FromParam;

Expand All @@ -128,9 +128,10 @@ export interface JoinDataParam {
asMap?: AsMap;
}

export type LocalParam = object | object[];

export type FromParam =
| ((localFieldValues: Primitive[], metadata: any) => any)
| object
| ((localFieldValues: Primitive[], metadata: any) => object[])
| object[];

export type AsMap =
Expand All @@ -146,15 +147,15 @@ The join method in `@objectwow/join` offers better performance compared to the j
### Traditional Approach (Database, Krakend, Hasura, GraphQL):

1. Loop through the original array.
2. `For each element, make a call` to the table/service containing the related data by its ID.
2. `For each element, make a call` to the `database/internal/external service` containing the related data by its `UID` (unique identifier).
3. Combine the data from both sources.
4. This results in a time complexity of `O(n x m)`, where `n` is the number of elements in the original array, and `m` is the number of elements fetched from the related table or service.

### @objectwow/join Approach:

1. Provides a `callback function` where the input is `an array of IDs`, allowing the developer to fetch related data from the table/service in a `single call`.
2. Uses JavaScript’s `new Map` to optimize the process, reducing the time complexity from O(m) to O(1), where m is the number of elements retrieved..
3. Combines the data efficiently after retrieving it in bulk through a `single call`.
1. Provides a `callback function` where the input is `UIDs`, allowing the developer to fetch related data from the `database/internal/external service` in a `single call`.
2. Uses JavaScript’s `new Map` to optimize the process, reducing the time complexity from O(m) to O(1), where `m` is the number of elements retrieved..
3. Combines the data efficiently after retrieving it in bulk through a single call.
4. This results in a time complexity of O(n), where `n` is the number of elements in the original array.

By fetching related data in bulk and leveraging efficient JavaScript data structures, `@objectwow/join` minimizes redundant calls and improves overall performance.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@objectwow/join",
"version": "0.2.5",
"version": "0.3.0",
"license": "MIT",
"description": "Join objects with functionality similar to MongoDB's $lookup",
"publishConfig": {
Expand Down
16 changes: 10 additions & 6 deletions src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,18 +244,22 @@ export class JoinData {
metadata?: any
): Promise<any[]> {
if (typeOf(from) === Types.Object) {
return [from];
throw new Error("from must be an array of objects");
}

if (typeOf(from) === Types.Array) {
return from as any[];
return from as object[];
}

const result = await (from as Function)(localFieldValues, metadata);
const fromArr =
typeOf(result) === Types.Array ? (result as object[]) : [result];
if (typeOf(from) === Types.Function) {
const result = await (from as Function)(localFieldValues, metadata);

return fromArr;
if (typeOf(result) === Types.Array) {
return result as object[];
}
}

throw new Error("from must be an array of objects");
}

protected generateResult(
Expand Down
5 changes: 2 additions & 3 deletions src/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ export type LocalValue = any | any[];
export type LocalParam = object | object[];

export type FromParam =
| ((localFieldValues: Primitive[], metadata: any) => any)
| object
| ((localFieldValues: Primitive[], metadata: any) => object[])
| object[];

export type AsMap =
Expand All @@ -24,7 +23,7 @@ export interface JoinDataParam {
local: LocalParam;

/**
* Object(s) or an asynchronous callback function that returns the data from the source.
* Objects or an asynchronous callback function that returns the data from the source.
*/
from: FromParam;

Expand Down
6 changes: 3 additions & 3 deletions tests/core.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ describe("JoinData - execute method full coverage", () => {
];

const param: JoinDataParam = {
from: fromFn,
from: await fromFn(),
local: [{ id: 1 }, { id: 2 }],
localField: "id",
fromField: "id",
Expand Down Expand Up @@ -98,7 +98,7 @@ describe("JoinData - execute method full coverage", () => {
];

const param: JoinDataParam = {
from: fromFn,
from: await fromFn(),
local: [{ id: 1 }, { id: 2 }],
localField: "id",
fromField: "id",
Expand Down Expand Up @@ -164,7 +164,7 @@ describe("JoinData - execute method full coverage", () => {
{ id: 2, items: [101, 201] },
];

const from = async () => [
const from = () => [
{ id: 101, product: "Product 101" },
{ id: 102, product: "Product 102" },
{ id: 201, product: "Product 201" },
Expand Down
20 changes: 0 additions & 20 deletions tests/singleton.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,24 +33,4 @@ describe("SingletonJoinData and joinData", () => {
expect(instance1).not.toBe(instance2);
});
});

describe("joinData function", () => {
it("should call the execute method of the JoinData instance with the correct parameters", async () => {
const params: JoinDataParam = {
from: () => ({}),
local: {},
localField: "id",
fromField: "foreignId",
as: "result",
};

const expectedResult: JoinDataResult = {
joinFailedValues: [],
allSuccess: true,
};

const result = await joinData(params);
expect(result).toEqual(expectedResult);
});
});
});