@natlibfi/melinda-record-matching 2.1.0 → 2.2.0-alpha.3

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.
Files changed (31) hide show
  1. package/dist/candidate-search/index.js +40 -10
  2. package/dist/candidate-search/index.js.map +1 -1
  3. package/dist/candidate-search/index.spec.js +16 -2
  4. package/dist/candidate-search/index.spec.js.map +1 -1
  5. package/dist/candidate-search/query-list/bib.js +2 -2
  6. package/dist/candidate-search/query-list/bib.js.map +1 -1
  7. package/dist/index.js +139 -39
  8. package/dist/index.js.map +1 -1
  9. package/dist/index.spec.js +12 -6
  10. package/dist/index.spec.js.map +1 -1
  11. package/dist/match-detection/features/bib/authors.js +1 -1
  12. package/dist/match-detection/features/bib/authors.js.map +1 -1
  13. package/dist/match-detection/features/bib/standard-identifier-factory.js +3 -2
  14. package/dist/match-detection/features/bib/standard-identifier-factory.js.map +1 -1
  15. package/dist/match-detection/index.js +36 -30
  16. package/dist/match-detection/index.js.map +1 -1
  17. package/dist/match-detection/index.spec.js +20 -2
  18. package/dist/match-detection/index.spec.js.map +1 -1
  19. package/dist/matching-utils.js +1 -1
  20. package/dist/matching-utils.js.map +1 -1
  21. package/package.json +16 -15
  22. package/src/candidate-search/index.js +36 -14
  23. package/src/candidate-search/index.spec.js +13 -3
  24. package/src/candidate-search/query-list/bib.js +2 -2
  25. package/src/index.js +94 -35
  26. package/src/index.spec.js +10 -6
  27. package/src/match-detection/features/bib/authors.js +1 -1
  28. package/src/match-detection/features/bib/standard-identifier-factory.js +2 -1
  29. package/src/match-detection/index.js +29 -19
  30. package/src/match-detection/index.spec.js +17 -3
  31. package/src/matching-utils.js +1 -1
package/dist/index.js CHANGED
@@ -48,6 +48,7 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
48
48
  * for the JavaScript code in this file.
49
49
  *
50
50
  */
51
+ //import inspect from 'util';
51
52
  var _default = ({
52
53
  detection: detectionOptions,
53
54
  search: searchOptions,
@@ -55,7 +56,8 @@ var _default = ({
55
56
  maxCandidates = 25,
56
57
  returnStrategy = false,
57
58
  returnQuery = false,
58
- returnNonMatches = false
59
+ returnNonMatches = false,
60
+ returnFailures = false
59
61
  }) => {
60
62
  const debug = (0, _debug.default)('@natlibfi/melinda-record-matching:index');
61
63
  const debugData = debug.extend('data');
@@ -66,6 +68,7 @@ var _default = ({
66
68
  debugData(`ReturnStrategy: ${JSON.stringify(returnStrategy)}`);
67
69
  debugData(`ReturnQuery: ${JSON.stringify(returnQuery)}`);
68
70
  debugData(`ReturnNonMatches: ${JSON.stringify(returnNonMatches)}`);
71
+ debugData(`ReturnFailures: ${JSON.stringify(returnFailures)}`);
69
72
  const detect = (0, matchDetection.default)(detectionOptions, returnStrategy);
70
73
  return record => {
71
74
  const search = (0, candidateSearch.default)({ ...searchOptions,
@@ -90,16 +93,22 @@ var _default = ({
90
93
  candidateCount = 0,
91
94
  nonMatches = [],
92
95
  duplicateCount = 0,
93
- nonMatchCount = 0
96
+ nonMatchCount = 0,
97
+ conversionFailures = [],
98
+ matchErrors = []
94
99
  }) {
95
100
  debugData(`Starting next matcher iteration.`);
96
101
  const {
97
102
  records,
103
+ failures,
98
104
  ...state
99
105
  } = await search(initialState);
100
- debugData(`Current state: ${JSON.stringify(state)}, matches: ${matches.length}, candidateCount: ${candidateCount}, nonMatches: ${nonMatches.length}`);
106
+ debugData(`Current state: ${JSON.stringify(state)}, matches: ${matches.length}, candidateCount: ${candidateCount}, nonMatches: ${nonMatches.length}, nonMatchCount: ${nonMatchCount}, conversionFailures: ${conversionFailures}, matchErrors: ${matchErrors.length}`);
101
107
  const recordSetSize = records.length;
102
- const newCandidateCount = candidateCount + recordSetSize;
108
+ const failureSetSize = failures.length;
109
+ const newCandidateCount = candidateCount + recordSetSize + failureSetSize;
110
+ const newConversionFailures = conversionFailures.concat(failures);
111
+ debugData(`Failures: ${failures.length}, ConversionFailures: ${conversionFailures.length}, NewConversionFailures: ${newConversionFailures.length}`);
103
112
 
104
113
  if (recordSetSize > 0) {
105
114
  return handleRecordSet();
@@ -112,7 +121,10 @@ var _default = ({
112
121
  matches,
113
122
  candidateCount: newCandidateCount,
114
123
  nonMatches,
115
- duplicateCount
124
+ nonMatchCount,
125
+ duplicateCount,
126
+ conversionFailures: newConversionFailures,
127
+ matchErrors
116
128
  });
117
129
  }
118
130
 
@@ -122,8 +134,11 @@ var _default = ({
122
134
  state,
123
135
  stopReason: '',
124
136
  nonMatches,
137
+ nonMatchCount,
125
138
  candidateCount: newCandidateCount,
126
- duplicateCount
139
+ duplicateCount,
140
+ conversionFailures: newConversionFailures,
141
+ matchErrors
127
142
  });
128
143
 
129
144
  function handleRecordSet() {
@@ -133,14 +148,16 @@ var _default = ({
133
148
  recordSetSize,
134
149
  maxMatches,
135
150
  matches,
136
- nonMatches
151
+ nonMatches,
152
+ nonMatchCount
137
153
  });
138
154
  const newDuplicateCount = duplicateCount + matchResult.duplicateCount;
139
155
  const newNonMatchCount = nonMatchCount + matchResult.nonMatchCount;
140
156
  const {
141
157
  newMatches,
142
- newNonMatches
143
- } = handleMatchResult(matchResult, matches, nonMatches);
158
+ newNonMatches,
159
+ newMatchErrors
160
+ } = handleMatchResult(matchResult, matches, nonMatches, matchErrors);
144
161
 
145
162
  if (maxMatchesFound({
146
163
  matches: newMatches,
@@ -153,7 +170,9 @@ var _default = ({
153
170
  nonMatches: newNonMatches,
154
171
  duplicateCount: newDuplicateCount,
155
172
  candidateCount: newCandidateCount,
156
- nonMatchCount: newNonMatchCount
173
+ nonMatchCount: newNonMatchCount,
174
+ conversionFailures: newConversionFailures,
175
+ matchErrors: newMatchErrors
157
176
  });
158
177
  }
159
178
 
@@ -165,7 +184,9 @@ var _default = ({
165
184
  nonMatches: newNonMatches,
166
185
  duplicateCount: newDuplicateCount,
167
186
  candidateCount: newCandidateCount,
168
- nonMatchCount: newNonMatchCount
187
+ nonMatchCount: newNonMatchCount,
188
+ conversionFailures: newConversionFailures,
189
+ matchErrors: newMatchErrors
169
190
  });
170
191
  }
171
192
 
@@ -175,11 +196,13 @@ var _default = ({
175
196
  candidateCount: newCandidateCount,
176
197
  nonMatches: newNonMatches,
177
198
  duplicateCount: newDuplicateCount,
178
- nonMatchCount: newNonMatchCount
199
+ nonMatchCount: newNonMatchCount,
200
+ conversionFailures: newConversionFailures,
201
+ matchErrors: newMatchErrors
179
202
  });
180
203
  }
181
204
 
182
- function handleMatchResult(matchResult, matches, nonMatches) {
205
+ function handleMatchResult(matchResult, matches, nonMatches, matchErrors) {
183
206
  debugData(`- Amount of new matches from record set: ${matchResult.matches.length}`); // eslint-disable-next-line functional/no-conditional-statement
184
207
 
185
208
  if (returnNonMatches) {
@@ -188,15 +211,20 @@ var _default = ({
188
211
 
189
212
  const newMatches = matches.concat(returnQuery ? addQuery(matchResult.matches) : matchResult.matches);
190
213
  const newNonMatches = returnNonMatches ? nonMatches.concat(returnQuery ? addQuery(matchResult.nonMatches) : matchResult.nonMatches) : [];
214
+ const newMatchErrors = matchErrors.concat(matchResult.matchErrors);
191
215
  debugData(`- Total amount of matches: ${newMatches.length}`); // eslint-disable-next-line functional/no-conditional-statement
192
216
 
193
217
  if (returnNonMatches) {
194
218
  debugData(`- Total amount of nonMatches: ${newNonMatches.length}`);
195
219
  }
196
220
 
221
+ debugData(`MatchResult: ${JSON.stringify(matchResult)}`);
222
+ debugData(`Old matchErrors: ${JSON.stringify(matchErrors)}, matchErrors from matchResult: ${JSON.stringify(matchResult.matchErrors)}, New matchErrors: ${JSON.stringify(newMatchErrors)}`);
223
+ debugData(`- Total amount of matchErrors: ${newMatchErrors.length}`);
197
224
  return {
198
225
  newMatches,
199
- newNonMatches
226
+ newNonMatches,
227
+ newMatchErrors
200
228
  };
201
229
  }
202
230
 
@@ -223,9 +251,10 @@ var _default = ({
223
251
  // - strategy (if returnStrategy option is true)
224
252
  // - treshold (if returnStrategy option is true)
225
253
  // - matchQuery (if returnQuery option is true)
254
+ // failures: array of conversionFailures from candidate-search and matchErrors from matchDetection in error format {status, payload: {message, id}} if returnFailures is true
226
255
  // we could have here also returnRecords/returnMatchRecords/returnNonMatchRecord options that could be turned false for not to return actual record data
227
256
  // matchStatus.status: boolean, true if matcher retrieved and handled all found candidate records, false if it did not
228
- // matchStatus.stopReason: string ('maxMatches','maxCandidates','maxedQueries',empty string/undefined), reason for stopping retrieving or handling the candidate records
257
+ // matchStatus.stopReason: string ('maxMatches','maxCandidates','maxedQueries','conversionFailures', empty string/undefined), reason for stopping retrieving or handling the candidate records
229
258
  // - only one stopReason is returned (if there would be several possible stopReasons, stopReason is picked in the above order)
230
259
  // - currently stopReason can be non-empty also in cases where status is true, if matcher hit the stop reason when handling the last available candidate record
231
260
 
@@ -237,18 +266,24 @@ var _default = ({
237
266
  nonMatches,
238
267
  duplicateCount,
239
268
  candidateCount,
240
- nonMatchCount
269
+ nonMatchCount,
270
+ conversionFailures,
271
+ matchErrors
241
272
  }) {
273
+ const conversionFailureCount = conversionFailures.length;
274
+ const matchErrorCount = matchErrors.length;
242
275
  checkCounts({
243
276
  matches,
244
277
  nonMatches,
245
278
  candidateCount,
246
279
  duplicateCount,
247
- nonMatchCount
280
+ nonMatchCount,
281
+ conversionFailureCount,
282
+ matchErrorCount
248
283
  });
249
- const matchStatus = getMatchState(state, stopReason); // add nonMatches to result only if returnNonMatches is 'true', otherwise nonMatches have not been gathered
284
+ const matchStatus = getMatchState(state, stopReason, conversionFailureCount, matchErrorCount); // add nonMatches to result only if returnNonMatches is 'true', otherwise nonMatches have not been gathered
250
285
 
251
- const result = returnNonMatches ? {
286
+ const matchesResult = returnNonMatches ? {
252
287
  matches,
253
288
  matchStatus,
254
289
  nonMatches
@@ -256,6 +291,11 @@ var _default = ({
256
291
  matches,
257
292
  matchStatus
258
293
  };
294
+ const failures = [...conversionFailures, ...matchErrors];
295
+ const result = returnFailures ? { ...matchesResult,
296
+ conversionFailures: failures
297
+ } : matchesResult;
298
+ debugData(`ReturnFailures ${returnFailures}`);
259
299
  debugData(`${JSON.stringify(result)}`);
260
300
  return result; // note that in cases where the matching has been stopped because of maxMatches checkCounts won't (in most cases) match
261
301
 
@@ -264,13 +304,15 @@ var _default = ({
264
304
  nonMatches,
265
305
  candidateCount,
266
306
  duplicateCount,
267
- nonMatchCount
307
+ nonMatchCount,
308
+ conversionFailureCount,
309
+ matchErrorCount
268
310
  }) {
269
311
  const matchCount = matches.length;
270
312
  debugData(`Return nonMatches: ${returnNonMatches}`);
271
313
  const chosenNonMatchCount = returnNonMatches ? nonMatches.length : nonMatchCount;
272
- const totalHandled = matchCount + nonMatchCount + duplicateCount;
273
- debug(`candidateCount: ${candidateCount}, matches: ${matchCount}, nonMatches: ${chosenNonMatchCount}, duplicateCount: ${duplicateCount}`);
314
+ const totalHandled = matchCount + chosenNonMatchCount + duplicateCount;
315
+ debug(`candidateCount: ${candidateCount}, matches: ${matchCount}, nonMatches: ${chosenNonMatchCount}, duplicateCount: ${duplicateCount}, conversionFailureCount: ${conversionFailureCount}, matchErrorCount: ${matchErrorCount}`);
274
316
  debug(`We got result for ${totalHandled} / ${candidateCount} retrieved candidates`);
275
317
 
276
318
  if (totalHandled !== candidateCount) {
@@ -279,20 +321,28 @@ var _default = ({
279
321
  }
280
322
 
281
323
  return;
282
- }
324
+ } // eslint-disable-next-line max-statements
283
325
 
284
- function getMatchState(state, stopReason) {
326
+
327
+ function getMatchState(state, stopReason, conversionFailuresCount, matchErrorCount) {
285
328
  debugData(`${JSON.stringify(state)}`);
329
+ debug(`We had ${conversionFailuresCount} retrieved candidates that could not be converted.`);
330
+ debug(`We had ${matchErrorCount} retrieved candidates that errored in matchDetection.`);
286
331
  debug(`Queries left ${state.queriesLeft}, Searches for current query left: ${state.resultSetOffset && state.resultSetOffset <= state.totalRecords}, non-retrieved records: ${state.totalRecords - state.queryCandidateCounter}, maxedQueries (${state.maxedQueries.length}): ${state.maxedQueries}`);
287
332
  debugData(`StopReason: <${stopReason}>`);
288
333
  const searchesLeft = state.resultSetOffset && state.resultSetOffset <= state.totalRecords;
289
334
  const nonRetrieved = searchesLeft ? state.totalRecords - state.queryCandidateCounter : 0;
290
- debugData(`nonRetrieved: ${nonRetrieved}`);
291
-
292
- if (state.queriesLeft > 0 || nonRetrieved > 0 || state.maxedQueries.length > 0) {
293
- const maxedQueriesStopReason = state.maxedQueries.length > 0 ? 'maxedQueries' : '';
294
- const newStopReason = stopReason === '' ? maxedQueriesStopReason : stopReason;
335
+ debugData(`nonRetrieved: ${nonRetrieved}`); // matchStatus.stopReason: string ('maxMatches','maxCandidates','maxedQueries','conversionFailures', empty string/undefined), reason for stopping retrieving or handling the candidate records
336
+ // 'maxMatches' and 'maxCandidates' are in stopReason, 'maxedQueries', 'conversionFailures' and 'matchErrors' are created here
337
+
338
+ if (state.queriesLeft > 0 || nonRetrieved > 0 || state.maxedQueries.length > 0 || conversionFailureCount > 0 || matchErrorCount > 0) {
339
+ const maxedQueriesStopReason = state.maxedQueries.length > 0 ? 'maxedQueries' : undefined;
340
+ const conversionFailuresStopReason = conversionFailureCount > 0 ? 'conversionFailures' : undefined;
341
+ const matchErrorsStopReason = matchErrorCount > 0 ? 'matchErrors' : undefined;
342
+ const newStopReason = stopReason === '' || stopReason === undefined ? maxedQueriesStopReason || conversionFailuresStopReason || matchErrorsStopReason : stopReason;
295
343
  debugData(`MaxedQueriesStopReason: <${maxedQueriesStopReason}>`);
344
+ debugData(`ConversionFailureStopReason <${conversionFailuresStopReason}>`);
345
+ debugData(`MatchErrorsStopReason <${matchErrorsStopReason}>`);
296
346
  debugData(`NewStopReason: <${newStopReason}>`);
297
347
  debug(`Match status: false`);
298
348
  return {
@@ -307,7 +357,12 @@ var _default = ({
307
357
  stopReason
308
358
  };
309
359
  }
310
- }
360
+ } // NOTES:
361
+ // - we could optimize by creating the featureSet for the incoming record once and using it for all database/candidateRecords
362
+ // - if creating the featureSet for the incoming record fails we have an unprocessable entity
363
+ // - if creating the featureSet for a candidate record fails we could skip that candidate - but list the case as a detectionFailure, same as conversionFailures
364
+ // eslint-disable-next-line max-statements
365
+
311
366
 
312
367
  function iterateRecords({
313
368
  records,
@@ -319,7 +374,8 @@ var _default = ({
319
374
  recordNonMatches = [],
320
375
  recordCount = 0,
321
376
  recordDuplicateCount = 0,
322
- recordNonMatchCount = 0
377
+ recordNonMatchCount = 0,
378
+ recordMatchErrors = []
323
379
  }) {
324
380
  // recordSetSize : total amount of records in the current record set
325
381
  // recordCount : amount of records from the current record set that have been handled
@@ -330,21 +386,57 @@ var _default = ({
330
386
  // matches : found matches in the current matcher job
331
387
  // recordMatches : found matches in the current record set
332
388
  // recordNonMatches : found nonMatches in the current record set (only if returnNonMatches setting is true)
389
+ // recordMatchErrors: errored matchDetection in the current record set
333
390
  const [candidate] = records;
334
391
  const newRecordCount = candidate ? recordCount + 1 : recordCount; // The matcher uses same matchDetection strategy for candidates from all candidate-searches -> matchDetection result for the same candidate is always same
335
392
  // Exceptions would happen if the candidate would have been updated in the database between candidate searches
336
393
  // Note that if returnNonMatches is false, matcher won't remember candidates that didn't match, so they will be matched again everytime they are retrieved by
337
394
  // different candidate search queries. Same candidate search query won't have duplicate records.
338
395
 
396
+ /* We could optimize and detect all retrieved candidates at once
397
+ const candidateRecords = records.map(record => record.record);
398
+ const recordsIsArray = Array.isArray(candidateRecords);
399
+ debug(`records is an array: ${recordsIsArray}`);
400
+ const result = detect(record, candidateRecords);
401
+ debug(`${JSON.stringify(result)}`);
402
+ */
403
+
339
404
  if (candidate) {
405
+ // eslint-disable-next-line functional/no-conditional-statement
340
406
  if (candidateNotInMatches(matches.concat(nonMatches), candidate)) {
341
407
  const {
342
408
  record: candidateRecord,
343
409
  id: candidateId
344
410
  } = candidate;
345
- debug(`Running matchDetection for record ${candidateId} (${newRecordCount}/${recordSetSize})`);
346
- const detectionResult = detect(record, candidateRecord);
347
- return handleDetectionResult(detectionResult, candidateId, candidateRecord);
411
+
412
+ try {
413
+ debug(`Running matchDetection for record ${candidateId} (${newRecordCount}/${recordSetSize})`); // we should handle errors from detection somehow - ie. cases where either record or candidateRecord errors
414
+
415
+ const detectionResult = detect(record, candidateRecord);
416
+ return handleDetectionResult(detectionResult, candidateId, candidateRecord);
417
+ } catch (error) {
418
+ debug(`MatchDetection errored: database record ${candidateId}: ${error}`);
419
+ const matchError = {
420
+ status: 422,
421
+ payload: {
422
+ message: `Matching errored for database record ${candidateId}. ${error.message}.`,
423
+ id: candidateId
424
+ }
425
+ };
426
+ const newRecordMatchErrors = recordMatchErrors.concat(matchError);
427
+ return iterateRecords({
428
+ records: records.slice(1),
429
+ recordSetSize,
430
+ maxMatches,
431
+ matches,
432
+ recordMatches,
433
+ recordCount: newRecordCount,
434
+ recordNonMatches,
435
+ recordDuplicateCount,
436
+ recordNonMatchCount,
437
+ recordMatchErrors: newRecordMatchErrors
438
+ });
439
+ }
348
440
  }
349
441
 
350
442
  return iterateRecords({
@@ -356,7 +448,8 @@ var _default = ({
356
448
  recordCount: newRecordCount,
357
449
  recordNonMatches,
358
450
  recordDuplicateCount: recordDuplicateCount + 1,
359
- recordNonMatchCount
451
+ recordNonMatchCount,
452
+ recordMatchErrors
360
453
  });
361
454
  }
362
455
 
@@ -365,7 +458,8 @@ var _default = ({
365
458
  matches: recordMatches,
366
459
  nonMatches: returnNonMatches ? recordNonMatches : [],
367
460
  duplicateCount: recordDuplicateCount,
368
- nonMatchCount: recordNonMatchCount
461
+ nonMatchCount: recordNonMatchCount,
462
+ matchErrors: recordMatchErrors
369
463
  };
370
464
 
371
465
  function handleDetectionResult(detectionResult, candidateId, candidateRecord) {
@@ -404,19 +498,23 @@ var _default = ({
404
498
  recordCount: newRecordCount,
405
499
  recordNonMatches,
406
500
  recordDuplicateCount,
407
- recordNonMatchCount: newRecordNonMatchCount
501
+ recordNonMatchCount: newRecordNonMatchCount,
502
+ recordMatchErrors
408
503
  });
409
504
  }
410
505
 
411
506
  function handleRecordMatch(isMatch, newMatch) {
412
507
  const newRecordMatches = isMatch ? recordMatches.concat(newMatch) : recordMatches;
413
508
  const newRecordNonMatches = isMatch ? recordNonMatches : recordNonMatches.concat(newMatch);
509
+ const newRecordNonMatchCount = isMatch ? recordNonMatchCount : recordNonMatchCount + 1;
414
510
  debugData(`- Total matches after this detection: ${matches.concat(newRecordMatches).length} (max: ${maxMatches})`); // eslint-disable-next-line functional/no-conditional-statement
415
511
 
416
512
  if (returnNonMatches) {
417
513
  debugData(`- Total nonMatches after this detection: ${nonMatches.concat(newRecordNonMatches).length}`);
418
514
  }
419
515
 
516
+ debugData(`- Total nonMatchCount after this detection: ${recordNonMatchCount}`);
517
+
420
518
  if (maxMatchesFound({
421
519
  matches: matches.concat(newRecordMatches),
422
520
  maxMatches
@@ -426,7 +524,8 @@ var _default = ({
426
524
  matches: newRecordMatches,
427
525
  nonMatches: returnNonMatches ? newRecordNonMatches : [],
428
526
  duplicateCount: recordDuplicateCount,
429
- nonMatchCount: recordNonMatchCount
527
+ nonMatchCount: newRecordNonMatchCount,
528
+ matchErrors: recordMatchErrors
430
529
  };
431
530
  }
432
531
 
@@ -439,7 +538,8 @@ var _default = ({
439
538
  recordCount: newRecordCount,
440
539
  recordNonMatches: returnNonMatches ? newRecordNonMatches : [],
441
540
  duplicateCount: recordDuplicateCount,
442
- recordNonMatchCount
541
+ recordNonMatchCount: newRecordNonMatchCount,
542
+ matchErrors: recordMatchErrors
443
543
  });
444
544
  }
445
545
 
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.js"],"names":["detection","detectionOptions","search","searchOptions","maxMatches","maxCandidates","returnStrategy","returnQuery","returnNonMatches","debug","debugData","extend","JSON","stringify","detect","record","iterate","initialState","matches","candidateCount","nonMatches","duplicateCount","nonMatchCount","records","state","length","recordSetSize","newCandidateCount","handleRecordSet","queriesLeft","searchCounter","query","returnResult","stopReason","matchResult","iterateRecords","newDuplicateCount","newNonMatchCount","newMatches","newNonMatches","handleMatchResult","maxMatchesFound","maxCandidatesRetrieved","concat","addQuery","map","match","matchQuery","checkCounts","matchStatus","getMatchState","result","matchCount","chosenNonMatchCount","totalHandled","resultSetOffset","totalRecords","queryCandidateCounter","maxedQueries","searchesLeft","nonRetrieved","maxedQueriesStopReason","newStopReason","status","recordMatches","recordNonMatches","recordCount","recordDuplicateCount","recordNonMatchCount","candidate","newRecordCount","candidateNotInMatches","candidateRecord","id","candidateId","detectionResult","handleDetectionResult","slice","strategy","treshold","probability","strategyResult","newMatch","handleRecordMatch","newRecordNonMatchCount","isMatch","newRecordMatches","newRecordNonMatches","newCandidateId","find"],"mappings":"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","sourcesContent":["/**\n*\n* @licstart The following is the entire license notice for the JavaScript code in this file.\n*\n* Melinda record matching modules for Javascript\n*\n* Copyright (C) 2020-2022 University Of Helsinki (The National Library Of Finland)\n*\n* This file is part of melinda-record-matching-js\n*\n* melinda-record-matching-js program is free software: you can redistribute it and/or modify\n* it under the terms of the GNU Lesser General Public License as\n* published by the Free Software Foundation, either version 3 of the\n* License, or (at your option) any later version.\n*\n* melinda-record-matching-js is distributed in the hope that it will be useful,\n* but WITHOUT ANY WARRANTY; without even the implied warranty of\n* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n* GNU Lesser General Public License for more details.\n*\n* You should have received a copy of the GNU Affero General Public License\n* along with this program. If not, see <http://www.gnu.org/licenses/>.\n*\n* @licend The above is the entire license notice\n* for the JavaScript code in this file.\n*\n*/\n\nimport createDebugLogger from 'debug';\nimport createSearchInterface, * as candidateSearch from './candidate-search';\nimport createDetectionInterface, * as matchDetection from './match-detection';\n\nexport {candidateSearch, matchDetection};\n\nexport default ({detection: detectionOptions, search: searchOptions, maxMatches = 1, maxCandidates = 25, returnStrategy = false, returnQuery = false, returnNonMatches = false}) => {\n const debug = createDebugLogger('@natlibfi/melinda-record-matching:index');\n const debugData = debug.extend('data');\n\n debugData(`DetectionOptions: ${JSON.stringify(detectionOptions)}`);\n debugData(`SearchOptions: ${JSON.stringify(searchOptions)}`);\n debugData(`MaxMatches: ${JSON.stringify(maxMatches)}`);\n debugData(`MaxCandidates: ${JSON.stringify(maxCandidates)}`);\n debugData(`ReturnStrategy: ${JSON.stringify(returnStrategy)}`);\n debugData(`ReturnQuery: ${JSON.stringify(returnQuery)}`);\n debugData(`ReturnNonMatches: ${JSON.stringify(returnNonMatches)}`);\n\n const detect = createDetectionInterface(detectionOptions, returnStrategy);\n\n return record => {\n const search = createSearchInterface({...searchOptions, record, maxCandidates});\n return iterate({});\n\n // candidateCount : amount of candidate records retrived from SRU for matching, NOT including current record set\n // matches : candidates that have been detected as matches by current matcher job\n // nonMatches : candidates that have been detected as non-matches by current matcher job (only if returnNonMatches is 'true')\n // duplicateCount : amount of candidate records that were retrieved from the SRU but not handled further because they were already found in the matches/nonMatches\n\n // state.totalRecords : amount of candidate records available to the current query (undefined, if there was no queries left)\n // state.query : current query (undefined if there was no queries left)\n // state.searchCounter : sequence for current search for current query (undefined, if there we no queries left)\n // state.queryCandidateCounter: amount of candidate records retrieved from SRU for matching for current query, including the current record set (undefined if there were no queries left)\n // state.queriesLeft : amount of queries left\n // state.queryCounter : sequence for current query\n // state.maxedQueries : queries that resulted in more than serverMaxResults hits\n\n async function iterate({initialState = {}, matches = [], candidateCount = 0, nonMatches = [], duplicateCount = 0, nonMatchCount = 0}) {\n debugData(`Starting next matcher iteration.`);\n const {records, ...state} = await search(initialState);\n\n debugData(`Current state: ${JSON.stringify(state)}, matches: ${matches.length}, candidateCount: ${candidateCount}, nonMatches: ${nonMatches.length}`);\n const recordSetSize = records.length;\n const newCandidateCount = candidateCount + recordSetSize;\n\n if (recordSetSize > 0) {\n return handleRecordSet();\n }\n\n if (state.queriesLeft > 0) {\n debug(`Empty record set ${state.searchCounter} for ${state.query}, but there are ${state.queriesLeft} queries left`);\n return iterate({initialState: state, matches, candidateCount: newCandidateCount, nonMatches, duplicateCount});\n }\n\n debug(`No (more) candidate records to check, no more queries left, matches: ${matches.length}`);\n return returnResult({matches, state, stopReason: '', nonMatches, candidateCount: newCandidateCount, duplicateCount});\n\n function handleRecordSet() {\n debug(`Checking record set of ${recordSetSize} candidate records for possible matches, found by ${state.searchCounter} search for ${state.query}`);\n\n const matchResult = iterateRecords({records, recordSetSize, maxMatches, matches, nonMatches});\n const newDuplicateCount = duplicateCount + matchResult.duplicateCount;\n const newNonMatchCount = nonMatchCount + matchResult.nonMatchCount;\n const {newMatches, newNonMatches} = handleMatchResult(matchResult, matches, nonMatches);\n\n if (maxMatchesFound({matches: newMatches, maxMatches})) {\n return returnResult({matches: newMatches, state, stopReason: 'maxMatches', nonMatches: newNonMatches, duplicateCount: newDuplicateCount, candidateCount: newCandidateCount, nonMatchCount: newNonMatchCount});\n }\n\n if (maxCandidatesRetrieved(newCandidateCount, maxCandidates)) {\n return returnResult({matches: newMatches, state, stopReason: 'maxCandidates', nonMatches: newNonMatches, duplicateCount: newDuplicateCount, candidateCount: newCandidateCount, nonMatchCount: newNonMatchCount});\n }\n\n return iterate({initialState: state, matches: newMatches, candidateCount: newCandidateCount, nonMatches: newNonMatches, duplicateCount: newDuplicateCount, nonMatchCount: newNonMatchCount});\n }\n\n function handleMatchResult(matchResult, matches, nonMatches) {\n debugData(`- Amount of new matches from record set: ${matchResult.matches.length}`);\n // eslint-disable-next-line functional/no-conditional-statement\n if (returnNonMatches) {\n debugData(`- Amount of new nonMatches from record set: ${matchResult.nonMatches.length}`);\n }\n\n const newMatches = matches.concat(returnQuery ? addQuery(matchResult.matches) : matchResult.matches);\n const newNonMatches = returnNonMatches ? nonMatches.concat(returnQuery ? addQuery(matchResult.nonMatches) : matchResult.nonMatches) : [];\n\n debugData(`- Total amount of matches: ${newMatches.length}`);\n // eslint-disable-next-line functional/no-conditional-statement\n if (returnNonMatches) {\n debugData(`- Total amount of nonMatches: ${newNonMatches.length}`);\n }\n\n return {newMatches, newNonMatches};\n }\n\n function addQuery(matches) {\n debugData(`Adding query ${state.query} to matches`);\n return matches.map((match) => ({...match, matchQuery: state.query}));\n }\n\n function maxCandidatesRetrieved(candidateCount, maxCandidates) {\n debugData(`Total amount of candidate records retrieved: ${newCandidateCount} (max: ${maxCandidates})`);\n if (maxCandidates && candidateCount >= maxCandidates) {\n debug(`Stopped matching because maximum number of candidate records ${candidateCount} / ${maxCandidates} have been retrieved`);\n return true;\n }\n }\n }\n\n // matches : array of matching candidate records\n // nonMatches : array of nonMatching candidate records (if returnNonMatches option is true, otherwise empty array)\n // - candidate.id\n // - candidate.record\n // - probability\n // - strategy (if returnStrategy option is true)\n // - treshold (if returnStrategy option is true)\n // - matchQuery (if returnQuery option is true)\n\n // we could have here also returnRecords/returnMatchRecords/returnNonMatchRecord options that could be turned false for not to return actual record data\n\n // matchStatus.status: boolean, true if matcher retrieved and handled all found candidate records, false if it did not\n // matchStatus.stopReason: string ('maxMatches','maxCandidates','maxedQueries',empty string/undefined), reason for stopping retrieving or handling the candidate records\n // - only one stopReason is returned (if there would be several possible stopReasons, stopReason is picked in the above order)\n // - currently stopReason can be non-empty also in cases where status is true, if matcher hit the stop reason when handling the last available candidate record\n\n function returnResult({matches, state, stopReason, nonMatches, duplicateCount, candidateCount, nonMatchCount}) {\n checkCounts({matches, nonMatches, candidateCount, duplicateCount, nonMatchCount});\n const matchStatus = getMatchState(state, stopReason);\n // add nonMatches to result only if returnNonMatches is 'true', otherwise nonMatches have not been gathered\n const result = returnNonMatches ? {matches, matchStatus, nonMatches} : {matches, matchStatus};\n debugData(`${JSON.stringify(result)}`);\n return result;\n\n // note that in cases where the matching has been stopped because of maxMatches checkCounts won't (in most cases) match\n\n function checkCounts({matches, nonMatches, candidateCount, duplicateCount, nonMatchCount}) {\n const matchCount = matches.length;\n debugData(`Return nonMatches: ${returnNonMatches}`);\n const chosenNonMatchCount = returnNonMatches ? nonMatches.length : nonMatchCount;\n const totalHandled = matchCount + nonMatchCount + duplicateCount;\n debug(`candidateCount: ${candidateCount}, matches: ${matchCount}, nonMatches: ${chosenNonMatchCount}, duplicateCount: ${duplicateCount}`);\n debug(`We got result for ${totalHandled} / ${candidateCount} retrieved candidates`);\n if (totalHandled !== candidateCount) {\n debug(`WARNING: Missing results for ${candidateCount - totalHandled} candidates`);\n return;\n }\n return;\n }\n\n function getMatchState(state, stopReason) {\n debugData(`${JSON.stringify(state)}`);\n debug(`Queries left ${state.queriesLeft}, Searches for current query left: ${state.resultSetOffset && state.resultSetOffset <= state.totalRecords}, non-retrieved records: ${state.totalRecords - state.queryCandidateCounter}, maxedQueries (${state.maxedQueries.length}): ${state.maxedQueries}`);\n\n debugData(`StopReason: <${stopReason}>`);\n\n const searchesLeft = state.resultSetOffset && state.resultSetOffset <= state.totalRecords;\n const nonRetrieved = searchesLeft ? state.totalRecords - state.queryCandidateCounter : 0;\n debugData(`nonRetrieved: ${nonRetrieved}`);\n\n if (state.queriesLeft > 0 || nonRetrieved > 0 || state.maxedQueries.length > 0) {\n const maxedQueriesStopReason = state.maxedQueries.length > 0 ? 'maxedQueries' : '';\n const newStopReason = stopReason === '' ? maxedQueriesStopReason : stopReason;\n debugData(`MaxedQueriesStopReason: <${maxedQueriesStopReason}>`);\n debugData(`NewStopReason: <${newStopReason}>`);\n debug(`Match status: false`);\n return {status: false, stopReason: newStopReason};\n }\n\n debug(`Match status: true`);\n return {status: true, stopReason};\n }\n }\n\n function iterateRecords({records, recordSetSize, maxMatches, matches = [], nonMatches = [], recordMatches = [], recordNonMatches = [], recordCount = 0, recordDuplicateCount = 0, recordNonMatchCount = 0}) {\n\n // recordSetSize : total amount of records in the current record set\n // recordCount : amount of records from the current record set that have been handled\n // maxMatches : setting for maximum amount found by current matcher job before the matcher job is stopped\n // recordDuplicateCount : amount of records from the current record set that are already included in matches/nonMatches results\n // recordNonMatchCount: amount of records from the current record set that are nonMatches (only is returnNonMatches setting is false)\n\n // records : non-handled records in the current record set\n // matches : found matches in the current matcher job\n // recordMatches : found matches in the current record set\n // recordNonMatches : found nonMatches in the current record set (only if returnNonMatches setting is true)\n\n const [candidate] = records;\n const newRecordCount = candidate ? recordCount + 1 : recordCount;\n\n // The matcher uses same matchDetection strategy for candidates from all candidate-searches -> matchDetection result for the same candidate is always same\n // Exceptions would happen if the candidate would have been updated in the database between candidate searches\n // Note that if returnNonMatches is false, matcher won't remember candidates that didn't match, so they will be matched again everytime they are retrieved by\n // different candidate search queries. Same candidate search query won't have duplicate records.\n\n if (candidate) {\n\n if (candidateNotInMatches(matches.concat(nonMatches), candidate)) {\n const {record: candidateRecord, id: candidateId} = candidate;\n debug(`Running matchDetection for record ${candidateId} (${newRecordCount}/${recordSetSize})`);\n const detectionResult = detect(record, candidateRecord);\n return handleDetectionResult(detectionResult, candidateId, candidateRecord);\n }\n return iterateRecords({records: records.slice(1), recordSetSize, maxMatches, matches, recordMatches, recordCount: newRecordCount, recordNonMatches, recordDuplicateCount: recordDuplicateCount + 1, recordNonMatchCount});\n }\n\n debug(`No more candidates, record set (${recordCount}/${recordSetSize}) done, ${recordMatches.length} matches found, ${recordDuplicateCount} candidates already handled, ${returnNonMatches ? `${recordNonMatches.length}` : `${recordNonMatchCount}`} nonMatches found.`);\n return {matches: recordMatches, nonMatches: returnNonMatches ? recordNonMatches : [], duplicateCount: recordDuplicateCount, nonMatchCount: recordNonMatchCount};\n\n function handleDetectionResult(detectionResult, candidateId, candidateRecord) {\n debugData(`MatchDetection results for ${candidateId} (${newRecordCount}/${recordSetSize}): ${JSON.stringify(detectionResult)}`);\n\n if (detectionResult.match || returnNonMatches) {\n debug(`${detectionResult.match ? `Record ${candidateId} (${newRecordCount}/${recordSetSize}) is a match!` : `Record ${candidateId} (${newRecordCount}/${recordSetSize}) is NOT a match!`}`);\n debugData(`Strategy: ${JSON.stringify(detectionResult.strategy)}, Treshold: ${JSON.stringify(detectionResult.treshold)}`);\n\n const matchResult = {\n probability: detectionResult.probability,\n candidate: {\n id: candidateId,\n record: candidateRecord\n }\n };\n const strategyResult = {\n strategy: detectionResult.strategy,\n treshold: detectionResult.treshold\n };\n const newMatch = returnStrategy ? {...matchResult, ...strategyResult} : {...matchResult};\n\n debugData(`${JSON.stringify(newMatch)}`);\n\n return handleRecordMatch(detectionResult.match, newMatch);\n }\n\n const newRecordNonMatchCount = recordNonMatchCount + 1;\n debugData(`- Total nonMatches after this detection: ${newRecordNonMatchCount}`);\n\n return iterateRecords({records: records.slice(1), recordSetSize, maxMatches, matches, recordMatches, recordCount: newRecordCount, recordNonMatches, recordDuplicateCount, recordNonMatchCount: newRecordNonMatchCount});\n }\n\n function handleRecordMatch(isMatch, newMatch) {\n const newRecordMatches = isMatch ? recordMatches.concat(newMatch) : recordMatches;\n const newRecordNonMatches = isMatch ? recordNonMatches : recordNonMatches.concat(newMatch);\n\n debugData(`- Total matches after this detection: ${matches.concat(newRecordMatches).length} (max: ${maxMatches})`);\n\n // eslint-disable-next-line functional/no-conditional-statement\n if (returnNonMatches) {\n debugData(`- Total nonMatches after this detection: ${nonMatches.concat(newRecordNonMatches).length}`);\n }\n\n if (maxMatchesFound({matches: matches.concat(newRecordMatches), maxMatches})) {\n debug(`MaxMatches (${maxMatches}) reached, handled candidates in record set: ${newRecordCount} non-handled candidates in record set ${recordSetSize - newRecordCount}`);\n return {matches: newRecordMatches, nonMatches: returnNonMatches ? newRecordNonMatches : [], duplicateCount: recordDuplicateCount, nonMatchCount: recordNonMatchCount};\n }\n\n return iterateRecords({records: records.slice(1), recordSetSize, maxMatches, matches, recordMatches: newRecordMatches, recordCount: newRecordCount, recordNonMatches: returnNonMatches ? newRecordNonMatches : [], duplicateCount: recordDuplicateCount, recordNonMatchCount});\n }\n\n function candidateNotInMatches(matches, candidate) {\n debug(`Checking that record ${candidate.id} is not already included in ${matches.length} matches/nonMatches`);\n const newCandidateId = candidate.id;\n debugData(`newCandidateId: ${newCandidateId}`);\n const result = matches.find(({candidate}) => candidate.id === newCandidateId);\n debugData(`Result: ${result}`);\n if (result) {\n debug(`${candidate.id} was already handled.`);\n return false;\n }\n debug(`${candidate.id} not found in matches/nonMatches`);\n return true;\n }\n }\n\n function maxMatchesFound({matches, maxMatches}) {\n if (maxMatches && matches.length >= maxMatches) {\n debug(`Stopping recordSet iteration: maxMatches (${maxMatches}) for matcher job found.`);\n return true;\n }\n }\n };\n};\n"],"file":"index.js"}
1
+ {"version":3,"sources":["../src/index.js"],"names":["detection","detectionOptions","search","searchOptions","maxMatches","maxCandidates","returnStrategy","returnQuery","returnNonMatches","returnFailures","debug","debugData","extend","JSON","stringify","detect","record","iterate","initialState","matches","candidateCount","nonMatches","duplicateCount","nonMatchCount","conversionFailures","matchErrors","records","failures","state","length","recordSetSize","failureSetSize","newCandidateCount","newConversionFailures","concat","handleRecordSet","queriesLeft","searchCounter","query","returnResult","stopReason","matchResult","iterateRecords","newDuplicateCount","newNonMatchCount","newMatches","newNonMatches","newMatchErrors","handleMatchResult","maxMatchesFound","maxCandidatesRetrieved","addQuery","map","match","matchQuery","conversionFailureCount","matchErrorCount","checkCounts","matchStatus","getMatchState","matchesResult","result","matchCount","chosenNonMatchCount","totalHandled","conversionFailuresCount","resultSetOffset","totalRecords","queryCandidateCounter","maxedQueries","searchesLeft","nonRetrieved","maxedQueriesStopReason","undefined","conversionFailuresStopReason","matchErrorsStopReason","newStopReason","status","recordMatches","recordNonMatches","recordCount","recordDuplicateCount","recordNonMatchCount","recordMatchErrors","candidate","newRecordCount","candidateNotInMatches","candidateRecord","id","candidateId","detectionResult","handleDetectionResult","error","matchError","payload","message","newRecordMatchErrors","slice","strategy","treshold","probability","strategyResult","newMatch","handleRecordMatch","newRecordNonMatchCount","isMatch","newRecordMatches","newRecordNonMatches","newCandidateId","find"],"mappings":"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","sourcesContent":["/**\n*\n* @licstart The following is the entire license notice for the JavaScript code in this file.\n*\n* Melinda record matching modules for Javascript\n*\n* Copyright (C) 2020-2022 University Of Helsinki (The National Library Of Finland)\n*\n* This file is part of melinda-record-matching-js\n*\n* melinda-record-matching-js program is free software: you can redistribute it and/or modify\n* it under the terms of the GNU Lesser General Public License as\n* published by the Free Software Foundation, either version 3 of the\n* License, or (at your option) any later version.\n*\n* melinda-record-matching-js is distributed in the hope that it will be useful,\n* but WITHOUT ANY WARRANTY; without even the implied warranty of\n* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n* GNU Lesser General Public License for more details.\n*\n* You should have received a copy of the GNU Affero General Public License\n* along with this program. If not, see <http://www.gnu.org/licenses/>.\n*\n* @licend The above is the entire license notice\n* for the JavaScript code in this file.\n*\n*/\n\nimport createDebugLogger from 'debug';\nimport createSearchInterface, * as candidateSearch from './candidate-search';\nimport createDetectionInterface, * as matchDetection from './match-detection';\n//import inspect from 'util';\n\nexport {candidateSearch, matchDetection};\n\nexport default ({detection: detectionOptions, search: searchOptions, maxMatches = 1, maxCandidates = 25, returnStrategy = false, returnQuery = false, returnNonMatches = false, returnFailures = false}) => {\n const debug = createDebugLogger('@natlibfi/melinda-record-matching:index');\n const debugData = debug.extend('data');\n\n debugData(`DetectionOptions: ${JSON.stringify(detectionOptions)}`);\n debugData(`SearchOptions: ${JSON.stringify(searchOptions)}`);\n debugData(`MaxMatches: ${JSON.stringify(maxMatches)}`);\n debugData(`MaxCandidates: ${JSON.stringify(maxCandidates)}`);\n debugData(`ReturnStrategy: ${JSON.stringify(returnStrategy)}`);\n debugData(`ReturnQuery: ${JSON.stringify(returnQuery)}`);\n debugData(`ReturnNonMatches: ${JSON.stringify(returnNonMatches)}`);\n debugData(`ReturnFailures: ${JSON.stringify(returnFailures)}`);\n\n\n const detect = createDetectionInterface(detectionOptions, returnStrategy);\n\n return record => {\n const search = createSearchInterface({...searchOptions, record, maxCandidates});\n return iterate({});\n\n // candidateCount : amount of candidate records retrived from SRU for matching, NOT including current record set\n // matches : candidates that have been detected as matches by current matcher job\n // nonMatches : candidates that have been detected as non-matches by current matcher job (only if returnNonMatches is 'true')\n // duplicateCount : amount of candidate records that were retrieved from the SRU but not handled further because they were already found in the matches/nonMatches\n\n // state.totalRecords : amount of candidate records available to the current query (undefined, if there was no queries left)\n // state.query : current query (undefined if there was no queries left)\n // state.searchCounter : sequence for current search for current query (undefined, if there we no queries left)\n // state.queryCandidateCounter: amount of candidate records retrieved from SRU for matching for current query, including the current record set (undefined if there were no queries left)\n // state.queriesLeft : amount of queries left\n // state.queryCounter : sequence for current query\n // state.maxedQueries : queries that resulted in more than serverMaxResults hits\n\n async function iterate({initialState = {}, matches = [], candidateCount = 0, nonMatches = [], duplicateCount = 0, nonMatchCount = 0, conversionFailures = [], matchErrors = []}) {\n debugData(`Starting next matcher iteration.`);\n const {records, failures, ...state} = await search(initialState);\n\n debugData(`Current state: ${JSON.stringify(state)}, matches: ${matches.length}, candidateCount: ${candidateCount}, nonMatches: ${nonMatches.length}, nonMatchCount: ${nonMatchCount}, conversionFailures: ${conversionFailures}, matchErrors: ${matchErrors.length}`);\n const recordSetSize = records.length;\n const failureSetSize = failures.length;\n const newCandidateCount = candidateCount + recordSetSize + failureSetSize;\n\n const newConversionFailures = conversionFailures.concat(failures);\n debugData(`Failures: ${failures.length}, ConversionFailures: ${conversionFailures.length}, NewConversionFailures: ${newConversionFailures.length}`);\n\n if (recordSetSize > 0) {\n return handleRecordSet();\n }\n\n if (state.queriesLeft > 0) {\n debug(`Empty record set ${state.searchCounter} for ${state.query}, but there are ${state.queriesLeft} queries left`);\n return iterate({initialState: state, matches, candidateCount: newCandidateCount, nonMatches, nonMatchCount, duplicateCount, conversionFailures: newConversionFailures, matchErrors});\n }\n\n debug(`No (more) candidate records to check, no more queries left, matches: ${matches.length}`);\n return returnResult({matches, state, stopReason: '', nonMatches, nonMatchCount, candidateCount: newCandidateCount, duplicateCount, conversionFailures: newConversionFailures, matchErrors});\n\n function handleRecordSet() {\n debug(`Checking record set of ${recordSetSize} candidate records for possible matches, found by ${state.searchCounter} search for ${state.query}`);\n\n const matchResult = iterateRecords({records, recordSetSize, maxMatches, matches, nonMatches, nonMatchCount});\n\n const newDuplicateCount = duplicateCount + matchResult.duplicateCount;\n const newNonMatchCount = nonMatchCount + matchResult.nonMatchCount;\n const {newMatches, newNonMatches, newMatchErrors} = handleMatchResult(matchResult, matches, nonMatches, matchErrors);\n\n if (maxMatchesFound({matches: newMatches, maxMatches})) {\n return returnResult({matches: newMatches, state, stopReason: 'maxMatches', nonMatches: newNonMatches, duplicateCount: newDuplicateCount, candidateCount: newCandidateCount, nonMatchCount: newNonMatchCount, conversionFailures: newConversionFailures, matchErrors: newMatchErrors});\n }\n\n if (maxCandidatesRetrieved(newCandidateCount, maxCandidates)) {\n return returnResult({matches: newMatches, state, stopReason: 'maxCandidates', nonMatches: newNonMatches, duplicateCount: newDuplicateCount, candidateCount: newCandidateCount, nonMatchCount: newNonMatchCount, conversionFailures: newConversionFailures, matchErrors: newMatchErrors});\n }\n\n return iterate({initialState: state, matches: newMatches, candidateCount: newCandidateCount, nonMatches: newNonMatches, duplicateCount: newDuplicateCount, nonMatchCount: newNonMatchCount, conversionFailures: newConversionFailures, matchErrors: newMatchErrors});\n }\n\n function handleMatchResult(matchResult, matches, nonMatches, matchErrors) {\n debugData(`- Amount of new matches from record set: ${matchResult.matches.length}`);\n // eslint-disable-next-line functional/no-conditional-statement\n if (returnNonMatches) {\n debugData(`- Amount of new nonMatches from record set: ${matchResult.nonMatches.length}`);\n }\n\n const newMatches = matches.concat(returnQuery ? addQuery(matchResult.matches) : matchResult.matches);\n const newNonMatches = returnNonMatches ? nonMatches.concat(returnQuery ? addQuery(matchResult.nonMatches) : matchResult.nonMatches) : [];\n const newMatchErrors = matchErrors.concat(matchResult.matchErrors);\n\n debugData(`- Total amount of matches: ${newMatches.length}`);\n // eslint-disable-next-line functional/no-conditional-statement\n if (returnNonMatches) {\n debugData(`- Total amount of nonMatches: ${newNonMatches.length}`);\n }\n\n debugData(`MatchResult: ${JSON.stringify(matchResult)}`);\n debugData(`Old matchErrors: ${JSON.stringify(matchErrors)}, matchErrors from matchResult: ${JSON.stringify(matchResult.matchErrors)}, New matchErrors: ${JSON.stringify(newMatchErrors)}`);\n\n debugData(`- Total amount of matchErrors: ${newMatchErrors.length}`);\n\n return {newMatches, newNonMatches, newMatchErrors};\n }\n\n function addQuery(matches) {\n debugData(`Adding query ${state.query} to matches`);\n return matches.map((match) => ({...match, matchQuery: state.query}));\n }\n\n function maxCandidatesRetrieved(candidateCount, maxCandidates) {\n debugData(`Total amount of candidate records retrieved: ${newCandidateCount} (max: ${maxCandidates})`);\n if (maxCandidates && candidateCount >= maxCandidates) {\n debug(`Stopped matching because maximum number of candidate records ${candidateCount} / ${maxCandidates} have been retrieved`);\n return true;\n }\n }\n }\n\n // matches : array of matching candidate records\n // nonMatches : array of nonMatching candidate records (if returnNonMatches option is true, otherwise empty array)\n // - candidate.id\n // - candidate.record\n // - probability\n // - strategy (if returnStrategy option is true)\n // - treshold (if returnStrategy option is true)\n // - matchQuery (if returnQuery option is true)\n // failures: array of conversionFailures from candidate-search and matchErrors from matchDetection in error format {status, payload: {message, id}} if returnFailures is true\n\n // we could have here also returnRecords/returnMatchRecords/returnNonMatchRecord options that could be turned false for not to return actual record data\n\n // matchStatus.status: boolean, true if matcher retrieved and handled all found candidate records, false if it did not\n // matchStatus.stopReason: string ('maxMatches','maxCandidates','maxedQueries','conversionFailures', empty string/undefined), reason for stopping retrieving or handling the candidate records\n // - only one stopReason is returned (if there would be several possible stopReasons, stopReason is picked in the above order)\n // - currently stopReason can be non-empty also in cases where status is true, if matcher hit the stop reason when handling the last available candidate record\n\n function returnResult({matches, state, stopReason, nonMatches, duplicateCount, candidateCount, nonMatchCount, conversionFailures, matchErrors}) {\n const conversionFailureCount = conversionFailures.length;\n const matchErrorCount = matchErrors.length;\n checkCounts({matches, nonMatches, candidateCount, duplicateCount, nonMatchCount, conversionFailureCount, matchErrorCount});\n const matchStatus = getMatchState(state, stopReason, conversionFailureCount, matchErrorCount);\n // add nonMatches to result only if returnNonMatches is 'true', otherwise nonMatches have not been gathered\n const matchesResult = returnNonMatches ? {matches, matchStatus, nonMatches} : {matches, matchStatus};\n const failures = [...conversionFailures, ...matchErrors];\n const result = returnFailures ? {...matchesResult, conversionFailures: failures} : matchesResult;\n debugData(`ReturnFailures ${returnFailures}`);\n debugData(`${JSON.stringify(result)}`);\n return result;\n\n // note that in cases where the matching has been stopped because of maxMatches checkCounts won't (in most cases) match\n\n function checkCounts({matches, nonMatches, candidateCount, duplicateCount, nonMatchCount, conversionFailureCount, matchErrorCount}) {\n const matchCount = matches.length;\n debugData(`Return nonMatches: ${returnNonMatches}`);\n const chosenNonMatchCount = returnNonMatches ? nonMatches.length : nonMatchCount;\n const totalHandled = matchCount + chosenNonMatchCount + duplicateCount;\n debug(`candidateCount: ${candidateCount}, matches: ${matchCount}, nonMatches: ${chosenNonMatchCount}, duplicateCount: ${duplicateCount}, conversionFailureCount: ${conversionFailureCount}, matchErrorCount: ${matchErrorCount}`);\n debug(`We got result for ${totalHandled} / ${candidateCount} retrieved candidates`);\n if (totalHandled !== candidateCount) {\n debug(`WARNING: Missing results for ${candidateCount - totalHandled} candidates`);\n return;\n }\n return;\n }\n\n // eslint-disable-next-line max-statements\n function getMatchState(state, stopReason, conversionFailuresCount, matchErrorCount) {\n debugData(`${JSON.stringify(state)}`);\n debug(`We had ${conversionFailuresCount} retrieved candidates that could not be converted.`);\n debug(`We had ${matchErrorCount} retrieved candidates that errored in matchDetection.`);\n debug(`Queries left ${state.queriesLeft}, Searches for current query left: ${state.resultSetOffset && state.resultSetOffset <= state.totalRecords}, non-retrieved records: ${state.totalRecords - state.queryCandidateCounter}, maxedQueries (${state.maxedQueries.length}): ${state.maxedQueries}`);\n\n debugData(`StopReason: <${stopReason}>`);\n\n const searchesLeft = state.resultSetOffset && state.resultSetOffset <= state.totalRecords;\n const nonRetrieved = searchesLeft ? state.totalRecords - state.queryCandidateCounter : 0;\n debugData(`nonRetrieved: ${nonRetrieved}`);\n\n // matchStatus.stopReason: string ('maxMatches','maxCandidates','maxedQueries','conversionFailures', empty string/undefined), reason for stopping retrieving or handling the candidate records\n // 'maxMatches' and 'maxCandidates' are in stopReason, 'maxedQueries', 'conversionFailures' and 'matchErrors' are created here\n\n if (state.queriesLeft > 0 || nonRetrieved > 0 || state.maxedQueries.length > 0 || conversionFailureCount > 0 || matchErrorCount > 0) {\n const maxedQueriesStopReason = state.maxedQueries.length > 0 ? 'maxedQueries' : undefined;\n const conversionFailuresStopReason = conversionFailureCount > 0 ? 'conversionFailures' : undefined;\n const matchErrorsStopReason = matchErrorCount > 0 ? 'matchErrors' : undefined;\n const newStopReason = stopReason === '' || stopReason === undefined ? maxedQueriesStopReason || conversionFailuresStopReason || matchErrorsStopReason : stopReason;\n debugData(`MaxedQueriesStopReason: <${maxedQueriesStopReason}>`);\n debugData(`ConversionFailureStopReason <${conversionFailuresStopReason}>`);\n debugData(`MatchErrorsStopReason <${matchErrorsStopReason}>`);\n debugData(`NewStopReason: <${newStopReason}>`);\n debug(`Match status: false`);\n return {status: false, stopReason: newStopReason};\n }\n\n debug(`Match status: true`);\n return {status: true, stopReason};\n }\n }\n\n // NOTES:\n // - we could optimize by creating the featureSet for the incoming record once and using it for all database/candidateRecords\n // - if creating the featureSet for the incoming record fails we have an unprocessable entity\n // - if creating the featureSet for a candidate record fails we could skip that candidate - but list the case as a detectionFailure, same as conversionFailures\n\n // eslint-disable-next-line max-statements\n function iterateRecords({records, recordSetSize, maxMatches, matches = [], nonMatches = [], recordMatches = [], recordNonMatches = [], recordCount = 0, recordDuplicateCount = 0, recordNonMatchCount = 0, recordMatchErrors = []}) {\n\n // recordSetSize : total amount of records in the current record set\n // recordCount : amount of records from the current record set that have been handled\n // maxMatches : setting for maximum amount found by current matcher job before the matcher job is stopped\n // recordDuplicateCount : amount of records from the current record set that are already included in matches/nonMatches results\n // recordNonMatchCount: amount of records from the current record set that are nonMatches (only is returnNonMatches setting is false)\n\n // records : non-handled records in the current record set\n // matches : found matches in the current matcher job\n // recordMatches : found matches in the current record set\n // recordNonMatches : found nonMatches in the current record set (only if returnNonMatches setting is true)\n // recordMatchErrors: errored matchDetection in the current record set\n\n const [candidate] = records;\n const newRecordCount = candidate ? recordCount + 1 : recordCount;\n\n // The matcher uses same matchDetection strategy for candidates from all candidate-searches -> matchDetection result for the same candidate is always same\n // Exceptions would happen if the candidate would have been updated in the database between candidate searches\n // Note that if returnNonMatches is false, matcher won't remember candidates that didn't match, so they will be matched again everytime they are retrieved by\n // different candidate search queries. Same candidate search query won't have duplicate records.\n\n /* We could optimize and detect all retrieved candidates at once\n const candidateRecords = records.map(record => record.record);\n const recordsIsArray = Array.isArray(candidateRecords);\n debug(`records is an array: ${recordsIsArray}`);\n const result = detect(record, candidateRecords);\n debug(`${JSON.stringify(result)}`);\n */\n\n if (candidate) {\n\n // eslint-disable-next-line functional/no-conditional-statement\n if (candidateNotInMatches(matches.concat(nonMatches), candidate)) {\n const {record: candidateRecord, id: candidateId} = candidate;\n try {\n debug(`Running matchDetection for record ${candidateId} (${newRecordCount}/${recordSetSize})`);\n // we should handle errors from detection somehow - ie. cases where either record or candidateRecord errors\n const detectionResult = detect(record, candidateRecord);\n\n return handleDetectionResult(detectionResult, candidateId, candidateRecord);\n } catch (error) {\n debug(`MatchDetection errored: database record ${candidateId}: ${error}`);\n\n const matchError = {status: 422, payload: {message: `Matching errored for database record ${candidateId}. ${error.message}.`, id: candidateId}};\n const newRecordMatchErrors = recordMatchErrors.concat(matchError);\n return iterateRecords({records: records.slice(1), recordSetSize, maxMatches, matches, recordMatches, recordCount: newRecordCount, recordNonMatches, recordDuplicateCount, recordNonMatchCount, recordMatchErrors: newRecordMatchErrors});\n }\n }\n\n return iterateRecords({records: records.slice(1), recordSetSize, maxMatches, matches, recordMatches, recordCount: newRecordCount, recordNonMatches, recordDuplicateCount: recordDuplicateCount + 1, recordNonMatchCount, recordMatchErrors});\n }\n\n debug(`No more candidates, record set (${recordCount}/${recordSetSize}) done, ${recordMatches.length} matches found, ${recordDuplicateCount} candidates already handled, ${returnNonMatches ? `${recordNonMatches.length}` : `${recordNonMatchCount}`} nonMatches found.`);\n return {matches: recordMatches, nonMatches: returnNonMatches ? recordNonMatches : [], duplicateCount: recordDuplicateCount, nonMatchCount: recordNonMatchCount, matchErrors: recordMatchErrors};\n\n function handleDetectionResult(detectionResult, candidateId, candidateRecord) {\n debugData(`MatchDetection results for ${candidateId} (${newRecordCount}/${recordSetSize}): ${JSON.stringify(detectionResult)}`);\n\n if (detectionResult.match || returnNonMatches) {\n debug(`${detectionResult.match ? `Record ${candidateId} (${newRecordCount}/${recordSetSize}) is a match!` : `Record ${candidateId} (${newRecordCount}/${recordSetSize}) is NOT a match!`}`);\n debugData(`Strategy: ${JSON.stringify(detectionResult.strategy)}, Treshold: ${JSON.stringify(detectionResult.treshold)}`);\n\n const matchResult = {\n probability: detectionResult.probability,\n candidate: {\n id: candidateId,\n record: candidateRecord\n }\n };\n const strategyResult = {\n strategy: detectionResult.strategy,\n treshold: detectionResult.treshold\n };\n const newMatch = returnStrategy ? {...matchResult, ...strategyResult} : {...matchResult};\n\n debugData(`${JSON.stringify(newMatch)}`);\n\n return handleRecordMatch(detectionResult.match, newMatch);\n }\n\n const newRecordNonMatchCount = recordNonMatchCount + 1;\n debugData(`- Total nonMatches after this detection: ${newRecordNonMatchCount}`);\n\n return iterateRecords({records: records.slice(1), recordSetSize, maxMatches, matches, recordMatches, recordCount: newRecordCount, recordNonMatches, recordDuplicateCount, recordNonMatchCount: newRecordNonMatchCount, recordMatchErrors});\n }\n\n function handleRecordMatch(isMatch, newMatch) {\n const newRecordMatches = isMatch ? recordMatches.concat(newMatch) : recordMatches;\n const newRecordNonMatches = isMatch ? recordNonMatches : recordNonMatches.concat(newMatch);\n const newRecordNonMatchCount = isMatch ? recordNonMatchCount : recordNonMatchCount + 1;\n\n debugData(`- Total matches after this detection: ${matches.concat(newRecordMatches).length} (max: ${maxMatches})`);\n\n // eslint-disable-next-line functional/no-conditional-statement\n if (returnNonMatches) {\n debugData(`- Total nonMatches after this detection: ${nonMatches.concat(newRecordNonMatches).length}`);\n }\n debugData(`- Total nonMatchCount after this detection: ${recordNonMatchCount}`);\n\n if (maxMatchesFound({matches: matches.concat(newRecordMatches), maxMatches})) {\n debug(`MaxMatches (${maxMatches}) reached, handled candidates in record set: ${newRecordCount} non-handled candidates in record set ${recordSetSize - newRecordCount}`);\n return {matches: newRecordMatches, nonMatches: returnNonMatches ? newRecordNonMatches : [], duplicateCount: recordDuplicateCount, nonMatchCount: newRecordNonMatchCount, matchErrors: recordMatchErrors};\n }\n\n return iterateRecords({records: records.slice(1), recordSetSize, maxMatches, matches, recordMatches: newRecordMatches, recordCount: newRecordCount, recordNonMatches: returnNonMatches ? newRecordNonMatches : [], duplicateCount: recordDuplicateCount, recordNonMatchCount: newRecordNonMatchCount, matchErrors: recordMatchErrors});\n }\n\n function candidateNotInMatches(matches, candidate) {\n debug(`Checking that record ${candidate.id} is not already included in ${matches.length} matches/nonMatches`);\n const newCandidateId = candidate.id;\n debugData(`newCandidateId: ${newCandidateId}`);\n const result = matches.find(({candidate}) => candidate.id === newCandidateId);\n debugData(`Result: ${result}`);\n if (result) {\n debug(`${candidate.id} was already handled.`);\n return false;\n }\n debug(`${candidate.id} not found in matches/nonMatches`);\n return true;\n }\n }\n\n function maxMatchesFound({matches, maxMatches}) {\n if (maxMatches && matches.length >= maxMatches) {\n debug(`Stopping recordSet iteration: maxMatches (${maxMatches}) for matcher job found.`);\n return true;\n }\n }\n };\n};\n"],"file":"index.js"}
@@ -62,7 +62,8 @@ describe('INDEX', () => {
62
62
  options,
63
63
  enabled = true,
64
64
  expectedMatchStatus,
65
- expectedStopReason
65
+ expectedStopReason,
66
+ expectedFailures
66
67
  }) {
67
68
  if (!enabled) {
68
69
  debug(`Disabled test!`);
@@ -78,15 +79,20 @@ describe('INDEX', () => {
78
79
  const {
79
80
  matches,
80
81
  matchStatus,
81
- nonMatches
82
+ nonMatches,
83
+ conversionFailures
82
84
  } = await match(record);
83
- debugData(`${matches.length}, ${matchStatus.status}/${matchStatus.stopReason}, ${nonMatches ? nonMatches.length : nonMatches}`);
85
+ debugData(`Matches: ${matches.length}, Status: ${matchStatus.status}/${matchStatus.stopReason}, NonMatches: ${nonMatches ? nonMatches.length : 'not returned'}, ConversionFailures: ${conversionFailures ? conversionFailures.length : 'not returned'}`);
86
+ (0, _chai.expect)(matchStatus.status).to.eql(expectedMatchStatus);
87
+ (0, _chai.expect)(matchStatus.stopReason).to.eql(expectedStopReason);
84
88
  const formattedMatchResult = formatRecordResults(matches);
85
89
  (0, _chai.expect)(formattedMatchResult).to.eql(expectedMatches);
86
90
  const formattedNonMatchResult = formatRecordResults(nonMatches);
87
- (0, _chai.expect)(formattedNonMatchResult).to.eql(expectedNonMatches);
88
- (0, _chai.expect)(matchStatus.status).to.eql(expectedMatchStatus);
89
- (0, _chai.expect)(matchStatus.stopReason).to.eql(expectedStopReason);
91
+ (0, _chai.expect)(formattedNonMatchResult).to.eql(expectedNonMatches); // eslint-disable-next-line functional/no-conditional-statement
92
+
93
+ if (expectedFailures) {
94
+ (0, _chai.expect)(conversionFailures).to.eql(expectedFailures);
95
+ }
90
96
 
91
97
  function formatOptions() {
92
98
  const contextFeatures = _.matchDetection.features[options.detection.strategy.type];