diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index d9043ba..8089323 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -9,7 +9,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: - node-version: 18 + node-version: 22 cache: npm - name: Install Puppeteer dependencies diff --git a/.nvmrc b/.nvmrc index 25bf17f..2bd5a0a 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -18 \ No newline at end of file +22 diff --git a/index.d.ts b/index.d.ts index f4f1faa..db6f409 100644 --- a/index.d.ts +++ b/index.d.ts @@ -98,6 +98,11 @@ interface BaseAdapterOptions { collectionSpecificFilterByOptions?: object; sortByOptions?: object; collectionSpecificSortByOptions?: object; + /** + * For Typesense versions before v30, set to true to use override_tags. + * For v30+, leave as false (default) to use curation_tags. + */ + useOverrideTags?: boolean; } type CollectionSearchParameters = Record>; diff --git a/package-lock.json b/package-lock.json index cc337e2..1b3799f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "typesense": "^2.1.0-8" + "typesense": "^3.0.0-2" }, "devDependencies": { "@babel/cli": "^7.24.1", @@ -12342,9 +12342,9 @@ } }, "node_modules/typesense": { - "version": "2.1.0-8", - "resolved": "https://registry.npmjs.org/typesense/-/typesense-2.1.0-8.tgz", - "integrity": "sha512-hJfmQdz5BWtPxdntthuohx7OqMVKiSJWD0rZw19Euti/tB7OMLq1ZBo/TatRpVnquJXM8c8EbLb9rhe0g8lGzA==", + "version": "3.0.0-2", + "resolved": "https://registry.npmjs.org/typesense/-/typesense-3.0.0-2.tgz", + "integrity": "sha512-MNNCozMqL7v+r/+x1A+6mmfOrxl14JhXsqlRxR/epLy2flShzIVvwLvnEeBXwWID0e0RUae3bpz1791YJiJL5A==", "license": "Apache-2.0", "dependencies": { "axios": "^1.8.4", @@ -21895,9 +21895,9 @@ "dev": true }, "typesense": { - "version": "2.1.0-8", - "resolved": "https://registry.npmjs.org/typesense/-/typesense-2.1.0-8.tgz", - "integrity": "sha512-hJfmQdz5BWtPxdntthuohx7OqMVKiSJWD0rZw19Euti/tB7OMLq1ZBo/TatRpVnquJXM8c8EbLb9rhe0g8lGzA==", + "version": "3.0.0-2", + "resolved": "https://registry.npmjs.org/typesense/-/typesense-3.0.0-2.tgz", + "integrity": "sha512-MNNCozMqL7v+r/+x1A+6mmfOrxl14JhXsqlRxR/epLy2flShzIVvwLvnEeBXwWID0e0RUae3bpz1791YJiJL5A==", "requires": { "axios": "^1.8.4", "loglevel": "^1.8.1", diff --git a/package.json b/package.json index cee2d0a..9f419f1 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "build:dist": "webpack", "test:type": "tsd", "build": "npm run build:lib && npm run build:dist", - "typesenseServer": "docker run -i -p 8108:8108 -v`pwd`/typesense-server-data/:/data typesense/typesense:29.0.rc29 --data-dir /data --api-key=xyz --listen-port 8108 --enable-cors", + "typesenseServer": "docker run -i -p 8108:8108 -v`pwd`/typesense-server-data/:/data typesense/typesense:30.0 --data-dir /data --api-key=xyz --listen-port 8108 --enable-cors", "createServerNodesFile": "echo '172.17.0.2:8107:8108,172.17.0.3:7107:7108,172.17.0.4:9107:9108' > `pwd`/typesense-server-nodes", "typesenseServer:0": "docker run -i -p 8108:8108 -p 8107:8107 -v/tmp/typesense-server-data-node-1/:/data -v`pwd`/typesense-server-nodes:/typesense-server-nodes typesense/typesense:0.19.0 --data-dir /data --api-key=xyz --listen-port 8108 --peering-port 8107 --enable-cors --nodes=/typesense-server-nodes", "typesenseServer:1": "docker run -i -p 7108:7108 -p 7107:7107 -v/tmp/.typesense-server-data-node-2/:/data -v`pwd`/typesense-server-nodes:/typesense-server-nodes typesense/typesense:0.19.0 --data-dir /data --api-key=xyz --listen-port 7108 --peering-port 7107 --enable-cors --nodes=/typesense-server-nodes", @@ -84,12 +84,12 @@ "webpack-cli": "^5.1.4" }, "dependencies": { - "typesense": "^2.1.0-8" + "typesense": "^3.0.0-2" }, "peerDependencies": { "@babel/runtime": "^7.24.1" }, "engines": { - "node": ">=18" + "node": ">=22" } } diff --git a/src/Configuration.js b/src/Configuration.js index 5db8339..5a4b176 100644 --- a/src/Configuration.js +++ b/src/Configuration.js @@ -60,6 +60,8 @@ export class Configuration { this.collectionSpecificFilterByOptions = options.collectionSpecificFilterByOptions ?? {}; this.collectionSpecificSortByOptions = options.collectionSpecificSortByOptions ?? {}; this.union = options.union ?? false; + // For Typesense v30+, use curation_tags. Set to true for older versions that use override_tags. + this.useOverrideTags = options.useOverrideTags ?? false; } validate() { diff --git a/src/SearchRequestAdapter.js b/src/SearchRequestAdapter.js index 3502621..2d64e2d 100644 --- a/src/SearchRequestAdapter.js +++ b/src/SearchRequestAdapter.js @@ -435,7 +435,8 @@ export class SearchRequestAdapter { } if (params.ruleContexts && params.ruleContexts.length > 0) { - typesenseSearchParams.override_tags = this._adaptRulesContextsToOverrideTags(params.ruleContexts); + const tagsParamName = this.configuration.useOverrideTags ? "override_tags" : "curation_tags"; + typesenseSearchParams[tagsParamName] = this._adaptRulesContextsToOverrideTags(params.ruleContexts); } // If a custom vector query is specified, set q=* diff --git a/test/SearchRequestAdpater.test.js b/test/SearchRequestAdpater.test.js index 04e2f85..09d121e 100644 --- a/test/SearchRequestAdpater.test.js +++ b/test/SearchRequestAdpater.test.js @@ -75,7 +75,7 @@ describe("SearchRequestAdapter", () => { enable_overrides: false, }); - //with an override tag + //with curation tags (default for v30+) result = subject._buildSearchParameters({ indexName: "collection2", params: { ruleContexts: ["context1", "context2"] }, @@ -84,6 +84,28 @@ describe("SearchRequestAdapter", () => { collection: "collection2", page: 1, q: "*", + curation_tags: "context1,context2", + }); + }); + }); + + describe("when useOverrideTags is true (for pre-v30)", () => { + it("uses override_tags instead of curation_tags", () => { + const subject = new SearchRequestAdapter( + [], + null, + new Configuration({ + useOverrideTags: true, + }), + ); + const result = subject._buildSearchParameters({ + indexName: "collection1", + params: { ruleContexts: ["context1", "context2"] }, + }); + expect(result).toEqual({ + collection: "collection1", + page: 1, + q: "*", override_tags: "context1,context2", }); }); diff --git a/test/support/populateProductsIndex.js b/test/support/populateProductsIndex.js index a4f1d14..23b4b2e 100644 --- a/test/support/populateProductsIndex.js +++ b/test/support/populateProductsIndex.js @@ -14,32 +14,39 @@ module.exports = (async () => { retryIntervalSeconds: 5, }); - const overrideWithoutTag = { - rule: { - query: "Samsung", - match: "contains", - }, - remove_matched_tokens: false, - metadata: { - promo_content: "20% on all Samsung Phones!", - }, - }; - - const overrideWithTag = { - rule: { - query: "Google", - match: "contains", - tags: ["Google"], - }, - remove_matched_tokens: false, - metadata: { - promo_content: "New Google Pixel!", - }, + const curationSetName = "products-curations"; + const curationSet = { + items: [ + { + id: "samsung-override", + rule: { + query: "Samsung", + match: "contains", + }, + remove_matched_tokens: false, + metadata: { + promo_content: "20% on all Samsung Phones!", + }, + }, + { + id: "google-override", + rule: { + query: "Google", + match: "contains", + tags: ["Google"], + }, + remove_matched_tokens: false, + metadata: { + promo_content: "New Google Pixel!", + }, + }, + ], }; const schema = { name: "products", enable_nested_fields: true, + curation_sets: [curationSetName], fields: [ { name: "name", @@ -103,8 +110,8 @@ module.exports = (async () => { try { const collection = await typesense.collections("products").retrieve(); console.log("Found existing schema"); - await typesense.collections("products").overrides().upsert("samsung-override", overrideWithoutTag); - await typesense.collections("products").overrides().upsert("google-override", overrideWithTag); + await typesense.curationSets(curationSetName).upsert(curationSet); + await typesense.collections("products").update({ curation_sets: [curationSetName] }); // console.log(JSON.stringify(collection, null, 2)); if (collection.num_documents !== products.length || process.env.FORCE_REINDEX === "true") { @@ -122,6 +129,7 @@ module.exports = (async () => { console.log("Creating schema: "); console.log(JSON.stringify(schema, null, 2)); + await typesense.curationSets(curationSetName).upsert(curationSet); await typesense.collections().create(schema); // const collectionRetrieved = await typesense @@ -142,8 +150,7 @@ module.exports = (async () => { }); }); - await typesense.collections("products").overrides().upsert("samsung-override", overrideWithoutTag); - await typesense.collections("products").overrides().upsert("google-override", overrideWithTag); + await typesense.curationSets(curationSetName).upsert(curationSet); try { const returnData = await typesense.collections("products").documents().import(products);