applesauce-sqlite 0.0.0-next-20251015123951 → 0.0.0-next-20251117143754

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,10 +1,10 @@
1
- import { Filter, NostrEvent } from "applesauce-core/helpers";
1
+ import { FilterWithAnd, NostrEvent } from "applesauce-core/helpers";
2
2
  import type { Statement } from "./statements.js";
3
3
  export declare const CREATE_SEARCH_TABLE_STATEMENT: Statement<[]>;
4
4
  export declare const INSERT_SEARCH_CONTENT_STATEMENT: Statement<[string, string, number, string, number]>;
5
5
  export declare const DELETE_SEARCH_CONTENT_STATEMENT: Statement<[string]>;
6
- /** Filter with search field */
7
- export type FilterWithSearch = Filter & {
6
+ /** Filter with search field and NIP-ND AND operator support */
7
+ export type FilterWithSearch = FilterWithAnd & {
8
8
  search?: string;
9
9
  order?: "created_at" | "rank";
10
10
  };
@@ -49,19 +49,49 @@ export function buildFilterConditions(filter) {
49
49
  conditions.push(`events.created_at <= ?`);
50
50
  params.push(filter.until);
51
51
  }
52
- // Handle tag filters (e.g., #e, #p, #t, #d, etc.)
52
+ // Handle AND tag filters (& prefix) first - NIP-ND
53
+ // AND takes precedence and requires ALL values to be present
54
+ for (const [key, values] of Object.entries(filter)) {
55
+ if (key.startsWith("&") && values && Array.isArray(values) && values.length > 0) {
56
+ const tagName = key.slice(1); // Remove the '&' prefix
57
+ // Use a single subquery with GROUP BY + HAVING COUNT for efficiency
58
+ // This is more efficient than multiple JOINs for the same tag
59
+ const placeholders = values.map(() => "?").join(", ");
60
+ conditions.push(`events.id IN (
61
+ SELECT event_id
62
+ FROM event_tags
63
+ WHERE tag_name = ? AND tag_value IN (${placeholders})
64
+ GROUP BY event_id
65
+ HAVING COUNT(DISTINCT tag_value) = ?
66
+ )`);
67
+ // Add parameters: tagName, all tag values, then count of values
68
+ params.push(tagName, ...values, values.length);
69
+ }
70
+ }
71
+ // Handle OR tag filters (# prefix)
72
+ // Skip values that are in AND tags (NIP-ND rule)
53
73
  for (const [key, values] of Object.entries(filter)) {
54
74
  if (key.startsWith("#") && values && Array.isArray(values) && values.length > 0) {
55
75
  const tagName = key.slice(1); // Remove the '#' prefix
76
+ // Check if there's a corresponding AND filter for this tag
77
+ const andKey = `&${tagName}`;
78
+ const andValues = filter[andKey];
79
+ // Filter out values that are in AND tags (NIP-ND rule)
80
+ const filteredValues = andValues
81
+ ? values.filter((v) => !andValues.includes(v))
82
+ : values;
83
+ // Only apply OR filter if there are values left after filtering
84
+ if (filteredValues.length === 0)
85
+ continue;
56
86
  // Use the event_tags table for efficient tag filtering
57
- const placeholders = values.map(() => "?").join(", ");
87
+ const placeholders = filteredValues.map(() => "?").join(", ");
58
88
  conditions.push(`events.id IN (
59
89
  SELECT DISTINCT event_id
60
90
  FROM event_tags
61
91
  WHERE tag_name = ? AND tag_value IN (${placeholders})
62
92
  )`);
63
- // Add parameters: tagName first, then all the tag values
64
- params.push(tagName, ...values);
93
+ // Add parameters: tagName first, then all the filtered tag values
94
+ params.push(tagName, ...filteredValues);
65
95
  }
66
96
  }
67
97
  return { conditions, params, search };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "applesauce-sqlite",
3
- "version": "0.0.0-next-20251015123951",
3
+ "version": "0.0.0-next-20251117143754",
4
4
  "description": "sqlite event databases for applesauce",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -102,7 +102,7 @@
102
102
  }
103
103
  },
104
104
  "dependencies": {
105
- "applesauce-core": "0.0.0-next-20251015123951"
105
+ "applesauce-core": "0.0.0-next-20251117143754"
106
106
  },
107
107
  "optionalDependencies": {
108
108
  "@libsql/client": "^0.15.15",
@@ -114,7 +114,7 @@
114
114
  "@hirez_io/observer-spy": "^2.2.0",
115
115
  "@types/better-sqlite3": "^7.6.13",
116
116
  "@types/ws": "^8.5.13",
117
- "applesauce-signers": "0.0.0-next-20251015123951",
117
+ "applesauce-signers": "^4.1.0",
118
118
  "nostr-tools": "^2.17.0",
119
119
  "rimraf": "^6.0.1",
120
120
  "typescript": "^5.7.3",