@meeovi/layer-search 1.0.6 → 1.0.7
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 +39 -5
- package/app/composables/bridges/searchkit-server.ts +51 -0
- package/app/composables/bridges/searchkit.ts +88 -0
- package/app/composables/index.ts +4 -1
- package/app/composables/module.ts +41 -0
- package/app/composables/utils/health.ts +13 -0
- package/app/plugins/search.js +1 -1
- package/app/utils/search/client.ts +40 -0
- package/dist/app/composables/adapter/meilisearch.d.ts +8 -0
- package/dist/app/composables/adapter/meilisearch.js +36 -0
- package/dist/app/composables/adapter/mock.d.ts +3 -0
- package/dist/app/composables/adapter/mock.js +19 -0
- package/dist/app/composables/adapter/opensearch.d.ts +8 -0
- package/dist/app/composables/adapter/opensearch.js +46 -0
- package/dist/app/composables/adapter/types.d.ts +12 -0
- package/dist/app/composables/adapter/types.js +1 -0
- package/dist/app/composables/bridges/instantsearch.d.ts +4 -0
- package/dist/app/composables/bridges/instantsearch.js +17 -0
- package/dist/app/composables/bridges/react.d.ts +12 -0
- package/dist/app/composables/bridges/react.js +34 -0
- package/dist/app/composables/bridges/vue.d.ts +9 -0
- package/dist/app/composables/bridges/vue.js +31 -0
- package/dist/app/composables/cli.d.ts +2 -0
- package/dist/app/composables/cli.js +69 -0
- package/dist/app/composables/config/schema.d.ts +5 -0
- package/dist/app/composables/config/schema.js +8 -0
- package/dist/app/composables/config.d.ts +7 -0
- package/dist/app/composables/config.js +11 -0
- package/dist/app/composables/core/Facets.d.ts +17 -0
- package/dist/app/composables/core/Facets.js +8 -0
- package/dist/app/composables/core/Filters.d.ts +18 -0
- package/dist/app/composables/core/Filters.js +11 -0
- package/dist/app/composables/core/Normalizers.d.ts +0 -0
- package/dist/app/composables/core/Normalizers.js +1 -0
- package/dist/app/composables/core/Pipeline.d.ts +8 -0
- package/dist/app/composables/core/Pipeline.js +16 -0
- package/dist/app/composables/core/QueryBuilder.d.ts +13 -0
- package/dist/app/composables/core/QueryBuilder.js +15 -0
- package/dist/app/composables/core/SearchContext.d.ts +18 -0
- package/dist/app/composables/core/SearchContext.js +38 -0
- package/dist/app/composables/core/SearchManager.d.ts +10 -0
- package/dist/app/composables/core/SearchManager.js +20 -0
- package/dist/app/composables/events.d.ts +11 -0
- package/dist/app/composables/events.js +1 -0
- package/dist/app/composables/index.d.ts +9 -0
- package/dist/app/composables/index.js +9 -0
- package/dist/app/composables/module.d.ts +17 -0
- package/dist/app/composables/module.js +39 -0
- package/dist/app/composables/types/api/global-search.d.ts +8 -0
- package/dist/app/composables/types/api/global-search.js +1 -0
- package/dist/app/composables/utils/normalizers.d.ts +1 -0
- package/dist/app/composables/utils/normalizers.js +6 -0
- package/dist/app/utils/search/client.d.ts +3 -0
- package/dist/app/utils/search/client.js +31 -0
- package/dist/layers/search/app/composables/adapter/meilisearch.d.ts +8 -0
- package/dist/layers/search/app/composables/adapter/meilisearch.js +36 -0
- package/dist/layers/search/app/composables/adapter/mock.d.ts +3 -0
- package/dist/layers/search/app/composables/adapter/mock.js +19 -0
- package/dist/layers/search/app/composables/adapter/opensearch.d.ts +8 -0
- package/dist/layers/search/app/composables/adapter/opensearch.js +46 -0
- package/dist/layers/search/app/composables/adapter/types.d.ts +12 -0
- package/dist/layers/search/app/composables/adapter/types.js +1 -0
- package/dist/layers/search/app/composables/bridges/instantsearch.d.ts +4 -0
- package/dist/layers/search/app/composables/bridges/instantsearch.js +17 -0
- package/dist/layers/search/app/composables/bridges/react.d.ts +12 -0
- package/dist/layers/search/app/composables/bridges/react.js +34 -0
- package/dist/layers/search/app/composables/bridges/searchkit-server.d.ts +3 -0
- package/dist/layers/search/app/composables/bridges/searchkit-server.js +44 -0
- package/dist/layers/search/app/composables/bridges/searchkit.d.ts +21 -0
- package/dist/layers/search/app/composables/bridges/searchkit.js +60 -0
- package/dist/layers/search/app/composables/bridges/vue.d.ts +9 -0
- package/dist/layers/search/app/composables/bridges/vue.js +31 -0
- package/dist/layers/search/app/composables/cli.d.ts +2 -0
- package/dist/layers/search/app/composables/cli.js +69 -0
- package/dist/layers/search/app/composables/config/schema.d.ts +5 -0
- package/dist/layers/search/app/composables/config/schema.js +8 -0
- package/dist/layers/search/app/composables/config.d.ts +7 -0
- package/dist/layers/search/app/composables/config.js +11 -0
- package/dist/layers/search/app/composables/core/Facets.d.ts +17 -0
- package/dist/layers/search/app/composables/core/Facets.js +8 -0
- package/dist/layers/search/app/composables/core/Filters.d.ts +18 -0
- package/dist/layers/search/app/composables/core/Filters.js +11 -0
- package/dist/layers/search/app/composables/core/Normalizers.d.ts +0 -0
- package/dist/layers/search/app/composables/core/Normalizers.js +1 -0
- package/dist/layers/search/app/composables/core/Pipeline.d.ts +8 -0
- package/dist/layers/search/app/composables/core/Pipeline.js +16 -0
- package/dist/layers/search/app/composables/core/QueryBuilder.d.ts +13 -0
- package/dist/layers/search/app/composables/core/QueryBuilder.js +15 -0
- package/dist/layers/search/app/composables/core/SearchContext.d.ts +18 -0
- package/dist/layers/search/app/composables/core/SearchContext.js +38 -0
- package/dist/layers/search/app/composables/core/SearchManager.d.ts +10 -0
- package/dist/layers/search/app/composables/core/SearchManager.js +20 -0
- package/dist/layers/search/app/composables/events.d.ts +11 -0
- package/dist/layers/search/app/composables/events.js +1 -0
- package/dist/layers/search/app/composables/index.d.ts +12 -0
- package/dist/layers/search/app/composables/index.js +12 -0
- package/dist/layers/search/app/composables/module.d.ts +17 -0
- package/dist/layers/search/app/composables/module.js +73 -0
- package/dist/layers/search/app/composables/types/api/global-search.d.ts +8 -0
- package/dist/layers/search/app/composables/types/api/global-search.js +1 -0
- package/dist/layers/search/app/composables/utils/health.d.ts +11 -0
- package/dist/layers/search/app/composables/utils/health.js +11 -0
- package/dist/layers/search/app/composables/utils/normalizers.d.ts +1 -0
- package/dist/layers/search/app/composables/utils/normalizers.js +6 -0
- package/dist/layers/search/app/utils/search/client.d.ts +3 -0
- package/dist/layers/search/app/utils/search/client.js +31 -0
- package/dist/layers/search/nuxt.config.d.ts +2 -0
- package/dist/layers/search/nuxt.config.js +7 -0
- package/dist/layers/search/test/runtime-adapter.spec.d.ts +1 -0
- package/dist/layers/search/test/runtime-adapter.spec.js +49 -0
- package/dist/nuxt.config.d.ts +2 -0
- package/dist/nuxt.config.js +7 -0
- package/dist/packages/core/src/adapters/auth.d.ts +9 -0
- package/dist/packages/core/src/adapters/auth.js +1 -0
- package/dist/packages/core/src/adapters/cart.d.ts +22 -0
- package/dist/packages/core/src/adapters/cart.js +1 -0
- package/dist/packages/core/src/adapters/catalog.d.ts +17 -0
- package/dist/packages/core/src/adapters/catalog.js +1 -0
- package/dist/packages/core/src/adapters/common.d.ts +9 -0
- package/dist/packages/core/src/adapters/common.js +1 -0
- package/dist/packages/core/src/adapters/lists.d.ts +17 -0
- package/dist/packages/core/src/adapters/lists.js +1 -0
- package/dist/packages/core/src/adapters/search.d.ts +21 -0
- package/dist/packages/core/src/adapters/search.js +1 -0
- package/dist/packages/core/src/plugins/defineAdapter.d.ts +2 -0
- package/dist/packages/core/src/plugins/defineAdapter.js +3 -0
- package/dist/packages/core/src/plugins/defineModule.d.ts +2 -0
- package/dist/packages/core/src/plugins/defineModule.js +3 -0
- package/dist/packages/core/src/plugins/registry.d.ts +14 -0
- package/dist/packages/core/src/plugins/registry.js +46 -0
- package/dist/packages/core/src/runtime/app.d.ts +2 -0
- package/dist/packages/core/src/runtime/app.js +20 -0
- package/dist/packages/core/src/runtime/context.d.ts +9 -0
- package/dist/packages/core/src/runtime/context.js +11 -0
- package/dist/packages/core/src/runtime/hooks.d.ts +5 -0
- package/dist/packages/core/src/runtime/hooks.js +18 -0
- package/dist/packages/core/src/runtime/lifecycle.d.ts +4 -0
- package/dist/packages/core/src/runtime/lifecycle.js +5 -0
- package/dist/packages/core/src/types/adapters.d.ts +14 -0
- package/dist/packages/core/src/types/adapters.js +1 -0
- package/dist/packages/core/src/types/app.d.ts +13 -0
- package/dist/packages/core/src/types/app.js +1 -0
- package/dist/packages/core/src/types/config.d.ts +8 -0
- package/dist/packages/core/src/types/config.js +1 -0
- package/dist/packages/core/src/types/events.d.ts +20 -0
- package/dist/packages/core/src/types/events.js +22 -0
- package/dist/packages/core/src/types/module.d.ts +15 -0
- package/dist/packages/core/src/types/module.js +1 -0
- package/package.json +9 -4
- package/test/runtime-adapter.spec.ts +61 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export function validateSearchConfig(config) {
|
|
2
|
+
if (!config.defaultProvider) {
|
|
3
|
+
throw new Error('[@meeovi/search] Missing defaultProvider');
|
|
4
|
+
}
|
|
5
|
+
if (!config.providers[config.defaultProvider]) {
|
|
6
|
+
throw new Error(`[@meeovi/search] Provider "${config.defaultProvider}" not found in config.providers`);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export declare const Facets: {
|
|
2
|
+
terms(field: string): {
|
|
3
|
+
type: string;
|
|
4
|
+
field: string;
|
|
5
|
+
};
|
|
6
|
+
range(field: string, ranges: {
|
|
7
|
+
from?: number;
|
|
8
|
+
to?: number;
|
|
9
|
+
}[]): {
|
|
10
|
+
type: string;
|
|
11
|
+
field: string;
|
|
12
|
+
ranges: {
|
|
13
|
+
from?: number;
|
|
14
|
+
to?: number;
|
|
15
|
+
}[];
|
|
16
|
+
};
|
|
17
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export declare const Filters: {
|
|
2
|
+
term(field: string, value: string): {
|
|
3
|
+
type: string;
|
|
4
|
+
field: string;
|
|
5
|
+
value: string;
|
|
6
|
+
};
|
|
7
|
+
range(field: string, min?: number, max?: number): {
|
|
8
|
+
type: string;
|
|
9
|
+
field: string;
|
|
10
|
+
min: number | undefined;
|
|
11
|
+
max: number | undefined;
|
|
12
|
+
};
|
|
13
|
+
boolean(field: string, value: boolean): {
|
|
14
|
+
type: string;
|
|
15
|
+
field: string;
|
|
16
|
+
value: boolean;
|
|
17
|
+
};
|
|
18
|
+
};
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export class SearchPipeline {
|
|
2
|
+
before = [];
|
|
3
|
+
after = [];
|
|
4
|
+
useBefore(fn) {
|
|
5
|
+
this.before.push(fn);
|
|
6
|
+
}
|
|
7
|
+
useAfter(fn) {
|
|
8
|
+
this.after.push(fn);
|
|
9
|
+
}
|
|
10
|
+
runBefore(query) {
|
|
11
|
+
return this.before.reduce((q, fn) => fn(q), query);
|
|
12
|
+
}
|
|
13
|
+
runAfter(result) {
|
|
14
|
+
return this.after.reduce((r, fn) => fn(r), result);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { SearchContext } from './SearchContext';
|
|
2
|
+
export interface BuiltSearchQuery {
|
|
3
|
+
term: string;
|
|
4
|
+
page: number;
|
|
5
|
+
pageSize: number;
|
|
6
|
+
sort?: string;
|
|
7
|
+
filters: Record<string, any>;
|
|
8
|
+
}
|
|
9
|
+
export declare class QueryBuilder {
|
|
10
|
+
private context;
|
|
11
|
+
constructor(context: SearchContext);
|
|
12
|
+
build(): BuiltSearchQuery;
|
|
13
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export class QueryBuilder {
|
|
2
|
+
context;
|
|
3
|
+
constructor(context) {
|
|
4
|
+
this.context = context;
|
|
5
|
+
}
|
|
6
|
+
build() {
|
|
7
|
+
return {
|
|
8
|
+
term: this.context.state.query,
|
|
9
|
+
page: this.context.state.page,
|
|
10
|
+
pageSize: this.context.state.pageSize,
|
|
11
|
+
sort: this.context.state.sort,
|
|
12
|
+
filters: this.context.state.filters
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface SearchContextState {
|
|
2
|
+
query: string;
|
|
3
|
+
page: number;
|
|
4
|
+
pageSize: number;
|
|
5
|
+
sort?: string;
|
|
6
|
+
filters: Record<string, any>;
|
|
7
|
+
}
|
|
8
|
+
export declare class SearchContext {
|
|
9
|
+
state: SearchContextState;
|
|
10
|
+
constructor(initial?: Partial<SearchContextState>);
|
|
11
|
+
setQuery(query: string): void;
|
|
12
|
+
setPage(page: number): void;
|
|
13
|
+
setPageSize(size: number): void;
|
|
14
|
+
setSort(sort: string | undefined): void;
|
|
15
|
+
setFilter(key: string, value: any): void;
|
|
16
|
+
removeFilter(key: string): void;
|
|
17
|
+
reset(): void;
|
|
18
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export class SearchContext {
|
|
2
|
+
state;
|
|
3
|
+
constructor(initial) {
|
|
4
|
+
this.state = {
|
|
5
|
+
query: '',
|
|
6
|
+
page: 1,
|
|
7
|
+
pageSize: 20,
|
|
8
|
+
filters: {},
|
|
9
|
+
...initial
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
setQuery(query) {
|
|
13
|
+
this.state.query = query;
|
|
14
|
+
}
|
|
15
|
+
setPage(page) {
|
|
16
|
+
this.state.page = page;
|
|
17
|
+
}
|
|
18
|
+
setPageSize(size) {
|
|
19
|
+
this.state.pageSize = size;
|
|
20
|
+
}
|
|
21
|
+
setSort(sort) {
|
|
22
|
+
this.state.sort = sort;
|
|
23
|
+
}
|
|
24
|
+
setFilter(key, value) {
|
|
25
|
+
this.state.filters[key] = value;
|
|
26
|
+
}
|
|
27
|
+
removeFilter(key) {
|
|
28
|
+
delete this.state.filters[key];
|
|
29
|
+
}
|
|
30
|
+
reset() {
|
|
31
|
+
this.state = {
|
|
32
|
+
query: '',
|
|
33
|
+
page: 1,
|
|
34
|
+
pageSize: 20,
|
|
35
|
+
filters: {}
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { SearchAdapter } from '@meeovi/core';
|
|
2
|
+
import { SearchContext, type SearchContextState } from './SearchContext';
|
|
3
|
+
import { SearchPipeline } from './Pipeline';
|
|
4
|
+
export declare class SearchManager<TItem = any> {
|
|
5
|
+
context: SearchContext;
|
|
6
|
+
pipeline: SearchPipeline;
|
|
7
|
+
adapter: SearchAdapter<TItem>;
|
|
8
|
+
constructor(adapter: SearchAdapter<TItem>, initial?: Partial<SearchContextState>);
|
|
9
|
+
search(): Promise<any>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { SearchContext } from './SearchContext';
|
|
2
|
+
import { QueryBuilder } from './QueryBuilder';
|
|
3
|
+
import { SearchPipeline } from './Pipeline';
|
|
4
|
+
export class SearchManager {
|
|
5
|
+
context;
|
|
6
|
+
pipeline;
|
|
7
|
+
adapter;
|
|
8
|
+
constructor(adapter, initial) {
|
|
9
|
+
this.context = new SearchContext(initial);
|
|
10
|
+
this.pipeline = new SearchPipeline();
|
|
11
|
+
this.adapter = adapter;
|
|
12
|
+
}
|
|
13
|
+
async search() {
|
|
14
|
+
const builder = new QueryBuilder(this.context);
|
|
15
|
+
let query = builder.build();
|
|
16
|
+
query = this.pipeline.runBefore(query);
|
|
17
|
+
const result = await this.adapter.search(query);
|
|
18
|
+
return this.pipeline.runAfter(result);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from './module';
|
|
2
|
+
export * from './adapter/types';
|
|
3
|
+
export * from './core/SearchManager';
|
|
4
|
+
export * from './core/SearchContext';
|
|
5
|
+
export * from './core/Filters';
|
|
6
|
+
export * from './core/Facets';
|
|
7
|
+
export * from './bridges/instantsearch';
|
|
8
|
+
export * from './bridges/vue';
|
|
9
|
+
export * from './bridges/react';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from './module';
|
|
2
|
+
export * from './adapter/types';
|
|
3
|
+
export * from './core/SearchManager';
|
|
4
|
+
export * from './core/SearchContext';
|
|
5
|
+
export * from './core/Filters';
|
|
6
|
+
export * from './core/Facets';
|
|
7
|
+
export * from './bridges/instantsearch';
|
|
8
|
+
export * from './bridges/vue';
|
|
9
|
+
export * from './bridges/react';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type AlternateContext } from '@meeovi/core';
|
|
2
|
+
import { type SearchModuleConfig } from './config/schema';
|
|
3
|
+
import type { MeeoviSearchItem } from './adapter/types';
|
|
4
|
+
declare module '@meeovi/core' {
|
|
5
|
+
interface AlternateConfig {
|
|
6
|
+
search?: SearchModuleConfig;
|
|
7
|
+
}
|
|
8
|
+
interface AlternateContext {
|
|
9
|
+
searchManager?: import('./core/SearchManager').SearchManager<MeeoviSearchItem>;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
declare const _default: {
|
|
13
|
+
id: string;
|
|
14
|
+
adapters: {};
|
|
15
|
+
setup(ctx: AlternateContext): Promise<void>;
|
|
16
|
+
};
|
|
17
|
+
export default _default;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { defineAlternateModule, useAlternateEventBus } from '@meeovi/core';
|
|
2
|
+
import { validateSearchConfig } from './config/schema';
|
|
3
|
+
import { createOpenSearchAdapter } from './adapter/opensearch';
|
|
4
|
+
import { createMeilisearchAdapter } from './adapter/meilisearch';
|
|
5
|
+
import { SearchManager } from './core/SearchManager';
|
|
6
|
+
export default defineAlternateModule({
|
|
7
|
+
id: 'search',
|
|
8
|
+
adapters: {},
|
|
9
|
+
async setup(ctx) {
|
|
10
|
+
const bus = useAlternateEventBus();
|
|
11
|
+
const config = ctx.config.search;
|
|
12
|
+
if (!config)
|
|
13
|
+
return;
|
|
14
|
+
validateSearchConfig(config);
|
|
15
|
+
if (config.defaultProvider === 'opensearch') {
|
|
16
|
+
const providerConfig = config.providers.opensearch;
|
|
17
|
+
if (providerConfig) {
|
|
18
|
+
this.adapters = {
|
|
19
|
+
search: createOpenSearchAdapter(providerConfig)
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
if (config.defaultProvider === 'meilisearch') {
|
|
24
|
+
const providerConfig = config.providers.meilisearch;
|
|
25
|
+
if (providerConfig) {
|
|
26
|
+
this.adapters = {
|
|
27
|
+
search: createMeilisearchAdapter(providerConfig)
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
const adapter = ctx.getAdapter('search');
|
|
32
|
+
if (adapter) {
|
|
33
|
+
ctx.searchManager = new SearchManager(adapter);
|
|
34
|
+
}
|
|
35
|
+
bus.on('app:ready', () => {
|
|
36
|
+
console.info('[@meeovi/search] Search module initialized');
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function normalizeOpenSearchHit(hit: any): any;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export function getIndexName() {
|
|
2
|
+
return (process.env.NUXT_PUBLIC_SEARCH_INDEX ||
|
|
3
|
+
process.env.SEARCH_INDEX ||
|
|
4
|
+
'default');
|
|
5
|
+
}
|
|
6
|
+
export function getSearchClient() {
|
|
7
|
+
const host = process.env.NUXT_PUBLIC_SEARCHKIT_HOST || process.env.SEARCHKIT_HOST;
|
|
8
|
+
if (!host) {
|
|
9
|
+
throw new Error('Searchkit host not configured via SEARCHKIT_HOST or NUXT_PUBLIC_SEARCHKIT_HOST');
|
|
10
|
+
}
|
|
11
|
+
// Defer importing heavy searchkit/instantsearch client until runtime.
|
|
12
|
+
// Consumers can replace this implementation with a provider-specific client.
|
|
13
|
+
// Here we attempt to use @searchkit/instantsearch-client if available.
|
|
14
|
+
// If not present, let the import fail so the plugin can fallback.
|
|
15
|
+
// The actual creation API differs between versions; adjust as needed.
|
|
16
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
17
|
+
const createClient = require('@searchkit/instantsearch-client');
|
|
18
|
+
if (!createClient) {
|
|
19
|
+
throw new Error('@searchkit/instantsearch-client is not installed');
|
|
20
|
+
}
|
|
21
|
+
// Create a minimal client. The factory API may vary; this is a best-effort
|
|
22
|
+
// placeholder that should be adapted to your Searchkit configuration.
|
|
23
|
+
// If your Searchkit installation exposes a helper, prefer using that.
|
|
24
|
+
try {
|
|
25
|
+
return createClient({ host });
|
|
26
|
+
}
|
|
27
|
+
catch (e) {
|
|
28
|
+
// rethrow with context
|
|
29
|
+
throw new Error('Failed to create Searchkit client: ' + (e?.message || e));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type SearchAdapterConfig } from '@meeovi/core';
|
|
2
|
+
interface MeiliConfig extends SearchAdapterConfig {
|
|
3
|
+
host: string;
|
|
4
|
+
apiKey?: string;
|
|
5
|
+
index: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function createMeilisearchAdapter(config: MeiliConfig): import("@meeovi/core").AlternateAdapter<SearchAdapterConfig>;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { defineAlternateAdapter } from '@meeovi/core';
|
|
2
|
+
export function createMeilisearchAdapter(config) {
|
|
3
|
+
const headers = {
|
|
4
|
+
'Content-Type': 'application/json'
|
|
5
|
+
};
|
|
6
|
+
if (config.apiKey) {
|
|
7
|
+
headers['Authorization'] = `Bearer ${config.apiKey}`;
|
|
8
|
+
}
|
|
9
|
+
const adapter = {
|
|
10
|
+
id: 'search:meilisearch',
|
|
11
|
+
type: 'search',
|
|
12
|
+
config,
|
|
13
|
+
async search(query) {
|
|
14
|
+
const params = new URLSearchParams({
|
|
15
|
+
q: query.term,
|
|
16
|
+
offset: String((query.page - 1) * query.pageSize),
|
|
17
|
+
limit: String(query.pageSize)
|
|
18
|
+
});
|
|
19
|
+
const res = await fetch(`${config.host}/indexes/${config.index}/search?${params}`, {
|
|
20
|
+
method: 'POST',
|
|
21
|
+
headers,
|
|
22
|
+
body: JSON.stringify({
|
|
23
|
+
filter: Object.entries(query.filters).map(([field, value]) => `${field} = ${value}`)
|
|
24
|
+
})
|
|
25
|
+
});
|
|
26
|
+
const json = await res.json();
|
|
27
|
+
return {
|
|
28
|
+
items: json.hits,
|
|
29
|
+
total: json.estimatedTotalHits ?? json.hits.length,
|
|
30
|
+
page: query.page,
|
|
31
|
+
pageSize: query.pageSize
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
return defineAlternateAdapter(adapter);
|
|
36
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { defineAlternateAdapter } from '@meeovi/core';
|
|
2
|
+
export function createMockSearchAdapter(items = []) {
|
|
3
|
+
const cfg = { provider: 'mock' };
|
|
4
|
+
const adapter = {
|
|
5
|
+
id: 'search:mock',
|
|
6
|
+
type: 'search',
|
|
7
|
+
config: cfg,
|
|
8
|
+
async search(query) {
|
|
9
|
+
const filtered = items.filter((item) => item.title.toLowerCase().includes(query.term.toLowerCase()));
|
|
10
|
+
return {
|
|
11
|
+
items: filtered,
|
|
12
|
+
total: filtered.length,
|
|
13
|
+
page: 1,
|
|
14
|
+
pageSize: filtered.length
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
return defineAlternateAdapter(adapter);
|
|
19
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type SearchAdapterConfig } from '@meeovi/core';
|
|
2
|
+
interface OpenSearchConfig extends SearchAdapterConfig {
|
|
3
|
+
endpoint: string;
|
|
4
|
+
index: string;
|
|
5
|
+
apiKey?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function createOpenSearchAdapter(config: OpenSearchConfig): import("@meeovi/core").AlternateAdapter<SearchAdapterConfig>;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { defineAlternateAdapter } from '@meeovi/core';
|
|
2
|
+
import { normalizeOpenSearchHit } from '../utils/normalizers';
|
|
3
|
+
export function createOpenSearchAdapter(config) {
|
|
4
|
+
const adapter = {
|
|
5
|
+
id: 'search:opensearch',
|
|
6
|
+
type: 'search',
|
|
7
|
+
config,
|
|
8
|
+
async search(query) {
|
|
9
|
+
const body = {
|
|
10
|
+
query: {
|
|
11
|
+
bool: {
|
|
12
|
+
must: [
|
|
13
|
+
{
|
|
14
|
+
multi_match: {
|
|
15
|
+
query: query.term,
|
|
16
|
+
fields: ['title^3', 'description', 'tags']
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
],
|
|
20
|
+
filter: Object.entries(query.filters).map(([field, value]) => ({
|
|
21
|
+
term: { [field]: value }
|
|
22
|
+
}))
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
from: (query.page - 1) * query.pageSize,
|
|
26
|
+
size: query.pageSize
|
|
27
|
+
};
|
|
28
|
+
const res = await fetch(`${config.endpoint}/${config.index}/_search`, {
|
|
29
|
+
method: 'POST',
|
|
30
|
+
headers: {
|
|
31
|
+
'Content-Type': 'application/json',
|
|
32
|
+
...(config.apiKey ? { Authorization: `Bearer ${config.apiKey}` } : {})
|
|
33
|
+
},
|
|
34
|
+
body: JSON.stringify(body)
|
|
35
|
+
});
|
|
36
|
+
const json = await res.json();
|
|
37
|
+
return {
|
|
38
|
+
items: json.hits.hits.map(normalizeOpenSearchHit),
|
|
39
|
+
total: json.hits.total.value,
|
|
40
|
+
page: query.page,
|
|
41
|
+
pageSize: query.pageSize
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
return defineAlternateAdapter(adapter);
|
|
46
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { SearchAdapter, SearchResult } from '@meeovi/core';
|
|
2
|
+
import type { BuiltSearchQuery } from '../core/QueryBuilder';
|
|
3
|
+
export interface MeeoviSearchItem {
|
|
4
|
+
id: string;
|
|
5
|
+
title: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
price?: number;
|
|
8
|
+
[key: string]: unknown;
|
|
9
|
+
}
|
|
10
|
+
export type MeeoviSearchAdapter = SearchAdapter<MeeoviSearchItem> & {
|
|
11
|
+
search(query: BuiltSearchQuery): Promise<SearchResult<MeeoviSearchItem>>;
|
|
12
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export function createInstantSearchBridge(manager) {
|
|
2
|
+
return {
|
|
3
|
+
searchFunction(helper) {
|
|
4
|
+
manager.context.setQuery(helper.state.query || '');
|
|
5
|
+
manager.context.setPage(helper.state.page || 1);
|
|
6
|
+
// map filters if needed from helper.state
|
|
7
|
+
return manager.search().then((result) => {
|
|
8
|
+
helper.setResults({
|
|
9
|
+
hits: result.items,
|
|
10
|
+
nbHits: result.total,
|
|
11
|
+
page: result.page - 1,
|
|
12
|
+
hitsPerPage: result.pageSize
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare function useReactSearch(): {
|
|
2
|
+
query: string;
|
|
3
|
+
setQuery: import("react").Dispatch<import("react").SetStateAction<string>>;
|
|
4
|
+
page: number;
|
|
5
|
+
setPage: import("react").Dispatch<import("react").SetStateAction<number>>;
|
|
6
|
+
pageSize: number;
|
|
7
|
+
setPageSize: import("react").Dispatch<import("react").SetStateAction<number>>;
|
|
8
|
+
results: any[];
|
|
9
|
+
total: number;
|
|
10
|
+
loading: boolean;
|
|
11
|
+
search: () => Promise<void>;
|
|
12
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { useState, useCallback } from 'react';
|
|
2
|
+
import { useAlternateContext } from '@meeovi/core';
|
|
3
|
+
export function useReactSearch() {
|
|
4
|
+
const ctx = useAlternateContext();
|
|
5
|
+
const manager = ctx.searchManager;
|
|
6
|
+
const [query, setQuery] = useState(manager.context.state.query);
|
|
7
|
+
const [page, setPage] = useState(manager.context.state.page);
|
|
8
|
+
const [pageSize, setPageSize] = useState(manager.context.state.pageSize);
|
|
9
|
+
const [results, setResults] = useState([]);
|
|
10
|
+
const [total, setTotal] = useState(0);
|
|
11
|
+
const [loading, setLoading] = useState(false);
|
|
12
|
+
const search = useCallback(async () => {
|
|
13
|
+
setLoading(true);
|
|
14
|
+
manager.context.setQuery(query);
|
|
15
|
+
manager.context.setPage(page);
|
|
16
|
+
manager.context.setPageSize(pageSize);
|
|
17
|
+
const res = await manager.search();
|
|
18
|
+
setResults(res.items);
|
|
19
|
+
setTotal(res.total);
|
|
20
|
+
setLoading(false);
|
|
21
|
+
}, [query, page, pageSize, manager]);
|
|
22
|
+
return {
|
|
23
|
+
query,
|
|
24
|
+
setQuery,
|
|
25
|
+
page,
|
|
26
|
+
setPage,
|
|
27
|
+
pageSize,
|
|
28
|
+
setPageSize,
|
|
29
|
+
results,
|
|
30
|
+
total,
|
|
31
|
+
loading,
|
|
32
|
+
search
|
|
33
|
+
};
|
|
34
|
+
}
|