@natlibfi/melinda-record-matching 2.1.0-alpha.2 → 2.2.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/candidate-search/index.js +39 -9
- package/dist/candidate-search/index.js.map +1 -1
- package/dist/candidate-search/index.spec.js +16 -2
- package/dist/candidate-search/index.spec.js.map +1 -1
- package/dist/index.js +55 -25
- package/dist/index.js.map +1 -1
- package/dist/index.spec.js +12 -6
- package/dist/index.spec.js.map +1 -1
- package/package.json +16 -15
- package/src/candidate-search/index.js +33 -14
- package/src/candidate-search/index.spec.js +13 -3
- package/src/index.js +43 -25
- package/src/index.spec.js +10 -6
|
@@ -21,6 +21,8 @@ var _marcRecordSerializers = require("@natlibfi/marc-record-serializers");
|
|
|
21
21
|
|
|
22
22
|
var _queryList = _interopRequireWildcard(require("./query-list"));
|
|
23
23
|
|
|
24
|
+
var _melindaCommons = require("@natlibfi/melinda-commons");
|
|
25
|
+
|
|
24
26
|
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
25
27
|
|
|
26
28
|
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
@@ -98,11 +100,10 @@ var _default = ({
|
|
|
98
100
|
} // state.totalRecords : amount of candidate records available to the current query (undefined, if there was no queries left)
|
|
99
101
|
// state.query : current query (undefined if there was no queries left)
|
|
100
102
|
// state.searchCounter : sequence for current search for current query (undefined, if there we no queries left)
|
|
101
|
-
// state.queryCandidateCounter: amount of
|
|
103
|
+
// state.queryCandidateCounter: amount of candidates (records+failures) retrieved from SRU for matching for current query, including the current record+failure set (undefined if there were no queries left)
|
|
102
104
|
// state.queriesLeft : amount of queries left
|
|
103
105
|
// state.queryCounter : sequence for current query
|
|
104
106
|
// state.maxedQueries : queries that resulted in more than serverMaxResults hits
|
|
105
|
-
// eslint-disable-next-line max-statements
|
|
106
107
|
|
|
107
108
|
|
|
108
109
|
return async ({
|
|
@@ -119,6 +120,7 @@ var _default = ({
|
|
|
119
120
|
if (query) {
|
|
120
121
|
const {
|
|
121
122
|
records,
|
|
123
|
+
failures,
|
|
122
124
|
nextOffset,
|
|
123
125
|
total
|
|
124
126
|
} = await retrieveRecords(); // If resultSetOffset === 1 this is the first search for the current query
|
|
@@ -127,7 +129,7 @@ var _default = ({
|
|
|
127
129
|
const newTotalRecords = resultSetOffset === 1 ? total : totalRecords;
|
|
128
130
|
const newQueryCounter = resultSetOffset === 1 ? queryCounter + 1 : queryCounter;
|
|
129
131
|
const newSearchCounter = resultSetOffset === 1 ? 1 : searchCounter + 1;
|
|
130
|
-
const newQueryCandidateCounter = resultSetOffset === 1 ? records.length : queryCandidateCounter + records.length;
|
|
132
|
+
const newQueryCandidateCounter = resultSetOffset === 1 ? records.length + failures.length : queryCandidateCounter + records.length + failures.length;
|
|
131
133
|
const maxedQuery = resultSetOffset === 1 ? checkMaxedQuery(query, total, serverMaxResult) : undefined;
|
|
132
134
|
const newMaxedQueries = maxedQuery ? maxedQueries.concat(maxedQuery) : maxedQueries;
|
|
133
135
|
|
|
@@ -135,6 +137,7 @@ var _default = ({
|
|
|
135
137
|
debug(`Next search will be for query ${queryOffset} ${query}, starting from record ${nextOffset}`);
|
|
136
138
|
return {
|
|
137
139
|
records,
|
|
140
|
+
failures,
|
|
138
141
|
queryOffset,
|
|
139
142
|
resultSetOffset: nextOffset,
|
|
140
143
|
queriesLeft: queryList.length - (queryOffset + 1),
|
|
@@ -151,6 +154,7 @@ var _default = ({
|
|
|
151
154
|
debug(`There are (${queryList.length - (queryOffset + 1)} queries left)`);
|
|
152
155
|
return {
|
|
153
156
|
records,
|
|
157
|
+
failures,
|
|
154
158
|
queryOffset: queryOffset + 1,
|
|
155
159
|
queriesLeft: queryList.length - (queryOffset + 1),
|
|
156
160
|
totalRecords: newTotalRecords,
|
|
@@ -165,6 +169,7 @@ var _default = ({
|
|
|
165
169
|
debug(`All ${queryList.length} queries done, there's no query for ${queryOffset}`);
|
|
166
170
|
return {
|
|
167
171
|
records: [],
|
|
172
|
+
failures: [],
|
|
168
173
|
queriesLeft: 0,
|
|
169
174
|
queryCounter,
|
|
170
175
|
maxedQueries
|
|
@@ -192,23 +197,34 @@ var _default = ({
|
|
|
192
197
|
totalRecords += total;
|
|
193
198
|
}).on('end', async nextOffset => {
|
|
194
199
|
try {
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
|
|
200
|
+
const recordPromises = await Promise.allSettled(promises);
|
|
201
|
+
debug(`All recordPromises: ${JSON.stringify(recordPromises)}`);
|
|
202
|
+
const filtered = recordPromises.filter(r => r.status === 'fulfilled').map(r => r.value);
|
|
203
|
+
const failures = recordPromises.filter(r => r.status === 'rejected').map(r => ({
|
|
204
|
+
status: r.reason.status,
|
|
205
|
+
payload: r.reason.payload
|
|
206
|
+
}));
|
|
207
|
+
debug(`Found ${JSON.stringify(recordPromises)} records`);
|
|
208
|
+
debug(`Found ${filtered.length} convertable candidates`);
|
|
209
|
+
debug(`Found ${failures.length} NON-convertable candidates`);
|
|
210
|
+
debug(`Converted: ${JSON.stringify(filtered)}.`);
|
|
211
|
+
debug(`Not converted: ${JSON.stringify(failures)}.`);
|
|
198
212
|
resolve({
|
|
199
213
|
nextOffset,
|
|
200
214
|
records: filtered,
|
|
215
|
+
failures,
|
|
201
216
|
total: totalRecords
|
|
202
217
|
});
|
|
203
218
|
} catch (err) {
|
|
219
|
+
debug(`Error caught on END`);
|
|
204
220
|
reject(err);
|
|
205
221
|
}
|
|
206
|
-
}).on('record',
|
|
222
|
+
}).on('record', foundRecordXML => {
|
|
207
223
|
promises.push(handleRecord()); // eslint-disable-line functional/immutable-data
|
|
208
224
|
|
|
209
225
|
async function handleRecord() {
|
|
210
226
|
try {
|
|
211
|
-
const foundRecordMarc = await _marcRecordSerializers.MARCXML.from(
|
|
227
|
+
const foundRecordMarc = await _marcRecordSerializers.MARCXML.from(foundRecordXML, {
|
|
212
228
|
subfieldValues: false
|
|
213
229
|
});
|
|
214
230
|
const foundRecordId = getRecordId(foundRecordMarc);
|
|
@@ -217,7 +233,15 @@ var _default = ({
|
|
|
217
233
|
id: foundRecordId
|
|
218
234
|
};
|
|
219
235
|
} catch (err) {
|
|
220
|
-
|
|
236
|
+
// What should this do?
|
|
237
|
+
const idFromXML = getRecordIdFromXML(foundRecordXML);
|
|
238
|
+
debugData(`Failed converting record: ${err.message}, id: ${idFromXML}, data: ${foundRecordXML}`); //return {message: `Failed converting record: ${err.message}`, id: idFromXML, data: foundRecordXML};
|
|
239
|
+
|
|
240
|
+
throw new _melindaCommons.Error(422, {
|
|
241
|
+
message: `Failed converting record: ${err.message}`,
|
|
242
|
+
id: idFromXML || '000000000',
|
|
243
|
+
data: foundRecordXML
|
|
244
|
+
});
|
|
221
245
|
}
|
|
222
246
|
}
|
|
223
247
|
});
|
|
@@ -237,6 +261,12 @@ var _default = ({
|
|
|
237
261
|
const [field] = record.get(/^001$/u);
|
|
238
262
|
return field ? field.value : '';
|
|
239
263
|
}
|
|
264
|
+
|
|
265
|
+
function getRecordIdFromXML(recordXML) {
|
|
266
|
+
//<controlfield tag=\"001\">015376846</controlfield
|
|
267
|
+
debug(`Cannot yet find possible database record id from recordXML (length ${recordXML.length})`);
|
|
268
|
+
return undefined;
|
|
269
|
+
}
|
|
240
270
|
};
|
|
241
271
|
|
|
242
272
|
exports.default = _default;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/candidate-search/index.js"],"names":["CandidateSearchError","Error","record","searchSpec","url","maxCandidates","maxRecordsPerRequest","serverMaxResult","MarcRecord","setValidationOptions","subfieldValues","debug","debugData","extend","JSON","stringify","adjustedMaxRecordsPerRequest","inputRecordId","getRecordId","queryList","client","version","retrieveAll","length","queryOffset","resultSetOffset","totalRecords","searchCounter","queryCandidateCounter","queryCounter","maxedQueries","query","records","nextOffset","total","retrieveRecords","newTotalRecords","newQueryCounter","newSearchCounter","newQueryCandidateCounter","maxedQuery","checkMaxedQuery","undefined","newMaxedQueries","concat","queriesLeft","Promise","resolve","reject","promises","searchRetrieve","startRecord","on","err","SruSearchError","all","filtered","filter","r","foundRecord","push","handleRecord","foundRecordMarc","MARCXML","from","foundRecordId","id","field","get","value"],"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 createClient, {SruSearchError} from '@natlibfi/sru-client';\nimport {MarcRecord} from '@natlibfi/marc-record';\nimport {MARCXML} from '@natlibfi/marc-record-serializers';\nimport generateQueryList from './query-list';\n\nexport {searchTypes} from './query-list';\n\nexport class CandidateSearchError extends Error {}\n\n// serverMaxResults : maximum size of total search result available from the server, defaults to Aleph's 20000\n\n// eslint-disable-next-line max-statements\nexport default ({record, searchSpec, url, maxCandidates, maxRecordsPerRequest = 50, serverMaxResult = 20000}) => {\n MarcRecord.setValidationOptions({subfieldValues: false});\n\n const debug = createDebugLogger('@natlibfi/melinda-record-matching:candidate-search');\n const debugData = debug.extend('data');\n\n debugData(`SearchSpec: ${JSON.stringify(searchSpec)}`);\n debugData(`Url: ${url}`);\n debugData(`MaxRecordsPerRequest ${maxRecordsPerRequest}`);\n debugData(`ServerMaxResult: ${serverMaxResult}`);\n debugData(`MaxCandidates: ${maxCandidates}`);\n\n // Do not retrieve more candidates than defined in maxCandidates\n const adjustedMaxRecordsPerRequest = maxRecordsPerRequest >= maxCandidates ? maxCandidates : maxRecordsPerRequest;\n\n const inputRecordId = getRecordId(record);\n const queryList = generateQueryList(record, searchSpec);\n const client = createClient({\n url,\n maxRecordsPerRequest: adjustedMaxRecordsPerRequest,\n version: '2.0',\n retrieveAll: false\n });\n\n debug(`Searching matches for ${inputRecordId}`);\n debug(`Generated queryList ${JSON.stringify(queryList)}`);\n if (queryList.length === 0) { // eslint-disable-line functional/no-conditional-statement\n throw new CandidateSearchError(`Generated query list contains no queries`);\n }\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\n // eslint-disable-next-line max-statements\n return async ({queryOffset = 0, resultSetOffset = 1, totalRecords = 0, searchCounter = 0, queryCandidateCounter = 0, queryCounter = 0, maxedQueries = []}) => {\n const query = queryList[queryOffset];\n\n if (query) {\n const {records, nextOffset, total} = await retrieveRecords();\n\n // If resultSetOffset === 1 this is the first search for the current query\n debugData(`ResultSetOffset: ${resultSetOffset}`);\n const newTotalRecords = resultSetOffset === 1 ? total : totalRecords;\n const newQueryCounter = resultSetOffset === 1 ? queryCounter + 1 : queryCounter;\n const newSearchCounter = resultSetOffset === 1 ? 1 : searchCounter + 1;\n const newQueryCandidateCounter = resultSetOffset === 1 ? records.length : queryCandidateCounter + records.length;\n\n const maxedQuery = resultSetOffset === 1 ? checkMaxedQuery(query, total, serverMaxResult) : undefined;\n const newMaxedQueries = maxedQuery ? maxedQueries.concat(maxedQuery) : maxedQueries;\n\n if (typeof nextOffset === 'number') {\n debug(`Next search will be for query ${queryOffset} ${query}, starting from record ${nextOffset}`);\n return {records, queryOffset, resultSetOffset: nextOffset, queriesLeft: queryList.length - (queryOffset + 1), totalRecords: newTotalRecords, query, searchCounter: newSearchCounter, queryCandidateCounter: newQueryCandidateCounter, queryCounter: newQueryCounter, maxedQueries: newMaxedQueries};\n }\n debug(`Query ${queryOffset} ${query} done.`);\n debug(`There are (${queryList.length - (queryOffset + 1)} queries left)`);\n return {records, queryOffset: queryOffset + 1, queriesLeft: queryList.length - (queryOffset + 1), totalRecords: newTotalRecords, query, searchCounter: newSearchCounter, queryCandidateCounter: newQueryCandidateCounter, queryCounter: newQueryCounter, maxedQueries: newMaxedQueries};\n }\n\n debug(`All ${queryList.length} queries done, there's no query for ${queryOffset}`);\n return {records: [], queriesLeft: 0, queryCounter, maxedQueries};\n\n function retrieveRecords() {\n return new Promise((resolve, reject) => {\n const promises = [];\n // eslint-disable-next-line functional/no-let\n let totalRecords = 0;\n\n debug(`Searching for candidates with query: ${query} (Offset ${resultSetOffset})`);\n\n client.searchRetrieve(query, {startRecord: resultSetOffset})\n .on('error', err => {\n // eslint-disable-next-line functional/no-conditional-statement\n if (err instanceof SruSearchError) {\n debug(`SRU SruSearchError for query: ${query}: ${err}`);\n reject(new CandidateSearchError(`SRU SruSearchError for query: ${query}: ${err}`));\n }\n debug(`SRU error for query: ${query}: ${err}`);\n reject(new CandidateSearchError(`SRU error for query: ${query}: ${err}`));\n })\n .on('total', total => {\n debug(`Got total: ${total}`);\n totalRecords += total;\n })\n .on('end', async nextOffset => {\n try {\n const records = await Promise.all(promises);\n const filtered = records.filter(r => r);\n\n debug(`Found ${filtered.length} candidates`);\n\n resolve({nextOffset, records: filtered, total: totalRecords});\n } catch (err) {\n reject(err);\n }\n })\n .on('record', foundRecord => {\n promises.push(handleRecord()); // eslint-disable-line functional/immutable-data\n\n async function handleRecord() {\n try {\n const foundRecordMarc = await MARCXML.from(foundRecord, {subfieldValues: false});\n const foundRecordId = getRecordId(foundRecordMarc);\n\n return {record: foundRecordMarc, id: foundRecordId};\n } catch (err) {\n throw new Error(`Failed converting record: ${err}, record: ${foundRecord}`);\n }\n }\n });\n });\n }\n };\n\n function checkMaxedQuery(query, total, serverMaxResult) {\n // eslint-disable-next-line functional/no-conditional-statement\n if (total >= serverMaxResult) {\n debug(`WARNING: Query ${query} resulted in ${total} hits which meets the serverMaxResult (${serverMaxResult}) `);\n return query;\n }\n }\n\n\n function getRecordId(record) {\n const [field] = record.get(/^001$/u);\n return field ? field.value : '';\n }\n};\n"],"file":"index.js"}
|
|
1
|
+
{"version":3,"sources":["../../src/candidate-search/index.js"],"names":["CandidateSearchError","Error","record","searchSpec","url","maxCandidates","maxRecordsPerRequest","serverMaxResult","MarcRecord","setValidationOptions","subfieldValues","debug","debugData","extend","JSON","stringify","adjustedMaxRecordsPerRequest","inputRecordId","getRecordId","queryList","client","version","retrieveAll","length","queryOffset","resultSetOffset","totalRecords","searchCounter","queryCandidateCounter","queryCounter","maxedQueries","query","records","failures","nextOffset","total","retrieveRecords","newTotalRecords","newQueryCounter","newSearchCounter","newQueryCandidateCounter","maxedQuery","checkMaxedQuery","undefined","newMaxedQueries","concat","queriesLeft","Promise","resolve","reject","promises","searchRetrieve","startRecord","on","err","SruSearchError","recordPromises","allSettled","filtered","filter","r","status","map","value","reason","payload","foundRecordXML","push","handleRecord","foundRecordMarc","MARCXML","from","foundRecordId","id","idFromXML","getRecordIdFromXML","message","MatchingError","data","field","get","recordXML"],"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 createClient, {SruSearchError} from '@natlibfi/sru-client';\nimport {MarcRecord} from '@natlibfi/marc-record';\nimport {MARCXML} from '@natlibfi/marc-record-serializers';\nimport generateQueryList from './query-list';\nimport {Error as MatchingError} from '@natlibfi/melinda-commons';\n\nexport {searchTypes} from './query-list';\n\nexport class CandidateSearchError extends Error {}\n\n// serverMaxResults : maximum size of total search result available from the server, defaults to Aleph's 20000\n\n// eslint-disable-next-line max-statements\nexport default ({record, searchSpec, url, maxCandidates, maxRecordsPerRequest = 50, serverMaxResult = 20000}) => {\n MarcRecord.setValidationOptions({subfieldValues: false});\n\n const debug = createDebugLogger('@natlibfi/melinda-record-matching:candidate-search');\n const debugData = debug.extend('data');\n\n debugData(`SearchSpec: ${JSON.stringify(searchSpec)}`);\n debugData(`Url: ${url}`);\n debugData(`MaxRecordsPerRequest ${maxRecordsPerRequest}`);\n debugData(`ServerMaxResult: ${serverMaxResult}`);\n debugData(`MaxCandidates: ${maxCandidates}`);\n\n // Do not retrieve more candidates than defined in maxCandidates\n const adjustedMaxRecordsPerRequest = maxRecordsPerRequest >= maxCandidates ? maxCandidates : maxRecordsPerRequest;\n\n const inputRecordId = getRecordId(record);\n const queryList = generateQueryList(record, searchSpec);\n const client = createClient({\n url,\n maxRecordsPerRequest: adjustedMaxRecordsPerRequest,\n version: '2.0',\n retrieveAll: false\n });\n\n debug(`Searching matches for ${inputRecordId}`);\n debug(`Generated queryList ${JSON.stringify(queryList)}`);\n if (queryList.length === 0) { // eslint-disable-line functional/no-conditional-statement\n throw new CandidateSearchError(`Generated query list contains no queries`);\n }\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 candidates (records+failures) retrieved from SRU for matching for current query, including the current record+failure 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\n return async ({queryOffset = 0, resultSetOffset = 1, totalRecords = 0, searchCounter = 0, queryCandidateCounter = 0, queryCounter = 0, maxedQueries = []}) => {\n const query = queryList[queryOffset];\n\n if (query) {\n const {records, failures, nextOffset, total} = await retrieveRecords();\n\n // If resultSetOffset === 1 this is the first search for the current query\n debugData(`ResultSetOffset: ${resultSetOffset}`);\n const newTotalRecords = resultSetOffset === 1 ? total : totalRecords;\n const newQueryCounter = resultSetOffset === 1 ? queryCounter + 1 : queryCounter;\n const newSearchCounter = resultSetOffset === 1 ? 1 : searchCounter + 1;\n const newQueryCandidateCounter = resultSetOffset === 1 ? records.length + failures.length : queryCandidateCounter + records.length + failures.length;\n\n const maxedQuery = resultSetOffset === 1 ? checkMaxedQuery(query, total, serverMaxResult) : undefined;\n const newMaxedQueries = maxedQuery ? maxedQueries.concat(maxedQuery) : maxedQueries;\n\n if (typeof nextOffset === 'number') {\n debug(`Next search will be for query ${queryOffset} ${query}, starting from record ${nextOffset}`);\n return {records, failures, queryOffset, resultSetOffset: nextOffset, queriesLeft: queryList.length - (queryOffset + 1), totalRecords: newTotalRecords, query, searchCounter: newSearchCounter, queryCandidateCounter: newQueryCandidateCounter, queryCounter: newQueryCounter, maxedQueries: newMaxedQueries};\n }\n debug(`Query ${queryOffset} ${query} done.`);\n debug(`There are (${queryList.length - (queryOffset + 1)} queries left)`);\n return {records, failures, queryOffset: queryOffset + 1, queriesLeft: queryList.length - (queryOffset + 1), totalRecords: newTotalRecords, query, searchCounter: newSearchCounter, queryCandidateCounter: newQueryCandidateCounter, queryCounter: newQueryCounter, maxedQueries: newMaxedQueries};\n }\n\n debug(`All ${queryList.length} queries done, there's no query for ${queryOffset}`);\n return {records: [], failures: [], queriesLeft: 0, queryCounter, maxedQueries};\n\n function retrieveRecords() {\n return new Promise((resolve, reject) => {\n const promises = [];\n // eslint-disable-next-line functional/no-let\n let totalRecords = 0;\n\n debug(`Searching for candidates with query: ${query} (Offset ${resultSetOffset})`);\n\n client.searchRetrieve(query, {startRecord: resultSetOffset})\n .on('error', err => {\n // eslint-disable-next-line functional/no-conditional-statement\n if (err instanceof SruSearchError) {\n debug(`SRU SruSearchError for query: ${query}: ${err}`);\n reject(new CandidateSearchError(`SRU SruSearchError for query: ${query}: ${err}`));\n }\n debug(`SRU error for query: ${query}: ${err}`);\n reject(new CandidateSearchError(`SRU error for query: ${query}: ${err}`));\n })\n .on('total', total => {\n debug(`Got total: ${total}`);\n totalRecords += total;\n })\n .on('end', async nextOffset => {\n try {\n const recordPromises = await Promise.allSettled(promises);\n debug(`All recordPromises: ${JSON.stringify(recordPromises)}`);\n const filtered = recordPromises.filter(r => r.status === 'fulfilled').map(r => r.value);\n const failures = recordPromises.filter(r => r.status === 'rejected').map(r => ({status: r.reason.status, payload: r.reason.payload}));\n\n debug(`Found ${JSON.stringify(recordPromises)} records`);\n debug(`Found ${filtered.length} convertable candidates`);\n debug(`Found ${failures.length} NON-convertable candidates`);\n debug(`Converted: ${JSON.stringify(filtered)}.`);\n debug(`Not converted: ${JSON.stringify(failures)}.`);\n\n\n resolve({nextOffset, records: filtered, failures, total: totalRecords});\n } catch (err) {\n debug(`Error caught on END`);\n reject(err);\n }\n })\n .on('record', foundRecordXML => {\n promises.push(handleRecord()); // eslint-disable-line functional/immutable-data\n\n async function handleRecord() {\n try {\n const foundRecordMarc = await MARCXML.from(foundRecordXML, {subfieldValues: false});\n const foundRecordId = getRecordId(foundRecordMarc);\n\n return {record: foundRecordMarc, id: foundRecordId};\n } catch (err) {\n // What should this do?\n const idFromXML = getRecordIdFromXML(foundRecordXML);\n debugData(`Failed converting record: ${err.message}, id: ${idFromXML}, data: ${foundRecordXML}`);\n //return {message: `Failed converting record: ${err.message}`, id: idFromXML, data: foundRecordXML};\n throw new MatchingError(422, {message: `Failed converting record: ${err.message}`, id: idFromXML || '000000000', data: foundRecordXML});\n }\n }\n });\n });\n }\n };\n\n function checkMaxedQuery(query, total, serverMaxResult) {\n // eslint-disable-next-line functional/no-conditional-statement\n if (total >= serverMaxResult) {\n debug(`WARNING: Query ${query} resulted in ${total} hits which meets the serverMaxResult (${serverMaxResult}) `);\n return query;\n }\n }\n\n\n function getRecordId(record) {\n const [field] = record.get(/^001$/u);\n return field ? field.value : '';\n }\n\n function getRecordIdFromXML(recordXML) {\n //<controlfield tag=\\\"001\\\">015376846</controlfield\n debug(`Cannot yet find possible database record id from recordXML (length ${recordXML.length})`);\n return undefined;\n }\n\n};\n"],"file":"index.js"}
|
|
@@ -8,6 +8,8 @@ var _fixugenHttpClient = _interopRequireDefault(require("@natlibfi/fixugen-http-
|
|
|
8
8
|
|
|
9
9
|
var _marcRecord = require("@natlibfi/marc-record");
|
|
10
10
|
|
|
11
|
+
var _melindaCommons = require("@natlibfi/melinda-commons");
|
|
12
|
+
|
|
11
13
|
var _ = _interopRequireWildcard(require("."));
|
|
12
14
|
|
|
13
15
|
var _debug = _interopRequireDefault(require("debug"));
|
|
@@ -107,6 +109,7 @@ describe('candidate-search', () => {
|
|
|
107
109
|
async function iterate({
|
|
108
110
|
searchOptions,
|
|
109
111
|
expectedSearchError,
|
|
112
|
+
expectedErrorStatus,
|
|
110
113
|
count = 1
|
|
111
114
|
}) {
|
|
112
115
|
const expectedResults = getFixture(`expectedResults${count}.json`);
|
|
@@ -117,8 +120,18 @@ describe('candidate-search', () => {
|
|
|
117
120
|
await search(searchOptions);
|
|
118
121
|
throw new Error('Expected an error');
|
|
119
122
|
} catch (err) {
|
|
123
|
+
debug(`Got an error: ${err}`);
|
|
120
124
|
(0, _chai.expect)(err).to.be.an('error');
|
|
121
|
-
|
|
125
|
+
const errorMessage = err instanceof _melindaCommons.Error ? err.payload.message : err.message;
|
|
126
|
+
const errorStatus = err instanceof _melindaCommons.Error ? err.status : undefined;
|
|
127
|
+
debug(`errorMessage: ${errorMessage}, errorStatus: ${errorStatus}`);
|
|
128
|
+
(0, _chai.expect)(errorMessage).to.match(new RegExp(expectedSearchError, 'u'));
|
|
129
|
+
|
|
130
|
+
if (expectedErrorStatus) {
|
|
131
|
+
(0, _chai.expect)(errorStatus).to.be(expectedErrorStatus);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
122
135
|
return;
|
|
123
136
|
}
|
|
124
137
|
} // eslint-disable-next-line functional/no-conditional-statement
|
|
@@ -130,7 +143,8 @@ describe('candidate-search', () => {
|
|
|
130
143
|
}
|
|
131
144
|
|
|
132
145
|
function formatResults(results) {
|
|
133
|
-
|
|
146
|
+
debug(results); //eslint-disable-line
|
|
147
|
+
|
|
134
148
|
return { ...results,
|
|
135
149
|
records: results.records.map(({
|
|
136
150
|
record,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/candidate-search/index.spec.js"],"names":["debug","describe","callback","path","__dirname","recurse","fixura","reader","READERS","JSON","getFixture","factoryOptions","searchOptions","expectedFactoryError","expectedSearchError","enabled","url","isCandidateSearchError","formatFactoryOptions","to","throw","CandidateSearchError","RegExp","search","iterate","stringify","maxRecordsPerRequest","maxServerResults","undefined","record","MarcRecord","subfieldValues","count","expectedResults","Error","err","be","an","message","match","results","formatResults","eql","records","map","id","toObject"],"mappings":";;AA4BA;;AACA;;AACA;;AACA;;AACA;;AACA;;;;;;;;
|
|
1
|
+
{"version":3,"sources":["../../src/candidate-search/index.spec.js"],"names":["debug","describe","callback","path","__dirname","recurse","fixura","reader","READERS","JSON","getFixture","factoryOptions","searchOptions","expectedFactoryError","expectedSearchError","enabled","url","isCandidateSearchError","formatFactoryOptions","to","throw","CandidateSearchError","RegExp","search","iterate","stringify","maxRecordsPerRequest","maxServerResults","undefined","record","MarcRecord","subfieldValues","expectedErrorStatus","count","expectedResults","Error","err","be","an","errorMessage","MatchingError","payload","message","errorStatus","status","match","results","formatResults","eql","records","map","id","toObject"],"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 {expect} from 'chai';\nimport {READERS} from '@natlibfi/fixura';\nimport generateTests from '@natlibfi/fixugen-http-client';\nimport {MarcRecord} from '@natlibfi/marc-record';\nimport {Error as MatchingError} from '@natlibfi/melinda-commons';\nimport createSearchInterface, {CandidateSearchError} from '.';\nimport createDebugLogger from 'debug';\n\nconst debug = createDebugLogger('@natlibfi/melinda-record-matching:candidate-search:test');\n\ndescribe('candidate-search', () => {\n generateTests({\n callback,\n path: [__dirname, '..', '..', 'test-fixtures', 'candidate-search', 'index'],\n recurse: false,\n fixura: {\n reader: READERS.JSON\n }\n });\n\n // eslint-disable-next-line max-statements\n async function callback({getFixture, factoryOptions, searchOptions, expectedFactoryError = false, expectedSearchError = false, enabled = true}) {\n const url = 'http://foo.bar';\n\n if (!enabled) {\n return;\n }\n\n if (expectedFactoryError) {\n if (expectedFactoryError.isCandidateSearchError) {\n expect(() => createSearchInterface({...formatFactoryOptions(), url})).to.throw(CandidateSearchError, new RegExp(expectedFactoryError, 'u'));\n return;\n }\n\n expect(() => createSearchInterface({...formatFactoryOptions(), url})).to.throw(new RegExp(expectedFactoryError, 'u'));\n return;\n }\n\n const search = createSearchInterface({...formatFactoryOptions(), url});\n await iterate({searchOptions, expectedSearchError});\n\n function formatFactoryOptions() {\n debug(`Using factoryOptions: ${JSON.stringify(factoryOptions)}`);\n return {\n ...factoryOptions,\n maxRecordsPerRequest: factoryOptions.maxRecordsPerRequest || 1,\n maxServerResults: factoryOptions.maxServerResults || undefined,\n record: new MarcRecord(factoryOptions.record, {subfieldValues: false})\n };\n }\n\n // eslint-disable-next-line max-statements\n async function iterate({searchOptions, expectedSearchError, expectedErrorStatus, count = 1}) {\n const expectedResults = getFixture(`expectedResults${count}.json`);\n\n if (expectedSearchError) { // eslint-disable-line functional/no-conditional-statement\n try {\n await search(searchOptions);\n throw new Error('Expected an error');\n } catch (err) {\n debug(`Got an error: ${err}`);\n expect(err).to.be.an('error');\n const errorMessage = err instanceof MatchingError ? err.payload.message : err.message;\n const errorStatus = err instanceof MatchingError ? err.status : undefined;\n debug(`errorMessage: ${errorMessage}, errorStatus: ${errorStatus}`);\n expect(errorMessage).to.match(new RegExp(expectedSearchError, 'u'));\n\n if (expectedErrorStatus) {\n expect(errorStatus).to.be(expectedErrorStatus);\n return;\n }\n return;\n }\n }\n\n // eslint-disable-next-line functional/no-conditional-statement\n if (!expectedSearchError) {\n const results = await search(searchOptions);\n expect(formatResults(results)).to.eql(expectedResults);\n }\n\n function formatResults(results) {\n debug(results); //eslint-disable-line\n return {\n ...results,\n records: results.records.map(({record, id}) => ({id, record: record.toObject()}))\n };\n }\n\n }\n }\n});\n"],"file":"index.spec.js"}
|
package/dist/index.js
CHANGED
|
@@ -55,7 +55,8 @@ var _default = ({
|
|
|
55
55
|
maxCandidates = 25,
|
|
56
56
|
returnStrategy = false,
|
|
57
57
|
returnQuery = false,
|
|
58
|
-
returnNonMatches = false
|
|
58
|
+
returnNonMatches = false,
|
|
59
|
+
returnFailures = false
|
|
59
60
|
}) => {
|
|
60
61
|
const debug = (0, _debug.default)('@natlibfi/melinda-record-matching:index');
|
|
61
62
|
const debugData = debug.extend('data');
|
|
@@ -66,6 +67,7 @@ var _default = ({
|
|
|
66
67
|
debugData(`ReturnStrategy: ${JSON.stringify(returnStrategy)}`);
|
|
67
68
|
debugData(`ReturnQuery: ${JSON.stringify(returnQuery)}`);
|
|
68
69
|
debugData(`ReturnNonMatches: ${JSON.stringify(returnNonMatches)}`);
|
|
70
|
+
debugData(`ReturnFailures: ${JSON.stringify(returnFailures)}`);
|
|
69
71
|
const detect = (0, matchDetection.default)(detectionOptions, returnStrategy);
|
|
70
72
|
return record => {
|
|
71
73
|
const search = (0, candidateSearch.default)({ ...searchOptions,
|
|
@@ -90,16 +92,21 @@ var _default = ({
|
|
|
90
92
|
candidateCount = 0,
|
|
91
93
|
nonMatches = [],
|
|
92
94
|
duplicateCount = 0,
|
|
93
|
-
nonMatchCount = 0
|
|
95
|
+
nonMatchCount = 0,
|
|
96
|
+
conversionFailures = []
|
|
94
97
|
}) {
|
|
95
98
|
debugData(`Starting next matcher iteration.`);
|
|
96
99
|
const {
|
|
97
100
|
records,
|
|
101
|
+
failures,
|
|
98
102
|
...state
|
|
99
103
|
} = await search(initialState);
|
|
100
|
-
debugData(`Current state: ${JSON.stringify(state)}, matches: ${matches.length}, candidateCount: ${candidateCount}, nonMatches: ${nonMatches.length}`);
|
|
104
|
+
debugData(`Current state: ${JSON.stringify(state)}, matches: ${matches.length}, candidateCount: ${candidateCount}, nonMatches: ${nonMatches.length}, nonMatchCount: ${nonMatchCount}, conversionFailures: ${conversionFailures}`);
|
|
101
105
|
const recordSetSize = records.length;
|
|
102
|
-
const
|
|
106
|
+
const failureSetSize = failures.length;
|
|
107
|
+
const newCandidateCount = candidateCount + recordSetSize + failureSetSize;
|
|
108
|
+
const newConversionFailures = conversionFailures.concat(failures);
|
|
109
|
+
debugData(`Failures: ${failures.length}, ConversionFailures: ${conversionFailures.length}, NewConversionFailures: ${newConversionFailures.length}`);
|
|
103
110
|
|
|
104
111
|
if (recordSetSize > 0) {
|
|
105
112
|
return handleRecordSet();
|
|
@@ -112,7 +119,9 @@ var _default = ({
|
|
|
112
119
|
matches,
|
|
113
120
|
candidateCount: newCandidateCount,
|
|
114
121
|
nonMatches,
|
|
115
|
-
|
|
122
|
+
nonMatchCount,
|
|
123
|
+
duplicateCount,
|
|
124
|
+
conversionFailures: newConversionFailures
|
|
116
125
|
});
|
|
117
126
|
}
|
|
118
127
|
|
|
@@ -122,8 +131,10 @@ var _default = ({
|
|
|
122
131
|
state,
|
|
123
132
|
stopReason: '',
|
|
124
133
|
nonMatches,
|
|
134
|
+
nonMatchCount,
|
|
125
135
|
candidateCount: newCandidateCount,
|
|
126
|
-
duplicateCount
|
|
136
|
+
duplicateCount,
|
|
137
|
+
conversionFailures: newConversionFailures
|
|
127
138
|
});
|
|
128
139
|
|
|
129
140
|
function handleRecordSet() {
|
|
@@ -133,7 +144,8 @@ var _default = ({
|
|
|
133
144
|
recordSetSize,
|
|
134
145
|
maxMatches,
|
|
135
146
|
matches,
|
|
136
|
-
nonMatches
|
|
147
|
+
nonMatches,
|
|
148
|
+
nonMatchCount
|
|
137
149
|
});
|
|
138
150
|
const newDuplicateCount = duplicateCount + matchResult.duplicateCount;
|
|
139
151
|
const newNonMatchCount = nonMatchCount + matchResult.nonMatchCount;
|
|
@@ -153,7 +165,8 @@ var _default = ({
|
|
|
153
165
|
nonMatches: newNonMatches,
|
|
154
166
|
duplicateCount: newDuplicateCount,
|
|
155
167
|
candidateCount: newCandidateCount,
|
|
156
|
-
nonMatchCount: newNonMatchCount
|
|
168
|
+
nonMatchCount: newNonMatchCount,
|
|
169
|
+
conversionFailures: newConversionFailures
|
|
157
170
|
});
|
|
158
171
|
}
|
|
159
172
|
|
|
@@ -165,7 +178,8 @@ var _default = ({
|
|
|
165
178
|
nonMatches: newNonMatches,
|
|
166
179
|
duplicateCount: newDuplicateCount,
|
|
167
180
|
candidateCount: newCandidateCount,
|
|
168
|
-
nonMatchCount: newNonMatchCount
|
|
181
|
+
nonMatchCount: newNonMatchCount,
|
|
182
|
+
conversionFailures: newConversionFailures
|
|
169
183
|
});
|
|
170
184
|
}
|
|
171
185
|
|
|
@@ -175,7 +189,8 @@ var _default = ({
|
|
|
175
189
|
candidateCount: newCandidateCount,
|
|
176
190
|
nonMatches: newNonMatches,
|
|
177
191
|
duplicateCount: newDuplicateCount,
|
|
178
|
-
nonMatchCount: newNonMatchCount
|
|
192
|
+
nonMatchCount: newNonMatchCount,
|
|
193
|
+
conversionFailures: newConversionFailures
|
|
179
194
|
});
|
|
180
195
|
}
|
|
181
196
|
|
|
@@ -225,7 +240,7 @@ var _default = ({
|
|
|
225
240
|
// - matchQuery (if returnQuery option is true)
|
|
226
241
|
// we could have here also returnRecords/returnMatchRecords/returnNonMatchRecord options that could be turned false for not to return actual record data
|
|
227
242
|
// 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
|
|
243
|
+
// matchStatus.stopReason: string ('maxMatches','maxCandidates','maxedQueries','conversionFailures', empty string/undefined), reason for stopping retrieving or handling the candidate records
|
|
229
244
|
// - only one stopReason is returned (if there would be several possible stopReasons, stopReason is picked in the above order)
|
|
230
245
|
// - 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
246
|
|
|
@@ -237,18 +252,21 @@ var _default = ({
|
|
|
237
252
|
nonMatches,
|
|
238
253
|
duplicateCount,
|
|
239
254
|
candidateCount,
|
|
240
|
-
nonMatchCount
|
|
255
|
+
nonMatchCount,
|
|
256
|
+
conversionFailures
|
|
241
257
|
}) {
|
|
258
|
+
const conversionFailureCount = conversionFailures.length;
|
|
242
259
|
checkCounts({
|
|
243
260
|
matches,
|
|
244
261
|
nonMatches,
|
|
245
262
|
candidateCount,
|
|
246
263
|
duplicateCount,
|
|
247
|
-
nonMatchCount
|
|
264
|
+
nonMatchCount,
|
|
265
|
+
conversionFailureCount
|
|
248
266
|
});
|
|
249
|
-
const matchStatus = getMatchState(state, stopReason); // add nonMatches to result only if returnNonMatches is 'true', otherwise nonMatches have not been gathered
|
|
267
|
+
const matchStatus = getMatchState(state, stopReason, conversionFailureCount); // add nonMatches to result only if returnNonMatches is 'true', otherwise nonMatches have not been gathered
|
|
250
268
|
|
|
251
|
-
const
|
|
269
|
+
const matchesResult = returnNonMatches ? {
|
|
252
270
|
matches,
|
|
253
271
|
matchStatus,
|
|
254
272
|
nonMatches
|
|
@@ -256,6 +274,10 @@ var _default = ({
|
|
|
256
274
|
matches,
|
|
257
275
|
matchStatus
|
|
258
276
|
};
|
|
277
|
+
const result = returnFailures ? { ...matchesResult,
|
|
278
|
+
conversionFailures
|
|
279
|
+
} : matchesResult;
|
|
280
|
+
debugData(`ReturnFailures ${returnFailures}`);
|
|
259
281
|
debugData(`${JSON.stringify(result)}`);
|
|
260
282
|
return result; // note that in cases where the matching has been stopped because of maxMatches checkCounts won't (in most cases) match
|
|
261
283
|
|
|
@@ -264,13 +286,14 @@ var _default = ({
|
|
|
264
286
|
nonMatches,
|
|
265
287
|
candidateCount,
|
|
266
288
|
duplicateCount,
|
|
267
|
-
nonMatchCount
|
|
289
|
+
nonMatchCount,
|
|
290
|
+
conversionFailureCount
|
|
268
291
|
}) {
|
|
269
292
|
const matchCount = matches.length;
|
|
270
293
|
debugData(`Return nonMatches: ${returnNonMatches}`);
|
|
271
294
|
const chosenNonMatchCount = returnNonMatches ? nonMatches.length : nonMatchCount;
|
|
272
|
-
const totalHandled = matchCount +
|
|
273
|
-
debug(`candidateCount: ${candidateCount}, matches: ${matchCount}, nonMatches: ${chosenNonMatchCount}, duplicateCount: ${duplicateCount}`);
|
|
295
|
+
const totalHandled = matchCount + chosenNonMatchCount + duplicateCount;
|
|
296
|
+
debug(`candidateCount: ${candidateCount}, matches: ${matchCount}, nonMatches: ${chosenNonMatchCount}, duplicateCount: ${duplicateCount}, conversionFailureCount: ${conversionFailureCount}`);
|
|
274
297
|
debug(`We got result for ${totalHandled} / ${candidateCount} retrieved candidates`);
|
|
275
298
|
|
|
276
299
|
if (totalHandled !== candidateCount) {
|
|
@@ -281,18 +304,22 @@ var _default = ({
|
|
|
281
304
|
return;
|
|
282
305
|
}
|
|
283
306
|
|
|
284
|
-
function getMatchState(state, stopReason) {
|
|
307
|
+
function getMatchState(state, stopReason, conversionFailuresCount) {
|
|
285
308
|
debugData(`${JSON.stringify(state)}`);
|
|
309
|
+
debug(`We had ${conversionFailuresCount} retrieved candidates that could not be converted.`);
|
|
286
310
|
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
311
|
debugData(`StopReason: <${stopReason}>`);
|
|
288
312
|
const searchesLeft = state.resultSetOffset && state.resultSetOffset <= state.totalRecords;
|
|
289
313
|
const nonRetrieved = searchesLeft ? state.totalRecords - state.queryCandidateCounter : 0;
|
|
290
|
-
debugData(`nonRetrieved: ${nonRetrieved}`);
|
|
314
|
+
debugData(`nonRetrieved: ${nonRetrieved}`); // matchStatus.stopReason: string ('maxMatches','maxCandidates','maxedQueries','conversionFailures', empty string/undefined), reason for stopping retrieving or handling the candidate records
|
|
315
|
+
// 'maxMatches' and 'maxCandidates' are in stopReason, 'maxedQueries' and 'conversionFailures' are created here
|
|
291
316
|
|
|
292
|
-
if (state.queriesLeft > 0 || nonRetrieved > 0 || state.maxedQueries.length > 0) {
|
|
293
|
-
const maxedQueriesStopReason = state.maxedQueries.length > 0 ? 'maxedQueries' :
|
|
294
|
-
const
|
|
317
|
+
if (state.queriesLeft > 0 || nonRetrieved > 0 || state.maxedQueries.length > 0 || conversionFailureCount > 0) {
|
|
318
|
+
const maxedQueriesStopReason = state.maxedQueries.length > 0 ? 'maxedQueries' : undefined;
|
|
319
|
+
const conversionFailuresStopReason = conversionFailureCount > 0 ? 'conversionFailures' : undefined;
|
|
320
|
+
const newStopReason = stopReason === '' || stopReason === undefined ? maxedQueriesStopReason || conversionFailuresStopReason : stopReason;
|
|
295
321
|
debugData(`MaxedQueriesStopReason: <${maxedQueriesStopReason}>`);
|
|
322
|
+
debugData(`ConversionFailureStopReason <${conversionFailuresStopReason}>`);
|
|
296
323
|
debugData(`NewStopReason: <${newStopReason}>`);
|
|
297
324
|
debug(`Match status: false`);
|
|
298
325
|
return {
|
|
@@ -411,12 +438,15 @@ var _default = ({
|
|
|
411
438
|
function handleRecordMatch(isMatch, newMatch) {
|
|
412
439
|
const newRecordMatches = isMatch ? recordMatches.concat(newMatch) : recordMatches;
|
|
413
440
|
const newRecordNonMatches = isMatch ? recordNonMatches : recordNonMatches.concat(newMatch);
|
|
441
|
+
const newRecordNonMatchCount = isMatch ? recordNonMatchCount : recordNonMatchCount + 1;
|
|
414
442
|
debugData(`- Total matches after this detection: ${matches.concat(newRecordMatches).length} (max: ${maxMatches})`); // eslint-disable-next-line functional/no-conditional-statement
|
|
415
443
|
|
|
416
444
|
if (returnNonMatches) {
|
|
417
445
|
debugData(`- Total nonMatches after this detection: ${nonMatches.concat(newRecordNonMatches).length}`);
|
|
418
446
|
}
|
|
419
447
|
|
|
448
|
+
debugData(`- Total nonMatchCount after this detection: ${recordNonMatchCount}`);
|
|
449
|
+
|
|
420
450
|
if (maxMatchesFound({
|
|
421
451
|
matches: matches.concat(newRecordMatches),
|
|
422
452
|
maxMatches
|
|
@@ -426,7 +456,7 @@ var _default = ({
|
|
|
426
456
|
matches: newRecordMatches,
|
|
427
457
|
nonMatches: returnNonMatches ? newRecordNonMatches : [],
|
|
428
458
|
duplicateCount: recordDuplicateCount,
|
|
429
|
-
nonMatchCount:
|
|
459
|
+
nonMatchCount: newRecordNonMatchCount
|
|
430
460
|
};
|
|
431
461
|
}
|
|
432
462
|
|
|
@@ -439,7 +469,7 @@ var _default = ({
|
|
|
439
469
|
recordCount: newRecordCount,
|
|
440
470
|
recordNonMatches: returnNonMatches ? newRecordNonMatches : [],
|
|
441
471
|
duplicateCount: recordDuplicateCount,
|
|
442
|
-
recordNonMatchCount
|
|
472
|
+
recordNonMatchCount: newRecordNonMatchCount
|
|
443
473
|
});
|
|
444
474
|
}
|
|
445
475
|
|
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","records","failures","state","length","recordSetSize","failureSetSize","newCandidateCount","newConversionFailures","concat","handleRecordSet","queriesLeft","searchCounter","query","returnResult","stopReason","matchResult","iterateRecords","newDuplicateCount","newNonMatchCount","newMatches","newNonMatches","handleMatchResult","maxMatchesFound","maxCandidatesRetrieved","addQuery","map","match","matchQuery","conversionFailureCount","checkCounts","matchStatus","getMatchState","matchesResult","result","matchCount","chosenNonMatchCount","totalHandled","conversionFailuresCount","resultSetOffset","totalRecords","queryCandidateCounter","maxedQueries","searchesLeft","nonRetrieved","maxedQueriesStopReason","undefined","conversionFailuresStopReason","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, 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 = []}) {\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}`);\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});\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});\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} = 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, conversionFailures: newConversionFailures});\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});\n }\n\n return iterate({initialState: state, matches: newMatches, candidateCount: newCandidateCount, nonMatches: newNonMatches, duplicateCount: newDuplicateCount, nonMatchCount: newNonMatchCount, conversionFailures: newConversionFailures});\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','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}) {\n const conversionFailureCount = conversionFailures.length;\n checkCounts({matches, nonMatches, candidateCount, duplicateCount, nonMatchCount, conversionFailureCount});\n const matchStatus = getMatchState(state, stopReason, conversionFailureCount);\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 result = returnFailures ? {...matchesResult, conversionFailures} : 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}) {\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}`);\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, conversionFailuresCount) {\n debugData(`${JSON.stringify(state)}`);\n debug(`We had ${conversionFailuresCount} retrieved candidates that could not be converted.`);\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' and 'conversionFailures' are created here\n\n if (state.queriesLeft > 0 || nonRetrieved > 0 || state.maxedQueries.length > 0 || conversionFailureCount > 0) {\n const maxedQueriesStopReason = state.maxedQueries.length > 0 ? 'maxedQueries' : undefined;\n const conversionFailuresStopReason = conversionFailureCount > 0 ? 'conversionFailures' : undefined;\n const newStopReason = stopReason === '' || stopReason === undefined ? maxedQueriesStopReason || conversionFailuresStopReason : stopReason;\n debugData(`MaxedQueriesStopReason: <${maxedQueriesStopReason}>`);\n debugData(`ConversionFailureStopReason <${conversionFailuresStopReason}>`);\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 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};\n }\n\n return iterateRecords({records: records.slice(1), recordSetSize, maxMatches, matches, recordMatches: newRecordMatches, recordCount: newRecordCount, recordNonMatches: returnNonMatches ? newRecordNonMatches : [], duplicateCount: recordDuplicateCount, recordNonMatchCount: newRecordNonMatchCount});\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"}
|
package/dist/index.spec.js
CHANGED
|
@@ -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(
|
|
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
|
-
|
|
89
|
-
|
|
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];
|
package/dist/index.spec.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.spec.js"],"names":["debug","debugData","extend","describe","callback","path","__dirname","recurse","fixura","reader","READERS","JSON","getFixture","options","enabled","expectedMatchStatus","expectedStopReason","record","MarcRecord","subfieldValues","expectedMatches","expectedNonMatches","match","formatOptions","matches","matchStatus","nonMatches","length","status","stopReason","
|
|
1
|
+
{"version":3,"sources":["../src/index.spec.js"],"names":["debug","debugData","extend","describe","callback","path","__dirname","recurse","fixura","reader","READERS","JSON","getFixture","options","enabled","expectedMatchStatus","expectedStopReason","expectedFailures","record","MarcRecord","subfieldValues","expectedMatches","expectedNonMatches","match","formatOptions","matches","matchStatus","nonMatches","conversionFailures","length","status","stopReason","to","eql","formattedMatchResult","formatRecordResults","formattedNonMatchResult","contextFeatures","matchDetection","features","detection","strategy","type","search","detect","map","v","stringify","candidate","formatCandidate","id","newId","newRecord","toObject"],"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 {expect} from 'chai';\nimport {READERS} from '@natlibfi/fixura';\nimport generateTests from '@natlibfi/fixugen-http-client';\nimport {MarcRecord} from '@natlibfi/marc-record';\nimport createMatchInterface, {matchDetection} from '.';\nimport createDebugLogger from 'debug';\n\nconst debug = createDebugLogger('@natlibfi/melinda-record-matching:index:test');\nconst debugData = debug.extend('data');\n\ndescribe('INDEX', () => {\n generateTests({\n callback,\n path: [__dirname, '..', 'test-fixtures', 'index'],\n recurse: false,\n fixura: {\n reader: READERS.JSON\n }\n });\n\n async function callback({getFixture, options, enabled = true, expectedMatchStatus, expectedStopReason, expectedFailures}) {\n\n if (!enabled) {\n debug(`Disabled test!`);\n return;\n }\n\n const record = new MarcRecord(getFixture('inputRecord.json'), {subfieldValues: false});\n const expectedMatches = getFixture('expectedMatches.json');\n const expectedNonMatches = getFixture('expectedNonMatches.json') || [];\n\n\n const match = createMatchInterface(formatOptions());\n const {matches, matchStatus, nonMatches, conversionFailures} = await match(record);\n debugData(`Matches: ${matches.length}, Status: ${matchStatus.status}/${matchStatus.stopReason}, NonMatches: ${nonMatches ? nonMatches.length : 'not returned'}, ConversionFailures: ${conversionFailures ? conversionFailures.length : 'not returned'}`);\n\n expect(matchStatus.status).to.eql(expectedMatchStatus);\n expect(matchStatus.stopReason).to.eql(expectedStopReason);\n\n const formattedMatchResult = formatRecordResults(matches);\n expect(formattedMatchResult).to.eql(expectedMatches);\n\n const formattedNonMatchResult = formatRecordResults(nonMatches);\n expect(formattedNonMatchResult).to.eql(expectedNonMatches);\n\n // eslint-disable-next-line functional/no-conditional-statement\n if (expectedFailures) {\n expect(conversionFailures).to.eql(expectedFailures);\n }\n\n function formatOptions() {\n const contextFeatures = matchDetection.features[options.detection.strategy.type];\n\n return {\n ...options,\n search: {\n ...options.search\n },\n detection: {\n ...options.detect,\n strategy: options.detection.strategy.features.map(v => contextFeatures[v]())\n }\n };\n }\n\n function formatRecordResults(matches) {\n if (matches) {\n debugData(JSON.stringify(matches));\n return matches.map((match) => ({\n ...match,\n candidate: formatCandidate(match.candidate)\n }));\n }\n return [];\n }\n\n // Format candidate to remove validationOptions from record\n function formatCandidate({id, record}) {\n const newId = id;\n const newRecord = record;\n return {\n id: newId,\n record: newRecord.toObject()\n };\n }\n\n\n }\n\n});\n"],"file":"index.spec.js"}
|
package/package.json
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"url": "git@github.com:natlibfi/melinda-record-matching-js.git"
|
|
15
15
|
},
|
|
16
16
|
"license": "LGPL-3.0+",
|
|
17
|
-
"version": "2.
|
|
17
|
+
"version": "2.2.0-alpha.2",
|
|
18
18
|
"main": "./dist/index.js",
|
|
19
19
|
"engines": {
|
|
20
20
|
"node": ">=14"
|
|
@@ -37,31 +37,32 @@
|
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"@natlibfi/marc-record": "^7.0.0",
|
|
39
39
|
"@natlibfi/marc-record-serializers": "^8.1.0",
|
|
40
|
-
"@natlibfi/
|
|
41
|
-
"
|
|
42
|
-
"
|
|
40
|
+
"@natlibfi/melinda-commons": "^12.0.3",
|
|
41
|
+
"@natlibfi/sru-client": "^5.0.1",
|
|
42
|
+
"debug": "^4.3.4",
|
|
43
|
+
"moment": "^2.29.2",
|
|
43
44
|
"natural": "^5.1.13",
|
|
44
45
|
"uuid": "^8.3.2",
|
|
45
|
-
"winston": "^3.
|
|
46
|
+
"winston": "^3.7.2"
|
|
46
47
|
},
|
|
47
48
|
"devDependencies": {
|
|
48
|
-
"@babel/cli": "^7.
|
|
49
|
-
"@babel/core": "^7.
|
|
50
|
-
"@babel/eslint-parser": "^7.
|
|
51
|
-
"@babel/node": "^7.16.
|
|
52
|
-
"@babel/preset-env": "^7.16.
|
|
53
|
-
"@babel/register": "^7.
|
|
49
|
+
"@babel/cli": "^7.17.6",
|
|
50
|
+
"@babel/core": "^7.17.9",
|
|
51
|
+
"@babel/eslint-parser": "^7.17.0",
|
|
52
|
+
"@babel/node": "^7.16.8",
|
|
53
|
+
"@babel/preset-env": "^7.16.11",
|
|
54
|
+
"@babel/register": "^7.17.7",
|
|
54
55
|
"@natlibfi/eslint-config-melinda-backend": "^2.0.0",
|
|
55
56
|
"@natlibfi/fixugen": "^1.0.2",
|
|
56
|
-
"@natlibfi/fixugen-http-client": "^
|
|
57
|
+
"@natlibfi/fixugen-http-client": "^2.0.1",
|
|
57
58
|
"@natlibfi/fixura": "^2.2.1",
|
|
58
59
|
"babel-plugin-istanbul": "^6.1.1",
|
|
59
60
|
"babel-plugin-rewire": "^1.2.0",
|
|
60
|
-
"chai": "^4.3.
|
|
61
|
+
"chai": "^4.3.6",
|
|
61
62
|
"chai-as-promised": "^7.1.1",
|
|
62
63
|
"cross-env": "^7.0.3",
|
|
63
|
-
"eslint": "^8.
|
|
64
|
-
"mocha": "^9.
|
|
64
|
+
"eslint": "^8.13.0",
|
|
65
|
+
"mocha": "^9.2.2",
|
|
65
66
|
"nodemon": "^2.0.15",
|
|
66
67
|
"nyc": "^15.1.0"
|
|
67
68
|
},
|
|
@@ -31,6 +31,7 @@ import createClient, {SruSearchError} from '@natlibfi/sru-client';
|
|
|
31
31
|
import {MarcRecord} from '@natlibfi/marc-record';
|
|
32
32
|
import {MARCXML} from '@natlibfi/marc-record-serializers';
|
|
33
33
|
import generateQueryList from './query-list';
|
|
34
|
+
import {Error as MatchingError} from '@natlibfi/melinda-commons';
|
|
34
35
|
|
|
35
36
|
export {searchTypes} from './query-list';
|
|
36
37
|
|
|
@@ -72,40 +73,39 @@ export default ({record, searchSpec, url, maxCandidates, maxRecordsPerRequest =
|
|
|
72
73
|
// state.totalRecords : amount of candidate records available to the current query (undefined, if there was no queries left)
|
|
73
74
|
// state.query : current query (undefined if there was no queries left)
|
|
74
75
|
// state.searchCounter : sequence for current search for current query (undefined, if there we no queries left)
|
|
75
|
-
// state.queryCandidateCounter: amount of
|
|
76
|
+
// state.queryCandidateCounter: amount of candidates (records+failures) retrieved from SRU for matching for current query, including the current record+failure set (undefined if there were no queries left)
|
|
76
77
|
// state.queriesLeft : amount of queries left
|
|
77
78
|
// state.queryCounter : sequence for current query
|
|
78
79
|
// state.maxedQueries : queries that resulted in more than serverMaxResults hits
|
|
79
80
|
|
|
80
81
|
|
|
81
|
-
// eslint-disable-next-line max-statements
|
|
82
82
|
return async ({queryOffset = 0, resultSetOffset = 1, totalRecords = 0, searchCounter = 0, queryCandidateCounter = 0, queryCounter = 0, maxedQueries = []}) => {
|
|
83
83
|
const query = queryList[queryOffset];
|
|
84
84
|
|
|
85
85
|
if (query) {
|
|
86
|
-
const {records, nextOffset, total} = await retrieveRecords();
|
|
86
|
+
const {records, failures, nextOffset, total} = await retrieveRecords();
|
|
87
87
|
|
|
88
88
|
// If resultSetOffset === 1 this is the first search for the current query
|
|
89
89
|
debugData(`ResultSetOffset: ${resultSetOffset}`);
|
|
90
90
|
const newTotalRecords = resultSetOffset === 1 ? total : totalRecords;
|
|
91
91
|
const newQueryCounter = resultSetOffset === 1 ? queryCounter + 1 : queryCounter;
|
|
92
92
|
const newSearchCounter = resultSetOffset === 1 ? 1 : searchCounter + 1;
|
|
93
|
-
const newQueryCandidateCounter = resultSetOffset === 1 ? records.length : queryCandidateCounter + records.length;
|
|
93
|
+
const newQueryCandidateCounter = resultSetOffset === 1 ? records.length + failures.length : queryCandidateCounter + records.length + failures.length;
|
|
94
94
|
|
|
95
95
|
const maxedQuery = resultSetOffset === 1 ? checkMaxedQuery(query, total, serverMaxResult) : undefined;
|
|
96
96
|
const newMaxedQueries = maxedQuery ? maxedQueries.concat(maxedQuery) : maxedQueries;
|
|
97
97
|
|
|
98
98
|
if (typeof nextOffset === 'number') {
|
|
99
99
|
debug(`Next search will be for query ${queryOffset} ${query}, starting from record ${nextOffset}`);
|
|
100
|
-
return {records, queryOffset, resultSetOffset: nextOffset, queriesLeft: queryList.length - (queryOffset + 1), totalRecords: newTotalRecords, query, searchCounter: newSearchCounter, queryCandidateCounter: newQueryCandidateCounter, queryCounter: newQueryCounter, maxedQueries: newMaxedQueries};
|
|
100
|
+
return {records, failures, queryOffset, resultSetOffset: nextOffset, queriesLeft: queryList.length - (queryOffset + 1), totalRecords: newTotalRecords, query, searchCounter: newSearchCounter, queryCandidateCounter: newQueryCandidateCounter, queryCounter: newQueryCounter, maxedQueries: newMaxedQueries};
|
|
101
101
|
}
|
|
102
102
|
debug(`Query ${queryOffset} ${query} done.`);
|
|
103
103
|
debug(`There are (${queryList.length - (queryOffset + 1)} queries left)`);
|
|
104
|
-
return {records, queryOffset: queryOffset + 1, queriesLeft: queryList.length - (queryOffset + 1), totalRecords: newTotalRecords, query, searchCounter: newSearchCounter, queryCandidateCounter: newQueryCandidateCounter, queryCounter: newQueryCounter, maxedQueries: newMaxedQueries};
|
|
104
|
+
return {records, failures, queryOffset: queryOffset + 1, queriesLeft: queryList.length - (queryOffset + 1), totalRecords: newTotalRecords, query, searchCounter: newSearchCounter, queryCandidateCounter: newQueryCandidateCounter, queryCounter: newQueryCounter, maxedQueries: newMaxedQueries};
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
debug(`All ${queryList.length} queries done, there's no query for ${queryOffset}`);
|
|
108
|
-
return {records: [], queriesLeft: 0, queryCounter, maxedQueries};
|
|
108
|
+
return {records: [], failures: [], queriesLeft: 0, queryCounter, maxedQueries};
|
|
109
109
|
|
|
110
110
|
function retrieveRecords() {
|
|
111
111
|
return new Promise((resolve, reject) => {
|
|
@@ -131,27 +131,39 @@ export default ({record, searchSpec, url, maxCandidates, maxRecordsPerRequest =
|
|
|
131
131
|
})
|
|
132
132
|
.on('end', async nextOffset => {
|
|
133
133
|
try {
|
|
134
|
-
const
|
|
135
|
-
|
|
134
|
+
const recordPromises = await Promise.allSettled(promises);
|
|
135
|
+
debug(`All recordPromises: ${JSON.stringify(recordPromises)}`);
|
|
136
|
+
const filtered = recordPromises.filter(r => r.status === 'fulfilled').map(r => r.value);
|
|
137
|
+
const failures = recordPromises.filter(r => r.status === 'rejected').map(r => ({status: r.reason.status, payload: r.reason.payload}));
|
|
136
138
|
|
|
137
|
-
debug(`Found ${
|
|
139
|
+
debug(`Found ${JSON.stringify(recordPromises)} records`);
|
|
140
|
+
debug(`Found ${filtered.length} convertable candidates`);
|
|
141
|
+
debug(`Found ${failures.length} NON-convertable candidates`);
|
|
142
|
+
debug(`Converted: ${JSON.stringify(filtered)}.`);
|
|
143
|
+
debug(`Not converted: ${JSON.stringify(failures)}.`);
|
|
138
144
|
|
|
139
|
-
|
|
145
|
+
|
|
146
|
+
resolve({nextOffset, records: filtered, failures, total: totalRecords});
|
|
140
147
|
} catch (err) {
|
|
148
|
+
debug(`Error caught on END`);
|
|
141
149
|
reject(err);
|
|
142
150
|
}
|
|
143
151
|
})
|
|
144
|
-
.on('record',
|
|
152
|
+
.on('record', foundRecordXML => {
|
|
145
153
|
promises.push(handleRecord()); // eslint-disable-line functional/immutable-data
|
|
146
154
|
|
|
147
155
|
async function handleRecord() {
|
|
148
156
|
try {
|
|
149
|
-
const foundRecordMarc = await MARCXML.from(
|
|
157
|
+
const foundRecordMarc = await MARCXML.from(foundRecordXML, {subfieldValues: false});
|
|
150
158
|
const foundRecordId = getRecordId(foundRecordMarc);
|
|
151
159
|
|
|
152
160
|
return {record: foundRecordMarc, id: foundRecordId};
|
|
153
161
|
} catch (err) {
|
|
154
|
-
|
|
162
|
+
// What should this do?
|
|
163
|
+
const idFromXML = getRecordIdFromXML(foundRecordXML);
|
|
164
|
+
debugData(`Failed converting record: ${err.message}, id: ${idFromXML}, data: ${foundRecordXML}`);
|
|
165
|
+
//return {message: `Failed converting record: ${err.message}`, id: idFromXML, data: foundRecordXML};
|
|
166
|
+
throw new MatchingError(422, {message: `Failed converting record: ${err.message}`, id: idFromXML || '000000000', data: foundRecordXML});
|
|
155
167
|
}
|
|
156
168
|
}
|
|
157
169
|
});
|
|
@@ -172,4 +184,11 @@ export default ({record, searchSpec, url, maxCandidates, maxRecordsPerRequest =
|
|
|
172
184
|
const [field] = record.get(/^001$/u);
|
|
173
185
|
return field ? field.value : '';
|
|
174
186
|
}
|
|
187
|
+
|
|
188
|
+
function getRecordIdFromXML(recordXML) {
|
|
189
|
+
//<controlfield tag=\"001\">015376846</controlfield
|
|
190
|
+
debug(`Cannot yet find possible database record id from recordXML (length ${recordXML.length})`);
|
|
191
|
+
return undefined;
|
|
192
|
+
}
|
|
193
|
+
|
|
175
194
|
};
|
|
@@ -30,6 +30,7 @@ import {expect} from 'chai';
|
|
|
30
30
|
import {READERS} from '@natlibfi/fixura';
|
|
31
31
|
import generateTests from '@natlibfi/fixugen-http-client';
|
|
32
32
|
import {MarcRecord} from '@natlibfi/marc-record';
|
|
33
|
+
import {Error as MatchingError} from '@natlibfi/melinda-commons';
|
|
33
34
|
import createSearchInterface, {CandidateSearchError} from '.';
|
|
34
35
|
import createDebugLogger from 'debug';
|
|
35
36
|
|
|
@@ -77,7 +78,7 @@ describe('candidate-search', () => {
|
|
|
77
78
|
}
|
|
78
79
|
|
|
79
80
|
// eslint-disable-next-line max-statements
|
|
80
|
-
async function iterate({searchOptions, expectedSearchError, count = 1}) {
|
|
81
|
+
async function iterate({searchOptions, expectedSearchError, expectedErrorStatus, count = 1}) {
|
|
81
82
|
const expectedResults = getFixture(`expectedResults${count}.json`);
|
|
82
83
|
|
|
83
84
|
if (expectedSearchError) { // eslint-disable-line functional/no-conditional-statement
|
|
@@ -85,8 +86,17 @@ describe('candidate-search', () => {
|
|
|
85
86
|
await search(searchOptions);
|
|
86
87
|
throw new Error('Expected an error');
|
|
87
88
|
} catch (err) {
|
|
89
|
+
debug(`Got an error: ${err}`);
|
|
88
90
|
expect(err).to.be.an('error');
|
|
89
|
-
|
|
91
|
+
const errorMessage = err instanceof MatchingError ? err.payload.message : err.message;
|
|
92
|
+
const errorStatus = err instanceof MatchingError ? err.status : undefined;
|
|
93
|
+
debug(`errorMessage: ${errorMessage}, errorStatus: ${errorStatus}`);
|
|
94
|
+
expect(errorMessage).to.match(new RegExp(expectedSearchError, 'u'));
|
|
95
|
+
|
|
96
|
+
if (expectedErrorStatus) {
|
|
97
|
+
expect(errorStatus).to.be(expectedErrorStatus);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
90
100
|
return;
|
|
91
101
|
}
|
|
92
102
|
}
|
|
@@ -98,7 +108,7 @@ describe('candidate-search', () => {
|
|
|
98
108
|
}
|
|
99
109
|
|
|
100
110
|
function formatResults(results) {
|
|
101
|
-
|
|
111
|
+
debug(results); //eslint-disable-line
|
|
102
112
|
return {
|
|
103
113
|
...results,
|
|
104
114
|
records: results.records.map(({record, id}) => ({id, record: record.toObject()}))
|
package/src/index.js
CHANGED
|
@@ -32,7 +32,7 @@ import createDetectionInterface, * as matchDetection from './match-detection';
|
|
|
32
32
|
|
|
33
33
|
export {candidateSearch, matchDetection};
|
|
34
34
|
|
|
35
|
-
export default ({detection: detectionOptions, search: searchOptions, maxMatches = 1, maxCandidates = 25, returnStrategy = false, returnQuery = false, returnNonMatches = false}) => {
|
|
35
|
+
export default ({detection: detectionOptions, search: searchOptions, maxMatches = 1, maxCandidates = 25, returnStrategy = false, returnQuery = false, returnNonMatches = false, returnFailures = false}) => {
|
|
36
36
|
const debug = createDebugLogger('@natlibfi/melinda-record-matching:index');
|
|
37
37
|
const debugData = debug.extend('data');
|
|
38
38
|
|
|
@@ -43,6 +43,8 @@ export default ({detection: detectionOptions, search: searchOptions, maxMatches
|
|
|
43
43
|
debugData(`ReturnStrategy: ${JSON.stringify(returnStrategy)}`);
|
|
44
44
|
debugData(`ReturnQuery: ${JSON.stringify(returnQuery)}`);
|
|
45
45
|
debugData(`ReturnNonMatches: ${JSON.stringify(returnNonMatches)}`);
|
|
46
|
+
debugData(`ReturnFailures: ${JSON.stringify(returnFailures)}`);
|
|
47
|
+
|
|
46
48
|
|
|
47
49
|
const detect = createDetectionInterface(detectionOptions, returnStrategy);
|
|
48
50
|
|
|
@@ -63,13 +65,17 @@ export default ({detection: detectionOptions, search: searchOptions, maxMatches
|
|
|
63
65
|
// state.queryCounter : sequence for current query
|
|
64
66
|
// state.maxedQueries : queries that resulted in more than serverMaxResults hits
|
|
65
67
|
|
|
66
|
-
async function iterate({initialState = {}, matches = [], candidateCount = 0, nonMatches = [], duplicateCount = 0, nonMatchCount = 0}) {
|
|
68
|
+
async function iterate({initialState = {}, matches = [], candidateCount = 0, nonMatches = [], duplicateCount = 0, nonMatchCount = 0, conversionFailures = []}) {
|
|
67
69
|
debugData(`Starting next matcher iteration.`);
|
|
68
|
-
const {records, ...state} = await search(initialState);
|
|
70
|
+
const {records, failures, ...state} = await search(initialState);
|
|
69
71
|
|
|
70
|
-
debugData(`Current state: ${JSON.stringify(state)}, matches: ${matches.length}, candidateCount: ${candidateCount}, nonMatches: ${nonMatches.length}`);
|
|
72
|
+
debugData(`Current state: ${JSON.stringify(state)}, matches: ${matches.length}, candidateCount: ${candidateCount}, nonMatches: ${nonMatches.length}, nonMatchCount: ${nonMatchCount}, conversionFailures: ${conversionFailures}`);
|
|
71
73
|
const recordSetSize = records.length;
|
|
72
|
-
const
|
|
74
|
+
const failureSetSize = failures.length;
|
|
75
|
+
const newCandidateCount = candidateCount + recordSetSize + failureSetSize;
|
|
76
|
+
|
|
77
|
+
const newConversionFailures = conversionFailures.concat(failures);
|
|
78
|
+
debugData(`Failures: ${failures.length}, ConversionFailures: ${conversionFailures.length}, NewConversionFailures: ${newConversionFailures.length}`);
|
|
73
79
|
|
|
74
80
|
if (recordSetSize > 0) {
|
|
75
81
|
return handleRecordSet();
|
|
@@ -77,29 +83,30 @@ export default ({detection: detectionOptions, search: searchOptions, maxMatches
|
|
|
77
83
|
|
|
78
84
|
if (state.queriesLeft > 0) {
|
|
79
85
|
debug(`Empty record set ${state.searchCounter} for ${state.query}, but there are ${state.queriesLeft} queries left`);
|
|
80
|
-
return iterate({initialState: state, matches, candidateCount: newCandidateCount, nonMatches, duplicateCount});
|
|
86
|
+
return iterate({initialState: state, matches, candidateCount: newCandidateCount, nonMatches, nonMatchCount, duplicateCount, conversionFailures: newConversionFailures});
|
|
81
87
|
}
|
|
82
88
|
|
|
83
89
|
debug(`No (more) candidate records to check, no more queries left, matches: ${matches.length}`);
|
|
84
|
-
return returnResult({matches, state, stopReason: '', nonMatches, candidateCount: newCandidateCount, duplicateCount});
|
|
90
|
+
return returnResult({matches, state, stopReason: '', nonMatches, nonMatchCount, candidateCount: newCandidateCount, duplicateCount, conversionFailures: newConversionFailures});
|
|
85
91
|
|
|
86
92
|
function handleRecordSet() {
|
|
87
93
|
debug(`Checking record set of ${recordSetSize} candidate records for possible matches, found by ${state.searchCounter} search for ${state.query}`);
|
|
88
94
|
|
|
89
|
-
const matchResult = iterateRecords({records, recordSetSize, maxMatches, matches, nonMatches});
|
|
95
|
+
const matchResult = iterateRecords({records, recordSetSize, maxMatches, matches, nonMatches, nonMatchCount});
|
|
96
|
+
|
|
90
97
|
const newDuplicateCount = duplicateCount + matchResult.duplicateCount;
|
|
91
98
|
const newNonMatchCount = nonMatchCount + matchResult.nonMatchCount;
|
|
92
99
|
const {newMatches, newNonMatches} = handleMatchResult(matchResult, matches, nonMatches);
|
|
93
100
|
|
|
94
101
|
if (maxMatchesFound({matches: newMatches, maxMatches})) {
|
|
95
|
-
return returnResult({matches: newMatches, state, stopReason: 'maxMatches', nonMatches: newNonMatches, duplicateCount: newDuplicateCount, candidateCount: newCandidateCount, nonMatchCount: newNonMatchCount});
|
|
102
|
+
return returnResult({matches: newMatches, state, stopReason: 'maxMatches', nonMatches: newNonMatches, duplicateCount: newDuplicateCount, candidateCount: newCandidateCount, nonMatchCount: newNonMatchCount, conversionFailures: newConversionFailures});
|
|
96
103
|
}
|
|
97
104
|
|
|
98
105
|
if (maxCandidatesRetrieved(newCandidateCount, maxCandidates)) {
|
|
99
|
-
return returnResult({matches: newMatches, state, stopReason: 'maxCandidates', nonMatches: newNonMatches, duplicateCount: newDuplicateCount, candidateCount: newCandidateCount, nonMatchCount: newNonMatchCount});
|
|
106
|
+
return returnResult({matches: newMatches, state, stopReason: 'maxCandidates', nonMatches: newNonMatches, duplicateCount: newDuplicateCount, candidateCount: newCandidateCount, nonMatchCount: newNonMatchCount, conversionFailures: newConversionFailures});
|
|
100
107
|
}
|
|
101
108
|
|
|
102
|
-
return iterate({initialState: state, matches: newMatches, candidateCount: newCandidateCount, nonMatches: newNonMatches, duplicateCount: newDuplicateCount, nonMatchCount: newNonMatchCount});
|
|
109
|
+
return iterate({initialState: state, matches: newMatches, candidateCount: newCandidateCount, nonMatches: newNonMatches, duplicateCount: newDuplicateCount, nonMatchCount: newNonMatchCount, conversionFailures: newConversionFailures});
|
|
103
110
|
}
|
|
104
111
|
|
|
105
112
|
function handleMatchResult(matchResult, matches, nonMatches) {
|
|
@@ -147,26 +154,29 @@ export default ({detection: detectionOptions, search: searchOptions, maxMatches
|
|
|
147
154
|
// we could have here also returnRecords/returnMatchRecords/returnNonMatchRecord options that could be turned false for not to return actual record data
|
|
148
155
|
|
|
149
156
|
// matchStatus.status: boolean, true if matcher retrieved and handled all found candidate records, false if it did not
|
|
150
|
-
// matchStatus.stopReason: string ('maxMatches','maxCandidates','maxedQueries',empty string/undefined), reason for stopping retrieving or handling the candidate records
|
|
157
|
+
// matchStatus.stopReason: string ('maxMatches','maxCandidates','maxedQueries','conversionFailures', empty string/undefined), reason for stopping retrieving or handling the candidate records
|
|
151
158
|
// - only one stopReason is returned (if there would be several possible stopReasons, stopReason is picked in the above order)
|
|
152
159
|
// - 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
|
|
153
160
|
|
|
154
|
-
function returnResult({matches, state, stopReason, nonMatches, duplicateCount, candidateCount, nonMatchCount}) {
|
|
155
|
-
|
|
156
|
-
|
|
161
|
+
function returnResult({matches, state, stopReason, nonMatches, duplicateCount, candidateCount, nonMatchCount, conversionFailures}) {
|
|
162
|
+
const conversionFailureCount = conversionFailures.length;
|
|
163
|
+
checkCounts({matches, nonMatches, candidateCount, duplicateCount, nonMatchCount, conversionFailureCount});
|
|
164
|
+
const matchStatus = getMatchState(state, stopReason, conversionFailureCount);
|
|
157
165
|
// add nonMatches to result only if returnNonMatches is 'true', otherwise nonMatches have not been gathered
|
|
158
|
-
const
|
|
166
|
+
const matchesResult = returnNonMatches ? {matches, matchStatus, nonMatches} : {matches, matchStatus};
|
|
167
|
+
const result = returnFailures ? {...matchesResult, conversionFailures} : matchesResult;
|
|
168
|
+
debugData(`ReturnFailures ${returnFailures}`);
|
|
159
169
|
debugData(`${JSON.stringify(result)}`);
|
|
160
170
|
return result;
|
|
161
171
|
|
|
162
172
|
// note that in cases where the matching has been stopped because of maxMatches checkCounts won't (in most cases) match
|
|
163
173
|
|
|
164
|
-
function checkCounts({matches, nonMatches, candidateCount, duplicateCount, nonMatchCount}) {
|
|
174
|
+
function checkCounts({matches, nonMatches, candidateCount, duplicateCount, nonMatchCount, conversionFailureCount}) {
|
|
165
175
|
const matchCount = matches.length;
|
|
166
176
|
debugData(`Return nonMatches: ${returnNonMatches}`);
|
|
167
177
|
const chosenNonMatchCount = returnNonMatches ? nonMatches.length : nonMatchCount;
|
|
168
|
-
const totalHandled = matchCount +
|
|
169
|
-
debug(`candidateCount: ${candidateCount}, matches: ${matchCount}, nonMatches: ${chosenNonMatchCount}, duplicateCount: ${duplicateCount}`);
|
|
178
|
+
const totalHandled = matchCount + chosenNonMatchCount + duplicateCount;
|
|
179
|
+
debug(`candidateCount: ${candidateCount}, matches: ${matchCount}, nonMatches: ${chosenNonMatchCount}, duplicateCount: ${duplicateCount}, conversionFailureCount: ${conversionFailureCount}`);
|
|
170
180
|
debug(`We got result for ${totalHandled} / ${candidateCount} retrieved candidates`);
|
|
171
181
|
if (totalHandled !== candidateCount) {
|
|
172
182
|
debug(`WARNING: Missing results for ${candidateCount - totalHandled} candidates`);
|
|
@@ -175,8 +185,9 @@ export default ({detection: detectionOptions, search: searchOptions, maxMatches
|
|
|
175
185
|
return;
|
|
176
186
|
}
|
|
177
187
|
|
|
178
|
-
function getMatchState(state, stopReason) {
|
|
188
|
+
function getMatchState(state, stopReason, conversionFailuresCount) {
|
|
179
189
|
debugData(`${JSON.stringify(state)}`);
|
|
190
|
+
debug(`We had ${conversionFailuresCount} retrieved candidates that could not be converted.`);
|
|
180
191
|
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}`);
|
|
181
192
|
|
|
182
193
|
debugData(`StopReason: <${stopReason}>`);
|
|
@@ -185,10 +196,15 @@ export default ({detection: detectionOptions, search: searchOptions, maxMatches
|
|
|
185
196
|
const nonRetrieved = searchesLeft ? state.totalRecords - state.queryCandidateCounter : 0;
|
|
186
197
|
debugData(`nonRetrieved: ${nonRetrieved}`);
|
|
187
198
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
199
|
+
// matchStatus.stopReason: string ('maxMatches','maxCandidates','maxedQueries','conversionFailures', empty string/undefined), reason for stopping retrieving or handling the candidate records
|
|
200
|
+
// 'maxMatches' and 'maxCandidates' are in stopReason, 'maxedQueries' and 'conversionFailures' are created here
|
|
201
|
+
|
|
202
|
+
if (state.queriesLeft > 0 || nonRetrieved > 0 || state.maxedQueries.length > 0 || conversionFailureCount > 0) {
|
|
203
|
+
const maxedQueriesStopReason = state.maxedQueries.length > 0 ? 'maxedQueries' : undefined;
|
|
204
|
+
const conversionFailuresStopReason = conversionFailureCount > 0 ? 'conversionFailures' : undefined;
|
|
205
|
+
const newStopReason = stopReason === '' || stopReason === undefined ? maxedQueriesStopReason || conversionFailuresStopReason : stopReason;
|
|
191
206
|
debugData(`MaxedQueriesStopReason: <${maxedQueriesStopReason}>`);
|
|
207
|
+
debugData(`ConversionFailureStopReason <${conversionFailuresStopReason}>`);
|
|
192
208
|
debugData(`NewStopReason: <${newStopReason}>`);
|
|
193
209
|
debug(`Match status: false`);
|
|
194
210
|
return {status: false, stopReason: newStopReason};
|
|
@@ -268,6 +284,7 @@ export default ({detection: detectionOptions, search: searchOptions, maxMatches
|
|
|
268
284
|
function handleRecordMatch(isMatch, newMatch) {
|
|
269
285
|
const newRecordMatches = isMatch ? recordMatches.concat(newMatch) : recordMatches;
|
|
270
286
|
const newRecordNonMatches = isMatch ? recordNonMatches : recordNonMatches.concat(newMatch);
|
|
287
|
+
const newRecordNonMatchCount = isMatch ? recordNonMatchCount : recordNonMatchCount + 1;
|
|
271
288
|
|
|
272
289
|
debugData(`- Total matches after this detection: ${matches.concat(newRecordMatches).length} (max: ${maxMatches})`);
|
|
273
290
|
|
|
@@ -275,13 +292,14 @@ export default ({detection: detectionOptions, search: searchOptions, maxMatches
|
|
|
275
292
|
if (returnNonMatches) {
|
|
276
293
|
debugData(`- Total nonMatches after this detection: ${nonMatches.concat(newRecordNonMatches).length}`);
|
|
277
294
|
}
|
|
295
|
+
debugData(`- Total nonMatchCount after this detection: ${recordNonMatchCount}`);
|
|
278
296
|
|
|
279
297
|
if (maxMatchesFound({matches: matches.concat(newRecordMatches), maxMatches})) {
|
|
280
298
|
debug(`MaxMatches (${maxMatches}) reached, handled candidates in record set: ${newRecordCount} non-handled candidates in record set ${recordSetSize - newRecordCount}`);
|
|
281
|
-
return {matches: newRecordMatches, nonMatches: returnNonMatches ? newRecordNonMatches : [], duplicateCount: recordDuplicateCount, nonMatchCount:
|
|
299
|
+
return {matches: newRecordMatches, nonMatches: returnNonMatches ? newRecordNonMatches : [], duplicateCount: recordDuplicateCount, nonMatchCount: newRecordNonMatchCount};
|
|
282
300
|
}
|
|
283
301
|
|
|
284
|
-
return iterateRecords({records: records.slice(1), recordSetSize, maxMatches, matches, recordMatches: newRecordMatches, recordCount: newRecordCount, recordNonMatches: returnNonMatches ? newRecordNonMatches : [], duplicateCount: recordDuplicateCount, recordNonMatchCount});
|
|
302
|
+
return iterateRecords({records: records.slice(1), recordSetSize, maxMatches, matches, recordMatches: newRecordMatches, recordCount: newRecordCount, recordNonMatches: returnNonMatches ? newRecordNonMatches : [], duplicateCount: recordDuplicateCount, recordNonMatchCount: newRecordNonMatchCount});
|
|
285
303
|
}
|
|
286
304
|
|
|
287
305
|
function candidateNotInMatches(matches, candidate) {
|
package/src/index.spec.js
CHANGED
|
@@ -46,7 +46,7 @@ describe('INDEX', () => {
|
|
|
46
46
|
}
|
|
47
47
|
});
|
|
48
48
|
|
|
49
|
-
async function callback({getFixture, options, enabled = true, expectedMatchStatus, expectedStopReason}) {
|
|
49
|
+
async function callback({getFixture, options, enabled = true, expectedMatchStatus, expectedStopReason, expectedFailures}) {
|
|
50
50
|
|
|
51
51
|
if (!enabled) {
|
|
52
52
|
debug(`Disabled test!`);
|
|
@@ -59,8 +59,11 @@ describe('INDEX', () => {
|
|
|
59
59
|
|
|
60
60
|
|
|
61
61
|
const match = createMatchInterface(formatOptions());
|
|
62
|
-
const {matches, matchStatus, nonMatches} = await match(record);
|
|
63
|
-
debugData(
|
|
62
|
+
const {matches, matchStatus, nonMatches, conversionFailures} = await match(record);
|
|
63
|
+
debugData(`Matches: ${matches.length}, Status: ${matchStatus.status}/${matchStatus.stopReason}, NonMatches: ${nonMatches ? nonMatches.length : 'not returned'}, ConversionFailures: ${conversionFailures ? conversionFailures.length : 'not returned'}`);
|
|
64
|
+
|
|
65
|
+
expect(matchStatus.status).to.eql(expectedMatchStatus);
|
|
66
|
+
expect(matchStatus.stopReason).to.eql(expectedStopReason);
|
|
64
67
|
|
|
65
68
|
const formattedMatchResult = formatRecordResults(matches);
|
|
66
69
|
expect(formattedMatchResult).to.eql(expectedMatches);
|
|
@@ -68,9 +71,10 @@ describe('INDEX', () => {
|
|
|
68
71
|
const formattedNonMatchResult = formatRecordResults(nonMatches);
|
|
69
72
|
expect(formattedNonMatchResult).to.eql(expectedNonMatches);
|
|
70
73
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
+
// eslint-disable-next-line functional/no-conditional-statement
|
|
75
|
+
if (expectedFailures) {
|
|
76
|
+
expect(conversionFailures).to.eql(expectedFailures);
|
|
77
|
+
}
|
|
74
78
|
|
|
75
79
|
function formatOptions() {
|
|
76
80
|
const contextFeatures = matchDetection.features[options.detection.strategy.type];
|