@miso.ai/client-sdk-ui 1.11.0-beta.2 → 1.11.0-beta.4
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/dist/cjs/actor/data.js +6 -7
- package/dist/cjs/actor/hub.js +1 -2
- package/dist/cjs/actor/sessions.js +4 -5
- package/dist/cjs/actor/views.js +3 -0
- package/dist/cjs/defaults/hybrid-search/answer-box.js +2 -2
- package/dist/cjs/version.js +1 -1
- package/dist/cjs/workflow/answer-based.js +20 -7
- package/dist/cjs/workflow/ask.js +1 -0
- package/dist/cjs/workflow/base.js +16 -13
- package/dist/cjs/workflow/hybrid-search-answer.js +86 -0
- package/dist/cjs/workflow/hybrid-search-results.js +88 -0
- package/dist/cjs/workflow/hybrid-search.js +184 -60
- package/dist/cjs/workflow/processors.js +0 -19
- package/package.json +2 -2
- package/src/actor/data.js +2 -4
- package/src/actor/hub.js +1 -2
- package/src/actor/sessions.js +4 -3
- package/src/actor/views.js +4 -0
- package/src/defaults/hybrid-search/answer-box.js +4 -4
- package/src/layout/trackable.js +1 -1
- package/src/version.js +1 -1
- package/src/workflow/answer-based.js +21 -5
- package/src/workflow/ask.js +2 -1
- package/src/workflow/base.js +13 -11
- package/src/workflow/hybrid-search-answer.js +78 -0
- package/src/workflow/hybrid-search-results.js +79 -0
- package/src/workflow/hybrid-search.js +194 -43
- package/src/workflow/processors.js +0 -21
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { trimObj } from '@miso.ai/commons';
|
|
2
|
+
import Workflow from './base.js';
|
|
3
|
+
import { fields } from '../actor/index.js';
|
|
4
|
+
import { ROLE } from '../constants.js';
|
|
5
|
+
import { mergeApiOptions } from './options.js';
|
|
6
|
+
import { writeKeywordsToData, composeFq } from './processors.js';
|
|
7
|
+
|
|
8
|
+
const ROLES_CONFIG = Object.freeze({
|
|
9
|
+
[ROLE.FACETS]: {
|
|
10
|
+
mapping: 'facet_counts',
|
|
11
|
+
},
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
export default class HybridSearchResults extends Workflow {
|
|
15
|
+
|
|
16
|
+
constructor(superworkflow) {
|
|
17
|
+
super({
|
|
18
|
+
name: 'hybrid-search/results',
|
|
19
|
+
plugin: superworkflow._plugin,
|
|
20
|
+
client: superworkflow._client,
|
|
21
|
+
options: superworkflow._options,
|
|
22
|
+
roles: [ROLE.PRODUCTS, ROLE.KEYWORDS, ROLE.HITS, ROLE.FACETS],
|
|
23
|
+
rolesConfig: ROLES_CONFIG,
|
|
24
|
+
superworkflow,
|
|
25
|
+
});
|
|
26
|
+
this.useInteractions(false);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
_initProperties(args) {
|
|
30
|
+
super._initProperties(args);
|
|
31
|
+
this._superworkflow = args.superworkflow;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
_initSubscriptions(args) {
|
|
35
|
+
super._initSubscriptions(args);
|
|
36
|
+
this._unsubscribes = [
|
|
37
|
+
...this._unsubscribes,
|
|
38
|
+
this._hub.on(fields.filters(), filters => this._refine(filters)),
|
|
39
|
+
];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
_initReset() {} // no reset here, will manually reset later
|
|
43
|
+
|
|
44
|
+
// query //
|
|
45
|
+
_refine(filters) {
|
|
46
|
+
// start a new session
|
|
47
|
+
this.restart();
|
|
48
|
+
|
|
49
|
+
// get stored query from sibling
|
|
50
|
+
const query = this._superworkflow._answer._hub.states[fields.query()];
|
|
51
|
+
const payload = this._buildPayload({ ...query, filters, answer: false });
|
|
52
|
+
const { session } = this;
|
|
53
|
+
const event = mergeApiOptions(this._options.resolved.api, { payload, session });
|
|
54
|
+
|
|
55
|
+
this._request(event);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
_buildPayload({ filters, ...payload } = {}) {
|
|
59
|
+
// borrow the work from the sibling
|
|
60
|
+
payload = this._superworkflow._answer._buildPayload(payload);
|
|
61
|
+
const fq = composeFq(filters); // TODO: combine with fq in payload?
|
|
62
|
+
return trimObj({
|
|
63
|
+
...payload,
|
|
64
|
+
fq,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
_request(event) {
|
|
69
|
+
this._hub.update(fields.request(), event);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// data //
|
|
73
|
+
_defaultProcessData(data) {
|
|
74
|
+
data = super._defaultProcessData(data);
|
|
75
|
+
data = writeKeywordsToData(data);
|
|
76
|
+
return data;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
}
|
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { API } from '@miso.ai/commons';
|
|
2
|
+
import { InteractionsActor } from '../actor/index.js';
|
|
3
|
+
import Workflow from './base.js';
|
|
2
4
|
import AnswerBasedWorkflow from './answer-based.js';
|
|
3
|
-
import {
|
|
4
|
-
import { fields } from '../actor/index.js';
|
|
5
|
-
import { ROLE, ORGANIC_QUESTION_SOURCE } from '../constants.js';
|
|
5
|
+
import { ROLE } from '../constants.js';
|
|
6
6
|
import { ListLayout, TextLayout, FacetsLayout } from '../layout/index.js';
|
|
7
|
-
import
|
|
7
|
+
import HybridSearchAnswer from './hybrid-search-answer.js';
|
|
8
|
+
import HybridSearchResults from './hybrid-search-results.js';
|
|
8
9
|
|
|
9
10
|
const DEFAULT_API_OPTIONS = Object.freeze({
|
|
10
11
|
...AnswerBasedWorkflow.DEFAULT_API_OPTIONS,
|
|
11
12
|
name: API.NAME.SEARCH,
|
|
12
13
|
payload: {
|
|
13
14
|
...AnswerBasedWorkflow.DEFAULT_API_OPTIONS.payload,
|
|
14
|
-
|
|
15
|
+
fl: ['cover_image', 'url', 'created_at', 'updated_at', 'published_at'],
|
|
15
16
|
},
|
|
16
17
|
});
|
|
17
18
|
|
|
@@ -43,7 +44,25 @@ const ROLES_CONFIG = Object.freeze({
|
|
|
43
44
|
},
|
|
44
45
|
});
|
|
45
46
|
|
|
46
|
-
|
|
47
|
+
const SUBWORKFLOW = Object.freeze({
|
|
48
|
+
ANSWER: 'answer',
|
|
49
|
+
RESULTS: 'results',
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
function getSubworkflowByRole(role) {
|
|
53
|
+
// TODO: ROLE.ERROR
|
|
54
|
+
switch (role) {
|
|
55
|
+
case ROLE.PRODUCTS:
|
|
56
|
+
case ROLE.HITS:
|
|
57
|
+
case ROLE.FACETS:
|
|
58
|
+
case ROLE.KEYWORDS:
|
|
59
|
+
return SUBWORKFLOW.RESULTS;
|
|
60
|
+
default:
|
|
61
|
+
return SUBWORKFLOW.ANSWER;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export default class HybridSearch extends Workflow {
|
|
47
66
|
|
|
48
67
|
constructor(plugin, client) {
|
|
49
68
|
super({
|
|
@@ -56,55 +75,187 @@ export default class HybridSearch extends AnswerBasedWorkflow {
|
|
|
56
75
|
});
|
|
57
76
|
}
|
|
58
77
|
|
|
59
|
-
|
|
60
|
-
super.
|
|
61
|
-
this.
|
|
62
|
-
|
|
63
|
-
this.
|
|
78
|
+
_initProperties(args) {
|
|
79
|
+
super._initProperties(args);
|
|
80
|
+
this._subworkflows = [
|
|
81
|
+
this._answer = new HybridSearchAnswer(this),
|
|
82
|
+
this._results = new HybridSearchResults(this),
|
|
64
83
|
];
|
|
65
84
|
}
|
|
66
85
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
question_source: qs || ORGANIC_QUESTION_SOURCE, // might be null, not undefined
|
|
77
|
-
},
|
|
78
|
-
});
|
|
86
|
+
_getSubworkflow(name) {
|
|
87
|
+
switch (name) {
|
|
88
|
+
case SUBWORKFLOW.ANSWER:
|
|
89
|
+
return this._answer;
|
|
90
|
+
case SUBWORKFLOW.RESULTS:
|
|
91
|
+
return this._results;
|
|
92
|
+
default:
|
|
93
|
+
throw new Error(`Invalid subworkflow: ${name}`);
|
|
94
|
+
}
|
|
79
95
|
}
|
|
80
96
|
|
|
81
|
-
|
|
82
|
-
const
|
|
83
|
-
const
|
|
97
|
+
_initActors() {
|
|
98
|
+
const hub = this._hub;
|
|
99
|
+
const client = this._client;
|
|
100
|
+
const options = this._options;
|
|
101
|
+
|
|
102
|
+
this._views = new HybridSearchViewsActor(this);
|
|
103
|
+
this._interactions = new InteractionsActor(hub, { client, options });
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
_initReset() {
|
|
107
|
+
this._results.reset();
|
|
108
|
+
this._answer.reset();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
restart({ answer = true } = {}) {
|
|
112
|
+
const previousSession = this.session;
|
|
113
|
+
this._sessions.restart();
|
|
84
114
|
const { session } = this;
|
|
115
|
+
if (!answer) {
|
|
116
|
+
session._answer = previousSession._answer || previousSession;
|
|
117
|
+
}
|
|
118
|
+
return this;
|
|
119
|
+
}
|
|
85
120
|
|
|
86
|
-
|
|
87
|
-
|
|
121
|
+
// properties //
|
|
122
|
+
get questionId() {
|
|
123
|
+
return this._answer.questionId;
|
|
124
|
+
}
|
|
88
125
|
|
|
89
|
-
|
|
90
|
-
|
|
126
|
+
get filters() {
|
|
127
|
+
return this._results._views.filters;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// query //
|
|
131
|
+
autoQuery(options) {
|
|
132
|
+
this._answer.autoQuery(options);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
query(args) {
|
|
136
|
+
this._answer.query(args);
|
|
91
137
|
}
|
|
92
138
|
|
|
93
|
-
//
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
139
|
+
// interactions //
|
|
140
|
+
_preprocessInteraction(payload) {
|
|
141
|
+
return this._answer._preprocessInteraction(payload);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// destroy //
|
|
145
|
+
_destroy() {
|
|
146
|
+
for (const subworkflow of this._subworkflows) {
|
|
147
|
+
subworkflow.destroy();
|
|
148
|
+
}
|
|
149
|
+
super._destroy();
|
|
99
150
|
}
|
|
100
151
|
|
|
101
152
|
}
|
|
102
153
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
154
|
+
class HybridSearchViewsActor {
|
|
155
|
+
|
|
156
|
+
constructor(workflow) {
|
|
157
|
+
this._workflow = workflow;
|
|
158
|
+
this._containers = new Set();
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
get(role) {
|
|
162
|
+
return this._getSubviews(role).get(role);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
addContainer(element) {
|
|
166
|
+
if (this._containers.has(element)) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
this._containers.add(element);
|
|
170
|
+
|
|
171
|
+
const { components } = element;
|
|
172
|
+
for (const component of components) {
|
|
173
|
+
this.addComponent(component);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
removeContainer(element) {
|
|
178
|
+
for (const subworkflow of this._workflow._subworkflows) {
|
|
179
|
+
subworkflow._views.removeContainer(element);
|
|
180
|
+
}
|
|
181
|
+
this._containers.delete(element);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
addComponent(element) {
|
|
185
|
+
this._getViewByElement(element).element = element;
|
|
186
|
+
this._addContainerToSubviews(element._container, element);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
removeComponent(element) {
|
|
190
|
+
this._getViewByElement(element).element = undefined;
|
|
191
|
+
this._removeContainerFromSubviewsIfNecessary(element._container);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
updateComponentRole(element, oldRole, newRole) {
|
|
195
|
+
// TODO
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
refreshElement(element) {
|
|
199
|
+
const view = element.isContainer ? this._containers.get(element) : this._getViewByElement(element);
|
|
200
|
+
view && view.refresh({ force: true });
|
|
106
201
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
202
|
+
|
|
203
|
+
// helpers //
|
|
204
|
+
_getSubworkflow(role) {
|
|
205
|
+
return this._workflow._getSubworkflow(getSubworkflowByRole(role));
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
_getAllSubviews() {
|
|
209
|
+
return this._workflow._subworkflows.flatMap(subworkflow => subworkflow._views);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
_getSubviews(role) {
|
|
213
|
+
return this._getSubworkflow(role)._views;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
_getViewByElement(element) {
|
|
217
|
+
let { role } = element;
|
|
218
|
+
if (!role) {
|
|
219
|
+
throw new Error('Component must have a role');
|
|
220
|
+
}
|
|
221
|
+
return this.get(role);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
_addContainerToSubviews(container, child) {
|
|
225
|
+
if (!container) {
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
const { role } = child;
|
|
229
|
+
const subviews = this._getSubviews(role);
|
|
230
|
+
if (subviews._containers.has(container)) {
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
// check if the container is in other subviews
|
|
234
|
+
for (const otherSubviews of this._getAllSubviews()) {
|
|
235
|
+
if (otherSubviews === subviews) {
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
if (otherSubviews._containers.has(container)) {
|
|
239
|
+
// find the conflicting element
|
|
240
|
+
for (const component of container.components) {
|
|
241
|
+
if (otherSubviews.containsElement(component)) {
|
|
242
|
+
throw new Error(`<${container.tagName}> cannot contain both <${component.tagName}> and <${child.tagName}>, for they don't share the same data lifecycle.`);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
throw new Error(`Failed to add container <${container.tagName}> with child component <${child.tagName}>.`);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
// add the container to the subviews
|
|
249
|
+
subviews.addContainer(container);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
_removeContainerFromSubviewsIfNecessary(container) {
|
|
253
|
+
if (!container || container.components.length > 0) {
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
for (const subviews of this._getAllSubviews()) {
|
|
257
|
+
subviews.removeContainer(container);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
110
261
|
}
|
|
@@ -73,27 +73,6 @@ export function writeAnswerStageToMeta(data) {
|
|
|
73
73
|
};
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
export function addDataInstructions(data, instructions) {
|
|
77
|
-
if (!instructions) {
|
|
78
|
-
return data;
|
|
79
|
-
}
|
|
80
|
-
return {
|
|
81
|
-
_inst: {
|
|
82
|
-
...data._inst,
|
|
83
|
-
...instructions,
|
|
84
|
-
},
|
|
85
|
-
...data,
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export function removeDataInstructions(data) {
|
|
90
|
-
if (typeof data !== 'object') {
|
|
91
|
-
return data;
|
|
92
|
-
}
|
|
93
|
-
const { _inst, ...rest } = data;
|
|
94
|
-
return rest;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
76
|
export function composeFq(filters) {
|
|
98
77
|
if (!filters) {
|
|
99
78
|
return undefined;
|