firestore-meilisearch 0.1.9 → 0.1.11
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 +3 -3
- package/__tests__/adapter.test.ts +9 -16
- package/__tests__/util.test.ts +11 -27
- package/lib/adapter.js +5 -13
- package/lib/import/config.js +11 -2
- package/lib/import/index.js +6 -5
- package/lib/index.js +2 -2
- package/lib/util.js +30 -8
- package/lib/version.js +1 -1
- package/package.json +2 -2
- package/src/adapter.ts +10 -17
- package/src/import/config.ts +14 -0
- package/src/import/index.ts +10 -5
- package/src/index.ts +10 -2
- package/src/util.ts +32 -6
- package/src/version.ts +1 -1
package/README.md
CHANGED
|
@@ -48,7 +48,7 @@ Documents indexed in Meilisearch must have an [unique id](https://docs.meilisear
|
|
|
48
48
|
|
|
49
49
|
**Important:** If your documents contain a field called `_firestore_id`, it will be ignored.
|
|
50
50
|
|
|
51
|
-
If you are using `GeoPoint`, the field should be named `_geo` to be recognized by Meilisearch for [geosearch](
|
|
51
|
+
If you are using `GeoPoint`, the field should be named `_geo` to be recognized by Meilisearch for [geosearch](https://docs.meilisearch.com/reference/features/geosearch.html#geosearch).
|
|
52
52
|
|
|
53
53
|
#### Backfill your Meilisearch data
|
|
54
54
|
|
|
@@ -102,7 +102,7 @@ All Firebase services offer a free tier of usage.
|
|
|
102
102
|
|
|
103
103
|
[][install-link]
|
|
104
104
|
|
|
105
|
-
[install-link]: https://
|
|
105
|
+
[install-link]: https://extensions.dev/extensions/meilisearch/firestore-meilisearch
|
|
106
106
|
|
|
107
107
|
### Firebase CLI
|
|
108
108
|
|
|
@@ -118,7 +118,7 @@ firebase ext:install meilisearch/firestore-meilisearch --project=[your-project-i
|
|
|
118
118
|
|
|
119
119
|
## 🤖 Compatibility with Meilisearch
|
|
120
120
|
|
|
121
|
-
This package only guarantees the compatibility with the [version v0.
|
|
121
|
+
This package only guarantees the compatibility with the [version v0.30.0 of Meilisearch](https://github.com/meilisearch/meilisearch/releases/tag/v0.30.0).
|
|
122
122
|
|
|
123
123
|
## ⚙️ Development Workflow and Contributing
|
|
124
124
|
|
|
@@ -1,41 +1,33 @@
|
|
|
1
1
|
import * as firebaseFunctionsTestInit from 'firebase-functions-test'
|
|
2
|
-
import { mocked } from 'ts-jest/utils'
|
|
3
2
|
import { mockConsoleInfo } from './__mocks__/console'
|
|
4
3
|
import { firestore } from 'firebase-admin/lib/firestore'
|
|
5
4
|
import { adaptDocument, adaptValues } from '../src/adapter'
|
|
6
|
-
import { getFieldsToIndex } from '../src/util'
|
|
7
5
|
import defaultDocument from './data/document'
|
|
8
6
|
|
|
9
|
-
jest.mock('../src/util')
|
|
10
|
-
|
|
11
7
|
// Mocking of Firebase functions
|
|
12
8
|
const firebaseMock = firebaseFunctionsTestInit()
|
|
13
9
|
|
|
14
10
|
describe('extensions process', () => {
|
|
15
11
|
describe('adaptDocument', () => {
|
|
16
|
-
const mockedGetFieldsToIndex = mocked(getFieldsToIndex, true)
|
|
17
|
-
|
|
18
12
|
test('adaptDocument with fields not set', () => {
|
|
19
|
-
mockedGetFieldsToIndex.mockReturnValueOnce([])
|
|
20
13
|
const snapshot = firebaseMock.firestore.makeDocumentSnapshot(
|
|
21
14
|
defaultDocument.document,
|
|
22
15
|
`docs/${defaultDocument.id}`
|
|
23
16
|
)
|
|
24
17
|
|
|
25
|
-
expect(adaptDocument(defaultDocument.id, snapshot)).toStrictEqual({
|
|
18
|
+
expect(adaptDocument(defaultDocument.id, snapshot, '')).toStrictEqual({
|
|
26
19
|
_firestore_id: defaultDocument.id,
|
|
27
20
|
...defaultDocument.document,
|
|
28
21
|
})
|
|
29
22
|
})
|
|
30
23
|
|
|
31
24
|
test('adaptDocument with fields not set and with id field in document', () => {
|
|
32
|
-
mockedGetFieldsToIndex.mockReturnValueOnce([])
|
|
33
25
|
const snapshot = firebaseMock.firestore.makeDocumentSnapshot(
|
|
34
26
|
{ id: '12345', ...defaultDocument.document },
|
|
35
27
|
`docs/${defaultDocument.id}`
|
|
36
28
|
)
|
|
37
29
|
|
|
38
|
-
expect(adaptDocument(defaultDocument.id, snapshot)).toStrictEqual({
|
|
30
|
+
expect(adaptDocument(defaultDocument.id, snapshot, '')).toStrictEqual({
|
|
39
31
|
_firestore_id: defaultDocument.id,
|
|
40
32
|
id: '12345',
|
|
41
33
|
...defaultDocument.document,
|
|
@@ -43,17 +35,18 @@ describe('extensions process', () => {
|
|
|
43
35
|
})
|
|
44
36
|
|
|
45
37
|
test('adaptDocument with fields set', () => {
|
|
46
|
-
mockedGetFieldsToIndex.mockReturnValueOnce([
|
|
47
|
-
'title',
|
|
48
|
-
'overview',
|
|
49
|
-
'release_date',
|
|
50
|
-
])
|
|
51
38
|
const snapshot = firebaseMock.firestore.makeDocumentSnapshot(
|
|
52
39
|
defaultDocument.document,
|
|
53
40
|
`docs/${defaultDocument.id}`
|
|
54
41
|
)
|
|
55
42
|
|
|
56
|
-
expect(
|
|
43
|
+
expect(
|
|
44
|
+
adaptDocument(
|
|
45
|
+
defaultDocument.id,
|
|
46
|
+
snapshot,
|
|
47
|
+
'title,overview,release_date'
|
|
48
|
+
)
|
|
49
|
+
).toStrictEqual({
|
|
57
50
|
_firestore_id: defaultDocument.id,
|
|
58
51
|
title: defaultDocument.document.title,
|
|
59
52
|
overview: defaultDocument.document.overview,
|
package/__tests__/util.test.ts
CHANGED
|
@@ -134,7 +134,7 @@ describe('getChangedDocumentId', () => {
|
|
|
134
134
|
describe('getFieldsToIndex', () => {
|
|
135
135
|
let util
|
|
136
136
|
let restoreEnv
|
|
137
|
-
let
|
|
137
|
+
let mockParseFieldsToIndex
|
|
138
138
|
const config = global.config
|
|
139
139
|
|
|
140
140
|
beforeEach(() => {
|
|
@@ -150,48 +150,32 @@ describe('getFieldsToIndex', () => {
|
|
|
150
150
|
|
|
151
151
|
test('return empty list', () => {
|
|
152
152
|
util = require('../src/util')
|
|
153
|
-
|
|
154
|
-
expect(
|
|
153
|
+
mockParseFieldsToIndex = util.parseFieldsToIndex()
|
|
154
|
+
expect(mockParseFieldsToIndex).toMatchObject([])
|
|
155
155
|
})
|
|
156
156
|
|
|
157
157
|
test('return list with one field', () => {
|
|
158
|
-
restoreEnv = mockedEnv({
|
|
159
|
-
...defaultEnvironment,
|
|
160
|
-
MEILISEARCH_FIELDS_TO_INDEX: 'field',
|
|
161
|
-
})
|
|
162
158
|
util = require('../src/util')
|
|
163
|
-
|
|
164
|
-
expect(
|
|
159
|
+
mockParseFieldsToIndex = util.parseFieldsToIndex('field')
|
|
160
|
+
expect(mockParseFieldsToIndex).toMatchObject(['field'])
|
|
165
161
|
})
|
|
166
162
|
|
|
167
163
|
test('return list with multiple fields', () => {
|
|
168
|
-
restoreEnv = mockedEnv({
|
|
169
|
-
...defaultEnvironment,
|
|
170
|
-
MEILISEARCH_FIELDS_TO_INDEX: 'field1,field2,field3',
|
|
171
|
-
})
|
|
172
164
|
util = require('../src/util')
|
|
173
|
-
|
|
174
|
-
expect(
|
|
165
|
+
mockParseFieldsToIndex = util.parseFieldsToIndex('field1,field2,field3')
|
|
166
|
+
expect(mockParseFieldsToIndex).toMatchObject(['field1', 'field2', 'field3'])
|
|
175
167
|
})
|
|
176
168
|
|
|
177
169
|
test('return list with multiple fields and spaces', () => {
|
|
178
|
-
restoreEnv = mockedEnv({
|
|
179
|
-
...defaultEnvironment,
|
|
180
|
-
MEILISEARCH_FIELDS_TO_INDEX: 'field1, field2, field3',
|
|
181
|
-
})
|
|
182
170
|
util = require('../src/util')
|
|
183
|
-
|
|
184
|
-
expect(
|
|
171
|
+
mockParseFieldsToIndex = util.parseFieldsToIndex('field1, field2, field3')
|
|
172
|
+
expect(mockParseFieldsToIndex).toMatchObject(['field1', 'field2', 'field3'])
|
|
185
173
|
})
|
|
186
174
|
|
|
187
175
|
test('return list of fiels with underscore', () => {
|
|
188
|
-
restoreEnv = mockedEnv({
|
|
189
|
-
...defaultEnvironment,
|
|
190
|
-
MEILISEARCH_FIELDS_TO_INDEX: 'field_1,field_2,field_3',
|
|
191
|
-
})
|
|
192
176
|
util = require('../src/util')
|
|
193
|
-
|
|
194
|
-
expect(
|
|
177
|
+
mockParseFieldsToIndex = util.parseFieldsToIndex('field_1,field_2,field_3')
|
|
178
|
+
expect(mockParseFieldsToIndex).toMatchObject([
|
|
195
179
|
'field_1',
|
|
196
180
|
'field_2',
|
|
197
181
|
'field_3',
|
package/lib/adapter.js
CHANGED
|
@@ -8,25 +8,17 @@ const logs = require("./logs");
|
|
|
8
8
|
* Adapts documents from the Firestore database to Meilisearch compatible documents.
|
|
9
9
|
* @param {string} documentId Document id.
|
|
10
10
|
* @param {DocumentSnapshot} snapshot Snapshot of the data contained in the document read from your Firestore database.
|
|
11
|
+
* @param {string} fieldsToIndex list of fields added in the document send to Meilisearch.
|
|
11
12
|
* @return {Record<string, any>} A properly formatted document to be added or updated in Meilisearch.
|
|
12
13
|
*/
|
|
13
|
-
function adaptDocument(documentId, snapshot) {
|
|
14
|
-
const fields = (0, util_1.
|
|
14
|
+
function adaptDocument(documentId, snapshot, fieldsToIndex) {
|
|
15
|
+
const fields = (0, util_1.parseFieldsToIndex)(fieldsToIndex);
|
|
15
16
|
const data = snapshot.data() || {};
|
|
16
17
|
if ('_firestore_id' in data) {
|
|
17
18
|
delete data.id;
|
|
18
19
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
22
|
-
const document = Object.keys(data).reduce((acc, key) => {
|
|
23
|
-
if (fields.includes(key)) {
|
|
24
|
-
const [field, value] = adaptValues(key, data[key]);
|
|
25
|
-
return { ...acc, [field]: value };
|
|
26
|
-
}
|
|
27
|
-
return acc;
|
|
28
|
-
}, { _firestore_id: documentId });
|
|
29
|
-
return document;
|
|
20
|
+
const document = (0, util_1.sanitizeDocuments)(fields, data);
|
|
21
|
+
return { _firestore_id: documentId, ...document };
|
|
30
22
|
}
|
|
31
23
|
exports.adaptDocument = adaptDocument;
|
|
32
24
|
/**
|
package/lib/import/config.js
CHANGED
|
@@ -19,7 +19,8 @@ program
|
|
|
19
19
|
.option('-i, --index <index>', "The Uid of the index in Meilisearch to import to. (An index will be created if it doesn't already exist.)")
|
|
20
20
|
.option('-b, --batch-size [batch-size]', 'Number of documents to stream into Meilisearch at once.', value => parseInt(value, 10), 1000)
|
|
21
21
|
.option('-H, --host <host>', 'The Host of your Meilisearch database. Example: http://localhost:7700.')
|
|
22
|
-
.option('-a, --api-key <api-key>', 'The Meilisearch API key with permission to perform actions on indexes. Both the private key and the master key are valid choices but we strongly recommend using the private key for security purposes.')
|
|
22
|
+
.option('-a, --api-key <api-key>', 'The Meilisearch API key with permission to perform actions on indexes. Both the private key and the master key are valid choices but we strongly recommend using the private key for security purposes.')
|
|
23
|
+
.option('-f, --fields-to-index <fields-to-index>', 'list of fields added in the document send to Meilisearch. Default contains all fields.');
|
|
23
24
|
const validateInput = (value, name, regex, sizeLimit) => {
|
|
24
25
|
if (!value || typeof value !== 'string' || value.trim() === '') {
|
|
25
26
|
return `Please supply a ${name}`;
|
|
@@ -82,6 +83,12 @@ const questions = [
|
|
|
82
83
|
name: 'apiKey',
|
|
83
84
|
type: 'input',
|
|
84
85
|
},
|
|
86
|
+
{
|
|
87
|
+
message: ' What fields do you want to index in Meilisearch? Create a comma-separated list of the field names, or leave it blank to include all fields. The id field is always indexed even when omitted from the list.',
|
|
88
|
+
name: 'fieldsToIndex',
|
|
89
|
+
default: '*',
|
|
90
|
+
type: 'input',
|
|
91
|
+
},
|
|
85
92
|
];
|
|
86
93
|
/**
|
|
87
94
|
* Parse the argument from the interactive or non-interactive command line.
|
|
@@ -108,10 +115,11 @@ async function parseConfig() {
|
|
|
108
115
|
indexUid: options.index,
|
|
109
116
|
host: options.host,
|
|
110
117
|
apiKey: options.apiKey,
|
|
118
|
+
fieldsToIndex: options.fieldsToIndex,
|
|
111
119
|
},
|
|
112
120
|
};
|
|
113
121
|
}
|
|
114
|
-
const { project, sourceCollectionPath, queryCollectionGroup, index, batchSize, host, apiKey, } = await inquirer.prompt(questions);
|
|
122
|
+
const { project, sourceCollectionPath, queryCollectionGroup, index, batchSize, host, apiKey, fieldsToIndex, } = await inquirer.prompt(questions);
|
|
115
123
|
return {
|
|
116
124
|
projectId: project,
|
|
117
125
|
sourceCollectionPath: sourceCollectionPath,
|
|
@@ -121,6 +129,7 @@ async function parseConfig() {
|
|
|
121
129
|
indexUid: index,
|
|
122
130
|
host: host,
|
|
123
131
|
apiKey: apiKey,
|
|
132
|
+
fieldsToIndex: fieldsToIndex,
|
|
124
133
|
},
|
|
125
134
|
};
|
|
126
135
|
}
|
package/lib/import/index.js
CHANGED
|
@@ -41,6 +41,7 @@ const run = async () => {
|
|
|
41
41
|
* @param {Index} index
|
|
42
42
|
*/
|
|
43
43
|
async function retrieveCollectionFromFirestore(database, config, index) {
|
|
44
|
+
var _a;
|
|
44
45
|
const batch = parseInt(config.batchSize);
|
|
45
46
|
let query;
|
|
46
47
|
let total = 0;
|
|
@@ -62,7 +63,7 @@ async function retrieveCollectionFromFirestore(database, config, index) {
|
|
|
62
63
|
const docs = snapshot.docs;
|
|
63
64
|
if (docs.length === 0)
|
|
64
65
|
break;
|
|
65
|
-
total += await sendDocumentsToMeilisearch(docs, index);
|
|
66
|
+
total += await sendDocumentsToMeilisearch(docs, index, ((_a = config.meilisearch) === null || _a === void 0 ? void 0 : _a.fieldsToIndex) || '');
|
|
66
67
|
if (docs.length) {
|
|
67
68
|
lastDocument = docs[docs.length - 1];
|
|
68
69
|
}
|
|
@@ -73,13 +74,13 @@ async function retrieveCollectionFromFirestore(database, config, index) {
|
|
|
73
74
|
}
|
|
74
75
|
/**
|
|
75
76
|
* Adapts documents and indexes them in Meilisearch.
|
|
76
|
-
* @param {
|
|
77
|
+
* @param {Change<DocumentSnapshot>} docs
|
|
77
78
|
* @param {Index} index
|
|
78
|
-
* @param {
|
|
79
|
+
* @param {string} fieldsToIndex list of fields added in the document send to Meilisearch.
|
|
79
80
|
*/
|
|
80
|
-
async function sendDocumentsToMeilisearch(docs, index) {
|
|
81
|
+
async function sendDocumentsToMeilisearch(docs, index, fieldsToIndex) {
|
|
81
82
|
const document = docs.map(snapshot => {
|
|
82
|
-
return (0, adapter_1.adaptDocument)(snapshot.id, snapshot);
|
|
83
|
+
return (0, adapter_1.adaptDocument)(snapshot.id, snapshot, fieldsToIndex);
|
|
83
84
|
});
|
|
84
85
|
try {
|
|
85
86
|
await index.addDocuments(document, { primaryKey: '_firestore_id' });
|
package/lib/index.js
CHANGED
|
@@ -56,7 +56,7 @@ async function handleAddDocument(documentId, snapshot) {
|
|
|
56
56
|
try {
|
|
57
57
|
logs.addDocument(documentId);
|
|
58
58
|
if ((0, validate_1.validateDocumentId)(documentId)) {
|
|
59
|
-
const document = (0, adapter_1.adaptDocument)(documentId, snapshot);
|
|
59
|
+
const document = (0, adapter_1.adaptDocument)(documentId, snapshot, config_1.config.meilisearch.fieldsToIndex || '');
|
|
60
60
|
const { taskUid } = await index.addDocuments([document], {
|
|
61
61
|
primaryKey: '_firestore_id',
|
|
62
62
|
});
|
|
@@ -98,7 +98,7 @@ async function handleUpdateDocument(documentId, after) {
|
|
|
98
98
|
try {
|
|
99
99
|
logs.updateDocument(documentId);
|
|
100
100
|
if ((0, validate_1.validateDocumentId)(documentId)) {
|
|
101
|
-
const document = (0, adapter_1.adaptDocument)(documentId, after);
|
|
101
|
+
const document = (0, adapter_1.adaptDocument)(documentId, after, config_1.config.meilisearch.fieldsToIndex || '');
|
|
102
102
|
const { taskUid } = await index.addDocuments([document]);
|
|
103
103
|
firebase_functions_1.logger.info(`Document update request for document with ID ${documentId} added to task list (task ID ${taskUid}).`);
|
|
104
104
|
}
|
package/lib/util.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
const config_1 = require("./config");
|
|
3
|
+
exports.sanitizeDocuments = exports.parseFieldsToIndex = exports.getChangedDocumentId = exports.getChangeType = exports.ChangeType = void 0;
|
|
5
4
|
var ChangeType;
|
|
6
5
|
(function (ChangeType) {
|
|
7
6
|
ChangeType[ChangeType["CREATE"] = 0] = "CREATE";
|
|
@@ -36,12 +35,35 @@ function getChangedDocumentId(change) {
|
|
|
36
35
|
}
|
|
37
36
|
exports.getChangedDocumentId = getChangedDocumentId;
|
|
38
37
|
/**
|
|
39
|
-
*
|
|
38
|
+
* Parse the fieldsToIndex string into an array.
|
|
39
|
+
*
|
|
40
|
+
* @param {string} fieldsToIndex
|
|
40
41
|
* @return {string[]} An array of fields.
|
|
41
42
|
*/
|
|
42
|
-
function
|
|
43
|
-
return
|
|
44
|
-
? config_1.config.meilisearch.fieldsToIndex.split(/[ ,]+/)
|
|
45
|
-
: [];
|
|
43
|
+
function parseFieldsToIndex(fieldsToIndex) {
|
|
44
|
+
return fieldsToIndex ? fieldsToIndex.split(/[ ,]+/) : [];
|
|
46
45
|
}
|
|
47
|
-
exports.
|
|
46
|
+
exports.parseFieldsToIndex = parseFieldsToIndex;
|
|
47
|
+
/**
|
|
48
|
+
* Remove unwanted fields from the document before it is send to Meilisearch.
|
|
49
|
+
*
|
|
50
|
+
* @param {string[]} fieldsToIndex
|
|
51
|
+
* @param {Record<string, any>} document
|
|
52
|
+
* @return {Record<string, any>} sanitized document
|
|
53
|
+
*
|
|
54
|
+
*/
|
|
55
|
+
function sanitizeDocuments(fieldsToIndex, document) {
|
|
56
|
+
if (fieldsToIndex.length === 0) {
|
|
57
|
+
return document;
|
|
58
|
+
}
|
|
59
|
+
if (fieldsToIndex.includes('*')) {
|
|
60
|
+
return document;
|
|
61
|
+
}
|
|
62
|
+
for (const key in document) {
|
|
63
|
+
if (!fieldsToIndex.includes(key)) {
|
|
64
|
+
delete document[key];
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return document;
|
|
68
|
+
}
|
|
69
|
+
exports.sanitizeDocuments = sanitizeDocuments;
|
package/lib/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "firestore-meilisearch",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.11",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"lint": "eslint .",
|
|
6
6
|
"lint:fix": "eslint . --fix",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"firebase-admin": "^9.8.0",
|
|
26
26
|
"firebase-functions": "^3.16.0",
|
|
27
27
|
"inquirer": "^8.2.2",
|
|
28
|
-
"meilisearch": "^0.
|
|
28
|
+
"meilisearch": "^0.30.0"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@babel/preset-typescript": "^7.15.0",
|
package/src/adapter.ts
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
import { DocumentSnapshot } from 'firebase-functions/lib/providers/firestore'
|
|
19
19
|
import { firestore } from 'firebase-admin/lib/firestore'
|
|
20
|
-
import {
|
|
20
|
+
import { parseFieldsToIndex, sanitizeDocuments } from './util'
|
|
21
21
|
import * as logs from './logs'
|
|
22
22
|
|
|
23
23
|
type MeilisearchGeoPoint = {
|
|
@@ -41,31 +41,24 @@ type FirestoreRow =
|
|
|
41
41
|
* Adapts documents from the Firestore database to Meilisearch compatible documents.
|
|
42
42
|
* @param {string} documentId Document id.
|
|
43
43
|
* @param {DocumentSnapshot} snapshot Snapshot of the data contained in the document read from your Firestore database.
|
|
44
|
+
* @param {string} fieldsToIndex list of fields added in the document send to Meilisearch.
|
|
44
45
|
* @return {Record<string, any>} A properly formatted document to be added or updated in Meilisearch.
|
|
45
46
|
*/
|
|
46
47
|
export function adaptDocument(
|
|
47
48
|
documentId: string,
|
|
48
|
-
snapshot: DocumentSnapshot
|
|
49
|
+
snapshot: DocumentSnapshot,
|
|
50
|
+
fieldsToIndex: string
|
|
49
51
|
): Record<string, any> {
|
|
50
|
-
const fields =
|
|
52
|
+
const fields = parseFieldsToIndex(fieldsToIndex)
|
|
53
|
+
|
|
51
54
|
const data = snapshot.data() || {}
|
|
52
55
|
if ('_firestore_id' in data) {
|
|
53
56
|
delete data.id
|
|
54
57
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
(acc, key) => {
|
|
60
|
-
if (fields.includes(key)) {
|
|
61
|
-
const [field, value] = adaptValues(key, data[key])
|
|
62
|
-
return { ...acc, [field]: value }
|
|
63
|
-
}
|
|
64
|
-
return acc
|
|
65
|
-
},
|
|
66
|
-
{ _firestore_id: documentId }
|
|
67
|
-
)
|
|
68
|
-
return document
|
|
58
|
+
|
|
59
|
+
const document = sanitizeDocuments(fields, data)
|
|
60
|
+
|
|
61
|
+
return { _firestore_id: documentId, ...document }
|
|
69
62
|
}
|
|
70
63
|
|
|
71
64
|
/**
|
package/src/import/config.ts
CHANGED
|
@@ -48,6 +48,10 @@ program
|
|
|
48
48
|
'-a, --api-key <api-key>',
|
|
49
49
|
'The Meilisearch API key with permission to perform actions on indexes. Both the private key and the master key are valid choices but we strongly recommend using the private key for security purposes.'
|
|
50
50
|
)
|
|
51
|
+
.option(
|
|
52
|
+
'-f, --fields-to-index <fields-to-index>',
|
|
53
|
+
'list of fields added in the document send to Meilisearch. Default contains all fields.'
|
|
54
|
+
)
|
|
51
55
|
|
|
52
56
|
const validateInput = (
|
|
53
57
|
value: string,
|
|
@@ -141,6 +145,13 @@ const questions = [
|
|
|
141
145
|
name: 'apiKey',
|
|
142
146
|
type: 'input',
|
|
143
147
|
},
|
|
148
|
+
{
|
|
149
|
+
message:
|
|
150
|
+
' What fields do you want to index in Meilisearch? Create a comma-separated list of the field names, or leave it blank to include all fields. The id field is always indexed even when omitted from the list.',
|
|
151
|
+
name: 'fieldsToIndex',
|
|
152
|
+
default: '*',
|
|
153
|
+
type: 'input',
|
|
154
|
+
},
|
|
144
155
|
]
|
|
145
156
|
|
|
146
157
|
export interface CLIConfig {
|
|
@@ -180,6 +191,7 @@ export async function parseConfig(): Promise<CLIConfig> {
|
|
|
180
191
|
indexUid: options.index,
|
|
181
192
|
host: options.host,
|
|
182
193
|
apiKey: options.apiKey,
|
|
194
|
+
fieldsToIndex: options.fieldsToIndex,
|
|
183
195
|
},
|
|
184
196
|
}
|
|
185
197
|
}
|
|
@@ -191,6 +203,7 @@ export async function parseConfig(): Promise<CLIConfig> {
|
|
|
191
203
|
batchSize,
|
|
192
204
|
host,
|
|
193
205
|
apiKey,
|
|
206
|
+
fieldsToIndex,
|
|
194
207
|
} = await inquirer.prompt(questions)
|
|
195
208
|
|
|
196
209
|
return {
|
|
@@ -202,6 +215,7 @@ export async function parseConfig(): Promise<CLIConfig> {
|
|
|
202
215
|
indexUid: index,
|
|
203
216
|
host: host,
|
|
204
217
|
apiKey: apiKey,
|
|
218
|
+
fieldsToIndex: fieldsToIndex,
|
|
205
219
|
},
|
|
206
220
|
}
|
|
207
221
|
}
|
package/src/import/index.ts
CHANGED
|
@@ -78,7 +78,11 @@ async function retrieveCollectionFromFirestore(
|
|
|
78
78
|
const docs = snapshot.docs
|
|
79
79
|
|
|
80
80
|
if (docs.length === 0) break
|
|
81
|
-
total += await sendDocumentsToMeilisearch(
|
|
81
|
+
total += await sendDocumentsToMeilisearch(
|
|
82
|
+
docs,
|
|
83
|
+
index,
|
|
84
|
+
config.meilisearch?.fieldsToIndex || ''
|
|
85
|
+
)
|
|
82
86
|
|
|
83
87
|
if (docs.length) {
|
|
84
88
|
lastDocument = docs[docs.length - 1]
|
|
@@ -92,16 +96,17 @@ async function retrieveCollectionFromFirestore(
|
|
|
92
96
|
|
|
93
97
|
/**
|
|
94
98
|
* Adapts documents and indexes them in Meilisearch.
|
|
95
|
-
* @param {
|
|
99
|
+
* @param {Change<DocumentSnapshot>} docs
|
|
96
100
|
* @param {Index} index
|
|
97
|
-
* @param {
|
|
101
|
+
* @param {string} fieldsToIndex list of fields added in the document send to Meilisearch.
|
|
98
102
|
*/
|
|
99
103
|
async function sendDocumentsToMeilisearch(
|
|
100
104
|
docs: DocumentSnapshot[],
|
|
101
|
-
index: Index
|
|
105
|
+
index: Index,
|
|
106
|
+
fieldsToIndex: string
|
|
102
107
|
): Promise<number> {
|
|
103
108
|
const document = docs.map(snapshot => {
|
|
104
|
-
return adaptDocument(snapshot.id, snapshot)
|
|
109
|
+
return adaptDocument(snapshot.id, snapshot, fieldsToIndex)
|
|
105
110
|
})
|
|
106
111
|
try {
|
|
107
112
|
await index.addDocuments(document, { primaryKey: '_firestore_id' })
|
package/src/index.ts
CHANGED
|
@@ -66,7 +66,11 @@ async function handleAddDocument(
|
|
|
66
66
|
try {
|
|
67
67
|
logs.addDocument(documentId)
|
|
68
68
|
if (validateDocumentId(documentId)) {
|
|
69
|
-
const document = adaptDocument(
|
|
69
|
+
const document = adaptDocument(
|
|
70
|
+
documentId,
|
|
71
|
+
snapshot,
|
|
72
|
+
config.meilisearch.fieldsToIndex || ''
|
|
73
|
+
)
|
|
70
74
|
const { taskUid } = await index.addDocuments([document], {
|
|
71
75
|
primaryKey: '_firestore_id',
|
|
72
76
|
})
|
|
@@ -119,7 +123,11 @@ async function handleUpdateDocument(
|
|
|
119
123
|
try {
|
|
120
124
|
logs.updateDocument(documentId)
|
|
121
125
|
if (validateDocumentId(documentId)) {
|
|
122
|
-
const document = adaptDocument(
|
|
126
|
+
const document = adaptDocument(
|
|
127
|
+
documentId,
|
|
128
|
+
after,
|
|
129
|
+
config.meilisearch.fieldsToIndex || ''
|
|
130
|
+
)
|
|
123
131
|
const { taskUid } = await index.addDocuments([document])
|
|
124
132
|
|
|
125
133
|
logger.info(
|
package/src/util.ts
CHANGED
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
|
|
18
18
|
import { DocumentSnapshot } from 'firebase-functions/lib/providers/firestore'
|
|
19
19
|
import { Change } from 'firebase-functions'
|
|
20
|
-
import { config } from './config'
|
|
21
20
|
|
|
22
21
|
export enum ChangeType {
|
|
23
22
|
CREATE,
|
|
@@ -53,11 +52,38 @@ export function getChangedDocumentId(change: Change<DocumentSnapshot>): string {
|
|
|
53
52
|
}
|
|
54
53
|
|
|
55
54
|
/**
|
|
56
|
-
*
|
|
55
|
+
* Parse the fieldsToIndex string into an array.
|
|
56
|
+
*
|
|
57
|
+
* @param {string} fieldsToIndex
|
|
57
58
|
* @return {string[]} An array of fields.
|
|
58
59
|
*/
|
|
59
|
-
export function
|
|
60
|
-
return
|
|
61
|
-
|
|
62
|
-
|
|
60
|
+
export function parseFieldsToIndex(fieldsToIndex: string): string[] {
|
|
61
|
+
return fieldsToIndex ? fieldsToIndex.split(/[ ,]+/) : []
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Remove unwanted fields from the document before it is send to Meilisearch.
|
|
66
|
+
*
|
|
67
|
+
* @param {string[]} fieldsToIndex
|
|
68
|
+
* @param {Record<string, any>} document
|
|
69
|
+
* @return {Record<string, any>} sanitized document
|
|
70
|
+
*
|
|
71
|
+
*/
|
|
72
|
+
export function sanitizeDocuments(
|
|
73
|
+
fieldsToIndex: string[],
|
|
74
|
+
document: Record<string, any>
|
|
75
|
+
): Record<string, any> {
|
|
76
|
+
if (fieldsToIndex.length === 0) {
|
|
77
|
+
return document
|
|
78
|
+
}
|
|
79
|
+
if (fieldsToIndex.includes('*')) {
|
|
80
|
+
return document
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
for (const key in document) {
|
|
84
|
+
if (!fieldsToIndex.includes(key)) {
|
|
85
|
+
delete document[key]
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return document
|
|
63
89
|
}
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '0.1.
|
|
1
|
+
export const version = '0.1.11'
|