Skip to content

Commit 1c86e3a

Browse files
authored
Merge pull request #260 from tharropoulos/v28-changes
Incorporate v28 changes
2 parents 7652a07 + 796e8a5 commit 1c86e3a

10 files changed

+278
-9
lines changed

src/Typesense/Client.ts

+3
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import Stopwords from "./Stopwords";
2121
import Stopword from "./Stopword";
2222
import Conversations from "./Conversations";
2323
import Conversation from "./Conversation";
24+
import Stemming from "./Stemming";
2425

2526
export default class Client {
2627
configuration: Configuration;
@@ -32,6 +33,7 @@ export default class Client {
3233
operations: Operations;
3334
multiSearch: MultiSearch;
3435
analytics: Analytics;
36+
stemming: Stemming;
3537
private readonly _collections: Collections;
3638
private readonly individualCollections: Record<string, Collection>;
3739
private readonly _aliases: Aliases;
@@ -67,6 +69,7 @@ export default class Client {
6769
this._stopwords = new Stopwords(this.apiCall);
6870
this.individualStopwords = {};
6971
this.analytics = new Analytics(this.apiCall);
72+
this.stemming = new Stemming(this.apiCall);
7073
this._conversations = new Conversations(this.apiCall);
7174
this.individualConversations = {};
7275
}

src/Typesense/Collection.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export type FieldType =
1515
| "float"
1616
| "bool"
1717
| "geopoint"
18+
| "geopolygon"
1819
| "geopoint[]"
1920
| "string[]"
2021
| "int32[]"
@@ -27,7 +28,11 @@ export type FieldType =
2728
| "string*"
2829
| "image";
2930

30-
export interface CollectionFieldSchema {
31+
export interface CollectionFieldSchema
32+
extends Pick<
33+
CollectionCreateSchema,
34+
"token_separators" | "symbols_to_index"
35+
> {
3136
name: string;
3237
type: FieldType;
3338
optional?: boolean;

src/Typesense/Documents.ts

+11-5
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,16 @@ import { ImportError } from "./Errors";
55
import { SearchOnlyDocuments } from "./SearchOnlyDocuments";
66

77
// Todo: use generic to extract filter_by values
8-
export interface DeleteQuery {
9-
filter_by?: string;
10-
batch_size?: number;
11-
ignore_not_found?: boolean;
12-
}
8+
export type DeleteQuery =
9+
| {
10+
truncate?: true;
11+
}
12+
| {
13+
truncate?: never;
14+
filter_by?: string;
15+
batch_size?: number;
16+
ignore_not_found?: boolean;
17+
};
1318

1419
export interface DeleteResponse {
1520
num_deleted: number;
@@ -93,6 +98,7 @@ export interface SearchParams {
9398
query_by_weights?: string | number[];
9499
prefix?: string | boolean | boolean[]; // default: true
95100
filter_by?: string;
101+
max_filter_by_candidates?: number; // default: 4
96102
enable_synonyms?: boolean; // default: true
97103
enable_analytics?: boolean; // default: true
98104
filter_curated_hits?: boolean; // default: false

src/Typesense/MultiSearch.ts

+2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const RESOURCEPATH = "/multi_search";
1313

1414
export interface MultiSearchRequestSchema extends SearchParams {
1515
collection?: string;
16+
rerank_hybrid_matches?: boolean;
1617
"x-typesense-api-key"?: string;
1718
}
1819

@@ -23,6 +24,7 @@ export interface MultiSearchRequestWithPresetSchema
2324
}
2425

2526
export interface MultiSearchRequestsSchema {
27+
union?: true;
2628
searches: (MultiSearchRequestSchema | MultiSearchRequestWithPresetSchema)[];
2729
}
2830

src/Typesense/Operations.ts

+8-3
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,19 @@ export default class Operations {
66
constructor(private apiCall: ApiCall) {}
77

88
async perform(
9-
// eslint-disable-next-line @typescript-eslint/ban-types -- Can't use `object` here, it needs to intersect with `{}`
10-
operationName: "vote" | "snapshot" | "cache/clear" | (string & {}),
9+
operationName:
10+
| "vote"
11+
| "snapshot"
12+
| "cache/clear"
13+
| "schema_changes"
14+
// eslint-disable-next-line @typescript-eslint/ban-types -- Can't use `object` here, it needs to intersect with `{}`
15+
| (string & {}),
1116
queryParameters: Record<string, any> = {},
1217
): Promise<any> {
1318
return this.apiCall.post(
1419
`${RESOURCEPATH}/${operationName}`,
1520
{},
16-
queryParameters
21+
queryParameters,
1722
);
1823
}
1924
}

src/Typesense/Stemming.ts

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import type ApiCall from "./ApiCall";
2+
import StemmingDictionaries from "./StemmingDictionaries";
3+
import StemmingDictionary from "./StemmingDictionary";
4+
5+
const RESOURCEPATH = "/stemming";
6+
7+
export default class Stemming {
8+
private readonly _stemmingDictionaries: StemmingDictionaries;
9+
private readonly individualStemmingDictionaries: Record<
10+
string,
11+
StemmingDictionary
12+
> = {};
13+
14+
constructor(private readonly apiCall: ApiCall) {
15+
this.apiCall = apiCall;
16+
this._stemmingDictionaries = new StemmingDictionaries(this.apiCall);
17+
}
18+
19+
dictionaries(): StemmingDictionaries;
20+
dictionaries(id: string): StemmingDictionary;
21+
dictionaries(id?: string): StemmingDictionaries | StemmingDictionary {
22+
if (id === undefined) {
23+
return this._stemmingDictionaries;
24+
} else {
25+
if (this.individualStemmingDictionaries[id] === undefined) {
26+
this.individualStemmingDictionaries[id] = new StemmingDictionary(
27+
id,
28+
this.apiCall,
29+
);
30+
}
31+
return this.individualStemmingDictionaries[id];
32+
}
33+
}
34+
35+
static get RESOURCEPATH() {
36+
return RESOURCEPATH;
37+
}
38+
}

src/Typesense/StemmingDictionaries.ts

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import ApiCall from "./ApiCall";
2+
import type { StemmingDictionaryCreateSchema } from "./StemmingDictionary";
3+
4+
const RESOURCEPATH = "/stemming/dictionaries";
5+
6+
export interface StemmingDictionariesRetrieveSchema {
7+
dictionaries: string[];
8+
}
9+
10+
export default class StemmingDictionaries {
11+
constructor(private readonly apiCall: ApiCall) {
12+
this.apiCall = apiCall;
13+
}
14+
15+
async upsert(
16+
id: string,
17+
wordRootCombinations: StemmingDictionaryCreateSchema[] | string,
18+
): Promise<StemmingDictionaryCreateSchema[] | string> {
19+
const wordRootCombinationsInJSONLFormat = Array.isArray(
20+
wordRootCombinations,
21+
)
22+
? wordRootCombinations.map((combo) => JSON.stringify(combo)).join("\n")
23+
: wordRootCombinations;
24+
25+
const resultsInJSONLFormat = await this.apiCall.performRequest<string>(
26+
27+
"post",
28+
this.endpointPath("import"),
29+
{
30+
queryParameters: {id},
31+
bodyParameters: wordRootCombinationsInJSONLFormat,
32+
additionalHeaders: {"Content-Type": "text/plain"},
33+
skipConnectionTimeout: true,
34+
}
35+
);
36+
37+
return Array.isArray(wordRootCombinations)
38+
? resultsInJSONLFormat
39+
.split("\n")
40+
.map((line) => JSON.parse(line) as StemmingDictionaryCreateSchema)
41+
: resultsInJSONLFormat;
42+
}
43+
44+
async retrieve(): Promise<StemmingDictionariesRetrieveSchema> {
45+
return this.apiCall.get<StemmingDictionariesRetrieveSchema>(
46+
this.endpointPath(),
47+
);
48+
}
49+
50+
private endpointPath(operation?: string): string {
51+
return operation === undefined
52+
? `${StemmingDictionaries.RESOURCEPATH}`
53+
: `${StemmingDictionaries.RESOURCEPATH}/${encodeURIComponent(operation)}`;
54+
}
55+
56+
static get RESOURCEPATH() {
57+
return RESOURCEPATH;
58+
}
59+
}

src/Typesense/StemmingDictionary.ts

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import ApiCall from "./ApiCall";
2+
import StemmingDictionaries from "./StemmingDictionaries";
3+
4+
export interface StemmingDictionaryCreateSchema {
5+
root: string;
6+
word: string;
7+
}
8+
9+
export interface StemmingDictionarySchema {
10+
id: string;
11+
words: StemmingDictionaryCreateSchema[];
12+
}
13+
14+
export default class StemmingDictionary {
15+
constructor(
16+
private id: string,
17+
private apiCall: ApiCall,
18+
) {}
19+
20+
async retrieve(): Promise<StemmingDictionarySchema> {
21+
return this.apiCall.get<StemmingDictionarySchema>(this.endpointPath());
22+
}
23+
24+
private endpointPath(): string {
25+
return `${StemmingDictionaries.RESOURCEPATH}/${encodeURIComponent(this.id)}`;
26+
}
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import chai from "chai";
2+
import chaiAsPromised from "chai-as-promised";
3+
import { Client as TypesenseClient } from "../../src/Typesense";
4+
import ApiCall from "../../src/Typesense/ApiCall";
5+
import axios from "axios";
6+
import MockAxiosAdapter from "axios-mock-adapter";
7+
8+
let expect = chai.expect;
9+
chai.use(chaiAsPromised);
10+
11+
describe("StemmingDictionaries", function () {
12+
let typesense;
13+
let stemmingDictionaries;
14+
let apiCall;
15+
let mockAxios;
16+
17+
beforeEach(function () {
18+
typesense = new TypesenseClient({
19+
nodes: [
20+
{
21+
host: "node0",
22+
port: "8108",
23+
protocol: "http",
24+
},
25+
],
26+
apiKey: "abcd",
27+
randomizeNodes: false,
28+
});
29+
stemmingDictionaries = typesense.stemming.dictionaries();
30+
apiCall = new ApiCall(typesense.configuration);
31+
mockAxios = new MockAxiosAdapter(axios);
32+
});
33+
34+
describe(".retrieve", function () {
35+
it("retrieves all stemming dictionaries", function (done) {
36+
mockAxios
37+
.onGet(
38+
apiCall.uriFor(
39+
"/stemming/dictionaries",
40+
typesense.configuration.nodes[0],
41+
),
42+
undefined,
43+
{
44+
Accept: "application/json, text/plain, */*",
45+
"Content-Type": "application/json",
46+
"X-TYPESENSE-API-KEY": typesense.configuration.apiKey,
47+
},
48+
)
49+
.reply(200, { dictionaries: ["set1", "set2"] });
50+
51+
let returnData = stemmingDictionaries.retrieve();
52+
53+
expect(returnData)
54+
.to.eventually.deep.equal({
55+
dictionaries: ["set1", "set2"],
56+
})
57+
.notify(done);
58+
});
59+
});
60+
});
+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import chai from "chai";
2+
import chaiAsPromised from "chai-as-promised";
3+
import { Client as TypesenseClient } from "../../src/Typesense";
4+
import ApiCall from "../../src/Typesense/ApiCall";
5+
import axios from "axios";
6+
import MockAxiosAdapter from "axios-mock-adapter";
7+
8+
let expect = chai.expect;
9+
chai.use(chaiAsPromised);
10+
11+
describe("StemmingDictionary", function () {
12+
let typesense;
13+
let stemmingDictionary;
14+
let apiCall;
15+
let mockAxios;
16+
17+
beforeEach(function () {
18+
typesense = new TypesenseClient({
19+
nodes: [
20+
{
21+
host: "node0",
22+
port: "8108",
23+
protocol: "http",
24+
},
25+
],
26+
apiKey: "abcd",
27+
randomizeNodes: false,
28+
});
29+
stemmingDictionary = typesense.stemming.dictionaries("set1");
30+
apiCall = new ApiCall(typesense.configuration);
31+
mockAxios = new MockAxiosAdapter(axios);
32+
});
33+
34+
describe(".retrieve", function () {
35+
it("retrieves the dictionary", function (done) {
36+
mockAxios
37+
.onGet(
38+
apiCall.uriFor(
39+
"/stemming/dictionaries/set1",
40+
typesense.configuration.nodes[0],
41+
),
42+
null,
43+
{
44+
Accept: "application/json, text/plain, */*",
45+
"Content-Type": "application/json",
46+
"X-TYPESENSE-API-KEY": typesense.configuration.apiKey,
47+
},
48+
)
49+
.reply(200, {
50+
id: "set1",
51+
words: [{ word: "people", root: "person" }],
52+
});
53+
54+
let returnData = stemmingDictionary.retrieve();
55+
56+
expect(returnData)
57+
.to.eventually.deep.equal({
58+
id: "set1",
59+
words: [{ word: "people", root: "person" }],
60+
})
61+
.notify(done);
62+
});
63+
});
64+
});

0 commit comments

Comments
 (0)