@tinacms/search 0.0.0-ef282d9-20241024212433 → 0.0.0-f1cec43-20251216232909
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/README.md +55 -0
- package/dist/client/index.d.ts +28 -13
- package/dist/fuzzy/cache.d.ts +11 -0
- package/dist/fuzzy/distance.d.ts +15 -0
- package/dist/fuzzy/index.d.ts +4 -0
- package/dist/fuzzy/types.d.ts +19 -0
- package/dist/fuzzy-search-wrapper.d.ts +46 -0
- package/dist/index-client.d.ts +30 -13
- package/dist/index-client.js +189 -205
- package/dist/index.d.ts +6 -1
- package/dist/index.js +545 -209
- package/dist/indexer/index.d.ts +1 -0
- package/dist/indexer/utils.d.ts +1 -1
- package/dist/pagination.d.ts +22 -0
- package/dist/types.d.ts +30 -11
- package/package.json +18 -19
- package/dist/index-client.mjs +0 -195
package/README.md
CHANGED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# @tinacms/search
|
|
2
|
+
|
|
3
|
+
Full-text search for TinaCMS powered by [search-index](https://github.com/fergiemcdowall/search-index).
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @tinacms/search
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { LocalSearchIndexClient } from "@tinacms/search";
|
|
15
|
+
|
|
16
|
+
const client = new LocalSearchIndexClient({
|
|
17
|
+
stopwordLanguages: ["eng"],
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
await client.onStartIndexing();
|
|
21
|
+
|
|
22
|
+
await client.put([
|
|
23
|
+
{
|
|
24
|
+
_id: "1",
|
|
25
|
+
title: "Getting Started",
|
|
26
|
+
body: "TinaCMS is a Git-backed headless CMS",
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
_id: "2",
|
|
30
|
+
title: "React Tutorial",
|
|
31
|
+
body: "Learn how to build React applications",
|
|
32
|
+
},
|
|
33
|
+
]);
|
|
34
|
+
|
|
35
|
+
// Basic search
|
|
36
|
+
const results = await client.query("TinaCMS", { limit: 10 });
|
|
37
|
+
|
|
38
|
+
// Fuzzy search (handles typos)
|
|
39
|
+
const fuzzyResults = await client.query("TinCMS tutrial", {
|
|
40
|
+
fuzzy: true,
|
|
41
|
+
limit: 10,
|
|
42
|
+
});
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## API
|
|
46
|
+
|
|
47
|
+
- `client.onStartIndexing()` - Initialize the index
|
|
48
|
+
- `client.put(documents)` - Index documents
|
|
49
|
+
- `client.query(query, options)` - Search the index
|
|
50
|
+
- `client.del(ids)` - Delete documents
|
|
51
|
+
- `client.export(filename)` - Export index to SQLite
|
|
52
|
+
|
|
53
|
+
## License
|
|
54
|
+
|
|
55
|
+
Apache 2.0
|
package/dist/client/index.d.ts
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
|
-
import type { SearchClient } from '../types';
|
|
1
|
+
import type { SearchClient, SearchOptions, SearchQueryResponse, IndexableDocument, SearchIndexResult } from '../types';
|
|
2
2
|
import { MemoryLevel } from 'memory-level';
|
|
3
|
+
import { FuzzySearchWrapper } from '../fuzzy-search-wrapper';
|
|
4
|
+
interface SearchIndex {
|
|
5
|
+
PUT: (docs: IndexableDocument[]) => Promise<void>;
|
|
6
|
+
DELETE: (ids: string[]) => Promise<void>;
|
|
7
|
+
QUERY: (query: {
|
|
8
|
+
AND: string[];
|
|
9
|
+
} | {
|
|
10
|
+
OR: string[];
|
|
11
|
+
}, options?: {
|
|
12
|
+
PAGE?: {
|
|
13
|
+
NUMBER: number;
|
|
14
|
+
SIZE: number;
|
|
15
|
+
};
|
|
16
|
+
}) => Promise<SearchIndexResult>;
|
|
17
|
+
DICTIONARY: (token?: {
|
|
18
|
+
FIELD: string;
|
|
19
|
+
}) => Promise<unknown[]>;
|
|
20
|
+
fuzzySearchWrapper?: FuzzySearchWrapper;
|
|
21
|
+
}
|
|
3
22
|
type TinaSearchIndexerClientOptions = {
|
|
4
23
|
stopwordLanguages?: string[];
|
|
5
24
|
tokenSplitRegex?: string;
|
|
@@ -10,23 +29,16 @@ type TinaCloudSearchIndexerClientOptions = {
|
|
|
10
29
|
indexerToken: string;
|
|
11
30
|
} & TinaSearchIndexerClientOptions;
|
|
12
31
|
export declare class LocalSearchIndexClient implements SearchClient {
|
|
13
|
-
searchIndex
|
|
32
|
+
searchIndex?: SearchIndex;
|
|
14
33
|
protected readonly memoryLevel: MemoryLevel;
|
|
15
34
|
private readonly stopwords;
|
|
16
35
|
private readonly tokenSplitRegex;
|
|
36
|
+
fuzzySearchWrapper?: FuzzySearchWrapper;
|
|
17
37
|
constructor(options: TinaSearchIndexerClientOptions);
|
|
18
38
|
onStartIndexing(): Promise<void>;
|
|
19
|
-
put(docs:
|
|
20
|
-
del(ids: string[]): Promise<
|
|
21
|
-
query(query: string, options:
|
|
22
|
-
cursor?: string;
|
|
23
|
-
limit?: number;
|
|
24
|
-
} | undefined): Promise<{
|
|
25
|
-
results: any[];
|
|
26
|
-
total: number;
|
|
27
|
-
nextCursor: string | null;
|
|
28
|
-
prevCursor: string | null;
|
|
29
|
-
}>;
|
|
39
|
+
put(docs: IndexableDocument[]): Promise<void>;
|
|
40
|
+
del(ids: string[]): Promise<void>;
|
|
41
|
+
query(query: string, options?: SearchOptions): Promise<SearchQueryResponse>;
|
|
30
42
|
export(filename: string): Promise<void>;
|
|
31
43
|
}
|
|
32
44
|
export declare class TinaCMSSearchIndexClient extends LocalSearchIndexClient {
|
|
@@ -34,6 +46,9 @@ export declare class TinaCMSSearchIndexClient extends LocalSearchIndexClient {
|
|
|
34
46
|
private readonly branch;
|
|
35
47
|
private readonly indexerToken;
|
|
36
48
|
constructor(options: TinaCloudSearchIndexerClientOptions);
|
|
49
|
+
private getUploadUrl;
|
|
50
|
+
private serializeIndex;
|
|
51
|
+
private uploadIndex;
|
|
37
52
|
onFinishIndexing(): Promise<void>;
|
|
38
53
|
}
|
|
39
54
|
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { FuzzyMatch, FuzzySearchOptions } from './types';
|
|
2
|
+
export declare class FuzzyCache {
|
|
3
|
+
private cache;
|
|
4
|
+
private maxSize;
|
|
5
|
+
constructor(maxSize?: number);
|
|
6
|
+
private getCacheKey;
|
|
7
|
+
get(query: string, options: Partial<FuzzySearchOptions>): FuzzyMatch[] | undefined;
|
|
8
|
+
set(query: string, options: Partial<FuzzySearchOptions>, results: FuzzyMatch[]): void;
|
|
9
|
+
clear(): void;
|
|
10
|
+
get size(): number;
|
|
11
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { FuzzySearchOptions, FuzzyMatch } from './types';
|
|
2
|
+
export declare function levenshteinDistance(str1: string, str2: string): number;
|
|
3
|
+
export declare function similarityScore(str1: string, str2: string, useTranspositions?: boolean): number;
|
|
4
|
+
export declare function damerauLevenshteinDistance(str1: string, str2: string): number;
|
|
5
|
+
/**
|
|
6
|
+
* Generate n-grams (character sequences) from a string
|
|
7
|
+
* Example: getNgrams("react", 2) => Set{"re", "ea", "ac", "ct"}
|
|
8
|
+
*/
|
|
9
|
+
export declare function getNgrams(str: string, n?: number): Set<string>;
|
|
10
|
+
/**
|
|
11
|
+
* Calculate the overlap ratio between two sets of n-grams
|
|
12
|
+
* Returns a value between 0 and 1
|
|
13
|
+
*/
|
|
14
|
+
export declare function ngramOverlap(ngrams1: Set<string>, ngrams2: Set<string>): number;
|
|
15
|
+
export declare function findSimilarTerms(query: string, dictionary: string[], options?: FuzzySearchOptions): FuzzyMatch[];
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export type { FuzzySearchOptions, FuzzyMatch } from './types';
|
|
2
|
+
export { DEFAULT_FUZZY_OPTIONS } from './types';
|
|
3
|
+
export { FuzzyCache } from './cache';
|
|
4
|
+
export { levenshteinDistance, similarityScore, damerauLevenshteinDistance, findSimilarTerms, getNgrams, ngramOverlap, } from './distance';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface FuzzySearchOptions {
|
|
2
|
+
maxDistance?: number;
|
|
3
|
+
minSimilarity?: number;
|
|
4
|
+
maxResults?: number;
|
|
5
|
+
useTranspositions?: boolean;
|
|
6
|
+
caseSensitive?: boolean;
|
|
7
|
+
/** Use n-gram filtering for candidate selection (supports transpositions) */
|
|
8
|
+
useNgramFilter?: boolean;
|
|
9
|
+
/** Size of n-grams for filtering (default: 2) */
|
|
10
|
+
ngramSize?: number;
|
|
11
|
+
/** Minimum n-gram overlap ratio to consider a candidate (0-1, default: 0.2) */
|
|
12
|
+
minNgramOverlap?: number;
|
|
13
|
+
}
|
|
14
|
+
export interface FuzzyMatch {
|
|
15
|
+
term: string;
|
|
16
|
+
distance: number;
|
|
17
|
+
similarity: number;
|
|
18
|
+
}
|
|
19
|
+
export declare const DEFAULT_FUZZY_OPTIONS: Required<FuzzySearchOptions>;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { FuzzySearchOptions, FuzzyMatch } from './fuzzy';
|
|
2
|
+
import type { SearchQueryResponse, SearchResult } from './types';
|
|
3
|
+
interface SearchIndex {
|
|
4
|
+
QUERY: (query: {
|
|
5
|
+
AND: string[];
|
|
6
|
+
} | {
|
|
7
|
+
OR: string[];
|
|
8
|
+
} | {
|
|
9
|
+
AND: (string | {
|
|
10
|
+
OR: string[];
|
|
11
|
+
})[];
|
|
12
|
+
}, options?: {
|
|
13
|
+
PAGE?: {
|
|
14
|
+
NUMBER: number;
|
|
15
|
+
SIZE: number;
|
|
16
|
+
};
|
|
17
|
+
}) => Promise<{
|
|
18
|
+
RESULT: SearchResult[];
|
|
19
|
+
RESULT_LENGTH: number;
|
|
20
|
+
}>;
|
|
21
|
+
DICTIONARY: (token?: {
|
|
22
|
+
FIELD: string;
|
|
23
|
+
}) => Promise<unknown[]>;
|
|
24
|
+
}
|
|
25
|
+
interface QueryOptions {
|
|
26
|
+
limit?: number;
|
|
27
|
+
cursor?: string;
|
|
28
|
+
fuzzy?: boolean;
|
|
29
|
+
fuzzyOptions?: FuzzySearchOptions;
|
|
30
|
+
}
|
|
31
|
+
export declare class FuzzySearchWrapper {
|
|
32
|
+
private cache;
|
|
33
|
+
private searchIndex;
|
|
34
|
+
constructor(searchIndex: SearchIndex, cacheSize?: number);
|
|
35
|
+
getDictionary(field?: string): Promise<string[]>;
|
|
36
|
+
findSimilar(query: string, field?: string, options?: FuzzySearchOptions): Promise<FuzzyMatch[]>;
|
|
37
|
+
expandQuery(query: string, options?: FuzzySearchOptions): Promise<{
|
|
38
|
+
original: string[];
|
|
39
|
+
expanded: string[];
|
|
40
|
+
matches: Record<string, FuzzyMatch[]>;
|
|
41
|
+
}>;
|
|
42
|
+
query(query: string, options?: QueryOptions): Promise<SearchQueryResponse>;
|
|
43
|
+
clearCache(): void;
|
|
44
|
+
getCacheSize(): number;
|
|
45
|
+
}
|
|
46
|
+
export {};
|
package/dist/index-client.d.ts
CHANGED
|
@@ -1,16 +1,33 @@
|
|
|
1
|
-
export type { SearchClient } from './types';
|
|
1
|
+
export type { SearchClient, SearchResult, SearchQueryResponse, IndexableDocument, } from './types';
|
|
2
2
|
export { processDocumentForIndexing } from './indexer/utils';
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
import type { FuzzyMatch } from './fuzzy';
|
|
4
|
+
interface SearchQuery {
|
|
5
|
+
AND: string[];
|
|
6
|
+
}
|
|
7
|
+
interface PaginationOptions {
|
|
5
8
|
limit?: number;
|
|
6
9
|
cursor?: string;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
10
|
+
}
|
|
11
|
+
interface PageOptions {
|
|
12
|
+
PAGE?: {
|
|
13
|
+
SIZE: number;
|
|
14
|
+
NUMBER: number;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
interface SearchIndexResponse {
|
|
18
|
+
RESULT: unknown[];
|
|
19
|
+
RESULT_LENGTH: number;
|
|
20
|
+
FUZZY_MATCHES?: Record<string, FuzzyMatch[]>;
|
|
21
|
+
NEXT_CURSOR?: string | null;
|
|
22
|
+
PREV_CURSOR?: string | null;
|
|
23
|
+
}
|
|
24
|
+
interface ParsedSearchResponse {
|
|
25
|
+
results: unknown[];
|
|
26
|
+
total: number;
|
|
27
|
+
prevCursor: string | null;
|
|
28
|
+
nextCursor: string | null;
|
|
29
|
+
fuzzyMatches?: Record<string, FuzzyMatch[]>;
|
|
30
|
+
}
|
|
31
|
+
export declare const queryToSearchIndexQuery: (query: string, stopwordLanguages?: string[]) => SearchQuery;
|
|
32
|
+
export declare const optionsToSearchIndexOptions: (options?: PaginationOptions) => PageOptions;
|
|
33
|
+
export declare const parseSearchIndexResponse: (data: SearchIndexResponse, options?: PaginationOptions) => ParsedSearchResponse;
|