Repository: maxnowack/signaldb
Pull request #257 (2023-10-11 19:26:17)

fix: ping mingo to 6.4.6
there is a bug introduced in 6.4.7 that breaks building in some environments

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/maxnowack/signaldb/pull/257?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Max+Nowack) Report
> Merging [#257](https://app.codecov.io/gh/maxnowack/signaldb/pull/257?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Max+Nowack) (8439e3e) into [main](https://app.codecov.io/gh/maxnowack/signaldb/commit/dfb9331d66268077a037ecd0d9315689091a7b6e?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Max+Nowack) (dfb9331) will **not change** coverage.
> Report is 1 commits behind head on main.
> The diff coverage is `n/a`.

```diff
@@            Coverage Diff            @@
##              main      #257   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           18        18           
  Lines          371       371           
  Branches        93        93           
=========================================
  Hits           371       371           
```





Patch:
diff --git a/package-lock.json b/package-lock.json
index 58d7ce7..5761e3c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,7 +10,7 @@
       "license": "MIT",
       "dependencies": {
         "fast-sort": "^3.4.0",
-        "mingo": "^6.4.4"
+        "mingo": "6.4.6"
       },
       "devDependencies": {
         "@commitlint/cli": "17.7.2",
@@ -6351,9 +6351,9 @@
       }
     },
     "node_modules/mingo": {
-      "version": "6.4.7",
-      "resolved": "https://registry.npmjs.org/mingo/-/mingo-6.4.7.tgz",
-      "integrity": "sha512-9CxaYNHl64KG/IPhIpTzNHajHB7HMj51bKy51Gdi3QzLOEbpK2mWzvs0U/3GCysytBycrkTjbNSP6GZ0rgh0hg=="
+      "version": "6.4.6",
+      "resolved": "https://registry.npmjs.org/mingo/-/mingo-6.4.6.tgz",
+      "integrity": "sha512-SMp06Eo5iEthCPpKXgEZ6DTZKxknpTqj49YN6iHpapj9DKltBCv0RFu+0mBBjMU0SiHR9pYkurkk74+VFGTqxw=="
     },
     "node_modules/minimalistic-assert": {
       "version": "1.0.1",
diff --git a/package.json b/package.json
index 7535385..3a15479 100644
--- a/package.json
+++ b/package.json
@@ -103,6 +103,6 @@
   },
   "dependencies": {
     "fast-sort": "^3.4.0",
-    "mingo": "^6.4.4"
+    "mingo": "6.4.6"
   }
 }
diff --git a/src/Collection/Cursor.ts b/src/Collection/Cursor.ts
index 71e5268..be8d1ee 100644
--- a/src/Collection/Cursor.ts
+++ b/src/Collection/Cursor.ts
@@ -56,7 +56,14 @@ export default class Cursor<T extends BaseItem, U = T> {
     const sorted = sort ? sortItems(filtered, sort) : filtered
     const skipped = skip ? sorted.slice(skip) : sorted
     const limited = limit ? skipped.slice(0, limit) : skipped
-    return limited.map(item => (this.options.fields ? project(item, this.options.fields) : item))
+    const idExcluded = this.options.fields && this.options.fields.id === 0
+    return limited.map((item) => {
+      if (!this.options.fields) return item
+      return {
+        ...idExcluded ? {} : { id: item.id },
+        ...project(item, this.options.fields),
+      }
+    })
   }
 
   private depend(changeEvents: { [P in keyof ObserveCallbacks<U>]?: true }) {
diff --git a/src/utils/project.spec.ts b/src/utils/project.spec.ts
index 5aa6536..4d51ef4 100644
--- a/src/utils/project.spec.ts
+++ b/src/utils/project.spec.ts
@@ -30,6 +30,23 @@ describe('project', () => {
     })
   })
 
+  it('should limit fields by disabling fields', () => {
+    const result = project(obj, {
+      age: 0,
+      address: 0,
+    })
+
+    expect(result).toEqual({
+      name: 'John Doe',
+      hobbies: ['reading', 'gaming'],
+    })
+  })
+
+  it('should remove nulls if deactivated', () => {
+    const result = project({ foo: null }, { foo: 0 })
+    expect(result).toEqual({})
+  })
+
   it('should handle nested fields that do not exist in the object', () => {
     const result = project(obj, {
       'address.zipCode': 1,
diff --git a/src/utils/project.ts b/src/utils/project.ts
index e2e5ab4..8b4f170 100644
--- a/src/utils/project.ts
+++ b/src/utils/project.ts
@@ -5,6 +5,16 @@ export default function project<T extends Record<string, any>>(
   item: T,
   fields: { [P in keyof T]?: 0 | 1 } & Record<string, 0 | 1>,
 ) {
+  const allFieldsDeactivated = Object.values(fields).every(value => value === 0)
+  if (allFieldsDeactivated) {
+    const result = { ...item }
+    Object.keys(fields).forEach((key) => {
+      const fieldValue = get(item, key)
+      if (fieldValue === undefined) return
+      set(result, key, undefined, true)
+    })
+    return result
+  }
   const result = {} as T
   Object.entries(fields).forEach(([key, value]) => {
     const fieldValue = get(item, key)
diff --git a/src/utils/set.spec.ts b/src/utils/set.spec.ts
index 6ca58e5..f546352 100644
--- a/src/utils/set.spec.ts
+++ b/src/utils/set.spec.ts
@@ -31,4 +31,10 @@ describe('set', () => {
     const result = set(obj, 'foo', 'baz')
     expect(result).toBe(obj)
   })
+
+  it('should delete the property if the value is undefined', () => {
+    const obj = { foo: 'bar', bar: 'baz' }
+    const result = set(obj, 'foo', undefined, true)
+    expect(result).toEqual({ bar: 'baz' })
+  })
 })
diff --git a/src/utils/set.ts b/src/utils/set.ts
index f038f8e..49cdde0 100644
--- a/src/utils/set.ts
+++ b/src/utils/set.ts
@@ -3,6 +3,7 @@ export default function set<T extends object, K>(
   obj: T,
   path: string,
   value: K,
+  deleteIfUndefined = false,
 ): T {
   if (obj == null) return obj
   const segments = path.split(/[.[\]]/g)
@@ -18,6 +19,10 @@ export default function set<T extends object, K>(
       }
       apply(node[key])
     } else {
+      if (deleteIfUndefined && value === undefined) {
+        delete node[segments[0]]
+        return
+      }
       node[segments[0]] = value
     }
   }


Test patch:
diff --git a/__tests__/Cursor.spec.ts b/__tests__/Cursor.spec.ts
index ab69561..a327f00 100644
--- a/__tests__/Cursor.spec.ts
+++ b/__tests__/Cursor.spec.ts
@@ -13,9 +13,9 @@ describe('Cursor', () => {
   }
 
   const items: TestItem[] = [
-    { id: 1, name: 'Item 1' },
-    { id: 2, name: 'Item 2' },
-    { id: 3, name: 'Item 3' },
+    { id: 1, name: 'Item 1', test: true },
+    { id: 2, name: 'Item 2', test: false },
+    { id: 3, name: 'Item 3', test: true },
   ]
 
   const collection = new Collection<TestItem>()
@@ -66,6 +66,37 @@ describe('Cursor', () => {
       expect(result).toEqual(expected)
     })
 
+    it('should return projected items when fields option is provided', () => {
+      expect(collection.find({}, { fields: { id: 1 } }).fetch()).toEqual([
+        { id: 1 },
+        { id: 2 },
+        { id: 3 },
+      ])
+      expect(collection.find({}, { fields: { name: 1 } }).fetch()).toEqual([
+        { id: 1, name: 'Item 1' },
+        { id: 2, name: 'Item 2' },
+        { id: 3, name: 'Item 3' },
+      ])
+      expect(collection.find({}, { fields: { name: 0 } }).fetch()).toEqual([
+        { id: 1, test: true },
+        { id: 2, test: false },
+        { id: 3, test: true },
+      ])
+    })
+
+    it('should include the id when when fields option is provided', () => {
+      expect(collection.find({}, { fields: { name: 1 } }).fetch()).toEqual([
+        { id: 1, name: 'Item 1' },
+        { id: 2, name: 'Item 2' },
+        { id: 3, name: 'Item 3' },
+      ])
+      expect(collection.find({}, { fields: { id: 0 } }).fetch()).toEqual([
+        { name: 'Item 1', test: true },
+        { name: 'Item 2', test: false },
+        { name: 'Item 3', test: true },
+      ])
+    })
+
     it('should return projected, sorted, limited, and skipped items when options are provided', () => {
       const cursor = collection.find({
         id: { $gt: 1 },