@pulseindex/sdk 3.1.0 → 3.2.0

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # Changelog
2
2
 
3
+ ## 3.2.0
4
+
5
+ ### Delete many entities in one call
6
+
7
+ `delete()` takes a single id, so clearing a catalogue meant one round trip per
8
+ row. There was no other way to do it through the API at all.
9
+
10
+ ```ts
11
+ for (const page of pages(idsToRemove, 10_000)) {
12
+ const { deletedCount } = await client.batchDelete(page);
13
+ }
14
+ ```
15
+
16
+ Up to 10,000 ids per call. A larger page is refused by name rather than
17
+ truncated, so a page that is too big fails loudly instead of deleting part of
18
+ itself and reporting success.
19
+
20
+ Ids that are unknown or already deleted are skipped rather than refused, so
21
+ retrying a page that half-applied is safe. `deletedCount` is the number of rows
22
+ that actually changed, which is lower than the number of ids you sent whenever
23
+ some were already gone.
24
+
3
25
  ## 3.1.0
4
26
 
5
27
  ### A radius no longer merges with your own OR
package/README.md CHANGED
@@ -140,6 +140,10 @@ await client.batchIndex([
140
140
  ]);
141
141
 
142
142
  await client.delete('1001');
143
+
144
+ // Clearing many rows: send ids in pages of up to 10,000. A larger page is
145
+ // refused by name rather than truncated.
146
+ await client.batchDelete([1002, 1003, 1004]);
143
147
  ```
144
148
 
145
149
  Low-level PHP-compatible helper:
@@ -293,6 +297,7 @@ than failing your own requests immediately; if it persists, contact support.
293
297
  | `client.index(id, attributes)` | `{ success }` | Upsert one entity |
294
298
  | `client.batchIndex(entities)` | `{ indexedCount }` | Batch upsert |
295
299
  | `client.delete(id)` | `{ success }` | Soft-delete an entity |
300
+ | `client.batchDelete(ids)` | `{ deletedCount }` | Soft-delete up to 10,000 entities in one call |
296
301
  | `client.health()` | `boolean` | Whether the service is ready to answer queries |
297
302
  | `client.servingStatus()` | `number` | Readiness as a status code, when you need more than a boolean |
298
303
  | `client.close()` | `void` | Shut down the channel pool |
package/dist/index.d.mts CHANGED
@@ -64,6 +64,13 @@ interface BatchIndexResponse {
64
64
  interface DeleteResponse {
65
65
  success: boolean;
66
66
  }
67
+ interface BatchDeleteResponse {
68
+ /**
69
+ * How many rows actually changed. Lower than the number of ids sent when
70
+ * some were unknown or already deleted, which is not an error.
71
+ */
72
+ deletedCount: number;
73
+ }
67
74
  interface RadiusOptions {
68
75
  lat: number;
69
76
  lng?: number;
@@ -233,6 +240,7 @@ interface SearchEngineServiceClient extends grpc.Client {
233
240
  indexEntity(request: unknown, metadata: grpc.Metadata, options: grpc.CallOptions, callback: grpc.requestCallback<unknown>): grpc.ClientUnaryCall;
234
241
  batchIndexEntities(request: unknown, metadata: grpc.Metadata, options: grpc.CallOptions, callback: grpc.requestCallback<unknown>): grpc.ClientUnaryCall;
235
242
  deleteEntity(request: unknown, metadata: grpc.Metadata, options: grpc.CallOptions, callback: grpc.requestCallback<unknown>): grpc.ClientUnaryCall;
243
+ batchDeleteEntities(request: unknown, metadata: grpc.Metadata, options: grpc.CallOptions, callback: grpc.requestCallback<unknown>): grpc.ClientUnaryCall;
236
244
  search(request: unknown, metadata: grpc.Metadata, options: grpc.CallOptions, callback: grpc.requestCallback<unknown>): grpc.ClientUnaryCall;
237
245
  }
238
246
  /** `grpc.health.v1.Health` — the readiness check that needs no scope. */
@@ -295,6 +303,26 @@ declare class PulseIndexClient implements QueryExecutor {
295
303
  batchIndex(entities: Array<EntityInput | BatchEntityInput>): Promise<BatchIndexResponse>;
296
304
  delete(entityId: EntityId, tenantId?: string): Promise<DeleteResponse>;
297
305
  deleteEntity(entityId: EntityId, tenantId?: string): Promise<boolean>;
306
+ /**
307
+ * Delete many entities in one call.
308
+ *
309
+ * `delete` takes a single id, so clearing a catalogue that way is one round
310
+ * trip per row. Send ids in pages of up to 10,000; the engine refuses a
311
+ * larger batch by name rather than truncating it, so a page that is too big
312
+ * fails loudly instead of deleting part of itself.
313
+ *
314
+ * Ids that are unknown or already deleted are skipped, so retrying a page
315
+ * that half-applied is safe. `deletedCount` is the number of rows that
316
+ * actually changed, which is lower than `entityIds.length` whenever some of
317
+ * them were already gone.
318
+ *
319
+ * ```ts
320
+ * for (const page of pages(allIds, 10_000)) {
321
+ * await client.batchDelete(page);
322
+ * }
323
+ * ```
324
+ */
325
+ batchDelete(entityIds: readonly EntityId[], tenantId?: string): Promise<BatchDeleteResponse>;
298
326
  /**
299
327
  * True only when the engine can serve reads.
300
328
  *
@@ -388,4 +416,4 @@ declare class PulseIndexQueryError extends PulseIndexError {
388
416
  constructor(message: string, options?: ConstructorParameters<typeof PulseIndexError>[1]);
389
417
  }
390
418
 
391
- export { type BatchEntityInput, type BatchIndexResponse, ConnectionManager, DEFAULT_LIMIT, type DeleteResponse, type EncodedEntity, type EntityAttributes, type EntityId, type EntityInput, FilterOperation, type FilterPredicate, GeoHash, type IndexEntityRequest, type IndexEntityResponse, PulseIndex, PulseIndexAuthError, PulseIndexClient, type PulseIndexClientConfig, PulseIndexConnectionError, PulseIndexError, PulseIndexQueryError, QueryBuilder, type RadiusOptions, type RangePredicate, SERVING_STATUS, type SearchQueryRequest, type SearchRequestOptions, type SearchResponse, type SortSpec, PulseIndex as default, encodeEntity, sslEnabled, toUint64String };
419
+ export { type BatchDeleteResponse, type BatchEntityInput, type BatchIndexResponse, ConnectionManager, DEFAULT_LIMIT, type DeleteResponse, type EncodedEntity, type EntityAttributes, type EntityId, type EntityInput, FilterOperation, type FilterPredicate, GeoHash, type IndexEntityRequest, type IndexEntityResponse, PulseIndex, PulseIndexAuthError, PulseIndexClient, type PulseIndexClientConfig, PulseIndexConnectionError, PulseIndexError, PulseIndexQueryError, QueryBuilder, type RadiusOptions, type RangePredicate, SERVING_STATUS, type SearchQueryRequest, type SearchRequestOptions, type SearchResponse, type SortSpec, PulseIndex as default, encodeEntity, sslEnabled, toUint64String };
package/dist/index.d.ts CHANGED
@@ -64,6 +64,13 @@ interface BatchIndexResponse {
64
64
  interface DeleteResponse {
65
65
  success: boolean;
66
66
  }
67
+ interface BatchDeleteResponse {
68
+ /**
69
+ * How many rows actually changed. Lower than the number of ids sent when
70
+ * some were unknown or already deleted, which is not an error.
71
+ */
72
+ deletedCount: number;
73
+ }
67
74
  interface RadiusOptions {
68
75
  lat: number;
69
76
  lng?: number;
@@ -233,6 +240,7 @@ interface SearchEngineServiceClient extends grpc.Client {
233
240
  indexEntity(request: unknown, metadata: grpc.Metadata, options: grpc.CallOptions, callback: grpc.requestCallback<unknown>): grpc.ClientUnaryCall;
234
241
  batchIndexEntities(request: unknown, metadata: grpc.Metadata, options: grpc.CallOptions, callback: grpc.requestCallback<unknown>): grpc.ClientUnaryCall;
235
242
  deleteEntity(request: unknown, metadata: grpc.Metadata, options: grpc.CallOptions, callback: grpc.requestCallback<unknown>): grpc.ClientUnaryCall;
243
+ batchDeleteEntities(request: unknown, metadata: grpc.Metadata, options: grpc.CallOptions, callback: grpc.requestCallback<unknown>): grpc.ClientUnaryCall;
236
244
  search(request: unknown, metadata: grpc.Metadata, options: grpc.CallOptions, callback: grpc.requestCallback<unknown>): grpc.ClientUnaryCall;
237
245
  }
238
246
  /** `grpc.health.v1.Health` — the readiness check that needs no scope. */
@@ -295,6 +303,26 @@ declare class PulseIndexClient implements QueryExecutor {
295
303
  batchIndex(entities: Array<EntityInput | BatchEntityInput>): Promise<BatchIndexResponse>;
296
304
  delete(entityId: EntityId, tenantId?: string): Promise<DeleteResponse>;
297
305
  deleteEntity(entityId: EntityId, tenantId?: string): Promise<boolean>;
306
+ /**
307
+ * Delete many entities in one call.
308
+ *
309
+ * `delete` takes a single id, so clearing a catalogue that way is one round
310
+ * trip per row. Send ids in pages of up to 10,000; the engine refuses a
311
+ * larger batch by name rather than truncating it, so a page that is too big
312
+ * fails loudly instead of deleting part of itself.
313
+ *
314
+ * Ids that are unknown or already deleted are skipped, so retrying a page
315
+ * that half-applied is safe. `deletedCount` is the number of rows that
316
+ * actually changed, which is lower than `entityIds.length` whenever some of
317
+ * them were already gone.
318
+ *
319
+ * ```ts
320
+ * for (const page of pages(allIds, 10_000)) {
321
+ * await client.batchDelete(page);
322
+ * }
323
+ * ```
324
+ */
325
+ batchDelete(entityIds: readonly EntityId[], tenantId?: string): Promise<BatchDeleteResponse>;
298
326
  /**
299
327
  * True only when the engine can serve reads.
300
328
  *
@@ -388,4 +416,4 @@ declare class PulseIndexQueryError extends PulseIndexError {
388
416
  constructor(message: string, options?: ConstructorParameters<typeof PulseIndexError>[1]);
389
417
  }
390
418
 
391
- export { type BatchEntityInput, type BatchIndexResponse, ConnectionManager, DEFAULT_LIMIT, type DeleteResponse, type EncodedEntity, type EntityAttributes, type EntityId, type EntityInput, FilterOperation, type FilterPredicate, GeoHash, type IndexEntityRequest, type IndexEntityResponse, PulseIndex, PulseIndexAuthError, PulseIndexClient, type PulseIndexClientConfig, PulseIndexConnectionError, PulseIndexError, PulseIndexQueryError, QueryBuilder, type RadiusOptions, type RangePredicate, SERVING_STATUS, type SearchQueryRequest, type SearchRequestOptions, type SearchResponse, type SortSpec, PulseIndex as default, encodeEntity, sslEnabled, toUint64String };
419
+ export { type BatchDeleteResponse, type BatchEntityInput, type BatchIndexResponse, ConnectionManager, DEFAULT_LIMIT, type DeleteResponse, type EncodedEntity, type EntityAttributes, type EntityId, type EntityInput, FilterOperation, type FilterPredicate, GeoHash, type IndexEntityRequest, type IndexEntityResponse, PulseIndex, PulseIndexAuthError, PulseIndexClient, type PulseIndexClientConfig, PulseIndexConnectionError, PulseIndexError, PulseIndexQueryError, QueryBuilder, type RadiusOptions, type RangePredicate, SERVING_STATUS, type SearchQueryRequest, type SearchRequestOptions, type SearchResponse, type SortSpec, PulseIndex as default, encodeEntity, sslEnabled, toUint64String };
package/dist/index.js CHANGED
@@ -1146,6 +1146,40 @@ var PulseIndexClient = class _PulseIndexClient {
1146
1146
  const response = await this.delete(entityId, tenantId || this.connection.tenantId);
1147
1147
  return response.success;
1148
1148
  }
1149
+ /**
1150
+ * Delete many entities in one call.
1151
+ *
1152
+ * `delete` takes a single id, so clearing a catalogue that way is one round
1153
+ * trip per row. Send ids in pages of up to 10,000; the engine refuses a
1154
+ * larger batch by name rather than truncating it, so a page that is too big
1155
+ * fails loudly instead of deleting part of itself.
1156
+ *
1157
+ * Ids that are unknown or already deleted are skipped, so retrying a page
1158
+ * that half-applied is safe. `deletedCount` is the number of rows that
1159
+ * actually changed, which is lower than `entityIds.length` whenever some of
1160
+ * them were already gone.
1161
+ *
1162
+ * ```ts
1163
+ * for (const page of pages(allIds, 10_000)) {
1164
+ * await client.batchDelete(page);
1165
+ * }
1166
+ * ```
1167
+ */
1168
+ async batchDelete(entityIds, tenantId) {
1169
+ const ids = entityIds.map((id, i) => toUint64String(id, `entityIds[${i}]`));
1170
+ const raw = await this.unary(
1171
+ (stub, metadata, options, callback) => stub.batchDeleteEntities(
1172
+ {
1173
+ entityIds: ids,
1174
+ tenantId: tenantId ?? this.connection.tenantId
1175
+ },
1176
+ metadata,
1177
+ options,
1178
+ callback
1179
+ )
1180
+ );
1181
+ return { deletedCount: Number(raw.deletedCount ?? 0) };
1182
+ }
1149
1183
  /**
1150
1184
  * True only when the engine can serve reads.
1151
1185
  *
package/dist/index.mjs CHANGED
@@ -1123,6 +1123,40 @@ var PulseIndexClient = class _PulseIndexClient {
1123
1123
  const response = await this.delete(entityId, tenantId || this.connection.tenantId);
1124
1124
  return response.success;
1125
1125
  }
1126
+ /**
1127
+ * Delete many entities in one call.
1128
+ *
1129
+ * `delete` takes a single id, so clearing a catalogue that way is one round
1130
+ * trip per row. Send ids in pages of up to 10,000; the engine refuses a
1131
+ * larger batch by name rather than truncating it, so a page that is too big
1132
+ * fails loudly instead of deleting part of itself.
1133
+ *
1134
+ * Ids that are unknown or already deleted are skipped, so retrying a page
1135
+ * that half-applied is safe. `deletedCount` is the number of rows that
1136
+ * actually changed, which is lower than `entityIds.length` whenever some of
1137
+ * them were already gone.
1138
+ *
1139
+ * ```ts
1140
+ * for (const page of pages(allIds, 10_000)) {
1141
+ * await client.batchDelete(page);
1142
+ * }
1143
+ * ```
1144
+ */
1145
+ async batchDelete(entityIds, tenantId) {
1146
+ const ids = entityIds.map((id, i) => toUint64String(id, `entityIds[${i}]`));
1147
+ const raw = await this.unary(
1148
+ (stub, metadata, options, callback) => stub.batchDeleteEntities(
1149
+ {
1150
+ entityIds: ids,
1151
+ tenantId: tenantId ?? this.connection.tenantId
1152
+ },
1153
+ metadata,
1154
+ options,
1155
+ callback
1156
+ )
1157
+ );
1158
+ return { deletedCount: Number(raw.deletedCount ?? 0) };
1159
+ }
1126
1160
  /**
1127
1161
  * True only when the engine can serve reads.
1128
1162
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pulseindex/sdk",
3
- "version": "3.1.0",
3
+ "version": "3.2.0",
4
4
  "description": "Official Node.js & TypeScript SDK for PulseIndex — hosted search and filtering for large entity sets",
5
5
  "license": "MIT",
6
6
  "author": "PulseIndex",
@@ -36,6 +36,10 @@ service SearchEngineService {
36
36
  // The entity id is excluded from subsequent Search results for that tenant.
37
37
  rpc DeleteEntity (DeleteEntityRequest) returns (DeleteEntityResponse);
38
38
 
39
+ // BatchDeleteEntities soft-deletes many entities in one RPC.
40
+ // Use this to clear a catalogue: DeleteEntity takes a single id.
41
+ rpc BatchDeleteEntities (BatchDeleteEntitiesRequest) returns (BatchDeleteEntitiesResponse);
42
+
39
43
  // Search applies the boolean filters (MUST / SHOULD / MUST_NOT) plus optional
40
44
  // numeric range predicates and returns matching entity IDs only.
41
45
  // When `limit` > 0, `total_matches` may be approximate; it is exact when
@@ -114,6 +118,28 @@ message DeleteEntityResponse {
114
118
  bool success = 1;
115
119
  }
116
120
 
121
+ // BatchDeleteEntitiesRequest deletes many entities in one tenant.
122
+ message BatchDeleteEntitiesRequest {
123
+ // Entity ids to delete. Ids that are unknown or already deleted are skipped
124
+ // rather than refused, so retrying a batch that half-applied is safe.
125
+ // Repeating an id inside one batch deletes it once.
126
+ //
127
+ // A batch above the server's maximum is refused with INVALID_ARGUMENT naming
128
+ // the ceiling, never silently truncated.
129
+ repeated uint64 entity_ids = 1;
130
+
131
+ // Tenant that owns the entities. Empty → "default". One tenant per batch.
132
+ string tenant_id = 2;
133
+ }
134
+
135
+ // BatchDeleteEntitiesResponse reports how many rows actually changed.
136
+ message BatchDeleteEntitiesResponse {
137
+ // Number of entities that were live and are now deleted. Lower than the
138
+ // number of ids sent when some were unknown or already deleted; that is not
139
+ // an error.
140
+ uint32 deleted_count = 1;
141
+ }
142
+
117
143
  // ---------------------------------------------------------------------------
118
144
  // Search predicates
119
145
  // ---------------------------------------------------------------------------